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