C + + Learning: Use LIBSSH2 to implement an interactive shell Ssh2 class, Linux and Windows generic.

Source: Internet
Author: User
Tags sin usleep

The use of SSH2 to implement shell automation testing, the actual work encountered very much. Various languages have corresponding libraries to use. For example, the C + + language can use Libssh2;python to use Paramkio libraries and so on. But these online help, are not very full, are more superficial. Shell automation, the basic three requirements, one is suitable for multiple types of operating system, the second is to be able to support the interactive shell, such as the use of sudo to execute, you need to enter the password, third, read the data should be non-blocking.

Paramkio does not seem to support interactivity (the shell command does not need to input different parameters according to the output, in fact, this situation encountered very much), and Paramkio in Windows is also very bad, to achieve non-blocking, looking for a few days have not found a good way.

Libssh2 on the surface is not interactive, the official example is not for the interactive shell for example, to the user of the library to bring a lot of trouble, more difficult to get started. In fact, the LIBSSH2 library is an interactive shell support, and support a variety of OS, implementation is quite simple, but the process is too painful to grope. About the compilation of Libssh2, is also relatively simple, here is not listed. LIBSSH2 is a C language, sample writing is very cumbersome, so I use C + + encapsulation, learning more intuitive.

The actual libssh2 of two official samples Scp.c and SCP_ECHO.C have provided an interactive shell implementation, just SCP_ ECHO.C about SSH2 Login authentication method, not like SCP.C use SSH2 keyboard Interactive way, General SSH2 Server by default provides this authentication method. SCP_ECHO.C not easy to debug through.


First, let's take a look at the usage code that implements the Ssh2 class:

#include <iostream> #include "ssh2.h" int main (int argc, const char * argv[]) {    using namespace std;    using namespace fish;        Ssh2 ssh ("127.0.0.1");    Ssh. Connect ("Test", "xxxxxx");    channel* channel = ssh. CreateChannel ();    Channel->write ("cd/;p wd");    Cout<<channel->read () <<endl;    Channel->write ("ssh 127.0.0.1");    Cout<<channel->read (":") <<endl;    Channel->write ("xxxxxx");    Cout<<channel->read () <<endl;    Channel->write ("pwd");    Cout<<channel->read () <<endl;    Delete Channel;    return 0;}

Both read and write are non-blocking, which is very handy for practical use. The code is not explained in detail.

The implementation code for the Ssh2 class is as follows:

    Class Ssh2    {public    :        Ssh2 (const string &SRVIP, int srvport=22);        ~ssh2 (void);                BOOL Connect (const string &username, const string &userpwd);        BOOL Disconnect (void);                channel* CreateChannel (const string &ptytype = "Vanilla");        Public:        static void S_kbdcallback (const char*, int, const char*, int, int, const libssh2_userauth_kbdint_prompt*, LIB ssh2_userauth_kbdint_response*, void **a);        static string S_password;            Private:        string m_srvip;        int    m_srvport;        string M_username;        string M_password;        int    M_sock;        Libssh2_session *m_session;    };

Since the SSH2 server is authenticated through the keyboard interactively through a callback function, two static members are used, one is a member function, and the other is a member variable (of course, this is an ugly way to do this, and there is no good way to find it yet). The code here, does not take into account multithreading security, because the actual requirements are not very strict. Of course, to support multi-threaded under SSH2 login authentication, it is also easy to implement.

