上一篇FTP客户端讲到如果制作一个简单的FTP客户端,功能实现了,但是后面我们发现了问题,就是FTP是使用明文进行操作的。对于普通情况来说就无所谓了。但有时候要安全的一点的话,就应该使用FTP的安全版本。有SFTP和FTPs,两者都是FTP的安全版本,但是两者的实现原理差别还是很大的,具体自己搜索了解。
0.环境安装
环境使用我的这一篇文章安装好libssh2库。
http://www.cnblogs.com/wunaozai/p/4528394.html
使用一个带有SFTP功能的FTP服务器。注意有些FTP服务器是不带SFTP功能的。这里我使用这个FreeSSHd作为SFTP服务器。
http://www.freesshd.com/?ctt=download
关于freesshd配置说两句,Server status标签 点击确定SSH server is running。SSH标签,确定配置完成。Authentication标签下Password authentication为Allowed,Public key authentication为Disabled,这样做的原因是我接下来要做的程序只支持密码登录,减少不必要的干扰,如果有需要,可以自己设定。Tunneling标签,所有选项选中,如果没有选中,本地如果网络复杂的话,可能会有问题。SFTP标签,选择一个作为FTP的根目录。Users标签,增加一个用户。基本设置就这些了。
1.示例讲解
我们先从libssh2中的示例程序进行讲解,libssh2源代码中的example文件夹中有几个常见的示例程序,我们此次先讲解上传文件和下载文件这两个基础功能。
下面这个是sftp_write_nonblock.c的源代码,已被折叠。
1 /* 2 * Sample showing how to do SFTP non-blocking write transfers. 3 * 4 * The sample code has default values for host name, user name, password 5 * and path to copy, but you can specify them on the command line like: 6 * 7 * "sftp 192.168.0.1 user password sftp_write_nonblock.c /tmp/sftp_write_nonblock.c" 8 */ 9 10 #include "libssh2_config.h" 11 #include <libssh2.h> 12 #include <libssh2_sftp.h> 13 14 #ifdef HAVE_WINSOCK2_H 15 # include <winsock2.h> 16 #endif 17 #ifdef HAVE_SYS_SOCKET_H 18 # include <sys/socket.h> 19 #endif 20 #ifdef HAVE_NETINET_IN_H 21 # include <netinet/in.h> 22 #endif 23 #ifdef HAVE_SYS_SELECT_H 24 # include <sys/select.h> 25 #endif 26 # ifdef HAVE_UNISTD_H 27 #include <unistd.h> 28 #endif 29 #ifdef HAVE_ARPA_INET_H 30 # include <arpa/inet.h> 31 #endif 32 #ifdef HAVE_SYS_TIME_H 33 # include <sys/time.h> 34 #endif 35 36 #include <sys/types.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <ctype.h> 41 #include <time.h> 42 43 static int waitsocket(int socket_fd, LIBSSH2_SESSION *session) 44 { 45 struct timeval timeout; 46 int rc; 47 fd_set fd; 48 fd_set *writefd = NULL; 49 fd_set *readfd = NULL; 50 int dir; 51 52 timeout.tv_sec = 10; 53 timeout.tv_usec = 0; 54 55 FD_ZERO(&fd); 56 57 FD_SET(socket_fd, &fd); 58 59 /* now make sure we wait in the correct direction */ 60 dir = libssh2_session_block_directions(session); 61 62 if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) 63 readfd = &fd; 64 65 if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) 66 writefd = &fd; 67 68 rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout); 69 70 return rc; 71 } 72 73 int main(int argc, char *argv[]) 74 { 75 unsigned long hostaddr; 76 int sock, i, auth_pw = 1; 77 struct sockaddr_in sin; 78 const char *fingerprint; 79 LIBSSH2_SESSION *session; 80 const char *username="username"; 81 const char *password="password"; 82 const char *loclfile="sftp_write_nonblock.c"; 83 const char *sftppath="/tmp/sftp_write_nonblock.c"; 84 int rc; 85 FILE *local; 86 LIBSSH2_SFTP *sftp_session; 87 LIBSSH2_SFTP_HANDLE *sftp_handle; 88 char mem[1024 * 100]; 89 size_t nread; 90 char *ptr; 91 time_t start; 92 long total = 0; 93 int duration; 94 95 #ifdef WIN32 96 WSADATA wsadata; 97 int err; 98 99 err = WSAStartup(MAKEWORD(2,0), &wsadata); 100 if (err != 0) { 101 fprintf(stderr, "WSAStartup failed with error: %d\n", err); 102 return 1; 103 } 104 #endif 105 106 if (argc > 1) { 107 hostaddr = inet_addr(argv[1]); 108 } else { 109 hostaddr = htonl(0x7F000001); 110 } 111 112 if (argc > 2) { 113 username = argv[2]; 114 } 115 if (argc > 3) { 116 password = argv[3]; 117 } 118 if (argc > 4) { 119 loclfile = argv[4]; 120 } 121 if (argc > 5) { 122 sftppath = argv[5]; 123 } 124 125 rc = libssh2_init (0); 126 if (rc != 0) { 127 fprintf (stderr, "libssh2 initialization failed (%d)\n", rc); 128 return 1; 129 } 130 131 local = fopen(loclfile, "rb"); 132 if (!local) { 133 fprintf(stderr, "Can't open local file %s\n", loclfile); 134 return -1; 135 } 136 137 /* 138 * The application code is responsible for creating the socket 139 * and establishing the connection 140 */ 141 sock = socket(AF_INET, SOCK_STREAM, 0); 142 143 sin.sin_family = AF_INET; 144 sin.sin_port = htons(22); 145 sin.sin_addr.s_addr = hostaddr; 146 if (connect(sock, (struct sockaddr*)(&sin), 147 sizeof(struct sockaddr_in)) != 0) { 148 fprintf(stderr, "failed to connect!\n"); 149 return -1; 150 } 151 152 /* Create a session instance */ 153 session = libssh2_session_init(); 154 if (!session) 155 return -1; 156 157 /* Since we have set non-blocking, tell libssh2 we are non-blocking */ 158 libssh2_session_set_blocking(session, 0); 159 160 /* ... start it up. This will trade welcome banners, exchange keys, 161 * and setup crypto, compression, and MAC layers 162 */ 163 while ((rc = libssh2_session_handshake(session, sock)) 164 == LIBSSH2_ERROR_EAGAIN); 165 if (rc) { 166 fprintf(stderr, "Failure establishing SSH session: %d\n", rc); 167 return -1; 168 } 169 170 /* At this point we havn't yet authenticated. The first thing to do is 171 * check the hostkey's fingerprint against our known hosts Your app may 172 * have it hard coded, may go to a file, may present it to the user, 173 * that's your call 174 */ 175 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 176 fprintf(stderr, "Fingerprint: "); 177 for(i = 0; i < 20; i++) { 178 fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); 179 } 180 fprintf(stderr, "\n"); 181 182 if (auth_pw) { 183 /* We could authenticate via password */ 184 while ((rc = libssh2_userauth_password(session, username, password)) == 185 LIBSSH2_ERROR_EAGAIN); 186 if (rc) { 187 fprintf(stderr, "Authentication by password failed.\n"); 188 goto shutdown; 189 } 190 } else { 191 /* Or by public key */ 192 while ((rc = libssh2_userauth_publickey_fromfile(session, username, 193 "/home/username/.ssh/id_rsa.pub", 194 "/home/username/.ssh/id_rsa", 195 password)) == 196 LIBSSH2_ERROR_EAGAIN); 197 if (rc) { 198 fprintf(stderr, "\tAuthentication by public key failed\n"); 199 goto shutdown; 200 } 201 } 202 203 fprintf(stderr, "libssh2_sftp_init()!\n"); 204 do { 205 sftp_session = libssh2_sftp_init(session); 206 207 if (!sftp_session && 208 (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { 209 fprintf(stderr, "Unable to init SFTP session\n"); 210 goto shutdown; 211 } 212 } while (!sftp_session); 213 214 fprintf(stderr, "libssh2_sftp_open()!\n"); 215 /* Request a file via SFTP */ 216 do { 217 sftp_handle = 218 libssh2_sftp_open(sftp_session, sftppath, 219 LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 220 LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| 221 LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); 222 223 if (!sftp_handle && 224 (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { 225 fprintf(stderr, "Unable to open file with SFTP\n"); 226 goto shutdown; 227 } 228 } while (!sftp_handle); 229 230 fprintf(stderr, "libssh2_sftp_open() is done, now send data!\n"); 231 232 start = time(NULL); 233 234 do { 235 nread = fread(mem, 1, sizeof(mem), local); 236 if (nread <= 0) { 237 /* end of file */ 238 break; 239 } 240 ptr = mem; 241 242 total += nread; 243 244 do { 245 /* write data in a loop until we block */ 246 while ((rc = libssh2_sftp_write(sftp_handle, ptr, nread)) == 247 LIBSSH2_ERROR_EAGAIN) { 248 waitsocket(sock, session); 249 } 250 if(rc < 0) 251 break; 252 ptr += rc; 253 nread -= rc; 254 255 } while (nread); 256 } while (rc > 0); 257 258 duration = (int)(time(NULL)-start); 259 260 fprintf(stderr, "%ld bytes in %d seconds makes %.1f bytes/sec\n", 261 total, duration, total/(double)duration); 262 263 264 fclose(local); 265 libssh2_sftp_close(sftp_handle); 266 libssh2_sftp_shutdown(sftp_session); 267 268 shutdown: 269 270 while (libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing") 271 == LIBSSH2_ERROR_EAGAIN); 272 libssh2_session_free(session); 273 274 #ifdef WIN32 275 closesocket(sock); 276 #else 277 close(sock); 278 #endif 279 fprintf(stderr, "all done\n"); 280 281 libssh2_exit(); 282 283 return 0; 284 }
下面这个是sftp_nonblock.c的源代码
1 /* 2 * Sample showing how to do SFTP non-blocking transfers. 3 * 4 * The sample code has default values for host name, user name, password 5 * and path to copy, but you can specify them on the command line like: 6 * 7 * "sftp_nonblock 192.168.0.1 user password /tmp/secrets" 8 */ 9 10 #include "libssh2_config.h" 11 #include <libssh2.h> 12 #include <libssh2_sftp.h> 13 14 #ifdef HAVE_WINSOCK2_H 15 # include <winsock2.h> 16 #endif 17 #ifdef HAVE_SYS_SOCKET_H 18 # include <sys/socket.h> 19 #endif 20 #ifdef HAVE_NETINET_IN_H 21 # include <netinet/in.h> 22 #endif 23 #ifdef HAVE_SYS_SELECT_H 24 # include <sys/select.h> 25 #endif 26 # ifdef HAVE_UNISTD_H 27 #include <unistd.h> 28 #endif 29 #ifdef HAVE_ARPA_INET_H 30 # include <arpa/inet.h> 31 #endif 32 #ifdef HAVE_SYS_TIME_H 33 # include <sys/time.h> 34 #endif 35 36 #include <sys/types.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <ctype.h> 41 42 /* diff in ms */ 43 static long tvdiff(struct timeval newer, struct timeval older) 44 { 45 return (newer.tv_sec-older.tv_sec)*1000+ 46 (newer.tv_usec-older.tv_usec)/1000; 47 } 48 49 static int waitsocket(int socket_fd, LIBSSH2_SESSION *session) 50 { 51 struct timeval timeout; 52 int rc; 53 fd_set fd; 54 fd_set *writefd = NULL; 55 fd_set *readfd = NULL; 56 int dir; 57 58 timeout.tv_sec = 10; 59 timeout.tv_usec = 0; 60 61 FD_ZERO(&fd); 62 63 FD_SET(socket_fd, &fd); 64 65 /* now make sure we wait in the correct direction */ 66 dir = libssh2_session_block_directions(session); 67 68 if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) 69 readfd = &fd; 70 71 if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) 72 writefd = &fd; 73 74 rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout); 75 76 return rc; 77 } 78 79 int main(int argc, char *argv[]) 80 { 81 unsigned long hostaddr; 82 int sock, i, auth_pw = 1; 83 struct sockaddr_in sin; 84 const char *fingerprint; 85 LIBSSH2_SESSION *session; 86 const char *username="username"; 87 const char *password="password"; 88 const char *sftppath="/tmp/TEST"; 89 struct timeval start; 90 struct timeval end; 91 int rc; 92 int total = 0; 93 long time_ms; 94 int spin = 0; 95 LIBSSH2_SFTP *sftp_session; 96 LIBSSH2_SFTP_HANDLE *sftp_handle; 97 98 #ifdef WIN32 99 WSADATA wsadata; 100 int err; 101 102 err = WSAStartup(MAKEWORD(2,0), &wsadata); 103 if (err != 0) { 104 fprintf(stderr, "WSAStartup failed with error: %d\n", err); 105 return 1; 106 } 107 #endif 108 109 if (argc > 1) { 110 hostaddr = inet_addr(argv[1]); 111 } else { 112 hostaddr = htonl(0x7F000001); 113 } 114 115 if (argc > 2) { 116 username = argv[2]; 117 } 118 if (argc > 3) { 119 password = argv[3]; 120 } 121 if (argc > 4) { 122 sftppath = argv[4]; 123 } 124 125 rc = libssh2_init (0); 126 if (rc != 0) { 127 fprintf (stderr, "libssh2 initialization failed (%d)\n", rc); 128 return 1; 129 } 130 131 /* 132 * The application code is responsible for creating the socket 133 * and establishing the connection 134 */ 135 sock = socket(AF_INET, SOCK_STREAM, 0); 136 137 sin.sin_family = AF_INET; 138 sin.sin_port = htons(22); 139 sin.sin_addr.s_addr = hostaddr; 140 if (connect(sock, (struct sockaddr*)(&sin), 141 sizeof(struct sockaddr_in)) != 0) { 142 fprintf(stderr, "failed to connect!\n"); 143 return -1; 144 } 145 146 /* Create a session instance */ 147 session = libssh2_session_init(); 148 if (!session) 149 return -1; 150 151 /* Since we have set non-blocking, tell libssh2 we are non-blocking */ 152 libssh2_session_set_blocking(session, 0); 153 154 gettimeofday(&start, NULL); 155 156 /* ... start it up. This will trade welcome banners, exchange keys, 157 * and setup crypto, compression, and MAC layers 158 */ 159 while ((rc = libssh2_session_handshake(session, sock)) == 160 LIBSSH2_ERROR_EAGAIN); 161 if (rc) { 162 fprintf(stderr, "Failure establishing SSH session: %d\n", rc); 163 return -1; 164 } 165 166 /* At this point we havn't yet authenticated. The first thing to do 167 * is check the hostkey's fingerprint against our known hosts Your app 168 * may have it hard coded, may go to a file, may present it to the 169 * user, that's your call 170 */ 171 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 172 fprintf(stderr, "Fingerprint: "); 173 for(i = 0; i < 20; i++) { 174 fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); 175 } 176 fprintf(stderr, "\n"); 177 178 if (auth_pw) { 179 /* We could authenticate via password */ 180 while ((rc = libssh2_userauth_password(session, username, password)) 181 == LIBSSH2_ERROR_EAGAIN); 182 if (rc) { 183 fprintf(stderr, "Authentication by password failed.\n"); 184 goto shutdown; 185 } 186 } else { 187 /* Or by public key */ 188 while ((rc = 189 libssh2_userauth_publickey_fromfile(session, username, 190 "/home/username/" 191 ".ssh/id_rsa.pub", 192 "/home/username/" 193 ".ssh/id_rsa", 194 password)) == 195 LIBSSH2_ERROR_EAGAIN); 196 if (rc) { 197 fprintf(stderr, "\tAuthentication by public key failed\n"); 198 goto shutdown; 199 } 200 } 201 #if 0 202 libssh2_trace(session, LIBSSH2_TRACE_CONN); 203 #endif 204 fprintf(stderr, "libssh2_sftp_init()!\n"); 205 do { 206 sftp_session = libssh2_sftp_init(session); 207 208 if(!sftp_session) { 209 if(libssh2_session_last_errno(session) == 210 LIBSSH2_ERROR_EAGAIN) { 211 fprintf(stderr, "non-blocking init\n"); 212 waitsocket(sock, session); /* now we wait */ 213 } 214 else { 215 fprintf(stderr, "Unable to init SFTP session\n"); 216 goto shutdown; 217 } 218 } 219 } while (!sftp_session); 220 221 fprintf(stderr, "libssh2_sftp_open()!\n"); 222 /* Request a file via SFTP */ 223 do { 224 sftp_handle = libssh2_sftp_open(sftp_session, sftppath, 225 LIBSSH2_FXF_READ, 0); 226 227 if (!sftp_handle) { 228 if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { 229 fprintf(stderr, "Unable to open file with SFTP\n"); 230 goto shutdown; 231 } 232 else { 233 fprintf(stderr, "non-blocking open\n"); 234 waitsocket(sock, session); /* now we wait */ 235 } 236 } 237 } while (!sftp_handle); 238 239 fprintf(stderr, "libssh2_sftp_open() is done, now receive data!\n"); 240 do { 241 char mem[1024*24]; 242 243 /* loop until we fail */ 244 while ((rc = libssh2_sftp_read(sftp_handle, mem, 245 sizeof(mem))) == LIBSSH2_ERROR_EAGAIN) { 246 spin++; 247 waitsocket(sock, session); /* now we wait */ 248 } 249 if (rc > 0) { 250 total += rc; 251 write(1, mem, rc); 252 } else { 253 break; 254 } 255 } while (1); 256 257 gettimeofday(&end, NULL); 258 time_ms = tvdiff(end, start); 259 fprintf(stderr, "Got %d bytes in %ld ms = %.1f bytes/sec spin: %d\n", total, 260 time_ms, total/(time_ms/1000.0), spin ); 261 262 libssh2_sftp_close(sftp_handle); 263 libssh2_sftp_shutdown(sftp_session); 264 265 shutdown: 266 267 fprintf(stderr, "libssh2_session_disconnect\n"); 268 while (libssh2_session_disconnect(session, 269 "Normal Shutdown, Thank you") == 270 LIBSSH2_ERROR_EAGAIN); 271 libssh2_session_free(session); 272 273 #ifdef WIN32 274 closesocket(sock); 275 #else 276 close(sock); 277 #endif 278 fprintf(stderr, "all done\n"); 279 280 libssh2_exit(); 281 282 return 0; 283 }
上面一个是发送文件到sftp服务器,下面是从sftp服务器获取文件。编译和运行结果如下图所示。
2.修改示例程序
软件提供的源代码是比较完整的,为了方便,对里面的功能进行修改。修改为符合本次使用的windows版本,仅支持密码访问。
sftp-write.c 用于把本地文件上传到sftp服务器中
1 #include "libssh2_config.h" 2 #include <libssh2.h> 3 #include <libssh2_sftp.h> 4 #include <winsock2.h> 5 #include <unistd.h> 6 #include <sys/time.h> 7 #include <sys/types.h> 8 #include <fcntl.h> 9 #include <errno.h> 10 #include <stdio.h> 11 #include <ctype.h> 12 13 #define PORT 22 14 #define HOST "127.0.0.1" 15 #define USER "user" 16 #define PWD "user" 17 #define FILENAME "wunaozai.txt" 18 #define LOCLFILE "wunaozai.txt" 19 20 long tvdiff(struct timeval newer, struct timeval older); 21 int waitsocket(int socket_fd, LIBSSH2_SESSION *session); 22 23 int main(int argc,char *argv[]) 24 { 25 int sock, i; 26 struct sockaddr_in sin; 27 const char *fingerprint; 28 LIBSSH2_SESSION *session; 29 LIBSSH2_SFTP *sftp_session; 30 LIBSSH2_SFTP_HANDLE *sftp_handle; 31 int rc; 32 FILE *local; 33 char mem[1024 * 100]; 34 size_t nread; 35 char *ptr; 36 time_t start; 37 long total = 0; 38 int duration; 39 int ret=-1; //用于表示返回结果 40 int err; 41 42 WSADATA wsadata; 43 err = WSAStartup(MAKEWORD(2,0), &wsadata); 44 if (err != 0) { 45 fprintf(stderr, "WSAStartup failed with error: %d\n", err); 46 return 1; 47 } 48 49 rc = libssh2_init (0); 50 if (rc != 0) { 51 fprintf (stderr, "libssh2 initialization failed (%d)\n", rc); 52 return 1; 53 } 54 55 local = fopen(LOCLFILE, "rb"); 56 if (!local) { 57 fprintf(stderr, "Can't open local file %s\n", LOCLFILE); 58 return -1; 59 } 60 61 /* 62 * The application code is responsible for creating the socket 63 * and establishing the connection 64 */ 65 sock = socket(AF_INET, SOCK_STREAM, 0); 66 67 sin.sin_family = AF_INET; 68 sin.sin_port = htons(PORT); 69 sin.sin_addr.S_un.S_addr = inet_addr(HOST); 70 if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) { 71 fprintf(stderr, "failed to connect!\n"); 72 return -1; 73 } 74 75 /* Create a session instance */ 76 session = libssh2_session_init(); 77 if (!session) return -1; 78 79 /* Since we have set non-blocking, tell libssh2 we are non-blocking */ 80 libssh2_session_set_blocking(session, 0); 81 82 /* ... start it up. This will trade welcome banners, exchange keys, 83 * and setup crypto, compression, and MAC layers 84 */ 85 while ((rc = libssh2_session_handshake(session, sock)) == LIBSSH2_ERROR_EAGAIN) 86 ; 87 if (rc) { 88 fprintf(stderr, "Failure establishing SSH session: %d\n", rc); 89 return -1; 90 } 91 92 //到这里我们还没有权限访问,所以接下来要做的是检查hostkey's finger,然后知道我们验证方式 93 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 94 fprintf(stderr, "Fingerprint: "); 95 for(i = 0; i < 20; i++) { 96 fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); 97 } 98 fprintf(stderr, "\n"); 99 100 //只能使用密码验证 101 while ((rc = libssh2_userauth_password(session, USER, PWD)) == LIBSSH2_ERROR_EAGAIN) 102 ; 103 if (rc) { 104 fprintf(stderr, "Authentication by password failed.\n"); 105 goto shutdown; 106 } 107 108 //fprintf(stderr, "libssh2_sftp_init()!\n"); 109 do { 110 sftp_session = libssh2_sftp_init(session); 111 if (!sftp_session && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { 112 fprintf(stderr, "Unable to init SFTP session\n"); 113 goto shutdown; 114 } 115 } while (!sftp_session); 116 //fprintf(stderr, "libssh2_sftp_open()!\n"); 117 118 119 //请求一个文件,通过ssh2方式 120 do { 121 sftp_handle = 122 libssh2_sftp_open(sftp_session, FILENAME, LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 123 LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| 124 LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); 125 126 if (!sftp_handle && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { 127 fprintf(stderr, "Unable to open file with SFTP\n"); //可能是没有写入权限,或者没有对应的目录 128 goto shutdown; 129 } 130 } while (!sftp_handle); 131 132 fprintf(stderr, "libssh2_sftp_open() is done, now send data!\n"); 133 134 start = time(NULL); 135 136 do { 137 nread = fread(mem, 1, sizeof(mem), local); 138 if (nread <= 0) { //文件结束 139 break; 140 } 141 ptr = mem; 142 total += nread; 143 do { 144 //持续写入文件 145 while ((rc = libssh2_sftp_write(sftp_handle, ptr, nread)) == LIBSSH2_ERROR_EAGAIN) { 146 waitsocket(sock, session); 147 } 148 if(rc < 0) 149 break; 150 ptr += rc; 151 nread -= rc; 152 } while (nread); 153 } while (rc > 0); 154 155 duration = (int)(time(NULL)-start); 156 157 fprintf(stderr, "%ldK bytes in %d seconds makes %.1fK bytes/sec\n", total/1024, duration+1, total/((double)duration+1)/1024); 158 159 160 fclose(local); 161 libssh2_sftp_close(sftp_handle); 162 libssh2_sftp_shutdown(sftp_session); 163 164 ret = 0; //如果执行到这一步,那么表示成功上传文件到服务器 165 shutdown: 166 167 while (libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing") == LIBSSH2_ERROR_EAGAIN) 168 ; 169 libssh2_session_free(session); 170 closesocket(sock); 171 fprintf(stderr, "all done\n"); 172 libssh2_exit(); 173 174 return ret; 175 } 176 177 int waitsocket(int socket_fd, LIBSSH2_SESSION *session) 178 { 179 struct timeval timeout; 180 int rc; 181 fd_set fd; 182 fd_set *writefd = NULL; 183 fd_set *readfd = NULL; 184 int dir; 185 186 timeout.tv_sec = 10; 187 timeout.tv_usec = 0; 188 189 FD_ZERO(&fd); 190 191 FD_SET(socket_fd, &fd); 192 193 /* now make sure we wait in the correct direction */ 194 dir = libssh2_session_block_directions(session); 195 196 if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) 197 readfd = &fd; 198 199 if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) 200 writefd = &fd; 201 202 rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout); 203 return rc; 204 } 205 /* diff in ms */ 206 long tvdiff(struct timeval newer, struct timeval older) 207 { 208 return (newer.tv_sec-older.tv_sec)*1000+ (newer.tv_usec-older.tv_usec)/1000; 209 }
sftp-read.c 用于把服务器上的文件下载到本地中
1 #include "libssh2_config.h" 2 #include <libssh2.h> 3 #include <libssh2_sftp.h> 4 #include <winsock2.h> 5 #include <unistd.h> 6 #include <sys/time.h> 7 #include <sys/types.h> 8 #include <fcntl.h> 9 #include <errno.h> 10 #include <stdio.h> 11 #include <ctype.h> 12 13 #define PORT 22 14 #define HOST "127.0.0.1" 15 #define USER "user" 16 #define PWD "user" 17 #define FILENAME "wunaozai.txt" 18 #define LOCLFILE "wunaozai.txt" 19 20 long tvdiff(struct timeval newer, struct timeval older); 21 int waitsocket(int socket_fd, LIBSSH2_SESSION *session); 22 23 24 int main(int argc, char *argv[]) 25 { 26 int sock, i; 27 struct sockaddr_in sin; 28 const char *fingerprint; 29 LIBSSH2_SESSION *session; 30 LIBSSH2_SFTP *sftp_session; 31 LIBSSH2_SFTP_HANDLE *sftp_handle; 32 struct timeval start; 33 struct timeval end; 34 int total = 0; 35 long time_ms; 36 int spin = 0; 37 int ret=0; 38 int rc=1; 39 FILE * fp; 40 41 WSADATA wsadata; 42 ret = WSAStartup(MAKEWORD(2,0), &wsadata); 43 if (ret != 0) { 44 fprintf(stderr, "WSAStartup failed with error: %d\n", ret); 45 return 1; 46 } 47 48 ret = libssh2_init (0); 49 if (ret != 0) { 50 fprintf (stderr, "libssh2 initialization failed (%d)\n", ret); 51 return 1; 52 } 53 54 sock = socket(AF_INET, SOCK_STREAM, 0); 55 sin.sin_family = AF_INET; 56 sin.sin_port = htons(PORT); //SFTP默认端口为22端口 57 sin.sin_addr.S_un.S_addr = inet_addr(HOST); 58 if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) { 59 fprintf(stderr, "failed to connect!\n"); 60 return -1; 61 } 62 63 session = libssh2_session_init(); //创建一个session 64 if (!session) return -1; 65 66 libssh2_session_set_blocking(session, 0); //设置为非阻塞方式 67 68 while ((ret = libssh2_session_handshake(session, sock)) == LIBSSH2_ERROR_EAGAIN) //创建一个SSH session 69 ; 70 71 if (ret) { 72 fprintf(stderr, "Failure establishing SSH session: %d\n", ret); 73 return -1; 74 } 75 //到这里我们还没有权限访问,所以接下来要做的是检查hostkey's finger 76 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 77 fprintf(stderr, "Fingerprint: "); 78 for(i = 0; i < 20; i++) { 79 fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); 80 } 81 fprintf(stderr, "\n"); 82 83 //只是用密码进行验证 84 while ((ret = libssh2_userauth_password(session, USER, PWD)) == LIBSSH2_ERROR_EAGAIN) 85 ; 86 if (ret) { 87 fprintf(stderr, "Authentication by password failed.\n"); 88 goto shutdown2; 89 } 90 91 fprintf(stderr, "libssh2_sftp_init()!\n"); 92 do { 93 sftp_session = libssh2_sftp_init(session); 94 if(!sftp_session) { 95 if(libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { 96 fprintf(stderr, "non-blocking init\n"); 97 waitsocket(sock, session); /* now we wait */ 98 } 99 else { 100 fprintf(stderr, "Unable to init SFTP session\n"); 101 goto shutdown2; 102 } 103 } 104 } while (!sftp_session); 105 106 fprintf(stderr, "libssh2_sftp_open()!\n"); 107 108 //请求一个文件,通过ssh方式 109 do { 110 sftp_handle = libssh2_sftp_open(sftp_session, FILENAME, LIBSSH2_FXF_READ, 0); 111 112 if (!sftp_handle) { 113 if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { 114 fprintf(stderr, "Unable to open file with SFTP\n"); 115 goto shutdown2; 116 } 117 else { 118 fprintf(stderr, "non-blocking open\n"); 119 waitsocket(sock, session); /* now we wait */ 120 } 121 } 122 } while (!sftp_handle); 123 124 gettimeofday(&start,NULL); 125 //打开文件进行保存 126 fp = fopen(LOCLFILE,"wb"); 127 if(fp==NULL) 128 { 129 fprintf(stderr,"Can't open local file %s\n",LOCLFILE); 130 return -1; 131 } 132 fprintf(stderr, "libssh2_sftp_open() is done, now receive data!\n"); 133 do { 134 char mem[1024*24]; 135 136 /* loop until we fail */ 137 while ((ret = libssh2_sftp_read(sftp_handle, mem, sizeof(mem))) == LIBSSH2_ERROR_EAGAIN) { 138 spin++; 139 waitsocket(sock, session); /* now we wait */ 140 } 141 if (ret > 0) { 142 total += ret; 143 //write(1, mem, ret); 144 fwrite(mem,1,ret,fp); //写入到文件中 145 } else { 146 break; 147 } 148 } while (1); 149 fclose(fp); 150 gettimeofday(&end,NULL); 151 time_ms = tvdiff(end, start); 152 //打印传输速率 153 fprintf(stderr, "Got %.4lf Mbytes in %.2lf sec = %.4lf Kbytes/sec spin: %d\n", total/1024.0/1024.0, 154 time_ms/1000.0, total/((time_ms+1)/1000.0)/1024/1024, spin ); 155 156 libssh2_sftp_close(sftp_handle); 157 libssh2_sftp_shutdown(sftp_session); 158 159 rc = 0;//执行到改行代码表示已经正常下载文件 160 161 shutdown2: 162 163 fprintf(stderr, "libssh2_session_disconnect\n"); 164 while (libssh2_session_disconnect(session, "Normal Shutdown, Thank you") == LIBSSH2_ERROR_EAGAIN) 165 ; 166 libssh2_session_free(session); 167 closesocket(sock); 168 fprintf(stderr, "all done\n"); 169 libssh2_exit(); 170 171 return rc; 172 } 173 174 long tvdiff(struct timeval newer, struct timeval older) 175 { 176 return (newer.tv_sec-older.tv_sec)*1000+(newer.tv_usec-older.tv_usec)/1000; 177 } 178 179 int waitsocket(int socket_fd, LIBSSH2_SESSION *session) 180 { 181 struct timeval timeout; 182 int ret; 183 fd_set fd; 184 fd_set *writefd = NULL; 185 fd_set *readfd = NULL; 186 int dir; 187 timeout.tv_sec = 10; 188 timeout.tv_usec = 0; 189 FD_ZERO(&fd); 190 FD_SET(socket_fd, &fd); 191 /* now make sure we wait in the correct direction */ 192 dir = libssh2_session_block_directions(session); 193 if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) 194 readfd = &fd; 195 if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) 196 writefd = &fd; 197 ret = select(socket_fd + 1, readfd, writefd, NULL, &timeout); 198 return ret; 199 }
至于用到的libssh2_config.h这个文件,没有的话可以在代码中注释掉.
下面这个是freesshd产生的日志中一部分
1 05-28-2015 14:18:05 HOST localhost SSH connection attempt. 2 05-28-2015 14:18:05 HOST localhost SSH user successfully logged on using password. 3 05-28-2015 14:18:06 SFTP service granted to user user. 4 05-28-2015 14:18:06 HOST localhost user is uploading wunaozai.txt (E:\wunaozai.txt) 5 05-28-2015 14:18:06 HOST localhost SSH user disconnected. 6 05-28-2015 14:18:23 HOST localhost SSH connection attempt. 7 05-28-2015 14:18:23 HOST localhost SSH user successfully logged on using password. 8 05-28-2015 14:18:23 SFTP service granted to user user. 9 05-28-2015 14:18:23 HOST localhost user is uploading wunaozai.txt (E:\wunaozai.txt) 10 05-28-2015 14:18:23 HOST localhost SSH user disconnected. 11 05-28-2015 14:18:49 HOST localhost SSH connection attempt. 12 05-28-2015 14:18:49 HOST localhost SSH user successfully logged on using password. 13 05-28-2015 14:18:49 SFTP service granted to user user. 14 05-28-2015 14:18:49 HOST localhost user is uploading wunaozai.txt (E:\wunaozai.txt) 15 05-28-2015 14:18:49 HOST localhost SSH user disconnected. 16 05-28-2015 14:18:55 HOST localhost SSH connection attempt. 17 05-28-2015 14:18:56 HOST localhost SSH user successfully logged on using password. 18 05-28-2015 14:18:56 SFTP service granted to user user. 19 05-28-2015 14:18:56 HOST localhost user is downloading wunaozai.txt (E:\wunaozai.txt) 20 05-28-2015 14:18:56 HOST localhost SSH user disconnected.
有了上面这两个主要的功能,SFTP的客户端就基本功能实现了,至于mkdir和dir功能就参考里面的示例程序,基本都可以看懂。
3.使用putty连接freesshd
了解过SFTP原理之后,就知道,SFTP其实跟FTP没有多大的关系,其实就是一个使用SSH协议,然后进行会话,会话过程保存为文件,嗯,大概就是这个样子了。所以我们可以使用普通的ssh软件进行登录,拿到该SFTP服务器站点的SHELL。然后可以各种操作,看起来很危险的样子,所以不管用什么SFTP服务器在配置用户的时候要注意的。 putty工具里面还有个PSFTP.exe这个工具可以连接到SFTP服务器,没事的也可以玩玩看。