Browse Source

示例代码。

sat23 3 years ago
parent
commit
2b5eff37d2

+ 238 - 1
example_ssh2/example_ssh2/WinSsh2Proc.cpp

@@ -138,7 +138,7 @@ bool CWinSsh2Proc::ssh2_execute_command(std::string cmd, std::string &result, DW
 		printf("Error\n");
 		goto shutdown;
 	}
-#if 0
+#if 1
 	// 请求一个模拟终端;
 	if( libssh2_channel_request_pty(pChannel,"VT100") != LIBSSH2_ERROR_NONE ) {
 		printf("ssh请求伪终端失败,退出。\n");
@@ -205,6 +205,7 @@ void CWinSsh2Proc::ssh2_read_channel(LIBSSH2_CHANNEL* pChannel, std::string &buf
 	char buf[1024] = "";
 	buffer.clear();
 	do {
+        // LIBSSH2_ERROR_EAGAIN = -37;表示读取完所有字节;
 		rc = libssh2_channel_read(pChannel, buf, sizeof(buf) - 1);
 		if(rc > 0) {
 			buf[rc] = 0;
@@ -340,4 +341,240 @@ shutdown:
 	closesocket(sock);//INVALID_SOCKET
 	
 	return bDone;
+}
+
+std::string CWindSSH2Proc::s_password;
+void CWindSSH2Proc::KbdCallback(const char* name, int name_len, const char* instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses, void **abstract)
+{
+    if ( num_prompts == 1 )
+    {
+        responses[0].text = strdup(s_password.c_str());
+        responses[0].length = (int)s_password.size();
+    }
+    prompts;
+    abstract;
+}
+
+CWindSSH2Proc::CWindSSH2Proc(const std::string &ip, int port/* =22 */)
+{
+    m_sock = INVALID_SOCKET;
+    m_session = NULL;
+    libssh2_init(0);
+}
+
+CWindSSH2Proc::~CWindSSH2Proc()
+{
+    Disconnect();
+    libssh2_exit();
+}
+
+bool CWindSSH2Proc::Connect(const std::string &userName, const std::string &password)
+{
+    m_sock = socket(AF_INET, SOCK_STREAM, 0);
+    sockaddr_in sin;
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(m_hostPort);
+    sin.sin_addr.s_addr = inet_addr(m_hostIP.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;
+    std::string fingerprint = libssh2_hostkey_hash(m_session, LIBSSH2_HOSTKEY_HASH_SHA1);
+    std::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(), password.c_str()) )
+        {
+            Disconnect();
+            return false;
+        }
+    }
+    else if ( auth_pw & 2 )
+    {
+        s_password = password;
+        if (libssh2_userauth_keyboard_interactive(m_session, userName.c_str(), &KbdCallback))
+        {
+            Disconnect();
+            return false;
+        }
+    }
+    else
+    {
+        Disconnect();
+        return false;
+    }
+
+    return true;
+}
+
+bool CWindSSH2Proc::Disconnect()
+{
+    if ( m_session )
+    {
+        libssh2_session_disconnect(m_session, "Bye Bye, Thank you");
+        libssh2_session_free(m_session);
+        m_session = NULL;
+    }
+
+    if ( m_sock != INVALID_SOCKET )
+    {
+#ifdef WIN32
+    closesocket(m_sock);
+#else
+        close(m_sock);
+#endif
+        m_sock = INVALID_SOCKET;
+    }
+
+    return true;
+}
+
+Channel* CWindSSH2Proc::CreateChannel(const std::string &ptyType /* = "vanilla" */)
+{
+    if ( NULL == m_session )
+    {
+        return 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
+    if ( libssh2_channel_request_pty(channel, ptyType.c_str()) )
+    {
+        libssh2_channel_free(channel);
+        return NULL;
+    }
+
+    // Open a shell on that pty;
+    if ( libssh2_channel_shell(channel) )
+    {
+        libssh2_channel_free(channel);
+        return NULL;
+    }
+
+    Channel *ret = new Channel(channel);
+
+    return ret;
+}
+
+Channel::Channel(LIBSSH2_CHANNEL *channel):m_channel(channel)
+{
+
+}
+
+Channel::~Channel()
+{
+    if (m_channel)
+    {
+        libssh2_channel_free(m_channel);
+        m_channel = NULL;
+    }
+}
+
+std::string Channel::Read( const std::string &strend /* = "$" */, int timeout /* = CHANNEL_READ_TIMEOUT */ )
+{
+    std::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%50 )
+    {
+        timeout += timeout%50;
+    }
+
+    while ( timeout > 0 )
+    {
+        int rc = libssh2_poll(fds, 1, 10);
+        if ( rc < 1 )
+        {
+            timeout -= 50;
+            Sleep(50*1000);
+            continue;
+        }
+
+        if ( fds->revents & LIBSSH2_POLLFD_POLLIN )
+        {
+            char szBuffer[64*1024] = {0};
+            size_t n = libssh2_channel_read(m_channel, szBuffer, sizeof(szBuffer));
+            if ( n == LIBSSH2_ERROR_EAGAIN )
+            {
+                // will read again;
+            }
+            else if ( n <= 0 )
+            {
+                return data;
+            }
+            else
+            {
+                data += std::string(szBuffer);
+                if ( strend.size() == 0 )
+                {
+                    return data;
+                }
+
+                size_t pos = data.rfind(strend);
+                if ( pos != std::string::npos && data.substr(pos, strend.size()).compare(strend) == 0 )
+                {
+                    return data;
+                }
+            }
+        }
+        
+        timeout -= 50;
+        Sleep(50*1000);
+    }
+
+    return data;
+}
+
+bool Channel::Write(const std::string &data)
+{
+    if ( NULL == m_channel )
+    {
+        return false;
+    }
+
+    std::string send_data = data;// + "\n";
+    send_data.append("\n");
+    return libssh2_channel_write_ex(m_channel, 0, send_data.c_str(), send_data.size());
 }

+ 180 - 22
example_ssh2/example_ssh2/WinSsh2Proc.h

@@ -11,35 +11,193 @@
 #pragma once
 
 #define SHUTDOWN(session, sock) \
-libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");\
-libssh2_session_free(session);\
-closesocket(sock);\
-libssh2_exit(); 
+    libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");\
+    libssh2_session_free(session);\
+    closesocket(sock);\
+    libssh2_exit(); 
 
 class CWinSsh2Proc
 {
 public:
-	CWinSsh2Proc(void);
-	~CWinSsh2Proc(void);
+    CWinSsh2Proc(void);
+    ~CWinSsh2Proc(void);
 
 private:
-	int				m_port;
-	std::string		m_host;
-	std::string		m_user;
-	std::string		m_pwd;
-	sockaddr_in		m_sin;
-
-	void ssh2_read_channel(LIBSSH2_CHANNEL* pChannel, std::string &buffer);
-	bool ssh2_init_session(LIBSSH2_SESSION** pSession, SOCKET &sock);
-	void ssh2_free(LIBSSH2_SESSION*	pSession, LIBSSH2_CHANNEL*	pChannel);	
+    int				m_port;
+    std::string		m_host;
+    std::string		m_user;
+    std::string		m_pwd;
+    sockaddr_in		m_sin;
+
+    void ssh2_read_channel(LIBSSH2_CHANNEL* pChannel, std::string &buffer);
+    bool ssh2_init_session(LIBSSH2_SESSION** pSession, SOCKET &sock);
+    void ssh2_free(LIBSSH2_SESSION*	pSession, LIBSSH2_CHANNEL*	pChannel);	
+public:
+    static bool InitSocket();
+    static int waitsocket(int socket_fd, LIBSSH2_SESSION *session);
+    bool ssh2_connect(std::string host, std::string user, std::string pwd, int port = 22);
+    void ssh2_disconnect();
+    bool ssh2_execute_command(std::string cmd, std::string &result, DWORD dwTimeout = 1000);
+    bool ssh2_sftp_download(std::string sftppath, std::string localpath);
+};
+
+// 读超时值;
+const int CHANNEL_READ_TIMEOUT = 3000;
+class Channel
+{
+public:
+    Channel(LIBSSH2_CHANNEL *channel);
+    ~Channel(void);
+
+    std::string Read( const std::string &strend = "$", int timeout = CHANNEL_READ_TIMEOUT );
+    bool   Write(const std::string &data);
+private:
+    Channel(const Channel&);
+    Channel operator=(const Channel&);
+private:
+    LIBSSH2_CHANNEL *m_channel;
+};
+
+class CWindSSH2Proc
+{
 public:
-	static bool InitSocket();
-	static int waitsocket(int socket_fd, LIBSSH2_SESSION *session);
-	bool ssh2_connect(std::string host, std::string user, std::string pwd, int port = 22);
-	void ssh2_disconnect();
-	bool ssh2_execute_command(std::string cmd, std::string &result, DWORD dwTimeout = 1000);
-	bool ssh2_sftp_download(std::string sftppath, std::string localpath);
+    CWindSSH2Proc(const std::string &ip, int port=22);
+    ~CWindSSH2Proc();
+
+    bool Connect(const std::string &userName, const std::string &password);
+    bool Disconnect();
+
+    Channel *CreateChannel(const std::string &ptyType = "vanilla");
+public:
+    static void KbdCallback(const char*, int, const char*, int, int, const LIBSSH2_USERAUTH_KBDINT_PROMPT *, LIBSSH2_USERAUTH_KBDINT_RESPONSE*, void **a);
+    static std::string s_password;
+
+private:
+    std::string     m_hostIP;
+    int             m_hostPort;
+    std::string     m_userName;
+    std::string     m_password;
+    SOCKET          m_sock;
+    LIBSSH2_SESSION *m_session;
 };
 
 
-#endif //__WIN_SSH2_20200506__
+namespace SSH2
+{
+    // 初始化SOKCE库;
+    bool InitSocket()
+    {
+        WSADATA wsadata;
+        int err = WSAStartup(MAKEWORD(2, 0), &wsadata);
+        if( err != 0 ) {
+            _ftprintf(stderr, _T("WSAStartup failed with error: %d\n"), err);
+            return false;
+        }
+
+        return true;
+    }
+
+    // 初始化LIBSSH2;
+    bool InitSSH2()
+    {
+        int rc = libssh2_init(0);
+        if ( rc != LIBSSH2_ERROR_NONE ) {
+            _ftprintf(stderr, _T("libssh2 initialization failed (%d)\n"), rc);
+            return false;
+        }
+
+        return true;
+    }
+
+    // 释放LIBSSH2;
+    void ExitSSH2()
+    {
+        libssh2_exit();
+    }
+
+    // 单Session、单Channel;
+    // 一个session拖多个channel,会不会有性能问题,拖几比较合适.
+    class CWindSSH2Proc
+    {
+    public:
+        CWindSSH2Proc();
+        ~CWindSSH2Proc();
+
+    public:
+        bool InitSession();
+        bool LoginSession();
+        bool OpenSession();
+        // create channel;
+        LIBSSH2_CHANNEL *CreateChannel();
+        /* 
+        一个channel对应一次读写操作,并发时就需要多个channel;
+        所以channel结构如下:        
+        typedef struct __CHANNEL__
+        {
+            LIBSSH2_CHANNEL *pChannel;      // Channel对象;
+            std::string strCommand;         // 要执行的命令;
+            std::string strResult;          // 执行结果;
+        };
+
+        如果扩展,那么一个Channel其实可以串行执行多组命令,扩展后如下
+        typedef struct __CHANNEL__
+        {
+            LIBSSH2_CHANNEL *pChannel;
+            int nCommandCount;              该Channel的所有命令总数
+            typedef struct __CMDPKG__
+            {
+                int nExecuteStatus;         // 命令执行状态; 0-未执行,1-执行中,2-执行完;
+                int nCommandId;             // 命令索引,自增;
+                std::string strCommand;     // 要执行的命令;
+                std::string strResult;      // 执行结果;
+            }CmdPkg, *pCmdPkg;
+            // 串行要执行的命令集;
+            std::vector<CmdPkg> vtCommandPackage;
+        };
+
+        再次扩展,那么可以多个Channel并发处理任务,需要为每个Channel定义ID、名称;
+        typedef struct __CHANNEL__
+        {
+            int ID;                         // 该Channel的ID;
+            std::string strName;            // 该Channel的名称,描述Channel专职某项功能;
+            LIBSSH2_CHANNEL *pChannel;      // 该Channel实例对象;
+            int nCommandCount;              // 该Channel的所有命令总数
+            typedef struct __CMDPKG__
+            {
+                int nExecuteStatus;         // 命令执行状态; 0-未执行,1-执行中,2-执行完;
+                int nCommandId;             // 命令索引,自增;
+                std::string strCommand;     // 要执行的命令;
+                std::string strResult;      // 执行结果;
+            }CmdPkg, *pCmdPkg;
+            // 串行要执行的命令集;
+            std::vector<CmdPkg> vtCommandPackage;
+        };
+
+        多个Channel不能在同一个目录,时间相差不远的情况下执行同一条.sh编译脚本,所以,命令细分下来,可以有以下几部分组成:
+        1、命令要执行的路径;
+        2、命令类型:sh脚本、文件、python脚本等为一类;bin命令为一类;
+        3、命令执行有无输出日志到指定文件
+
+        // 创建新的通道;
+        LIBSSH2_CHANNEL CreateChannel();
+        // 获取空闲的通道;
+        LIBSSH2_CHANNEL GetFreeChannel();
+        // 添加执行命令到指定通道的命令集尾部;
+        bool AppendCommand(LIBSSH2_CHANNEL *pChannel, const std::string cmd);
+        // 插入执行命令到指定通道的未执行命令集头部;
+        bool InsertCommand(LIBSSH2_CHANNEL *pChannel, const std::string cmd);
+        // 删除指定通道的某条命令(未执行的);
+        void DeleteCommand(LIBSSH2_CHANNEL *pChannel, const std::string cmd);
+        // 查询某条命令执行状态和结果;
+        bool QueryCommand(LIBSSH2_CHANNEL *pChannel, int &nStatus, std::string &strResult);
+        */
+    };
+
+    // 一个sftp多次使用;
+    class CWindSFTPProc
+    {
+
+    };
+};
+
+#endif //__WIN_SSH2_20200506__

+ 1 - 1
example_ssh2/example_ssh2/example_ssh2.cpp

@@ -50,7 +50,7 @@ int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 		CWinSsh2Proc winssh2;
 		if ( winssh2.InitSocket() )
 		{
-			if ( winssh2.ssh2_connect("10.201.251.254", "wjf", "wjf2019"))
+			if ( winssh2.ssh2_connect("10.126.69.29", "wangjianfeng", "wangjianfeng@2021"))
 			{
 				std::string data;
 				winssh2.ssh2_execute_command("ls", data);