85f2941966776a770b543d3d8c824fc2a2d474ae
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / vssh / wolfssh.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #ifdef USE_WOLFSSH
26
27 #include <limits.h>
28
29 #include <wolfssh/ssh.h>
30 #include <wolfssh/wolfsftp.h>
31 #include "urldata.h"
32 #include "connect.h"
33 #include "sendf.h"
34 #include "progress.h"
35 #include "curl_path.h"
36 #include "strtoofft.h"
37 #include "transfer.h"
38 #include "speedcheck.h"
39 #include "select.h"
40 #include "multiif.h"
41 #include "warnless.h"
42
43 /* The last 3 #include files should be in this order */
44 #include "curl_printf.h"
45 #include "curl_memory.h"
46 #include "memdebug.h"
47
48 static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
49 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
50 static CURLcode wssh_do(struct Curl_easy *data, bool *done);
51 #if 0
52 static CURLcode wscp_done(struct Curl_easy *data,
53                           CURLcode, bool premature);
54 static CURLcode wscp_doing(struct Curl_easy *data,
55                            bool *dophase_done);
56 static CURLcode wscp_disconnect(struct Curl_easy *data,
57                                 struct connectdata *conn,
58                                 bool dead_connection);
59 #endif
60 static CURLcode wsftp_done(struct Curl_easy *data,
61                            CURLcode, bool premature);
62 static CURLcode wsftp_doing(struct Curl_easy *data,
63                             bool *dophase_done);
64 static CURLcode wsftp_disconnect(struct Curl_easy *data,
65                                  struct connectdata *conn,
66                                  bool dead);
67 static int wssh_getsock(struct Curl_easy *data,
68                         struct connectdata *conn,
69                         curl_socket_t *sock);
70 static CURLcode wssh_setup_connection(struct Curl_easy *data,
71                                       struct connectdata *conn);
72
73 #if 0
74 /*
75  * SCP protocol handler.
76  */
77
78 const struct Curl_handler Curl_handler_scp = {
79   "SCP",                                /* scheme */
80   wssh_setup_connection,                /* setup_connection */
81   wssh_do,                              /* do_it */
82   wscp_done,                            /* done */
83   ZERO_NULL,                            /* do_more */
84   wssh_connect,                         /* connect_it */
85   wssh_multi_statemach,                 /* connecting */
86   wscp_doing,                           /* doing */
87   wssh_getsock,                         /* proto_getsock */
88   wssh_getsock,                         /* doing_getsock */
89   ZERO_NULL,                            /* domore_getsock */
90   wssh_getsock,                         /* perform_getsock */
91   wscp_disconnect,                      /* disconnect */
92   ZERO_NULL,                            /* readwrite */
93   ZERO_NULL,                            /* connection_check */
94   ZERO_NULL,                            /* attach connection */
95   PORT_SSH,                             /* defport */
96   CURLPROTO_SCP,                        /* protocol */
97   PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
98   | PROTOPT_NOURLQUERY                  /* flags */
99 };
100
101 #endif
102
103 /*
104  * SFTP protocol handler.
105  */
106
107 const struct Curl_handler Curl_handler_sftp = {
108   "SFTP",                               /* scheme */
109   wssh_setup_connection,                /* setup_connection */
110   wssh_do,                              /* do_it */
111   wsftp_done,                           /* done */
112   ZERO_NULL,                            /* do_more */
113   wssh_connect,                         /* connect_it */
114   wssh_multi_statemach,                 /* connecting */
115   wsftp_doing,                          /* doing */
116   wssh_getsock,                         /* proto_getsock */
117   wssh_getsock,                         /* doing_getsock */
118   ZERO_NULL,                            /* domore_getsock */
119   wssh_getsock,                         /* perform_getsock */
120   wsftp_disconnect,                     /* disconnect */
121   ZERO_NULL,                            /* readwrite */
122   ZERO_NULL,                            /* connection_check */
123   ZERO_NULL,                            /* attach connection */
124   PORT_SSH,                             /* defport */
125   CURLPROTO_SFTP,                       /* protocol */
126   CURLPROTO_SFTP,                       /* family */
127   PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
128   | PROTOPT_NOURLQUERY                  /* flags */
129 };
130
131 /*
132  * SSH State machine related code
133  */
134 /* This is the ONLY way to change SSH state! */
135 static void state(struct Curl_easy *data, sshstate nowstate)
136 {
137   struct connectdata *conn = data->conn;
138   struct ssh_conn *sshc = &conn->proto.sshc;
139 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
140   /* for debug purposes */
141   static const char * const names[] = {
142     "SSH_STOP",
143     "SSH_INIT",
144     "SSH_S_STARTUP",
145     "SSH_HOSTKEY",
146     "SSH_AUTHLIST",
147     "SSH_AUTH_PKEY_INIT",
148     "SSH_AUTH_PKEY",
149     "SSH_AUTH_PASS_INIT",
150     "SSH_AUTH_PASS",
151     "SSH_AUTH_AGENT_INIT",
152     "SSH_AUTH_AGENT_LIST",
153     "SSH_AUTH_AGENT",
154     "SSH_AUTH_HOST_INIT",
155     "SSH_AUTH_HOST",
156     "SSH_AUTH_KEY_INIT",
157     "SSH_AUTH_KEY",
158     "SSH_AUTH_GSSAPI",
159     "SSH_AUTH_DONE",
160     "SSH_SFTP_INIT",
161     "SSH_SFTP_REALPATH",
162     "SSH_SFTP_QUOTE_INIT",
163     "SSH_SFTP_POSTQUOTE_INIT",
164     "SSH_SFTP_QUOTE",
165     "SSH_SFTP_NEXT_QUOTE",
166     "SSH_SFTP_QUOTE_STAT",
167     "SSH_SFTP_QUOTE_SETSTAT",
168     "SSH_SFTP_QUOTE_SYMLINK",
169     "SSH_SFTP_QUOTE_MKDIR",
170     "SSH_SFTP_QUOTE_RENAME",
171     "SSH_SFTP_QUOTE_RMDIR",
172     "SSH_SFTP_QUOTE_UNLINK",
173     "SSH_SFTP_QUOTE_STATVFS",
174     "SSH_SFTP_GETINFO",
175     "SSH_SFTP_FILETIME",
176     "SSH_SFTP_TRANS_INIT",
177     "SSH_SFTP_UPLOAD_INIT",
178     "SSH_SFTP_CREATE_DIRS_INIT",
179     "SSH_SFTP_CREATE_DIRS",
180     "SSH_SFTP_CREATE_DIRS_MKDIR",
181     "SSH_SFTP_READDIR_INIT",
182     "SSH_SFTP_READDIR",
183     "SSH_SFTP_READDIR_LINK",
184     "SSH_SFTP_READDIR_BOTTOM",
185     "SSH_SFTP_READDIR_DONE",
186     "SSH_SFTP_DOWNLOAD_INIT",
187     "SSH_SFTP_DOWNLOAD_STAT",
188     "SSH_SFTP_CLOSE",
189     "SSH_SFTP_SHUTDOWN",
190     "SSH_SCP_TRANS_INIT",
191     "SSH_SCP_UPLOAD_INIT",
192     "SSH_SCP_DOWNLOAD_INIT",
193     "SSH_SCP_DOWNLOAD",
194     "SSH_SCP_DONE",
195     "SSH_SCP_SEND_EOF",
196     "SSH_SCP_WAIT_EOF",
197     "SSH_SCP_WAIT_CLOSE",
198     "SSH_SCP_CHANNEL_FREE",
199     "SSH_SESSION_DISCONNECT",
200     "SSH_SESSION_FREE",
201     "QUIT"
202   };
203
204   /* a precaution to make sure the lists are in sync */
205   DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
206
207   if(sshc->state != nowstate) {
208     infof(data, "wolfssh %p state change from %s to %s",
209           (void *)sshc, names[sshc->state], names[nowstate]);
210   }
211 #endif
212
213   sshc->state = nowstate;
214 }
215
216 static ssize_t wscp_send(struct Curl_easy *data, int sockindex,
217                          const void *mem, size_t len, CURLcode *err)
218 {
219   ssize_t nwrite = 0;
220   (void)data;
221   (void)sockindex; /* we only support SCP on the fixed known primary socket */
222   (void)mem;
223   (void)len;
224   (void)err;
225
226   return nwrite;
227 }
228
229 static ssize_t wscp_recv(struct Curl_easy *data, int sockindex,
230                          char *mem, size_t len, CURLcode *err)
231 {
232   ssize_t nread = 0;
233   (void)data;
234   (void)sockindex; /* we only support SCP on the fixed known primary socket */
235   (void)mem;
236   (void)len;
237   (void)err;
238
239   return nread;
240 }
241
242 /* return number of sent bytes */
243 static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
244                           const void *mem, size_t len, CURLcode *err)
245 {
246   struct connectdata *conn = data->conn;
247   struct ssh_conn *sshc = &conn->proto.sshc;
248   word32 offset[2];
249   int rc;
250   (void)sockindex;
251
252   offset[0] = (word32)sshc->offset&0xFFFFFFFF;
253   offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
254
255   rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
256                                     sshc->handleSz,
257                                     &offset[0],
258                                     (byte *)mem, (word32)len);
259
260   if(rc == WS_FATAL_ERROR)
261     rc = wolfSSH_get_error(sshc->ssh_session);
262   if(rc == WS_WANT_READ) {
263     conn->waitfor = KEEP_RECV;
264     *err = CURLE_AGAIN;
265     return -1;
266   }
267   else if(rc == WS_WANT_WRITE) {
268     conn->waitfor = KEEP_SEND;
269     *err = CURLE_AGAIN;
270     return -1;
271   }
272   if(rc < 0) {
273     failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc);
274     return -1;
275   }
276   DEBUGASSERT(rc == (int)len);
277   infof(data, "sent %zd bytes SFTP from offset %zd",
278         len, sshc->offset);
279   sshc->offset += len;
280   return (ssize_t)rc;
281 }
282
283 /*
284  * Return number of received (decrypted) bytes
285  * or <0 on error
286  */
287 static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
288                           char *mem, size_t len, CURLcode *err)
289 {
290   int rc;
291   struct connectdata *conn = data->conn;
292   struct ssh_conn *sshc = &conn->proto.sshc;
293   word32 offset[2];
294   (void)sockindex;
295
296   offset[0] = (word32)sshc->offset&0xFFFFFFFF;
297   offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
298
299   rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle,
300                                    sshc->handleSz,
301                                    &offset[0],
302                                    (byte *)mem, (word32)len);
303   if(rc == WS_FATAL_ERROR)
304     rc = wolfSSH_get_error(sshc->ssh_session);
305   if(rc == WS_WANT_READ) {
306     conn->waitfor = KEEP_RECV;
307     *err = CURLE_AGAIN;
308     return -1;
309   }
310   else if(rc == WS_WANT_WRITE) {
311     conn->waitfor = KEEP_SEND;
312     *err = CURLE_AGAIN;
313     return -1;
314   }
315
316   DEBUGASSERT(rc <= (int)len);
317
318   if(rc < 0) {
319     failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc);
320     return -1;
321   }
322   sshc->offset += len;
323
324   return (ssize_t)rc;
325 }
326
327 /*
328  * SSH setup and connection
329  */
330 static CURLcode wssh_setup_connection(struct Curl_easy *data,
331                                       struct connectdata *conn)
332 {
333   struct SSHPROTO *ssh;
334   (void)conn;
335
336   data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
337   if(!ssh)
338     return CURLE_OUT_OF_MEMORY;
339
340   return CURLE_OK;
341 }
342
343 static Curl_recv wscp_recv, wsftp_recv;
344 static Curl_send wscp_send, wsftp_send;
345
346 static int userauth(byte authtype,
347                     WS_UserAuthData* authdata,
348                     void *ctx)
349 {
350   struct Curl_easy *data = ctx;
351   DEBUGF(infof(data, "wolfssh callback: type %s",
352                authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" :
353                "PUBLICCKEY"));
354   if(authtype == WOLFSSH_USERAUTH_PASSWORD) {
355     authdata->sf.password.password = (byte *)data->conn->passwd;
356     authdata->sf.password.passwordSz = (word32) strlen(data->conn->passwd);
357   }
358
359   return 0;
360 }
361
362 static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
363 {
364   struct connectdata *conn = data->conn;
365   struct ssh_conn *sshc;
366   curl_socket_t sock = conn->sock[FIRSTSOCKET];
367   int rc;
368
369   /* initialize per-handle data if not already */
370   if(!data->req.p.ssh)
371     wssh_setup_connection(data, conn);
372
373   /* We default to persistent connections. We set this already in this connect
374      function to make the re-use checks properly be able to check this bit. */
375   connkeep(conn, "SSH default");
376
377   if(conn->handler->protocol & CURLPROTO_SCP) {
378     conn->recv[FIRSTSOCKET] = wscp_recv;
379     conn->send[FIRSTSOCKET] = wscp_send;
380   }
381   else {
382     conn->recv[FIRSTSOCKET] = wsftp_recv;
383     conn->send[FIRSTSOCKET] = wsftp_send;
384   }
385   sshc = &conn->proto.sshc;
386   sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
387   if(!sshc->ctx) {
388     failf(data, "No wolfSSH context");
389     goto error;
390   }
391
392   sshc->ssh_session = wolfSSH_new(sshc->ctx);
393   if(!sshc->ssh_session) {
394     failf(data, "No wolfSSH session");
395     goto error;
396   }
397
398   rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user);
399   if(rc != WS_SUCCESS) {
400     failf(data, "wolfSSH failed to set user name");
401     goto error;
402   }
403
404   /* set callback for authentication */
405   wolfSSH_SetUserAuth(sshc->ctx, userauth);
406   wolfSSH_SetUserAuthCtx(sshc->ssh_session, data);
407
408   rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock);
409   if(rc) {
410     failf(data, "wolfSSH failed to set socket");
411     goto error;
412   }
413
414 #if 0
415   wolfSSH_Debugging_ON();
416 #endif
417
418   *done = TRUE;
419   if(conn->handler->protocol & CURLPROTO_SCP)
420     state(data, SSH_INIT);
421   else
422     state(data, SSH_SFTP_INIT);
423
424   return wssh_multi_statemach(data, done);
425   error:
426   wolfSSH_free(sshc->ssh_session);
427   wolfSSH_CTX_free(sshc->ctx);
428   return CURLE_FAILED_INIT;
429 }
430
431 /*
432  * wssh_statemach_act() runs the SSH state machine as far as it can without
433  * blocking and without reaching the end.  The data the pointer 'block' points
434  * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it
435  * wants to be called again when the socket is ready
436  */
437
438 static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
439 {
440   CURLcode result = CURLE_OK;
441   struct connectdata *conn = data->conn;
442   struct ssh_conn *sshc = &conn->proto.sshc;
443   struct SSHPROTO *sftp_scp = data->req.p.ssh;
444   WS_SFTPNAME *name;
445   int rc = 0;
446   *block = FALSE; /* we're not blocking by default */
447
448   do {
449     switch(sshc->state) {
450     case SSH_INIT:
451       state(data, SSH_S_STARTUP);
452       break;
453
454     case SSH_S_STARTUP:
455       rc = wolfSSH_connect(sshc->ssh_session);
456       if(rc != WS_SUCCESS)
457         rc = wolfSSH_get_error(sshc->ssh_session);
458       if(rc == WS_WANT_READ) {
459         *block = TRUE;
460         conn->waitfor = KEEP_RECV;
461         return CURLE_OK;
462       }
463       else if(rc == WS_WANT_WRITE) {
464         *block = TRUE;
465         conn->waitfor = KEEP_SEND;
466         return CURLE_OK;
467       }
468       else if(rc != WS_SUCCESS) {
469         state(data, SSH_STOP);
470         return CURLE_SSH;
471       }
472       infof(data, "wolfssh connected");
473       state(data, SSH_STOP);
474       break;
475     case SSH_STOP:
476       break;
477
478     case SSH_SFTP_INIT:
479       rc = wolfSSH_SFTP_connect(sshc->ssh_session);
480       if(rc != WS_SUCCESS)
481         rc = wolfSSH_get_error(sshc->ssh_session);
482       if(rc == WS_WANT_READ) {
483         *block = TRUE;
484         conn->waitfor = KEEP_RECV;
485         return CURLE_OK;
486       }
487       else if(rc == WS_WANT_WRITE) {
488         *block = TRUE;
489         conn->waitfor = KEEP_SEND;
490         return CURLE_OK;
491       }
492       else if(rc == WS_SUCCESS) {
493         infof(data, "wolfssh SFTP connected");
494         state(data, SSH_SFTP_REALPATH);
495       }
496       else {
497         failf(data, "wolfssh SFTP connect error %d", rc);
498         return CURLE_SSH;
499       }
500       break;
501     case SSH_SFTP_REALPATH:
502       name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
503       rc = wolfSSH_get_error(sshc->ssh_session);
504       if(rc == WS_WANT_READ) {
505         *block = TRUE;
506         conn->waitfor = KEEP_RECV;
507         return CURLE_OK;
508       }
509       else if(rc == WS_WANT_WRITE) {
510         *block = TRUE;
511         conn->waitfor = KEEP_SEND;
512         return CURLE_OK;
513       }
514       else if(name && (rc == WS_SUCCESS)) {
515         sshc->homedir = malloc(name->fSz + 1);
516         if(!sshc->homedir) {
517           sshc->actualcode = CURLE_OUT_OF_MEMORY;
518         }
519         else {
520           memcpy(sshc->homedir, name->fName, name->fSz);
521           sshc->homedir[name->fSz] = 0;
522           infof(data, "wolfssh SFTP realpath succeeded");
523         }
524         wolfSSH_SFTPNAME_list_free(name);
525         state(data, SSH_STOP);
526         return CURLE_OK;
527       }
528       failf(data, "wolfssh SFTP realpath %d", rc);
529       return CURLE_SSH;
530
531     case SSH_SFTP_QUOTE_INIT:
532       result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
533       if(result) {
534         sshc->actualcode = result;
535         state(data, SSH_STOP);
536         break;
537       }
538
539       if(data->set.quote) {
540         infof(data, "Sending quote commands");
541         sshc->quote_item = data->set.quote;
542         state(data, SSH_SFTP_QUOTE);
543       }
544       else {
545         state(data, SSH_SFTP_GETINFO);
546       }
547       break;
548     case SSH_SFTP_GETINFO:
549       if(data->set.get_filetime) {
550         state(data, SSH_SFTP_FILETIME);
551       }
552       else {
553         state(data, SSH_SFTP_TRANS_INIT);
554       }
555       break;
556     case SSH_SFTP_TRANS_INIT:
557       if(data->set.upload)
558         state(data, SSH_SFTP_UPLOAD_INIT);
559       else {
560         if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
561           state(data, SSH_SFTP_READDIR_INIT);
562         else
563           state(data, SSH_SFTP_DOWNLOAD_INIT);
564       }
565       break;
566     case SSH_SFTP_UPLOAD_INIT: {
567       word32 flags;
568       WS_SFTP_FILEATRB createattrs;
569       if(data->state.resume_from) {
570         WS_SFTP_FILEATRB attrs;
571         if(data->state.resume_from < 0) {
572           rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path,
573                                  &attrs);
574           if(rc != WS_SUCCESS)
575             break;
576
577           if(rc) {
578             data->state.resume_from = 0;
579           }
580           else {
581             curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0];
582             if(size < 0) {
583               failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
584               return CURLE_BAD_DOWNLOAD_RESUME;
585             }
586             data->state.resume_from = size;
587           }
588         }
589       }
590
591       if(data->set.remote_append)
592         /* Try to open for append, but create if nonexisting */
593         flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND;
594       else if(data->state.resume_from > 0)
595         /* If we have restart position then open for append */
596         flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND;
597       else
598         /* Clear file before writing (normal behavior) */
599         flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC;
600
601       memset(&createattrs, 0, sizeof(createattrs));
602       createattrs.per = (word32)data->set.new_file_perms;
603       sshc->handleSz = sizeof(sshc->handle);
604       rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
605                              flags, &createattrs,
606                              sshc->handle, &sshc->handleSz);
607       if(rc == WS_FATAL_ERROR)
608         rc = wolfSSH_get_error(sshc->ssh_session);
609       if(rc == WS_WANT_READ) {
610         *block = TRUE;
611         conn->waitfor = KEEP_RECV;
612         return CURLE_OK;
613       }
614       else if(rc == WS_WANT_WRITE) {
615         *block = TRUE;
616         conn->waitfor = KEEP_SEND;
617         return CURLE_OK;
618       }
619       else if(rc == WS_SUCCESS) {
620         infof(data, "wolfssh SFTP open succeeded");
621       }
622       else {
623         failf(data, "wolfssh SFTP upload open failed: %d", rc);
624         return CURLE_SSH;
625       }
626       state(data, SSH_SFTP_DOWNLOAD_STAT);
627
628       /* If we have a restart point then we need to seek to the correct
629          position. */
630       if(data->state.resume_from > 0) {
631         /* Let's read off the proper amount of bytes from the input. */
632         int seekerr = CURL_SEEKFUNC_OK;
633         if(conn->seek_func) {
634           Curl_set_in_callback(data, true);
635           seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
636                                     SEEK_SET);
637           Curl_set_in_callback(data, false);
638         }
639
640         if(seekerr != CURL_SEEKFUNC_OK) {
641           curl_off_t passed = 0;
642
643           if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
644             failf(data, "Could not seek stream");
645             return CURLE_FTP_COULDNT_USE_REST;
646           }
647           /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
648           do {
649             size_t readthisamountnow =
650               (data->state.resume_from - passed > data->set.buffer_size) ?
651               (size_t)data->set.buffer_size :
652               curlx_sotouz(data->state.resume_from - passed);
653
654             size_t actuallyread;
655             Curl_set_in_callback(data, true);
656             actuallyread = data->state.fread_func(data->state.buffer, 1,
657                                                   readthisamountnow,
658                                                   data->state.in);
659             Curl_set_in_callback(data, false);
660
661             passed += actuallyread;
662             if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
663               /* this checks for greater-than only to make sure that the
664                  CURL_READFUNC_ABORT return code still aborts */
665               failf(data, "Failed to read data");
666               return CURLE_FTP_COULDNT_USE_REST;
667             }
668           } while(passed < data->state.resume_from);
669         }
670
671         /* now, decrease the size of the read */
672         if(data->state.infilesize > 0) {
673           data->state.infilesize -= data->state.resume_from;
674           data->req.size = data->state.infilesize;
675           Curl_pgrsSetUploadSize(data, data->state.infilesize);
676         }
677
678         sshc->offset += data->state.resume_from;
679       }
680       if(data->state.infilesize > 0) {
681         data->req.size = data->state.infilesize;
682         Curl_pgrsSetUploadSize(data, data->state.infilesize);
683       }
684       /* upload data */
685       Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
686
687       /* not set by Curl_setup_transfer to preserve keepon bits */
688       conn->sockfd = conn->writesockfd;
689
690       if(result) {
691         state(data, SSH_SFTP_CLOSE);
692         sshc->actualcode = result;
693       }
694       else {
695         /* store this original bitmask setup to use later on if we can't
696            figure out a "real" bitmask */
697         sshc->orig_waitfor = data->req.keepon;
698
699         /* we want to use the _sending_ function even when the socket turns
700            out readable as the underlying libssh2 sftp send function will deal
701            with both accordingly */
702         conn->cselect_bits = CURL_CSELECT_OUT;
703
704         /* since we don't really wait for anything at this point, we want the
705            state machine to move on as soon as possible so we set a very short
706            timeout here */
707         Curl_expire(data, 0, EXPIRE_RUN_NOW);
708
709         state(data, SSH_STOP);
710       }
711       break;
712     }
713     case SSH_SFTP_DOWNLOAD_INIT:
714       sshc->handleSz = sizeof(sshc->handle);
715       rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
716                              WOLFSSH_FXF_READ, NULL,
717                              sshc->handle, &sshc->handleSz);
718       if(rc == WS_FATAL_ERROR)
719         rc = wolfSSH_get_error(sshc->ssh_session);
720       if(rc == WS_WANT_READ) {
721         *block = TRUE;
722         conn->waitfor = KEEP_RECV;
723         return CURLE_OK;
724       }
725       else if(rc == WS_WANT_WRITE) {
726         *block = TRUE;
727         conn->waitfor = KEEP_SEND;
728         return CURLE_OK;
729       }
730       else if(rc == WS_SUCCESS) {
731         infof(data, "wolfssh SFTP open succeeded");
732         state(data, SSH_SFTP_DOWNLOAD_STAT);
733         return CURLE_OK;
734       }
735
736       failf(data, "wolfssh SFTP open failed: %d", rc);
737       return CURLE_SSH;
738
739     case SSH_SFTP_DOWNLOAD_STAT: {
740       WS_SFTP_FILEATRB attrs;
741       curl_off_t size;
742
743       rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs);
744       if(rc == WS_FATAL_ERROR)
745         rc = wolfSSH_get_error(sshc->ssh_session);
746       if(rc == WS_WANT_READ) {
747         *block = TRUE;
748         conn->waitfor = KEEP_RECV;
749         return CURLE_OK;
750       }
751       else if(rc == WS_WANT_WRITE) {
752         *block = TRUE;
753         conn->waitfor = KEEP_SEND;
754         return CURLE_OK;
755       }
756       else if(rc == WS_SUCCESS) {
757         infof(data, "wolfssh STAT succeeded");
758       }
759       else {
760         failf(data, "wolfssh SFTP open failed: %d", rc);
761         data->req.size = -1;
762         data->req.maxdownload = -1;
763         Curl_pgrsSetDownloadSize(data, -1);
764         return CURLE_SSH;
765       }
766
767       size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0];
768
769       data->req.size = size;
770       data->req.maxdownload = size;
771       Curl_pgrsSetDownloadSize(data, size);
772
773       infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
774
775       /* We cannot seek with wolfSSH so resuming and range requests are not
776          possible */
777       if(data->state.use_range || data->state.resume_from) {
778         infof(data, "wolfSSH cannot do range/seek on SFTP");
779         return CURLE_BAD_DOWNLOAD_RESUME;
780       }
781
782       /* Setup the actual download */
783       if(data->req.size == 0) {
784         /* no data to transfer */
785         Curl_setup_transfer(data, -1, -1, FALSE, -1);
786         infof(data, "File already completely downloaded");
787         state(data, SSH_STOP);
788         break;
789       }
790       Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
791
792       /* not set by Curl_setup_transfer to preserve keepon bits */
793       conn->writesockfd = conn->sockfd;
794
795       /* we want to use the _receiving_ function even when the socket turns
796          out writableable as the underlying libssh2 recv function will deal
797          with both accordingly */
798       conn->cselect_bits = CURL_CSELECT_IN;
799
800       if(result) {
801         /* this should never occur; the close state should be entered
802            at the time the error occurs */
803         state(data, SSH_SFTP_CLOSE);
804         sshc->actualcode = result;
805       }
806       else {
807         state(data, SSH_STOP);
808       }
809       break;
810     }
811     case SSH_SFTP_CLOSE:
812       if(sshc->handleSz)
813         rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
814                                 sshc->handleSz);
815       else
816         rc = WS_SUCCESS; /* directory listing */
817       if(rc == WS_WANT_READ) {
818         *block = TRUE;
819         conn->waitfor = KEEP_RECV;
820         return CURLE_OK;
821       }
822       else if(rc == WS_WANT_WRITE) {
823         *block = TRUE;
824         conn->waitfor = KEEP_SEND;
825         return CURLE_OK;
826       }
827       else if(rc == WS_SUCCESS) {
828         state(data, SSH_STOP);
829         return CURLE_OK;
830       }
831
832       failf(data, "wolfssh SFTP CLOSE failed: %d", rc);
833       return CURLE_SSH;
834
835     case SSH_SFTP_READDIR_INIT:
836       Curl_pgrsSetDownloadSize(data, -1);
837       if(data->set.opt_no_body) {
838         state(data, SSH_STOP);
839         break;
840       }
841       state(data, SSH_SFTP_READDIR);
842       break;
843
844     case SSH_SFTP_READDIR:
845       name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
846       if(!name)
847         rc = wolfSSH_get_error(sshc->ssh_session);
848       else
849         rc = WS_SUCCESS;
850
851       if(rc == WS_WANT_READ) {
852         *block = TRUE;
853         conn->waitfor = KEEP_RECV;
854         return CURLE_OK;
855       }
856       else if(rc == WS_WANT_WRITE) {
857         *block = TRUE;
858         conn->waitfor = KEEP_SEND;
859         return CURLE_OK;
860       }
861       else if(name && (rc == WS_SUCCESS)) {
862         WS_SFTPNAME *origname = name;
863         result = CURLE_OK;
864         while(name) {
865           char *line = aprintf("%s\n",
866                                data->set.list_only ?
867                                name->fName : name->lName);
868           if(!line) {
869             state(data, SSH_SFTP_CLOSE);
870             sshc->actualcode = CURLE_OUT_OF_MEMORY;
871             break;
872           }
873           result = Curl_client_write(data, CLIENTWRITE_BODY,
874                                      line, strlen(line));
875           free(line);
876           if(result) {
877             sshc->actualcode = result;
878             break;
879           }
880           name = name->next;
881         }
882         wolfSSH_SFTPNAME_list_free(origname);
883         state(data, SSH_STOP);
884         return result;
885       }
886       failf(data, "wolfssh SFTP ls failed: %d", rc);
887       return CURLE_SSH;
888
889     case SSH_SFTP_SHUTDOWN:
890       Curl_safefree(sshc->homedir);
891       wolfSSH_free(sshc->ssh_session);
892       wolfSSH_CTX_free(sshc->ctx);
893       state(data, SSH_STOP);
894       return CURLE_OK;
895     default:
896       break;
897     }
898   } while(!rc && (sshc->state != SSH_STOP));
899   return result;
900 }
901
902 /* called repeatedly until done from multi.c */
903 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
904 {
905   struct connectdata *conn = data->conn;
906   struct ssh_conn *sshc = &conn->proto.sshc;
907   CURLcode result = CURLE_OK;
908   bool block; /* we store the status and use that to provide a ssh_getsock()
909                  implementation */
910   do {
911     result = wssh_statemach_act(data, &block);
912     *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
913     /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
914        try again */
915     if(*done) {
916       DEBUGF(infof(data, "wssh_statemach_act says DONE"));
917     }
918   } while(!result && !*done && !block);
919
920   return result;
921 }
922
923 static
924 CURLcode wscp_perform(struct Curl_easy *data,
925                       bool *connected,
926                       bool *dophase_done)
927 {
928   (void)data;
929   (void)connected;
930   (void)dophase_done;
931   return CURLE_OK;
932 }
933
934 static
935 CURLcode wsftp_perform(struct Curl_easy *data,
936                        bool *connected,
937                        bool *dophase_done)
938 {
939   CURLcode result = CURLE_OK;
940   struct connectdata *conn = data->conn;
941
942   DEBUGF(infof(data, "DO phase starts"));
943
944   *dophase_done = FALSE; /* not done yet */
945
946   /* start the first command in the DO phase */
947   state(data, SSH_SFTP_QUOTE_INIT);
948
949   /* run the state-machine */
950   result = wssh_multi_statemach(data, dophase_done);
951
952   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
953
954   if(*dophase_done) {
955     DEBUGF(infof(data, "DO phase is complete"));
956   }
957
958   return result;
959 }
960
961 /*
962  * The DO function is generic for both protocols.
963  */
964 static CURLcode wssh_do(struct Curl_easy *data, bool *done)
965 {
966   CURLcode result;
967   bool connected = 0;
968   struct connectdata *conn = data->conn;
969   struct ssh_conn *sshc = &conn->proto.sshc;
970
971   *done = FALSE; /* default to false */
972   data->req.size = -1; /* make sure this is unknown at this point */
973   sshc->actualcode = CURLE_OK; /* reset error code */
974   sshc->secondCreateDirs = 0;   /* reset the create dir attempt state
975                                    variable */
976
977   Curl_pgrsSetUploadCounter(data, 0);
978   Curl_pgrsSetDownloadCounter(data, 0);
979   Curl_pgrsSetUploadSize(data, -1);
980   Curl_pgrsSetDownloadSize(data, -1);
981
982   if(conn->handler->protocol & CURLPROTO_SCP)
983     result = wscp_perform(data, &connected,  done);
984   else
985     result = wsftp_perform(data, &connected,  done);
986
987   return result;
988 }
989
990 static CURLcode wssh_block_statemach(struct Curl_easy *data,
991                                     bool disconnect)
992 {
993   struct connectdata *conn = data->conn;
994   struct ssh_conn *sshc = &conn->proto.sshc;
995   CURLcode result = CURLE_OK;
996
997   while((sshc->state != SSH_STOP) && !result) {
998     bool block;
999     timediff_t left = 1000;
1000     struct curltime now = Curl_now();
1001
1002     result = wssh_statemach_act(data, &block);
1003     if(result)
1004       break;
1005
1006     if(!disconnect) {
1007       if(Curl_pgrsUpdate(data))
1008         return CURLE_ABORTED_BY_CALLBACK;
1009
1010       result = Curl_speedcheck(data, now);
1011       if(result)
1012         break;
1013
1014       left = Curl_timeleft(data, NULL, FALSE);
1015       if(left < 0) {
1016         failf(data, "Operation timed out");
1017         return CURLE_OPERATION_TIMEDOUT;
1018       }
1019     }
1020
1021     if(!result) {
1022       int dir = conn->waitfor;
1023       curl_socket_t sock = conn->sock[FIRSTSOCKET];
1024       curl_socket_t fd_read = CURL_SOCKET_BAD;
1025       curl_socket_t fd_write = CURL_SOCKET_BAD;
1026       if(dir == KEEP_RECV)
1027         fd_read = sock;
1028       else if(dir == KEEP_SEND)
1029         fd_write = sock;
1030
1031       /* wait for the socket to become ready */
1032       (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
1033                               left>1000?1000:left); /* ignore result */
1034     }
1035   }
1036
1037   return result;
1038 }
1039
1040 /* generic done function for both SCP and SFTP called from their specific
1041    done functions */
1042 static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
1043 {
1044   CURLcode result = CURLE_OK;
1045   struct SSHPROTO *sftp_scp = data->req.p.ssh;
1046
1047   if(!status) {
1048     /* run the state-machine */
1049     result = wssh_block_statemach(data, FALSE);
1050   }
1051   else
1052     result = status;
1053
1054   if(sftp_scp)
1055     Curl_safefree(sftp_scp->path);
1056   if(Curl_pgrsDone(data))
1057     return CURLE_ABORTED_BY_CALLBACK;
1058
1059   data->req.keepon = 0; /* clear all bits */
1060   return result;
1061 }
1062
1063 #if 0
1064 static CURLcode wscp_done(struct Curl_easy *data,
1065                          CURLcode code, bool premature)
1066 {
1067   CURLcode result = CURLE_OK;
1068   (void)conn;
1069   (void)code;
1070   (void)premature;
1071
1072   return result;
1073 }
1074
1075 static CURLcode wscp_doing(struct Curl_easy *data,
1076                           bool *dophase_done)
1077 {
1078   CURLcode result = CURLE_OK;
1079   (void)conn;
1080   (void)dophase_done;
1081
1082   return result;
1083 }
1084
1085 static CURLcode wscp_disconnect(struct Curl_easy *data,
1086                                 struct connectdata *conn, bool dead_connection)
1087 {
1088   CURLcode result = CURLE_OK;
1089   (void)data;
1090   (void)conn;
1091   (void)dead_connection;
1092
1093   return result;
1094 }
1095 #endif
1096
1097 static CURLcode wsftp_done(struct Curl_easy *data,
1098                           CURLcode code, bool premature)
1099 {
1100   (void)premature;
1101   state(data, SSH_SFTP_CLOSE);
1102
1103   return wssh_done(data, code);
1104 }
1105
1106 static CURLcode wsftp_doing(struct Curl_easy *data,
1107                            bool *dophase_done)
1108 {
1109   CURLcode result = wssh_multi_statemach(data, dophase_done);
1110
1111   if(*dophase_done) {
1112     DEBUGF(infof(data, "DO phase is complete"));
1113   }
1114   return result;
1115 }
1116
1117 static CURLcode wsftp_disconnect(struct Curl_easy *data,
1118                                  struct connectdata *conn,
1119                                  bool dead)
1120 {
1121   CURLcode result = CURLE_OK;
1122   (void)dead;
1123
1124   DEBUGF(infof(data, "SSH DISCONNECT starts now"));
1125
1126   if(conn->proto.sshc.ssh_session) {
1127     /* only if there's a session still around to use! */
1128     state(data, SSH_SFTP_SHUTDOWN);
1129     result = wssh_block_statemach(data, TRUE);
1130   }
1131
1132   DEBUGF(infof(data, "SSH DISCONNECT is done"));
1133   return result;
1134 }
1135
1136 static int wssh_getsock(struct Curl_easy *data,
1137                         struct connectdata *conn,
1138                         curl_socket_t *sock)
1139 {
1140   int bitmap = GETSOCK_BLANK;
1141   int dir = conn->waitfor;
1142   (void)data;
1143   sock[0] = conn->sock[FIRSTSOCKET];
1144
1145   if(dir == KEEP_RECV)
1146     bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
1147   else if(dir == KEEP_SEND)
1148     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
1149
1150   return bitmap;
1151 }
1152
1153 void Curl_ssh_version(char *buffer, size_t buflen)
1154 {
1155   (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
1156 }
1157
1158 CURLcode Curl_ssh_init(void)
1159 {
1160   if(WS_SUCCESS != wolfSSH_Init()) {
1161     DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
1162     return CURLE_FAILED_INIT;
1163   }
1164
1165   return CURLE_OK;
1166 }
1167 void Curl_ssh_cleanup(void)
1168 {
1169 }
1170
1171 #endif /* USE_WOLFSSH */