String Ssh2::s_password; void Ssh2::s_kbdcallback (const char *name, int name_len, const char *instruction, int instruct Ion_len, int num_prompts, const libssh2_userauth_kbdint_prompt *PR Ompts, Libssh2_userauth_kbdint_response *responses, void **abstrac        T) {(void) name;        (void) Name_len;        (void) instruction;        (void) Instruction_len;            if (num_prompts = = 1) {Responses[0].text = StrDup (S_password.c_str ());        Responses[0].length = (int) s_password.size ();        } (void) prompts;    (void) abstract; } ssh2::ssh2 (const string &AMP;SRVIP, int srvport): M_srvip (SRVIP), M_srvport (srvport) {M_sock =-        1;        M_session = NULL;    Libssh2_init (0);        } ssh2::~ssh2 (void) {Disconnect ();    Libssh2_exit (); } bool Ssh2::connect(const string &username, const string &userpwd)                {M_sock = socket (af_inet, sock_stream, 0);        Sockaddr_in sin;        sin.sin_family = af_inet;        Sin.sin_port = htons (22);        SIN.SIN_ADDR.S_ADDR = inet_addr (M_srvip.c_str ());            if (Connect (M_sock, (sockaddr*) (&sin), sizeof (SOCKADDR_IN))! = 0) {Disconnect ();        return false;        } m_session = Libssh2_session_init ();            if (Libssh2_session_handshake (M_session, M_sock)) {Disconnect ();        return false;        } int auth_pw = 0;        String fingerprint = Libssh2_hostkey_hash (m_session, LIBSSH2_HOSTKEY_HASH_SHA1);        String userauthlist = Libssh2_userauth_list (M_session, Username.c_str (), (int) username.size ());        if (Strstr (Userauthlist.c_str (), "password") = NULL) {AUTH_PW |= 1; } if (Strstr (Userauthlist.c_str (), "keyboard-interactive") = NULL)       {AUTH_PW |= 2;        } if (Strstr (Userauthlist.c_str (), "publickey") = NULL) {AUTH_PW |= 4; } if (AUTH_PW & 1) {/* We could authenticate via password */if (LIBSSH2                _userauth_password (M_session, Username.c_str (), Userpwd.c_str ())) {Disconnect ();            return false; }} else if (AUTH_PW & 2) {/* Or via keyboard-interactive */S_password =            Userpwd; if (Libssh2_userauth_keyboard_interactive (M_session, Username.c_str (), &s_kbdcallback)) {D                Isconnect ();            return false;            }} else {Disconnect ();        return false;    } return true;  } bool Ssh2::D isconnect (void) {if (m_session) {Libssh2_session_disconnect (m_session, "Bye Bye, THank you ");            Libssh2_session_free (m_session);        M_session = NULL;            if (m_sock! =-1) {#ifdef WIN32 closesocket (m_sock); #else close (m_sock); #endif        M_sock =-1;    } return true; } channel* Ssh2::createchannel (const string &ptytype) {if (NULL = = m_session) {R        Eturn NULL;        } Libssh2_channel *channel = NULL; /* Request a shell */if (!        Channel = Libssh2_channel_open_session (m_session))) {return NULL;         }/* Request a terminal with ' vanilla ' terminal emulation * see/etc/termcap for more options */if (Libssh2_channel_request_pty (channel, Ptytype.c_str ())) {Libssh2_channel_free (Channe            L);        return NULL;                        }/* Open a SHELL on that pty */if (Libssh2_channel_shell (channel)) { LibSsh2_channel_free (channel);        return NULL;        } Channel *ret = new Channel (channel);        Cout<<ret->read () <<endl;    return ret; }

Here are two function calls, one is libssh2_channel_request_pty specify the channel type, and if you use Xterm, you can use color display. The Libssh2_channel_shell is used to specify an interactive shell.


Here is the definition of the Channel class:

const int channel_read_timeout =;        Class Channel    {public    :        Channel (Libssh2_channel *channel);        ~channel (void);                String Read (const string &strend = "$", int timeout = channel_read_timeout);        BOOL   Write (const string &data);    Private:        Channel (const channel&);        Channel operator= (const channel&);    Private:        libssh2_channel *m_channel;    };

The implementation of the Channel class:

Channel::channel (Libssh2_channel*channel): M_channel (Channel) {} channel::~channel (void)            {if (M_channel) {libssh2_channel_free (M_channel);        M_channel = NULL;        }} string Channel::read (const string &strend, int timeout) {string data;        if (NULL = = M_channel) {return data;        } LIBSSH2_POLLFD *fds = new Libssh2_pollfd;        Fds->type = Libssh2_pollfd_channel;        Fds->fd.channel = M_channel; fds->events = Libssh2_pollfd_pollin |                Libssh2_pollfd_pollout;        if (timeout%) {timeout + = timeout% 50;            } while (timeout>0) {int rc = (Libssh2_poll (FDS, 1, 10));                if (RC < 1) {timeout-= 50;                Usleep (50*1000);            Continue } if (Fds->revents & Libssh2_pollfd_poLLIN) {char buffer[64*1024] = "";                size_t n = libssh2_channel_read (m_channel, buffer, sizeof (buffer));                if (n = = Libssh2_error_eagain) {//fprintf (stderr, "would read again\n");                } else if (n <= 0) {return data;                    } else {data + = string (buffer);                    if ("" = = Strend) {return data;                    } size_t pos = Data.rfind (strend);                        if (pos! = String::npos && data.substr (POS, strend.size ()) = = Strend) {                    return data;            }}} timeout-= 50;        Usleep (50*1000);        } cout<< "read data timeout" <<endl;    return data; } bOol channel::write (const string &data) {if (NULL = = M_channel) {return false;        } string send_data = Data + "\ n";        Return libssh2_channel_write_ex (m_channel, 0, Send_data.c_str (), send_data.size ()) = = Data.size ();    return true; }

The main concern here is the Libssh2_poll function, which is mainly to detect if the channel has data to read. The read function simply encapsulates, increasing the desired result and having a timeout. SSH2 is to support multiple channel read and write, can play a number of channel at the same time, perform different operations separately, for the SSH2 has a login limit, it is quite useful. Currently the SSH2 client is basically available. You might as well try it. For a simple example of LIBSSH2 library, it is estimated that there is no online, of course, about LIBSSH2 installation, online is a lot.

The header file used contains the following:

#ifdef win32#include <windows.h> #include <winsock2.h> #else # include <sys/socket.h> #include < netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #endif # include <sys/types.h> #include <fcntl.h> #include <errno.h> #include <stdio.h> #include <ctype.h> #include <libssh2.h># Include <libssh2_sftp.h>


C + + is more complex and wants to use LIBSSH2 convenience in Python to extend the Python language. Of course it's a little more complicated to implement. But the complexity is also very low, only need to use boost::p Ython class, the extension of Python is ok. Under Windows and Linux, it's not complicated. Follow up with time to tidy up.






C + + Learning: Use LIBSSH2 to implement an interactive shell Ssh2 class, Linux and Windows generic.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.