quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

wolfssh.c (36372B)


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