Return to wolfssh.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / vssh |
1.1 ! misho 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 */