File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / vssh / wolfssh.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>