1 /*
2  * Sample showing how to do an SCP non-blocking upload transfer.
3  */
4 
5 #include "libssh2_config.h"
6 
7 #include <libssh2.h>
8 
9 #ifdef HAVE_WINSOCK2_H
10 # include <winsock2.h>
11 #endif
12 #ifdef HAVE_SYS_SOCKET_H
13 # include <sys/socket.h>
14 #endif
15 #ifdef HAVE_NETINET_IN_H
16 # include <netinet/in.h>
17 #endif
18 #ifdef HAVE_SYS_SELECT_H
19 # include <sys/select.h>
20 #endif
21 # ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
24 #ifdef HAVE_ARPA_INET_H
25 # include <arpa/inet.h>
26 #endif
27 #ifdef HAVE_SYS_TIME_H
28 # include <sys/time.h>
29 #endif
30 
31 #include <sys/types.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <time.h>
37 
waitsocket(int socket_fd,LIBSSH2_SESSION * session)38 static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
39 {
40     struct timeval timeout;
41     int rc;
42     fd_set fd;
43     fd_set *writefd = NULL;
44     fd_set *readfd = NULL;
45     int dir;
46 
47     timeout.tv_sec = 10;
48     timeout.tv_usec = 0;
49 
50     FD_ZERO(&fd);
51 
52     FD_SET(socket_fd, &fd);
53 
54     /* now make sure we wait in the correct direction */
55     dir = libssh2_session_block_directions(session);
56 
57     if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
58         readfd = &fd;
59 
60     if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
61         writefd = &fd;
62 
63     rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
64 
65     return rc;
66 }
67 
main(int argc,char * argv[])68 int main(int argc, char *argv[])
69 {
70     unsigned long hostaddr;
71     int sock, i, auth_pw = 1;
72     struct sockaddr_in sin;
73     const char *fingerprint;
74     LIBSSH2_SESSION *session = NULL;
75     LIBSSH2_CHANNEL *channel;
76     const char *username = "username";
77     const char *password = "password";
78     const char *loclfile = "scp_write.c";
79     const char *scppath = "/tmp/TEST";
80     FILE *local;
81     int rc;
82     char mem[1024*100];
83     size_t nread;
84     char *ptr;
85     struct stat fileinfo;
86     time_t start;
87     long total = 0;
88     int duration;
89     size_t prev;
90 
91 #ifdef WIN32
92     WSADATA wsadata;
93     int err;
94 
95     err = WSAStartup(MAKEWORD(2, 0), &wsadata);
96     if(err != 0) {
97         fprintf(stderr, "WSAStartup failed with error: %d\n", err);
98         return 1;
99     }
100 #endif
101 
102     if(argc > 1) {
103         hostaddr = inet_addr(argv[1]);
104     }
105     else {
106         hostaddr = htonl(0x7F000001);
107     }
108     if(argc > 2) {
109         username = argv[2];
110     }
111     if(argc > 3) {
112         password = argv[3];
113     }
114     if(argc > 4) {
115         loclfile = argv[4];
116     }
117     if(argc > 5) {
118         scppath = argv[5];
119     }
120 
121     rc = libssh2_init(0);
122     if(rc != 0) {
123         fprintf(stderr, "libssh2 initialization failed (%d)\n", rc);
124         return 1;
125     }
126 
127     local = fopen(loclfile, "rb");
128     if(!local) {
129         fprintf(stderr, "Can't local file %s\n", loclfile);
130         return -1;
131     }
132 
133     stat(loclfile, &fileinfo);
134 
135     /* Ultra basic "connect to port 22 on localhost"
136      * Your code is responsible for creating the socket establishing the
137      * connection
138      */
139     sock = socket(AF_INET, SOCK_STREAM, 0);
140 
141     sin.sin_family = AF_INET;
142     sin.sin_port = htons(22);
143     sin.sin_addr.s_addr = hostaddr;
144     if(connect(sock, (struct sockaddr*)(&sin),
145                sizeof(struct sockaddr_in)) != 0) {
146         fprintf(stderr, "failed to connect!\n");
147         return -1;
148     }
149 
150     /* Create a session instance
151      */
152     session = libssh2_session_init();
153     if(!session)
154         return -1;
155 
156     /* Since we have set non-blocking, tell libssh2 we are non-blocking */
157     libssh2_session_set_blocking(session, 0);
158 
159     /* ... start it up. This will trade welcome banners, exchange keys,
160      * and setup crypto, compression, and MAC layers
161      */
162     while((rc = libssh2_session_handshake(session, sock))
163           == LIBSSH2_ERROR_EAGAIN);
164     if(rc) {
165         fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
166         return -1;
167     }
168 
169     /* At this point we havn't yet authenticated.  The first thing to do
170      * is check the hostkey's fingerprint against our known hosts Your app
171      * may have it hard coded, may go to a file, may present it to the
172      * user, that's your call
173      */
174     fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
175     fprintf(stderr, "Fingerprint: ");
176     for(i = 0; i < 20; i++) {
177         fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
178     }
179     fprintf(stderr, "\n");
180 
181     if(auth_pw) {
182         /* We could authenticate via password */
183         while((rc = libssh2_userauth_password(session, username, password)) ==
184               LIBSSH2_ERROR_EAGAIN);
185         if(rc) {
186             fprintf(stderr, "Authentication by password failed.\n");
187             goto shutdown;
188         }
189     }
190     else {
191         /* Or by public key */
192 #define HOME "/home/username/"
193         while((rc = libssh2_userauth_publickey_fromfile(session, username,
194                                                         HOME ".ssh/id_rsa.pub",
195                                                         HOME ".ssh/id_rsa",
196                                                         password)) ==
197               LIBSSH2_ERROR_EAGAIN);
198         if(rc) {
199             fprintf(stderr, "\tAuthentication by public key failed\n");
200             goto shutdown;
201         }
202     }
203 
204     /* Send a file via scp. The mode parameter must only have permissions! */
205     do {
206         channel = libssh2_scp_send(session, scppath, fileinfo.st_mode & 0777,
207                                    (unsigned long)fileinfo.st_size);
208 
209         if((!channel) && (libssh2_session_last_errno(session) !=
210                           LIBSSH2_ERROR_EAGAIN)) {
211             char *err_msg;
212 
213             libssh2_session_last_error(session, &err_msg, NULL, 0);
214             fprintf(stderr, "%s\n", err_msg);
215             goto shutdown;
216         }
217     } while(!channel);
218 
219     fprintf(stderr, "SCP session waiting to send file\n");
220     start = time(NULL);
221     do {
222         nread = fread(mem, 1, sizeof(mem), local);
223         if(nread <= 0) {
224             /* end of file */
225             break;
226         }
227         ptr = mem;
228 
229         total += nread;
230 
231         prev = 0;
232         do {
233             while((rc = libssh2_channel_write(channel, ptr, nread)) ==
234                   LIBSSH2_ERROR_EAGAIN) {
235                 waitsocket(sock, session);
236                 prev = 0;
237             }
238             if(rc < 0) {
239                 fprintf(stderr, "ERROR %d total %ld / %d prev %d\n", rc,
240                         total, (int)nread, (int)prev);
241                 break;
242             }
243             else {
244                 prev = nread;
245 
246                 /* rc indicates how many bytes were written this time */
247                 nread -= rc;
248                 ptr += rc;
249             }
250         } while(nread);
251     } while(!nread); /* only continue if nread was drained */
252 
253     duration = (int)(time(NULL)-start);
254 
255     fprintf(stderr, "%ld bytes in %d seconds makes %.1f bytes/sec\n",
256            total, duration, total/(double)duration);
257 
258     fprintf(stderr, "Sending EOF\n");
259     while(libssh2_channel_send_eof(channel) == LIBSSH2_ERROR_EAGAIN);
260 
261     fprintf(stderr, "Waiting for EOF\n");
262     while(libssh2_channel_wait_eof(channel) == LIBSSH2_ERROR_EAGAIN);
263 
264     fprintf(stderr, "Waiting for channel to close\n");
265     while(libssh2_channel_wait_closed(channel) == LIBSSH2_ERROR_EAGAIN);
266 
267     libssh2_channel_free(channel);
268     channel = NULL;
269 
270  shutdown:
271 
272     while(libssh2_session_disconnect(session,
273                                      "Normal Shutdown,") ==
274           LIBSSH2_ERROR_EAGAIN);
275     libssh2_session_free(session);
276 
277 #ifdef WIN32
278     closesocket(sock);
279 #else
280     close(sock);
281 #endif
282     fprintf(stderr, "all done\n");
283 
284     libssh2_exit();
285 
286     return 0;
287 }
288