Annotation of embedaddon/curl/lib/vssh/wolfssh.c, revision 1.1
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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>