ActivePerl + OpenSSL + Zlib + Libssh2 + Visual Studio 2008
SSH2是一套安全通讯协议框架(早期的SSH1由于存在安全漏洞,现在已经不用了),基于SSH2协议的产品目前主要有openssh(http://www.openssh.org/ ),putty(http://www.putty.org/ ),SSH Secure Shell Client(从http://www.moodisk.com/zh_CN/index.html?src=download.php 可以下载)等,这些都是开源的,但是这些代码非常难懂而且复杂,一个个函数深层次的调用很快就让人在C语言代码的海洋中迷失了方向,妄图通过从这些开源软件中抽取程序代码段来“组装”自己的应用程序是非一般人所能实现的。不过还好网路上出现了一些开源的SSH2开发库,利用
这些开发库开发自己的SSH2程序却要简单得多,由于这些开发库都是开源的,往往是针对linux平台的,而且一般只提供了源代码。在windows上利用这些库还必须要完成:编译有关依赖库–>编译ssh2库–>集成到开发环境(如Visual Studio)中–>熟悉SSH2库函数用法–>开始编写自己的程序。由于开发基于ssh2协议的例子网上很少,中文资料就更少。本人在完成这么一个开发环境就断断续续耗费了我一周的时间,现在终于可以开始编写的基于SSH2协议的程序了。我不敢独享,整理出一篇博文,和各位IT同仁分享。
这些开发库开发自己的SSH2程序却要简单得多,由于这些开发库都是开源的,往往是针对linux平台的,而且一般只提供了源代码。在windows上利用这些库还必须要完成:编译有关依赖库–>编译ssh2库–>集成到开发环境(如Visual Studio)中–>熟悉SSH2库函数用法–>开始编写自己的程序。由于开发基于ssh2协议的例子网上很少,中文资料就更少。本人在完成这么一个开发环境就断断续续耗费了我一周的时间,现在终于可以开始编写的基于SSH2协议的程序了。我不敢独享,整理出一篇博文,和各位IT同仁分享。
本文的内容安排是:首先介绍如何编译各种依赖库,然后介绍如何把这些依赖库继集成到Visual Studio 2008中,接下来介绍基于SSH2协议的程序的一般框架,最后举一个实际的开发例子。附录A是我自己开发的一个实际例子,附录B列出了全部的 libssh2库函数。
一·准备一些工具
1、安装Visual Studio 2008开发环境(最好是英文版的,我的是VS2008版本是9.0.30729.1 SP, Windows SDK 6.1)。什么?你不会装!你去google上搜一下“Visual Studio 2008安装过程详解”或者点击参考文章:http://dev.yesky.com/msdn/329/7823829.shtml
2、安装最新的MSDN文档库(可选,不装也行)。蛮大的,从网上下载要一些时间,或者参考在线文档http://msdn.microsoft.com/en-us/library/default.aspx
3、安装解压缩工具winRAR。从http://cncspace.newhua.com/down/files/wrar380sc.exe 下载并安装。
4、安装汇编工具nasm。从http://www.nasm.us/pub/nasm/releasebuilds/2.06/win32/nasm-2.06-installer.exe 下载并安装。
5、安装脚本语言ActivePerl。从http://downloads.activestate.com/ActivePerl/Windows/5.10/ActivePerl-5.10.0.1005-MSWin32-x86-290470.msi 下载,然后安装(安装过程中选择默认选项即可)。
5、安装脚本语言ActivePerl。从http://downloads.activestate.com/ActivePerl/Windows/5.10/ActivePerl-5.10.0.1005-MSWin32-x86-290470.msi 下载,然后安装(安装过程中选择默认选项即可)。
二、编译各种依赖库
LibSSH2库依赖openssl和zlib两个库,所以我们必须先编译zlib和openssl两个库。
1、zlib库。网上提供了源码和目标DLL安装包,我们直接下载DLL安装比较快捷,从http://www.zlib.net/zlib123-dll.zip 下载,并解压到C:/zlib下(最终存在目录C:/zlib/include即表示正确),把C:/zlib/zlib1.dll拷贝到c:/windows/system32下。
2、OpenSSL库。OpenSSL库网上只有源代码,我们首先必须编译。从http://www.openssl.org/source/openssl-0.9.8k.tar.gz 下载源代码包,然后解压到目录C:/openssl-0.9.8k下(最终存在目录C:/openssl-0.9.8k/apps即表示正确)。进入 Visual Studio 2008的命令提示符(开始–>所有程序–>Microsoft Visual Studio 2008–>Visual Studio Tools–>Visual Studio 2008 Command Prompt),依次输入如下命令:
mkdir c:/openssl_lib
cd C:/openssl-0.9.8k
perl Configure VC-WIN32 –prefix=c:/openssl_lib
—输出如下的信息:……RC4_CHUNK is undefinedConfigured for VC-WIN32.
ms/do_masm
nmake -f ms/nt.mak
—好了,去喝杯咖啡吧,半个小时后应该编译完了。
nmake -f ms/nt.mak test
—如何库编译正确,你应该看到“passwd all tests”字样。
nmake -f ms/nt.mak install
—现在应该在c:/openssl_lib下安装了openssl库文件和头文件了。
—如果编译出错,那么也可以查看文件C:/openssl-0.9.8k/INSTALL.W32,里面列举了一些错误处理方法。
3、LibSSH2库。LibSSH2库网上只有源代码,我们首先必须编译。从http://nchc.dl.sourceforge.net/sourceforge/libssh2/libssh2-1.1.tar.gz 下载源代码包,然后解压到目录C:/libssh2-1.1下(存在目录C:/libssh2-1.1/include表示正确),创建目录“C: /libssh2/lib”和“C:/libssh2/include”,在Visual Studio 2008 IDE中打开C:/libssh2-1.1/win32/libssh2.dsw,编辑文件libssh2.h,把如下的第54行
# define LIBSSH2_API __declspec(dllexport)
替换成:
# define LIBSSH2_API// __declspec(dllexport)
在左边的Solution Explorer中右击libssh2_lib–>Properties–>Configuration Properties:
–>C/C++ –> General –>选择Additional Include Dirextories–>附加
;C:/openssl_lib/include;C:/zlib/include
–>Code Generation –> Runtime Library –>选择Multi-thread(/MT)
–>Librarian –> General:
Output File –> C:/libssh2/lib/libssh2.lib
Additional Dependencies –> libeay32.lib ssleay32.lib zdll.lib
Additional Library Directories –> 附加路径C:/openssl_lib/lib;C:/zlib/lib
最后点击“OK”确定。
现在可以开始编译了,右击libssh2_lib选择Build。如果编译成功那么在C:/libssh2/lib下存在文件 libssh2.lib了。接下来再把C:/libssh2-1.1/include下的全部文件拷贝到C:/libssh2/include下,把文件 C:/libssh2-1.1/win32/libssh2_config.h也拷贝到C:/libssh2/include下。好了,那么我们最终编译的库和头文件布局如下:
C:/libssh2/include下有文件:libssh2.h,libssh2_config.h,libssh2_publickey.h,libssh2_sftp.h;
C:/libssh2/lib下有文件libssh2.lib
— 如果编译失败,你可以从这里 直接下载我编译好的LibSSH2库。
三·一个实例
windwos平台上基于SSH2协议的程序框架是:
初始化Winsock动态库(WSAStartup())
|
V
创建标准的socket套接字并连接(socket(),connect())
|
V
创建和启用一个SSH2会话(libssh2_session_init(),libssh2_session_startup())
|
V
获取指纹数据(libssh2_hostkey_hash())
|
V
认证(libssh2_userauth_password()或libssh2_userauth_publickey_fromfile())
|
V
初始化sftp子系统(libssh2_sftp_init())
|
V
进行各种sftp操作命令(如创建目录、下载等,函数参见本文附录B)
|
V
关闭sftp子系统(libssh2_sftp_shutdown())
|
V
拆除ssh2会话(libssh2_session_disconnect())
|
V
释放ssh2会话结构(libssh2_session_free())
|
V
关闭标准socket套接字(closesocket())
注意:上面列出的函数的具体用法请参阅网站在线文档:http://www.libssh2.org/wiki/index.php/Documentation
下面用一个具体的实例进一步说明如何开发一个ssh2世纪功能,它实现一个简单的功能:在服务器上创建一个目录,列出另外一个目录下的文件。本实例主要展示SSH2协议的程序框架。
1、在Visual Studio 2008开发环境中新建一个工程: File–>New–>Project…–>展开Visual C++ –> 选择Win32 Console Application,Name处输入testsftp,Location处出入C:/projects,Solution Name处输入testftp,勾选Create directory for solution,最后点击OK进入下一步再点击Next。这一页只点选Console application,其他项都不要勾选或点选。最后点击Finish按钮完成新工程的创建。
2、在左边的工程浏览器窗口中展开Source Files,双击“testsftp.cpp”文件打开,里面只有寥寥数行:
// newsftp.cpp : Defines the entry point for the console application.
//
//
#include “stdafx.h”
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
{
return 0;
}
3、在屏幕左边的Solution Explorer中右击工程testsftp–>Properties–>Configuration Properties:
–>C/C++ –> General –>选择Additional Include Dirextories–>加C:/libssh2/include
–> Code Generation –>Runtime Library –>选择Multi-thread(/MT)
–>Linker
–> General –>选择Additional Library Directories –> 加C:/libssh2/lib
–> Input –>选择Additional Dependencies –> 加libssh2.lib
–> Command Line –>在Additional options中加 Ws2_32.lib
最后点击“OK”确定。
4、开始编译。按F7或点击菜单Build–>Build Solution。
附录A:我的testsftp.cpp文件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
// testsftp.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <libssh2_config.h> #include <io.h> #include <libssh2.h> #include <libssh2_sftp.h> #ifdef HAVE_WINSOCK2_H # include <winsock2.h> #endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif #ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> #endif # ifdef HAVE_UNISTD_H #include <unistd.h> #endif #ifdef HAVE_ARPA_INET_H # include <arpa/inet.h> #endif #ifdef HAVE_SYS_TIME_H # include <sys/time.h> #endif #include <sys/types.h> #include <fcntl.h> #include <errno.h> #include <stdio.h> #include <ctype.h> int _tmain(int argc, char* argv[]) { int sock, i, auth_pw = 1; struct sockaddr_in sin; const char *fingerprint; LIBSSH2_SESSION *session; int rc; LIBSSH2_SFTP *sftp_session; LIBSSH2_SFTP_HANDLE *sftp_handle; #ifdef WIN32 WSADATA wsadata; WSAStartup(MAKEWORD(2,0), &wsadata); #endif /* * The application code is responsible for creating the socket * and establishing the connection */ if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET){ fprintf(stderr,"failed to create a socket!/n"); return -1; } sin.sin_family = AF_INET; sin.sin_port = htons(22); if((sin.sin_addr.s_addr = inet_addr("192.168.104.105"))==INADDR_NONE){ fprintf(stderr,"The address is invalid!/n"); return -1; } if(connect(sock, (struct sockaddr*)(&sin),sizeof(struct sockaddr_in))!= 0){ fprintf(stderr, "failed to connect!/n"); return -1; } /* Create a session instance*/ if(!(session = libssh2_session_init())){ fprintf(stderr,"Init SSH session failed!/n"); goto CLOSESOCKET; } /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ if((rc = libssh2_session_startup(session, sock))){ fprintf(stderr, "Failure establishing SSH session: %d/n", rc); libssh2_session_free(session); goto CLOSESOCKET; } /* At this point we havn't yet authenticated. The first thing to do * is check the hostkey's fingerprint against our known hosts Your app * may have it hard coded, may go to a file, may present it to the * user, that's your call */ if((fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5))){ printf("Fingerprint: "); for(i = 0; i < 16; i++) { printf("%02X ", (unsigned char)fingerprint[i]); } printf("/n"); } if (auth_pw) { /* We could authenticate via password */ if ((libssh2_userauth_password(session, "sftpuser", "abc123"))) { printf("Authentication by password failed./n"); goto SHUTDOWN; } } else { /* Or by public key */ if (libssh2_userauth_publickey_fromfile(session, "sftpuser","/home/username/.ssh/id_rsa.pub","/home/username/.ssh/id_rsa","abc123")) { printf("/tAuthentication by public key failed/n"); goto SHUTDOWN; } } fprintf(stderr, "libssh2_sftp_init()!/n"); if(!(sftp_session = libssh2_sftp_init(session))){ fprintf(stderr, "Unable to init SFTP session/n"); goto SHUTDOWN; } /* Since we have not set non-blocking, tell libssh2 we are blocking */ libssh2_session_set_blocking(session, 1); fprintf(stderr, "libssh2_sftp_opendir()!/n"); //建目录 if(libssh2_sftp_mkdir(sftp_session, "sftpdir/cba", LIBSSH2_SFTP_S_IRWXU| LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IXGRP| LIBSSH2_SFTP_S_IROTH|LIBSSH2_SFTP_S_IXOTH)==-1) fprintf(stderr,"Create dir failed!/n"); //浏览一个目录中的文件 if(!(sftp_handle = libssh2_sftp_opendir(sftp_session, "sftpdir"))){ fprintf(stderr, "Unable to open dir with SFTP/n"); goto SHUTDOWN; } fprintf(stderr, "libssh2_sftp_opendir() is done, now receive listing!/n"); do { char mem[512]; char longentry[512]; LIBSSH2_SFTP_ATTRIBUTES attrs; /* loop until we fail */ rc = libssh2_sftp_readdir_ex(sftp_handle, mem, sizeof(mem), longentry, sizeof(longentry), &attrs); if(rc > 0) { /* rc is the length of the file name in the mem buffer */ if (longentry[0] != '/0') { printf("%s/n", longentry); } else { if(attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { /* this should check what permissions it is and print the output accordingly */ printf("--fix----- "); } else { printf("---------- "); } if(attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) { printf("%4ld %4ld ", attrs.uid, attrs.gid); } else { printf(" - - "); } if(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) { /* attrs.filesize is an uint64_t according to the docs but there is no really good and portable 64bit type for C before C99, and correspondingly there was no good printf() option for it... */ printf("%8lld ", attrs.filesize); } printf("%s/n", mem); } } else break; } while (1); libssh2_sftp_closedir(sftp_handle); libssh2_sftp_shutdown(sftp_session); SHUTDOWN: libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); libssh2_session_free(session); CLOSESOCKET: #ifdef WIN32 Sleep(1000); closesocket(sock); #else sleep(1); close(sock); #endif printf("all done/n"); return 0; } |
附录B:libssh2.lib中函数列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
1. Session API: libssh2_session_init() libssh2_session_abstract() libssh2_session_callback_set() libssh2_banner_set() libssh2_session_startup() libssh2_session_disconnect() libssh2_session_free() libssh2_hostkey_hash() libssh2_session_method_pref() libssh2_session_methods() libssh2_session_last_error() libssh2_session_flag() 2. Userauth API: libssh2_userauth_list() libssh2_userauth_authenticated() libssh2_userauth_password() libssh2_userauth_publickey_fromfile() libssh2_userauth_hostbased_fromfile() libssh2_userauth_keyboard_interactive() 3. Channel API: Channel Creation and Setup libssh2_channel_open_session() libssh2_channel_direct_tcpip() libssh2_channel_forward_listen() libssh2_channel_forward_cancel() libssh2_channel_forward_accept() libssh2_channel_setenv() libssh2_channel_request_pty() libssh2_channel_process_startup() libssh2_channel_x11_req() libssh2_scp_recv() libssh2_scp_send() Channel Shutdown and Destruction libssh2_channel_send_eof() libssh2_channel_eof() libssh2_channel_close() libssh2_channel_wait_closed() libssh2_channel_get_exit_status() libssh2_channel_free() Input/Output libssh2_channel_set_blocking() libssh2_channel_read() libssh2_channel_write() libssh2_channel_handle_extended_data() libssh2_channel_flush() libssh2_poll_channel_read() libssh2_poll() Windowing libssh2_channel_window_read() libssh2_channel_receive_window_adjust() libssh2_channel_window_write() 4. SFTP Subsystem: Protocol startup and shutdown libssh2_sftp_init() libssh2_sftp_shutdown() libssh2_sftp_last_error() File/Directory Access libssh2_sftp_open() libssh2_sftp_opendir() libssh2_sftp_read() libssh2_sftp_readdir() libssh2_sftp_write() libssh2_sftp_close() libssh2_sftp_closedir() libssh2_sftp_seek() libssh2_sftp_rewind() libssh2_sftp_tell() libssh2_sftp_fstat() libssh2_sftp_fsetstat() File/Directory Manipulation libssh2_sftp_rename() libssh2_sftp_unlink() libssh2_sftp_mkdir() libssh2_sftp_rmdir() libssh2_sftp_stat() libssh2_sftp_lstat() libssh2_sftp_setstat() libssh2_sftp_symlink() libssh2_sftp_readlink() libssh2_sftp_realpath() 5. Publickey Subsystem: libssh2_publickey_init() libssh2_publickey_shutdown() libssh2_publickey_add() libssh2_publickey_remove() libssh2_publickey_list_fetch() libssh2_publickey_list_free() |
参考文献:
1. Using libcurl with SSH support in Visual Studio 2008.pdf(点击这里 下载)
发表评论
要发表评论,您必须先登录。