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