Annotation of embedaddon/curl/lib/vssh/libssh2.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 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: /* #define CURL_LIBSSH2_DEBUG */
! 24:
! 25: #include "curl_setup.h"
! 26:
! 27: #ifdef USE_LIBSSH2
! 28:
! 29: #include <limits.h>
! 30:
! 31: #include <libssh2.h>
! 32: #include <libssh2_sftp.h>
! 33:
! 34: #ifdef HAVE_FCNTL_H
! 35: #include <fcntl.h>
! 36: #endif
! 37:
! 38: #ifdef HAVE_NETINET_IN_H
! 39: #include <netinet/in.h>
! 40: #endif
! 41: #ifdef HAVE_ARPA_INET_H
! 42: #include <arpa/inet.h>
! 43: #endif
! 44: #ifdef HAVE_UTSNAME_H
! 45: #include <sys/utsname.h>
! 46: #endif
! 47: #ifdef HAVE_NETDB_H
! 48: #include <netdb.h>
! 49: #endif
! 50: #ifdef __VMS
! 51: #include <in.h>
! 52: #include <inet.h>
! 53: #endif
! 54:
! 55: #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
! 56: #undef in_addr_t
! 57: #define in_addr_t unsigned long
! 58: #endif
! 59:
! 60: #include <curl/curl.h>
! 61: #include "urldata.h"
! 62: #include "sendf.h"
! 63: #include "hostip.h"
! 64: #include "progress.h"
! 65: #include "transfer.h"
! 66: #include "escape.h"
! 67: #include "http.h" /* for HTTP proxy tunnel stuff */
! 68: #include "ssh.h"
! 69: #include "url.h"
! 70: #include "speedcheck.h"
! 71: #include "getinfo.h"
! 72: #include "strdup.h"
! 73: #include "strcase.h"
! 74: #include "vtls/vtls.h"
! 75: #include "connect.h"
! 76: #include "strerror.h"
! 77: #include "inet_ntop.h"
! 78: #include "parsedate.h" /* for the week day and month names */
! 79: #include "sockaddr.h" /* required for Curl_sockaddr_storage */
! 80: #include "strtoofft.h"
! 81: #include "multiif.h"
! 82: #include "select.h"
! 83: #include "warnless.h"
! 84: #include "curl_path.h"
! 85:
! 86: /* The last 3 #include files should be in this order */
! 87: #include "curl_printf.h"
! 88: #include "curl_memory.h"
! 89: #include "memdebug.h"
! 90:
! 91: #if LIBSSH2_VERSION_NUM >= 0x010206
! 92: /* libssh2_sftp_statvfs and friends were added in 1.2.6 */
! 93: #define HAS_STATVFS_SUPPORT 1
! 94: #endif
! 95:
! 96: #define sftp_libssh2_last_error(s) curlx_ultosi(libssh2_sftp_last_error(s))
! 97:
! 98: #define sftp_libssh2_realpath(s,p,t,m) \
! 99: libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \
! 100: (t), (m), LIBSSH2_SFTP_REALPATH)
! 101:
! 102:
! 103: /* Local functions: */
! 104: static const char *sftp_libssh2_strerror(int err);
! 105: static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
! 106: static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
! 107: static LIBSSH2_FREE_FUNC(my_libssh2_free);
! 108:
! 109: static CURLcode ssh_force_knownhost_key_type(struct connectdata *conn);
! 110: static CURLcode ssh_connect(struct connectdata *conn, bool *done);
! 111: static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done);
! 112: static CURLcode ssh_do(struct connectdata *conn, bool *done);
! 113:
! 114: static CURLcode scp_done(struct connectdata *conn,
! 115: CURLcode, bool premature);
! 116: static CURLcode scp_doing(struct connectdata *conn,
! 117: bool *dophase_done);
! 118: static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection);
! 119:
! 120: static CURLcode sftp_done(struct connectdata *conn,
! 121: CURLcode, bool premature);
! 122: static CURLcode sftp_doing(struct connectdata *conn,
! 123: bool *dophase_done);
! 124: static CURLcode sftp_disconnect(struct connectdata *conn, bool dead);
! 125: static
! 126: CURLcode sftp_perform(struct connectdata *conn,
! 127: bool *connected,
! 128: bool *dophase_done);
! 129: static int ssh_getsock(struct connectdata *conn, curl_socket_t *sock);
! 130: static int ssh_perform_getsock(const struct connectdata *conn,
! 131: curl_socket_t *sock);
! 132: static CURLcode ssh_setup_connection(struct connectdata *conn);
! 133:
! 134: /*
! 135: * SCP protocol handler.
! 136: */
! 137:
! 138: const struct Curl_handler Curl_handler_scp = {
! 139: "SCP", /* scheme */
! 140: ssh_setup_connection, /* setup_connection */
! 141: ssh_do, /* do_it */
! 142: scp_done, /* done */
! 143: ZERO_NULL, /* do_more */
! 144: ssh_connect, /* connect_it */
! 145: ssh_multi_statemach, /* connecting */
! 146: scp_doing, /* doing */
! 147: ssh_getsock, /* proto_getsock */
! 148: ssh_getsock, /* doing_getsock */
! 149: ZERO_NULL, /* domore_getsock */
! 150: ssh_perform_getsock, /* perform_getsock */
! 151: scp_disconnect, /* disconnect */
! 152: ZERO_NULL, /* readwrite */
! 153: ZERO_NULL, /* connection_check */
! 154: PORT_SSH, /* defport */
! 155: CURLPROTO_SCP, /* protocol */
! 156: PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
! 157: | PROTOPT_NOURLQUERY /* flags */
! 158: };
! 159:
! 160:
! 161: /*
! 162: * SFTP protocol handler.
! 163: */
! 164:
! 165: const struct Curl_handler Curl_handler_sftp = {
! 166: "SFTP", /* scheme */
! 167: ssh_setup_connection, /* setup_connection */
! 168: ssh_do, /* do_it */
! 169: sftp_done, /* done */
! 170: ZERO_NULL, /* do_more */
! 171: ssh_connect, /* connect_it */
! 172: ssh_multi_statemach, /* connecting */
! 173: sftp_doing, /* doing */
! 174: ssh_getsock, /* proto_getsock */
! 175: ssh_getsock, /* doing_getsock */
! 176: ZERO_NULL, /* domore_getsock */
! 177: ssh_perform_getsock, /* perform_getsock */
! 178: sftp_disconnect, /* disconnect */
! 179: ZERO_NULL, /* readwrite */
! 180: ZERO_NULL, /* connection_check */
! 181: PORT_SSH, /* defport */
! 182: CURLPROTO_SFTP, /* protocol */
! 183: PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
! 184: | PROTOPT_NOURLQUERY /* flags */
! 185: };
! 186:
! 187: static void
! 188: kbd_callback(const char *name, int name_len, const char *instruction,
! 189: int instruction_len, int num_prompts,
! 190: const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
! 191: LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
! 192: void **abstract)
! 193: {
! 194: struct connectdata *conn = (struct connectdata *)*abstract;
! 195:
! 196: #ifdef CURL_LIBSSH2_DEBUG
! 197: fprintf(stderr, "name=%s\n", name);
! 198: fprintf(stderr, "name_len=%d\n", name_len);
! 199: fprintf(stderr, "instruction=%s\n", instruction);
! 200: fprintf(stderr, "instruction_len=%d\n", instruction_len);
! 201: fprintf(stderr, "num_prompts=%d\n", num_prompts);
! 202: #else
! 203: (void)name;
! 204: (void)name_len;
! 205: (void)instruction;
! 206: (void)instruction_len;
! 207: #endif /* CURL_LIBSSH2_DEBUG */
! 208: if(num_prompts == 1) {
! 209: responses[0].text = strdup(conn->passwd);
! 210: responses[0].length = curlx_uztoui(strlen(conn->passwd));
! 211: }
! 212: (void)prompts;
! 213: (void)abstract;
! 214: } /* kbd_callback */
! 215:
! 216: static CURLcode sftp_libssh2_error_to_CURLE(int err)
! 217: {
! 218: switch(err) {
! 219: case LIBSSH2_FX_OK:
! 220: return CURLE_OK;
! 221:
! 222: case LIBSSH2_FX_NO_SUCH_FILE:
! 223: case LIBSSH2_FX_NO_SUCH_PATH:
! 224: return CURLE_REMOTE_FILE_NOT_FOUND;
! 225:
! 226: case LIBSSH2_FX_PERMISSION_DENIED:
! 227: case LIBSSH2_FX_WRITE_PROTECT:
! 228: case LIBSSH2_FX_LOCK_CONFlICT:
! 229: return CURLE_REMOTE_ACCESS_DENIED;
! 230:
! 231: case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
! 232: case LIBSSH2_FX_QUOTA_EXCEEDED:
! 233: return CURLE_REMOTE_DISK_FULL;
! 234:
! 235: case LIBSSH2_FX_FILE_ALREADY_EXISTS:
! 236: return CURLE_REMOTE_FILE_EXISTS;
! 237:
! 238: case LIBSSH2_FX_DIR_NOT_EMPTY:
! 239: return CURLE_QUOTE_ERROR;
! 240:
! 241: default:
! 242: break;
! 243: }
! 244:
! 245: return CURLE_SSH;
! 246: }
! 247:
! 248: static CURLcode libssh2_session_error_to_CURLE(int err)
! 249: {
! 250: switch(err) {
! 251: /* Ordered by order of appearance in libssh2.h */
! 252: case LIBSSH2_ERROR_NONE:
! 253: return CURLE_OK;
! 254:
! 255: /* This is the error returned by libssh2_scp_recv2
! 256: * on unknown file */
! 257: case LIBSSH2_ERROR_SCP_PROTOCOL:
! 258: return CURLE_REMOTE_FILE_NOT_FOUND;
! 259:
! 260: case LIBSSH2_ERROR_SOCKET_NONE:
! 261: return CURLE_COULDNT_CONNECT;
! 262:
! 263: case LIBSSH2_ERROR_ALLOC:
! 264: return CURLE_OUT_OF_MEMORY;
! 265:
! 266: case LIBSSH2_ERROR_SOCKET_SEND:
! 267: return CURLE_SEND_ERROR;
! 268:
! 269: case LIBSSH2_ERROR_HOSTKEY_INIT:
! 270: case LIBSSH2_ERROR_HOSTKEY_SIGN:
! 271: case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED:
! 272: case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
! 273: return CURLE_PEER_FAILED_VERIFICATION;
! 274:
! 275: case LIBSSH2_ERROR_PASSWORD_EXPIRED:
! 276: return CURLE_LOGIN_DENIED;
! 277:
! 278: case LIBSSH2_ERROR_SOCKET_TIMEOUT:
! 279: case LIBSSH2_ERROR_TIMEOUT:
! 280: return CURLE_OPERATION_TIMEDOUT;
! 281:
! 282: case LIBSSH2_ERROR_EAGAIN:
! 283: return CURLE_AGAIN;
! 284: }
! 285:
! 286: return CURLE_SSH;
! 287: }
! 288:
! 289: static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc)
! 290: {
! 291: (void)abstract; /* arg not used */
! 292: return malloc(count);
! 293: }
! 294:
! 295: static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc)
! 296: {
! 297: (void)abstract; /* arg not used */
! 298: return realloc(ptr, count);
! 299: }
! 300:
! 301: static LIBSSH2_FREE_FUNC(my_libssh2_free)
! 302: {
! 303: (void)abstract; /* arg not used */
! 304: if(ptr) /* ssh2 agent sometimes call free with null ptr */
! 305: free(ptr);
! 306: }
! 307:
! 308: /*
! 309: * SSH State machine related code
! 310: */
! 311: /* This is the ONLY way to change SSH state! */
! 312: static void state(struct connectdata *conn, sshstate nowstate)
! 313: {
! 314: struct ssh_conn *sshc = &conn->proto.sshc;
! 315: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
! 316: /* for debug purposes */
! 317: static const char * const names[] = {
! 318: "SSH_STOP",
! 319: "SSH_INIT",
! 320: "SSH_S_STARTUP",
! 321: "SSH_HOSTKEY",
! 322: "SSH_AUTHLIST",
! 323: "SSH_AUTH_PKEY_INIT",
! 324: "SSH_AUTH_PKEY",
! 325: "SSH_AUTH_PASS_INIT",
! 326: "SSH_AUTH_PASS",
! 327: "SSH_AUTH_AGENT_INIT",
! 328: "SSH_AUTH_AGENT_LIST",
! 329: "SSH_AUTH_AGENT",
! 330: "SSH_AUTH_HOST_INIT",
! 331: "SSH_AUTH_HOST",
! 332: "SSH_AUTH_KEY_INIT",
! 333: "SSH_AUTH_KEY",
! 334: "SSH_AUTH_GSSAPI",
! 335: "SSH_AUTH_DONE",
! 336: "SSH_SFTP_INIT",
! 337: "SSH_SFTP_REALPATH",
! 338: "SSH_SFTP_QUOTE_INIT",
! 339: "SSH_SFTP_POSTQUOTE_INIT",
! 340: "SSH_SFTP_QUOTE",
! 341: "SSH_SFTP_NEXT_QUOTE",
! 342: "SSH_SFTP_QUOTE_STAT",
! 343: "SSH_SFTP_QUOTE_SETSTAT",
! 344: "SSH_SFTP_QUOTE_SYMLINK",
! 345: "SSH_SFTP_QUOTE_MKDIR",
! 346: "SSH_SFTP_QUOTE_RENAME",
! 347: "SSH_SFTP_QUOTE_RMDIR",
! 348: "SSH_SFTP_QUOTE_UNLINK",
! 349: "SSH_SFTP_QUOTE_STATVFS",
! 350: "SSH_SFTP_GETINFO",
! 351: "SSH_SFTP_FILETIME",
! 352: "SSH_SFTP_TRANS_INIT",
! 353: "SSH_SFTP_UPLOAD_INIT",
! 354: "SSH_SFTP_CREATE_DIRS_INIT",
! 355: "SSH_SFTP_CREATE_DIRS",
! 356: "SSH_SFTP_CREATE_DIRS_MKDIR",
! 357: "SSH_SFTP_READDIR_INIT",
! 358: "SSH_SFTP_READDIR",
! 359: "SSH_SFTP_READDIR_LINK",
! 360: "SSH_SFTP_READDIR_BOTTOM",
! 361: "SSH_SFTP_READDIR_DONE",
! 362: "SSH_SFTP_DOWNLOAD_INIT",
! 363: "SSH_SFTP_DOWNLOAD_STAT",
! 364: "SSH_SFTP_CLOSE",
! 365: "SSH_SFTP_SHUTDOWN",
! 366: "SSH_SCP_TRANS_INIT",
! 367: "SSH_SCP_UPLOAD_INIT",
! 368: "SSH_SCP_DOWNLOAD_INIT",
! 369: "SSH_SCP_DOWNLOAD",
! 370: "SSH_SCP_DONE",
! 371: "SSH_SCP_SEND_EOF",
! 372: "SSH_SCP_WAIT_EOF",
! 373: "SSH_SCP_WAIT_CLOSE",
! 374: "SSH_SCP_CHANNEL_FREE",
! 375: "SSH_SESSION_DISCONNECT",
! 376: "SSH_SESSION_FREE",
! 377: "QUIT"
! 378: };
! 379:
! 380: /* a precaution to make sure the lists are in sync */
! 381: DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
! 382:
! 383: if(sshc->state != nowstate) {
! 384: infof(conn->data, "SFTP %p state change from %s to %s\n",
! 385: (void *)sshc, names[sshc->state], names[nowstate]);
! 386: }
! 387: #endif
! 388:
! 389: sshc->state = nowstate;
! 390: }
! 391:
! 392:
! 393: #ifdef HAVE_LIBSSH2_KNOWNHOST_API
! 394: static int sshkeycallback(struct Curl_easy *easy,
! 395: const struct curl_khkey *knownkey, /* known */
! 396: const struct curl_khkey *foundkey, /* found */
! 397: enum curl_khmatch match,
! 398: void *clientp)
! 399: {
! 400: (void)easy;
! 401: (void)knownkey;
! 402: (void)foundkey;
! 403: (void)clientp;
! 404:
! 405: /* we only allow perfect matches, and we reject everything else */
! 406: return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE;
! 407: }
! 408: #endif
! 409:
! 410: /*
! 411: * Earlier libssh2 versions didn't have the ability to seek to 64bit positions
! 412: * with 32bit size_t.
! 413: */
! 414: #ifdef HAVE_LIBSSH2_SFTP_SEEK64
! 415: #define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
! 416: #else
! 417: #define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
! 418: #endif
! 419:
! 420: /*
! 421: * Earlier libssh2 versions didn't do SCP properly beyond 32bit sizes on 32bit
! 422: * architectures so we check of the necessary function is present.
! 423: */
! 424: #ifndef HAVE_LIBSSH2_SCP_SEND64
! 425: #define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0)
! 426: #else
! 427: #define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c), \
! 428: (libssh2_uint64_t)d, 0, 0)
! 429: #endif
! 430:
! 431: /*
! 432: * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64.
! 433: */
! 434: #ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE
! 435: #define libssh2_session_startup(x,y) libssh2_session_handshake(x,y)
! 436: #endif
! 437:
! 438: static CURLcode ssh_knownhost(struct connectdata *conn)
! 439: {
! 440: CURLcode result = CURLE_OK;
! 441:
! 442: #ifdef HAVE_LIBSSH2_KNOWNHOST_API
! 443: struct Curl_easy *data = conn->data;
! 444:
! 445: if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
! 446: /* we're asked to verify the host against a file */
! 447: struct ssh_conn *sshc = &conn->proto.sshc;
! 448: int rc;
! 449: int keytype;
! 450: size_t keylen;
! 451: const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
! 452: &keylen, &keytype);
! 453: int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE;
! 454: int keybit = 0;
! 455:
! 456: if(remotekey) {
! 457: /*
! 458: * A subject to figure out is what host name we need to pass in here.
! 459: * What host name does OpenSSH store in its file if an IDN name is
! 460: * used?
! 461: */
! 462: struct libssh2_knownhost *host;
! 463: enum curl_khmatch keymatch;
! 464: curl_sshkeycallback func =
! 465: data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback;
! 466: struct curl_khkey knownkey;
! 467: struct curl_khkey *knownkeyp = NULL;
! 468: struct curl_khkey foundkey;
! 469:
! 470: switch(keytype) {
! 471: case LIBSSH2_HOSTKEY_TYPE_RSA:
! 472: keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
! 473: break;
! 474: case LIBSSH2_HOSTKEY_TYPE_DSS:
! 475: keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
! 476: break;
! 477: #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
! 478: case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
! 479: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
! 480: break;
! 481: #endif
! 482: #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
! 483: case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
! 484: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
! 485: break;
! 486: #endif
! 487: #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
! 488: case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
! 489: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
! 490: break;
! 491: #endif
! 492: #ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
! 493: case LIBSSH2_HOSTKEY_TYPE_ED25519:
! 494: keybit = LIBSSH2_KNOWNHOST_KEY_ED25519;
! 495: break;
! 496: #endif
! 497: default:
! 498: infof(data, "unsupported key type, can't check knownhosts!\n");
! 499: keybit = 0;
! 500: break;
! 501: }
! 502: if(!keybit)
! 503: /* no check means failure! */
! 504: rc = CURLKHSTAT_REJECT;
! 505: else {
! 506: #ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP
! 507: keycheck = libssh2_knownhost_checkp(sshc->kh,
! 508: conn->host.name,
! 509: (conn->remote_port != PORT_SSH)?
! 510: conn->remote_port:-1,
! 511: remotekey, keylen,
! 512: LIBSSH2_KNOWNHOST_TYPE_PLAIN|
! 513: LIBSSH2_KNOWNHOST_KEYENC_RAW|
! 514: keybit,
! 515: &host);
! 516: #else
! 517: keycheck = libssh2_knownhost_check(sshc->kh,
! 518: conn->host.name,
! 519: remotekey, keylen,
! 520: LIBSSH2_KNOWNHOST_TYPE_PLAIN|
! 521: LIBSSH2_KNOWNHOST_KEYENC_RAW|
! 522: keybit,
! 523: &host);
! 524: #endif
! 525:
! 526: infof(data, "SSH host check: %d, key: %s\n", keycheck,
! 527: (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
! 528: host->key:"<none>");
! 529:
! 530: /* setup 'knownkey' */
! 531: if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
! 532: knownkey.key = host->key;
! 533: knownkey.len = 0;
! 534: knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
! 535: CURLKHTYPE_RSA : CURLKHTYPE_DSS;
! 536: knownkeyp = &knownkey;
! 537: }
! 538:
! 539: /* setup 'foundkey' */
! 540: foundkey.key = remotekey;
! 541: foundkey.len = keylen;
! 542: foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
! 543: CURLKHTYPE_RSA : CURLKHTYPE_DSS;
! 544:
! 545: /*
! 546: * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
! 547: * curl_khmatch enum are ever modified, we need to introduce a
! 548: * translation table here!
! 549: */
! 550: keymatch = (enum curl_khmatch)keycheck;
! 551:
! 552: /* Ask the callback how to behave */
! 553: Curl_set_in_callback(data, true);
! 554: rc = func(data, knownkeyp, /* from the knownhosts file */
! 555: &foundkey, /* from the remote host */
! 556: keymatch, data->set.ssh_keyfunc_userp);
! 557: Curl_set_in_callback(data, false);
! 558: }
! 559: }
! 560: else
! 561: /* no remotekey means failure! */
! 562: rc = CURLKHSTAT_REJECT;
! 563:
! 564: switch(rc) {
! 565: default: /* unknown return codes will equal reject */
! 566: /* FALLTHROUGH */
! 567: case CURLKHSTAT_REJECT:
! 568: state(conn, SSH_SESSION_FREE);
! 569: /* FALLTHROUGH */
! 570: case CURLKHSTAT_DEFER:
! 571: /* DEFER means bail out but keep the SSH_HOSTKEY state */
! 572: result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
! 573: break;
! 574: case CURLKHSTAT_FINE:
! 575: case CURLKHSTAT_FINE_ADD_TO_FILE:
! 576: /* proceed */
! 577: if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
! 578: /* the found host+key didn't match but has been told to be fine
! 579: anyway so we add it in memory */
! 580: int addrc = libssh2_knownhost_add(sshc->kh,
! 581: conn->host.name, NULL,
! 582: remotekey, keylen,
! 583: LIBSSH2_KNOWNHOST_TYPE_PLAIN|
! 584: LIBSSH2_KNOWNHOST_KEYENC_RAW|
! 585: keybit, NULL);
! 586: if(addrc)
! 587: infof(data, "Warning adding the known host %s failed!\n",
! 588: conn->host.name);
! 589: else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) {
! 590: /* now we write the entire in-memory list of known hosts to the
! 591: known_hosts file */
! 592: int wrc =
! 593: libssh2_knownhost_writefile(sshc->kh,
! 594: data->set.str[STRING_SSH_KNOWNHOSTS],
! 595: LIBSSH2_KNOWNHOST_FILE_OPENSSH);
! 596: if(wrc) {
! 597: infof(data, "Warning, writing %s failed!\n",
! 598: data->set.str[STRING_SSH_KNOWNHOSTS]);
! 599: }
! 600: }
! 601: }
! 602: break;
! 603: }
! 604: }
! 605: #else /* HAVE_LIBSSH2_KNOWNHOST_API */
! 606: (void)conn;
! 607: #endif
! 608: return result;
! 609: }
! 610:
! 611: static CURLcode ssh_check_fingerprint(struct connectdata *conn)
! 612: {
! 613: struct ssh_conn *sshc = &conn->proto.sshc;
! 614: struct Curl_easy *data = conn->data;
! 615: const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
! 616: char md5buffer[33];
! 617:
! 618: const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
! 619: LIBSSH2_HOSTKEY_HASH_MD5);
! 620:
! 621: if(fingerprint) {
! 622: /* The fingerprint points to static storage (!), don't free() it. */
! 623: int i;
! 624: for(i = 0; i < 16; i++)
! 625: msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
! 626: infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
! 627: }
! 628:
! 629: /* Before we authenticate we check the hostkey's MD5 fingerprint
! 630: * against a known fingerprint, if available.
! 631: */
! 632: if(pubkey_md5 && strlen(pubkey_md5) == 32) {
! 633: if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
! 634: if(fingerprint)
! 635: failf(data,
! 636: "Denied establishing ssh session: mismatch md5 fingerprint. "
! 637: "Remote %s is not equal to %s", md5buffer, pubkey_md5);
! 638: else
! 639: failf(data,
! 640: "Denied establishing ssh session: md5 fingerprint not available");
! 641: state(conn, SSH_SESSION_FREE);
! 642: sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
! 643: return sshc->actualcode;
! 644: }
! 645: infof(data, "MD5 checksum match!\n");
! 646: /* as we already matched, we skip the check for known hosts */
! 647: return CURLE_OK;
! 648: }
! 649: return ssh_knownhost(conn);
! 650: }
! 651:
! 652: /*
! 653: * ssh_force_knownhost_key_type() will check the known hosts file and try to
! 654: * force a specific public key type from the server if an entry is found.
! 655: */
! 656: static CURLcode ssh_force_knownhost_key_type(struct connectdata *conn)
! 657: {
! 658: CURLcode result = CURLE_OK;
! 659:
! 660: #ifdef HAVE_LIBSSH2_KNOWNHOST_API
! 661:
! 662: #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
! 663: static const char * const hostkey_method_ssh_ed25519
! 664: = "ssh-ed25519";
! 665: #endif
! 666: #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
! 667: static const char * const hostkey_method_ssh_ecdsa_521
! 668: = "ecdsa-sha2-nistp521";
! 669: #endif
! 670: #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
! 671: static const char * const hostkey_method_ssh_ecdsa_384
! 672: = "ecdsa-sha2-nistp384";
! 673: #endif
! 674: #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
! 675: static const char * const hostkey_method_ssh_ecdsa_256
! 676: = "ecdsa-sha2-nistp256";
! 677: #endif
! 678: static const char * const hostkey_method_ssh_rsa
! 679: = "ssh-rsa";
! 680: static const char * const hostkey_method_ssh_dss
! 681: = "ssh-dss";
! 682:
! 683: const char *hostkey_method = NULL;
! 684: struct ssh_conn *sshc = &conn->proto.sshc;
! 685: struct Curl_easy *data = conn->data;
! 686: struct libssh2_knownhost* store = NULL;
! 687: const char *kh_name_end = NULL;
! 688: size_t kh_name_size = 0;
! 689: int port = 0;
! 690: bool found = false;
! 691:
! 692: if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
! 693: /* lets try to find our host in the known hosts file */
! 694: while(!libssh2_knownhost_get(sshc->kh, &store, store)) {
! 695: /* For non-standard ports, the name will be enclosed in */
! 696: /* square brackets, followed by a colon and the port */
! 697: if(store) {
! 698: if(store->name) {
! 699: if(store->name[0] == '[') {
! 700: kh_name_end = strstr(store->name, "]:");
! 701: if(!kh_name_end) {
! 702: infof(data, "Invalid host pattern %s in %s\n",
! 703: store->name, data->set.str[STRING_SSH_KNOWNHOSTS]);
! 704: continue;
! 705: }
! 706: port = atoi(kh_name_end + 2);
! 707: if(kh_name_end && (port == conn->remote_port)) {
! 708: kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end);
! 709: if(strncmp(store->name + 1,
! 710: conn->host.name, kh_name_size) == 0) {
! 711: found = true;
! 712: break;
! 713: }
! 714: }
! 715: }
! 716: else if(strcmp(store->name, conn->host.name) == 0) {
! 717: found = true;
! 718: break;
! 719: }
! 720: }
! 721: else {
! 722: found = true;
! 723: break;
! 724: }
! 725: }
! 726: }
! 727:
! 728: if(found) {
! 729: infof(data, "Found host %s in %s\n",
! 730: conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
! 731:
! 732: switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) {
! 733: #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
! 734: case LIBSSH2_KNOWNHOST_KEY_ED25519:
! 735: hostkey_method = hostkey_method_ssh_ed25519;
! 736: break;
! 737: #endif
! 738: #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
! 739: case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
! 740: hostkey_method = hostkey_method_ssh_ecdsa_521;
! 741: break;
! 742: #endif
! 743: #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
! 744: case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
! 745: hostkey_method = hostkey_method_ssh_ecdsa_384;
! 746: break;
! 747: #endif
! 748: #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
! 749: case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
! 750: hostkey_method = hostkey_method_ssh_ecdsa_256;
! 751: break;
! 752: #endif
! 753: case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
! 754: hostkey_method = hostkey_method_ssh_rsa;
! 755: break;
! 756: case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
! 757: hostkey_method = hostkey_method_ssh_dss;
! 758: break;
! 759: case LIBSSH2_KNOWNHOST_KEY_RSA1:
! 760: failf(data, "Found host key type RSA1 which is not supported\n");
! 761: return CURLE_SSH;
! 762: default:
! 763: failf(data, "Unknown host key type: %i\n",
! 764: (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK));
! 765: return CURLE_SSH;
! 766: }
! 767:
! 768: infof(data, "Set \"%s\" as SSH hostkey type\n", hostkey_method);
! 769: result = libssh2_session_error_to_CURLE(
! 770: libssh2_session_method_pref(
! 771: sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method));
! 772: }
! 773: else {
! 774: infof(data, "Did not find host %s in %s\n",
! 775: conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
! 776: }
! 777: }
! 778:
! 779: #endif /* HAVE_LIBSSH2_KNOWNHOST_API */
! 780:
! 781: return result;
! 782: }
! 783:
! 784: /*
! 785: * ssh_statemach_act() runs the SSH state machine as far as it can without
! 786: * blocking and without reaching the end. The data the pointer 'block' points
! 787: * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
! 788: * meaning it wants to be called again when the socket is ready
! 789: */
! 790:
! 791: static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
! 792: {
! 793: CURLcode result = CURLE_OK;
! 794: struct Curl_easy *data = conn->data;
! 795: struct SSHPROTO *sftp_scp = data->req.protop;
! 796: struct ssh_conn *sshc = &conn->proto.sshc;
! 797: curl_socket_t sock = conn->sock[FIRSTSOCKET];
! 798: char *new_readdir_line;
! 799: int rc = LIBSSH2_ERROR_NONE;
! 800: int err;
! 801: int seekerr = CURL_SEEKFUNC_OK;
! 802: *block = 0; /* we're not blocking by default */
! 803:
! 804: do {
! 805:
! 806: switch(sshc->state) {
! 807: case SSH_INIT:
! 808: sshc->secondCreateDirs = 0;
! 809: sshc->nextstate = SSH_NO_STATE;
! 810: sshc->actualcode = CURLE_OK;
! 811:
! 812: /* Set libssh2 to non-blocking, since everything internally is
! 813: non-blocking */
! 814: libssh2_session_set_blocking(sshc->ssh_session, 0);
! 815:
! 816: result = ssh_force_knownhost_key_type(conn);
! 817: if(result) {
! 818: state(conn, SSH_SESSION_FREE);
! 819: break;
! 820: }
! 821:
! 822: state(conn, SSH_S_STARTUP);
! 823: /* FALLTHROUGH */
! 824:
! 825: case SSH_S_STARTUP:
! 826: rc = libssh2_session_startup(sshc->ssh_session, (int)sock);
! 827: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 828: break;
! 829: }
! 830: if(rc) {
! 831: char *err_msg = NULL;
! 832: (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
! 833: failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
! 834:
! 835: state(conn, SSH_SESSION_FREE);
! 836: sshc->actualcode = CURLE_FAILED_INIT;
! 837: break;
! 838: }
! 839:
! 840: state(conn, SSH_HOSTKEY);
! 841:
! 842: /* FALLTHROUGH */
! 843: case SSH_HOSTKEY:
! 844: /*
! 845: * Before we authenticate we should check the hostkey's fingerprint
! 846: * against our known hosts. How that is handled (reading from file,
! 847: * whatever) is up to us.
! 848: */
! 849: result = ssh_check_fingerprint(conn);
! 850: if(!result)
! 851: state(conn, SSH_AUTHLIST);
! 852: /* ssh_check_fingerprint sets state appropriately on error */
! 853: break;
! 854:
! 855: case SSH_AUTHLIST:
! 856: /*
! 857: * Figure out authentication methods
! 858: * NB: As soon as we have provided a username to an openssh server we
! 859: * must never change it later. Thus, always specify the correct username
! 860: * here, even though the libssh2 docs kind of indicate that it should be
! 861: * possible to get a 'generic' list (not user-specific) of authentication
! 862: * methods, presumably with a blank username. That won't work in my
! 863: * experience.
! 864: * So always specify it here.
! 865: */
! 866: sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
! 867: conn->user,
! 868: curlx_uztoui(strlen(conn->user)));
! 869:
! 870: if(!sshc->authlist) {
! 871: if(libssh2_userauth_authenticated(sshc->ssh_session)) {
! 872: sshc->authed = TRUE;
! 873: infof(data, "SSH user accepted with no authentication\n");
! 874: state(conn, SSH_AUTH_DONE);
! 875: break;
! 876: }
! 877: err = libssh2_session_last_errno(sshc->ssh_session);
! 878: if(err == LIBSSH2_ERROR_EAGAIN)
! 879: rc = LIBSSH2_ERROR_EAGAIN;
! 880: else {
! 881: state(conn, SSH_SESSION_FREE);
! 882: sshc->actualcode = libssh2_session_error_to_CURLE(err);
! 883: }
! 884: break;
! 885: }
! 886: infof(data, "SSH authentication methods available: %s\n",
! 887: sshc->authlist);
! 888:
! 889: state(conn, SSH_AUTH_PKEY_INIT);
! 890: break;
! 891:
! 892: case SSH_AUTH_PKEY_INIT:
! 893: /*
! 894: * Check the supported auth types in the order I feel is most secure
! 895: * with the requested type of authentication
! 896: */
! 897: sshc->authed = FALSE;
! 898:
! 899: if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
! 900: (strstr(sshc->authlist, "publickey") != NULL)) {
! 901: bool out_of_memory = FALSE;
! 902:
! 903: sshc->rsa_pub = sshc->rsa = NULL;
! 904:
! 905: if(data->set.str[STRING_SSH_PRIVATE_KEY])
! 906: sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]);
! 907: else {
! 908: /* To ponder about: should really the lib be messing about with the
! 909: HOME environment variable etc? */
! 910: char *home = curl_getenv("HOME");
! 911:
! 912: /* If no private key file is specified, try some common paths. */
! 913: if(home) {
! 914: /* Try ~/.ssh first. */
! 915: sshc->rsa = aprintf("%s/.ssh/id_rsa", home);
! 916: if(!sshc->rsa)
! 917: out_of_memory = TRUE;
! 918: else if(access(sshc->rsa, R_OK) != 0) {
! 919: Curl_safefree(sshc->rsa);
! 920: sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
! 921: if(!sshc->rsa)
! 922: out_of_memory = TRUE;
! 923: else if(access(sshc->rsa, R_OK) != 0) {
! 924: Curl_safefree(sshc->rsa);
! 925: }
! 926: }
! 927: free(home);
! 928: }
! 929: if(!out_of_memory && !sshc->rsa) {
! 930: /* Nothing found; try the current dir. */
! 931: sshc->rsa = strdup("id_rsa");
! 932: if(sshc->rsa && access(sshc->rsa, R_OK) != 0) {
! 933: Curl_safefree(sshc->rsa);
! 934: sshc->rsa = strdup("id_dsa");
! 935: if(sshc->rsa && access(sshc->rsa, R_OK) != 0) {
! 936: Curl_safefree(sshc->rsa);
! 937: /* Out of guesses. Set to the empty string to avoid
! 938: * surprising info messages. */
! 939: sshc->rsa = strdup("");
! 940: }
! 941: }
! 942: }
! 943: }
! 944:
! 945: /*
! 946: * Unless the user explicitly specifies a public key file, let
! 947: * libssh2 extract the public key from the private key file.
! 948: * This is done by simply passing sshc->rsa_pub = NULL.
! 949: */
! 950: if(data->set.str[STRING_SSH_PUBLIC_KEY]
! 951: /* treat empty string the same way as NULL */
! 952: && data->set.str[STRING_SSH_PUBLIC_KEY][0]) {
! 953: sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]);
! 954: if(!sshc->rsa_pub)
! 955: out_of_memory = TRUE;
! 956: }
! 957:
! 958: if(out_of_memory || sshc->rsa == NULL) {
! 959: Curl_safefree(sshc->rsa);
! 960: Curl_safefree(sshc->rsa_pub);
! 961: state(conn, SSH_SESSION_FREE);
! 962: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 963: break;
! 964: }
! 965:
! 966: sshc->passphrase = data->set.ssl.key_passwd;
! 967: if(!sshc->passphrase)
! 968: sshc->passphrase = "";
! 969:
! 970: if(sshc->rsa_pub)
! 971: infof(data, "Using SSH public key file '%s'\n", sshc->rsa_pub);
! 972: infof(data, "Using SSH private key file '%s'\n", sshc->rsa);
! 973:
! 974: state(conn, SSH_AUTH_PKEY);
! 975: }
! 976: else {
! 977: state(conn, SSH_AUTH_PASS_INIT);
! 978: }
! 979: break;
! 980:
! 981: case SSH_AUTH_PKEY:
! 982: /* The function below checks if the files exists, no need to stat() here.
! 983: */
! 984: rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
! 985: conn->user,
! 986: curlx_uztoui(
! 987: strlen(conn->user)),
! 988: sshc->rsa_pub,
! 989: sshc->rsa, sshc->passphrase);
! 990: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 991: break;
! 992: }
! 993:
! 994: Curl_safefree(sshc->rsa_pub);
! 995: Curl_safefree(sshc->rsa);
! 996:
! 997: if(rc == 0) {
! 998: sshc->authed = TRUE;
! 999: infof(data, "Initialized SSH public key authentication\n");
! 1000: state(conn, SSH_AUTH_DONE);
! 1001: }
! 1002: else {
! 1003: char *err_msg = NULL;
! 1004: (void)libssh2_session_last_error(sshc->ssh_session,
! 1005: &err_msg, NULL, 0);
! 1006: infof(data, "SSH public key authentication failed: %s\n", err_msg);
! 1007: state(conn, SSH_AUTH_PASS_INIT);
! 1008: rc = 0; /* clear rc and continue */
! 1009: }
! 1010: break;
! 1011:
! 1012: case SSH_AUTH_PASS_INIT:
! 1013: if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
! 1014: (strstr(sshc->authlist, "password") != NULL)) {
! 1015: state(conn, SSH_AUTH_PASS);
! 1016: }
! 1017: else {
! 1018: state(conn, SSH_AUTH_HOST_INIT);
! 1019: rc = 0; /* clear rc and continue */
! 1020: }
! 1021: break;
! 1022:
! 1023: case SSH_AUTH_PASS:
! 1024: rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
! 1025: curlx_uztoui(strlen(conn->user)),
! 1026: conn->passwd,
! 1027: curlx_uztoui(strlen(conn->passwd)),
! 1028: NULL);
! 1029: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1030: break;
! 1031: }
! 1032: if(rc == 0) {
! 1033: sshc->authed = TRUE;
! 1034: infof(data, "Initialized password authentication\n");
! 1035: state(conn, SSH_AUTH_DONE);
! 1036: }
! 1037: else {
! 1038: state(conn, SSH_AUTH_HOST_INIT);
! 1039: rc = 0; /* clear rc and continue */
! 1040: }
! 1041: break;
! 1042:
! 1043: case SSH_AUTH_HOST_INIT:
! 1044: if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
! 1045: (strstr(sshc->authlist, "hostbased") != NULL)) {
! 1046: state(conn, SSH_AUTH_HOST);
! 1047: }
! 1048: else {
! 1049: state(conn, SSH_AUTH_AGENT_INIT);
! 1050: }
! 1051: break;
! 1052:
! 1053: case SSH_AUTH_HOST:
! 1054: state(conn, SSH_AUTH_AGENT_INIT);
! 1055: break;
! 1056:
! 1057: case SSH_AUTH_AGENT_INIT:
! 1058: #ifdef HAVE_LIBSSH2_AGENT_API
! 1059: if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
! 1060: && (strstr(sshc->authlist, "publickey") != NULL)) {
! 1061:
! 1062: /* Connect to the ssh-agent */
! 1063: /* The agent could be shared by a curl thread i believe
! 1064: but nothing obvious as keys can be added/removed at any time */
! 1065: if(!sshc->ssh_agent) {
! 1066: sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
! 1067: if(!sshc->ssh_agent) {
! 1068: infof(data, "Could not create agent object\n");
! 1069:
! 1070: state(conn, SSH_AUTH_KEY_INIT);
! 1071: break;
! 1072: }
! 1073: }
! 1074:
! 1075: rc = libssh2_agent_connect(sshc->ssh_agent);
! 1076: if(rc == LIBSSH2_ERROR_EAGAIN)
! 1077: break;
! 1078: if(rc < 0) {
! 1079: infof(data, "Failure connecting to agent\n");
! 1080: state(conn, SSH_AUTH_KEY_INIT);
! 1081: rc = 0; /* clear rc and continue */
! 1082: }
! 1083: else {
! 1084: state(conn, SSH_AUTH_AGENT_LIST);
! 1085: }
! 1086: }
! 1087: else
! 1088: #endif /* HAVE_LIBSSH2_AGENT_API */
! 1089: state(conn, SSH_AUTH_KEY_INIT);
! 1090: break;
! 1091:
! 1092: case SSH_AUTH_AGENT_LIST:
! 1093: #ifdef HAVE_LIBSSH2_AGENT_API
! 1094: rc = libssh2_agent_list_identities(sshc->ssh_agent);
! 1095:
! 1096: if(rc == LIBSSH2_ERROR_EAGAIN)
! 1097: break;
! 1098: if(rc < 0) {
! 1099: infof(data, "Failure requesting identities to agent\n");
! 1100: state(conn, SSH_AUTH_KEY_INIT);
! 1101: rc = 0; /* clear rc and continue */
! 1102: }
! 1103: else {
! 1104: state(conn, SSH_AUTH_AGENT);
! 1105: sshc->sshagent_prev_identity = NULL;
! 1106: }
! 1107: #endif
! 1108: break;
! 1109:
! 1110: case SSH_AUTH_AGENT:
! 1111: #ifdef HAVE_LIBSSH2_AGENT_API
! 1112: /* as prev_identity evolves only after an identity user auth finished we
! 1113: can safely request it again as long as EAGAIN is returned here or by
! 1114: libssh2_agent_userauth */
! 1115: rc = libssh2_agent_get_identity(sshc->ssh_agent,
! 1116: &sshc->sshagent_identity,
! 1117: sshc->sshagent_prev_identity);
! 1118: if(rc == LIBSSH2_ERROR_EAGAIN)
! 1119: break;
! 1120:
! 1121: if(rc == 0) {
! 1122: rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
! 1123: sshc->sshagent_identity);
! 1124:
! 1125: if(rc < 0) {
! 1126: if(rc != LIBSSH2_ERROR_EAGAIN) {
! 1127: /* tried and failed? go to next identity */
! 1128: sshc->sshagent_prev_identity = sshc->sshagent_identity;
! 1129: }
! 1130: break;
! 1131: }
! 1132: }
! 1133:
! 1134: if(rc < 0)
! 1135: infof(data, "Failure requesting identities to agent\n");
! 1136: else if(rc == 1)
! 1137: infof(data, "No identity would match\n");
! 1138:
! 1139: if(rc == LIBSSH2_ERROR_NONE) {
! 1140: sshc->authed = TRUE;
! 1141: infof(data, "Agent based authentication successful\n");
! 1142: state(conn, SSH_AUTH_DONE);
! 1143: }
! 1144: else {
! 1145: state(conn, SSH_AUTH_KEY_INIT);
! 1146: rc = 0; /* clear rc and continue */
! 1147: }
! 1148: #endif
! 1149: break;
! 1150:
! 1151: case SSH_AUTH_KEY_INIT:
! 1152: if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
! 1153: && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
! 1154: state(conn, SSH_AUTH_KEY);
! 1155: }
! 1156: else {
! 1157: state(conn, SSH_AUTH_DONE);
! 1158: }
! 1159: break;
! 1160:
! 1161: case SSH_AUTH_KEY:
! 1162: /* Authentication failed. Continue with keyboard-interactive now. */
! 1163: rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
! 1164: conn->user,
! 1165: curlx_uztoui(
! 1166: strlen(conn->user)),
! 1167: &kbd_callback);
! 1168: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1169: break;
! 1170: }
! 1171: if(rc == 0) {
! 1172: sshc->authed = TRUE;
! 1173: infof(data, "Initialized keyboard interactive authentication\n");
! 1174: }
! 1175: state(conn, SSH_AUTH_DONE);
! 1176: break;
! 1177:
! 1178: case SSH_AUTH_DONE:
! 1179: if(!sshc->authed) {
! 1180: failf(data, "Authentication failure");
! 1181: state(conn, SSH_SESSION_FREE);
! 1182: sshc->actualcode = CURLE_LOGIN_DENIED;
! 1183: break;
! 1184: }
! 1185:
! 1186: /*
! 1187: * At this point we have an authenticated ssh session.
! 1188: */
! 1189: infof(data, "Authentication complete\n");
! 1190:
! 1191: Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */
! 1192:
! 1193: conn->sockfd = sock;
! 1194: conn->writesockfd = CURL_SOCKET_BAD;
! 1195:
! 1196: if(conn->handler->protocol == CURLPROTO_SFTP) {
! 1197: state(conn, SSH_SFTP_INIT);
! 1198: break;
! 1199: }
! 1200: infof(data, "SSH CONNECT phase done\n");
! 1201: state(conn, SSH_STOP);
! 1202: break;
! 1203:
! 1204: case SSH_SFTP_INIT:
! 1205: /*
! 1206: * Start the libssh2 sftp session
! 1207: */
! 1208: sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
! 1209: if(!sshc->sftp_session) {
! 1210: char *err_msg = NULL;
! 1211: if(libssh2_session_last_errno(sshc->ssh_session) ==
! 1212: LIBSSH2_ERROR_EAGAIN) {
! 1213: rc = LIBSSH2_ERROR_EAGAIN;
! 1214: break;
! 1215: }
! 1216:
! 1217: (void)libssh2_session_last_error(sshc->ssh_session,
! 1218: &err_msg, NULL, 0);
! 1219: failf(data, "Failure initializing sftp session: %s", err_msg);
! 1220: state(conn, SSH_SESSION_FREE);
! 1221: sshc->actualcode = CURLE_FAILED_INIT;
! 1222: break;
! 1223: }
! 1224: state(conn, SSH_SFTP_REALPATH);
! 1225: break;
! 1226:
! 1227: case SSH_SFTP_REALPATH:
! 1228: {
! 1229: char tempHome[PATH_MAX];
! 1230:
! 1231: /*
! 1232: * Get the "home" directory
! 1233: */
! 1234: rc = sftp_libssh2_realpath(sshc->sftp_session, ".",
! 1235: tempHome, PATH_MAX-1);
! 1236: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1237: break;
! 1238: }
! 1239: if(rc > 0) {
! 1240: /* It seems that this string is not always NULL terminated */
! 1241: tempHome[rc] = '\0';
! 1242: sshc->homedir = strdup(tempHome);
! 1243: if(!sshc->homedir) {
! 1244: state(conn, SSH_SFTP_CLOSE);
! 1245: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 1246: break;
! 1247: }
! 1248: conn->data->state.most_recent_ftp_entrypath = sshc->homedir;
! 1249: }
! 1250: else {
! 1251: /* Return the error type */
! 1252: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1253: if(err)
! 1254: result = sftp_libssh2_error_to_CURLE(err);
! 1255: else
! 1256: /* in this case, the error wasn't in the SFTP level but for example
! 1257: a time-out or similar */
! 1258: result = CURLE_SSH;
! 1259: sshc->actualcode = result;
! 1260: DEBUGF(infof(data, "error = %d makes libcurl = %d\n",
! 1261: err, (int)result));
! 1262: state(conn, SSH_STOP);
! 1263: break;
! 1264: }
! 1265: }
! 1266: /* This is the last step in the SFTP connect phase. Do note that while
! 1267: we get the homedir here, we get the "workingpath" in the DO action
! 1268: since the homedir will remain the same between request but the
! 1269: working path will not. */
! 1270: DEBUGF(infof(data, "SSH CONNECT phase done\n"));
! 1271: state(conn, SSH_STOP);
! 1272: break;
! 1273:
! 1274: case SSH_SFTP_QUOTE_INIT:
! 1275:
! 1276: result = Curl_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
! 1277: if(result) {
! 1278: sshc->actualcode = result;
! 1279: state(conn, SSH_STOP);
! 1280: break;
! 1281: }
! 1282:
! 1283: if(data->set.quote) {
! 1284: infof(data, "Sending quote commands\n");
! 1285: sshc->quote_item = data->set.quote;
! 1286: state(conn, SSH_SFTP_QUOTE);
! 1287: }
! 1288: else {
! 1289: state(conn, SSH_SFTP_GETINFO);
! 1290: }
! 1291: break;
! 1292:
! 1293: case SSH_SFTP_POSTQUOTE_INIT:
! 1294: if(data->set.postquote) {
! 1295: infof(data, "Sending quote commands\n");
! 1296: sshc->quote_item = data->set.postquote;
! 1297: state(conn, SSH_SFTP_QUOTE);
! 1298: }
! 1299: else {
! 1300: state(conn, SSH_STOP);
! 1301: }
! 1302: break;
! 1303:
! 1304: case SSH_SFTP_QUOTE:
! 1305: /* Send any quote commands */
! 1306: {
! 1307: const char *cp;
! 1308:
! 1309: /*
! 1310: * Support some of the "FTP" commands
! 1311: *
! 1312: * 'sshc->quote_item' is already verified to be non-NULL before it
! 1313: * switched to this state.
! 1314: */
! 1315: char *cmd = sshc->quote_item->data;
! 1316: sshc->acceptfail = FALSE;
! 1317:
! 1318: /* if a command starts with an asterisk, which a legal SFTP command never
! 1319: can, the command will be allowed to fail without it causing any
! 1320: aborts or cancels etc. It will cause libcurl to act as if the command
! 1321: is successful, whatever the server reponds. */
! 1322:
! 1323: if(cmd[0] == '*') {
! 1324: cmd++;
! 1325: sshc->acceptfail = TRUE;
! 1326: }
! 1327:
! 1328: if(strcasecompare("pwd", cmd)) {
! 1329: /* output debug output if that is requested */
! 1330: char *tmp = aprintf("257 \"%s\" is current directory.\n",
! 1331: sftp_scp->path);
! 1332: if(!tmp) {
! 1333: result = CURLE_OUT_OF_MEMORY;
! 1334: state(conn, SSH_SFTP_CLOSE);
! 1335: sshc->nextstate = SSH_NO_STATE;
! 1336: break;
! 1337: }
! 1338: if(data->set.verbose) {
! 1339: Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4);
! 1340: Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
! 1341: }
! 1342: /* this sends an FTP-like "header" to the header callback so that the
! 1343: current directory can be read very similar to how it is read when
! 1344: using ordinary FTP. */
! 1345: result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp));
! 1346: free(tmp);
! 1347: if(result) {
! 1348: state(conn, SSH_SFTP_CLOSE);
! 1349: sshc->nextstate = SSH_NO_STATE;
! 1350: sshc->actualcode = result;
! 1351: }
! 1352: else
! 1353: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1354: break;
! 1355: }
! 1356: {
! 1357: /*
! 1358: * the arguments following the command must be separated from the
! 1359: * command with a space so we can check for it unconditionally
! 1360: */
! 1361: cp = strchr(cmd, ' ');
! 1362: if(cp == NULL) {
! 1363: failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
! 1364: state(conn, SSH_SFTP_CLOSE);
! 1365: sshc->nextstate = SSH_NO_STATE;
! 1366: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1367: break;
! 1368: }
! 1369:
! 1370: /*
! 1371: * also, every command takes at least one argument so we get that
! 1372: * first argument right now
! 1373: */
! 1374: result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
! 1375: if(result) {
! 1376: if(result == CURLE_OUT_OF_MEMORY)
! 1377: failf(data, "Out of memory");
! 1378: else
! 1379: failf(data, "Syntax error: Bad first parameter");
! 1380: state(conn, SSH_SFTP_CLOSE);
! 1381: sshc->nextstate = SSH_NO_STATE;
! 1382: sshc->actualcode = result;
! 1383: break;
! 1384: }
! 1385:
! 1386: /*
! 1387: * SFTP is a binary protocol, so we don't send text commands
! 1388: * to the server. Instead, we scan for commands used by
! 1389: * OpenSSH's sftp program and call the appropriate libssh2
! 1390: * functions.
! 1391: */
! 1392: if(strncasecompare(cmd, "chgrp ", 6) ||
! 1393: strncasecompare(cmd, "chmod ", 6) ||
! 1394: strncasecompare(cmd, "chown ", 6) ) {
! 1395: /* attribute change */
! 1396:
! 1397: /* sshc->quote_path1 contains the mode to set */
! 1398: /* get the destination */
! 1399: result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
! 1400: if(result) {
! 1401: if(result == CURLE_OUT_OF_MEMORY)
! 1402: failf(data, "Out of memory");
! 1403: else
! 1404: failf(data, "Syntax error in chgrp/chmod/chown: "
! 1405: "Bad second parameter");
! 1406: Curl_safefree(sshc->quote_path1);
! 1407: state(conn, SSH_SFTP_CLOSE);
! 1408: sshc->nextstate = SSH_NO_STATE;
! 1409: sshc->actualcode = result;
! 1410: break;
! 1411: }
! 1412: memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
! 1413: state(conn, SSH_SFTP_QUOTE_STAT);
! 1414: break;
! 1415: }
! 1416: if(strncasecompare(cmd, "ln ", 3) ||
! 1417: strncasecompare(cmd, "symlink ", 8)) {
! 1418: /* symbolic linking */
! 1419: /* sshc->quote_path1 is the source */
! 1420: /* get the destination */
! 1421: result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
! 1422: if(result) {
! 1423: if(result == CURLE_OUT_OF_MEMORY)
! 1424: failf(data, "Out of memory");
! 1425: else
! 1426: failf(data,
! 1427: "Syntax error in ln/symlink: Bad second parameter");
! 1428: Curl_safefree(sshc->quote_path1);
! 1429: state(conn, SSH_SFTP_CLOSE);
! 1430: sshc->nextstate = SSH_NO_STATE;
! 1431: sshc->actualcode = result;
! 1432: break;
! 1433: }
! 1434: state(conn, SSH_SFTP_QUOTE_SYMLINK);
! 1435: break;
! 1436: }
! 1437: else if(strncasecompare(cmd, "mkdir ", 6)) {
! 1438: /* create dir */
! 1439: state(conn, SSH_SFTP_QUOTE_MKDIR);
! 1440: break;
! 1441: }
! 1442: else if(strncasecompare(cmd, "rename ", 7)) {
! 1443: /* rename file */
! 1444: /* first param is the source path */
! 1445: /* second param is the dest. path */
! 1446: result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
! 1447: if(result) {
! 1448: if(result == CURLE_OUT_OF_MEMORY)
! 1449: failf(data, "Out of memory");
! 1450: else
! 1451: failf(data, "Syntax error in rename: Bad second parameter");
! 1452: Curl_safefree(sshc->quote_path1);
! 1453: state(conn, SSH_SFTP_CLOSE);
! 1454: sshc->nextstate = SSH_NO_STATE;
! 1455: sshc->actualcode = result;
! 1456: break;
! 1457: }
! 1458: state(conn, SSH_SFTP_QUOTE_RENAME);
! 1459: break;
! 1460: }
! 1461: else if(strncasecompare(cmd, "rmdir ", 6)) {
! 1462: /* delete dir */
! 1463: state(conn, SSH_SFTP_QUOTE_RMDIR);
! 1464: break;
! 1465: }
! 1466: else if(strncasecompare(cmd, "rm ", 3)) {
! 1467: state(conn, SSH_SFTP_QUOTE_UNLINK);
! 1468: break;
! 1469: }
! 1470: #ifdef HAS_STATVFS_SUPPORT
! 1471: else if(strncasecompare(cmd, "statvfs ", 8)) {
! 1472: state(conn, SSH_SFTP_QUOTE_STATVFS);
! 1473: break;
! 1474: }
! 1475: #endif
! 1476:
! 1477: failf(data, "Unknown SFTP command");
! 1478: Curl_safefree(sshc->quote_path1);
! 1479: Curl_safefree(sshc->quote_path2);
! 1480: state(conn, SSH_SFTP_CLOSE);
! 1481: sshc->nextstate = SSH_NO_STATE;
! 1482: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1483: break;
! 1484: }
! 1485: }
! 1486: break;
! 1487:
! 1488: case SSH_SFTP_NEXT_QUOTE:
! 1489: Curl_safefree(sshc->quote_path1);
! 1490: Curl_safefree(sshc->quote_path2);
! 1491:
! 1492: sshc->quote_item = sshc->quote_item->next;
! 1493:
! 1494: if(sshc->quote_item) {
! 1495: state(conn, SSH_SFTP_QUOTE);
! 1496: }
! 1497: else {
! 1498: if(sshc->nextstate != SSH_NO_STATE) {
! 1499: state(conn, sshc->nextstate);
! 1500: sshc->nextstate = SSH_NO_STATE;
! 1501: }
! 1502: else {
! 1503: state(conn, SSH_SFTP_GETINFO);
! 1504: }
! 1505: }
! 1506: break;
! 1507:
! 1508: case SSH_SFTP_QUOTE_STAT:
! 1509: {
! 1510: char *cmd = sshc->quote_item->data;
! 1511: sshc->acceptfail = FALSE;
! 1512:
! 1513: /* if a command starts with an asterisk, which a legal SFTP command never
! 1514: can, the command will be allowed to fail without it causing any
! 1515: aborts or cancels etc. It will cause libcurl to act as if the command
! 1516: is successful, whatever the server reponds. */
! 1517:
! 1518: if(cmd[0] == '*') {
! 1519: cmd++;
! 1520: sshc->acceptfail = TRUE;
! 1521: }
! 1522:
! 1523: if(!strncasecompare(cmd, "chmod", 5)) {
! 1524: /* Since chown and chgrp only set owner OR group but libssh2 wants to
! 1525: * set them both at once, we need to obtain the current ownership
! 1526: * first. This takes an extra protocol round trip.
! 1527: */
! 1528: rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
! 1529: curlx_uztoui(strlen(sshc->quote_path2)),
! 1530: LIBSSH2_SFTP_STAT,
! 1531: &sshc->quote_attrs);
! 1532: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1533: break;
! 1534: }
! 1535: if(rc != 0 && !sshc->acceptfail) { /* get those attributes */
! 1536: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1537: Curl_safefree(sshc->quote_path1);
! 1538: Curl_safefree(sshc->quote_path2);
! 1539: failf(data, "Attempt to get SFTP stats failed: %s",
! 1540: sftp_libssh2_strerror(err));
! 1541: state(conn, SSH_SFTP_CLOSE);
! 1542: sshc->nextstate = SSH_NO_STATE;
! 1543: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1544: break;
! 1545: }
! 1546: }
! 1547:
! 1548: /* Now set the new attributes... */
! 1549: if(strncasecompare(cmd, "chgrp", 5)) {
! 1550: sshc->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
! 1551: sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
! 1552: if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
! 1553: !sshc->acceptfail) {
! 1554: Curl_safefree(sshc->quote_path1);
! 1555: Curl_safefree(sshc->quote_path2);
! 1556: failf(data, "Syntax error: chgrp gid not a number");
! 1557: state(conn, SSH_SFTP_CLOSE);
! 1558: sshc->nextstate = SSH_NO_STATE;
! 1559: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1560: break;
! 1561: }
! 1562: }
! 1563: else if(strncasecompare(cmd, "chmod", 5)) {
! 1564: sshc->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
! 1565: sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
! 1566: /* permissions are octal */
! 1567: if(sshc->quote_attrs.permissions == 0 &&
! 1568: !ISDIGIT(sshc->quote_path1[0])) {
! 1569: Curl_safefree(sshc->quote_path1);
! 1570: Curl_safefree(sshc->quote_path2);
! 1571: failf(data, "Syntax error: chmod permissions not a number");
! 1572: state(conn, SSH_SFTP_CLOSE);
! 1573: sshc->nextstate = SSH_NO_STATE;
! 1574: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1575: break;
! 1576: }
! 1577: }
! 1578: else if(strncasecompare(cmd, "chown", 5)) {
! 1579: sshc->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
! 1580: sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
! 1581: if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
! 1582: !sshc->acceptfail) {
! 1583: Curl_safefree(sshc->quote_path1);
! 1584: Curl_safefree(sshc->quote_path2);
! 1585: failf(data, "Syntax error: chown uid not a number");
! 1586: state(conn, SSH_SFTP_CLOSE);
! 1587: sshc->nextstate = SSH_NO_STATE;
! 1588: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1589: break;
! 1590: }
! 1591: }
! 1592:
! 1593: /* Now send the completed structure... */
! 1594: state(conn, SSH_SFTP_QUOTE_SETSTAT);
! 1595: break;
! 1596: }
! 1597:
! 1598: case SSH_SFTP_QUOTE_SETSTAT:
! 1599: rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
! 1600: curlx_uztoui(strlen(sshc->quote_path2)),
! 1601: LIBSSH2_SFTP_SETSTAT,
! 1602: &sshc->quote_attrs);
! 1603: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1604: break;
! 1605: }
! 1606: if(rc != 0 && !sshc->acceptfail) {
! 1607: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1608: Curl_safefree(sshc->quote_path1);
! 1609: Curl_safefree(sshc->quote_path2);
! 1610: failf(data, "Attempt to set SFTP stats failed: %s",
! 1611: sftp_libssh2_strerror(err));
! 1612: state(conn, SSH_SFTP_CLOSE);
! 1613: sshc->nextstate = SSH_NO_STATE;
! 1614: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1615: break;
! 1616: }
! 1617: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1618: break;
! 1619:
! 1620: case SSH_SFTP_QUOTE_SYMLINK:
! 1621: rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
! 1622: curlx_uztoui(strlen(sshc->quote_path1)),
! 1623: sshc->quote_path2,
! 1624: curlx_uztoui(strlen(sshc->quote_path2)),
! 1625: LIBSSH2_SFTP_SYMLINK);
! 1626: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1627: break;
! 1628: }
! 1629: if(rc != 0 && !sshc->acceptfail) {
! 1630: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1631: Curl_safefree(sshc->quote_path1);
! 1632: Curl_safefree(sshc->quote_path2);
! 1633: failf(data, "symlink command failed: %s",
! 1634: sftp_libssh2_strerror(err));
! 1635: state(conn, SSH_SFTP_CLOSE);
! 1636: sshc->nextstate = SSH_NO_STATE;
! 1637: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1638: break;
! 1639: }
! 1640: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1641: break;
! 1642:
! 1643: case SSH_SFTP_QUOTE_MKDIR:
! 1644: rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
! 1645: curlx_uztoui(strlen(sshc->quote_path1)),
! 1646: data->set.new_directory_perms);
! 1647: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1648: break;
! 1649: }
! 1650: if(rc != 0 && !sshc->acceptfail) {
! 1651: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1652: Curl_safefree(sshc->quote_path1);
! 1653: failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
! 1654: state(conn, SSH_SFTP_CLOSE);
! 1655: sshc->nextstate = SSH_NO_STATE;
! 1656: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1657: break;
! 1658: }
! 1659: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1660: break;
! 1661:
! 1662: case SSH_SFTP_QUOTE_RENAME:
! 1663: rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
! 1664: curlx_uztoui(strlen(sshc->quote_path1)),
! 1665: sshc->quote_path2,
! 1666: curlx_uztoui(strlen(sshc->quote_path2)),
! 1667: LIBSSH2_SFTP_RENAME_OVERWRITE |
! 1668: LIBSSH2_SFTP_RENAME_ATOMIC |
! 1669: LIBSSH2_SFTP_RENAME_NATIVE);
! 1670:
! 1671: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1672: break;
! 1673: }
! 1674: if(rc != 0 && !sshc->acceptfail) {
! 1675: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1676: Curl_safefree(sshc->quote_path1);
! 1677: Curl_safefree(sshc->quote_path2);
! 1678: failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
! 1679: state(conn, SSH_SFTP_CLOSE);
! 1680: sshc->nextstate = SSH_NO_STATE;
! 1681: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1682: break;
! 1683: }
! 1684: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1685: break;
! 1686:
! 1687: case SSH_SFTP_QUOTE_RMDIR:
! 1688: rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
! 1689: curlx_uztoui(strlen(sshc->quote_path1)));
! 1690: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1691: break;
! 1692: }
! 1693: if(rc != 0 && !sshc->acceptfail) {
! 1694: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1695: Curl_safefree(sshc->quote_path1);
! 1696: failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
! 1697: state(conn, SSH_SFTP_CLOSE);
! 1698: sshc->nextstate = SSH_NO_STATE;
! 1699: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1700: break;
! 1701: }
! 1702: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1703: break;
! 1704:
! 1705: case SSH_SFTP_QUOTE_UNLINK:
! 1706: rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
! 1707: curlx_uztoui(strlen(sshc->quote_path1)));
! 1708: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1709: break;
! 1710: }
! 1711: if(rc != 0 && !sshc->acceptfail) {
! 1712: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1713: Curl_safefree(sshc->quote_path1);
! 1714: failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
! 1715: state(conn, SSH_SFTP_CLOSE);
! 1716: sshc->nextstate = SSH_NO_STATE;
! 1717: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1718: break;
! 1719: }
! 1720: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1721: break;
! 1722:
! 1723: #ifdef HAS_STATVFS_SUPPORT
! 1724: case SSH_SFTP_QUOTE_STATVFS:
! 1725: {
! 1726: LIBSSH2_SFTP_STATVFS statvfs;
! 1727: rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
! 1728: curlx_uztoui(strlen(sshc->quote_path1)),
! 1729: &statvfs);
! 1730:
! 1731: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1732: break;
! 1733: }
! 1734: if(rc != 0 && !sshc->acceptfail) {
! 1735: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1736: Curl_safefree(sshc->quote_path1);
! 1737: failf(data, "statvfs command failed: %s", sftp_libssh2_strerror(err));
! 1738: state(conn, SSH_SFTP_CLOSE);
! 1739: sshc->nextstate = SSH_NO_STATE;
! 1740: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1741: break;
! 1742: }
! 1743: else if(rc == 0) {
! 1744: char *tmp = aprintf("statvfs:\n"
! 1745: "f_bsize: %llu\n" "f_frsize: %llu\n"
! 1746: "f_blocks: %llu\n" "f_bfree: %llu\n"
! 1747: "f_bavail: %llu\n" "f_files: %llu\n"
! 1748: "f_ffree: %llu\n" "f_favail: %llu\n"
! 1749: "f_fsid: %llu\n" "f_flag: %llu\n"
! 1750: "f_namemax: %llu\n",
! 1751: statvfs.f_bsize, statvfs.f_frsize,
! 1752: statvfs.f_blocks, statvfs.f_bfree,
! 1753: statvfs.f_bavail, statvfs.f_files,
! 1754: statvfs.f_ffree, statvfs.f_favail,
! 1755: statvfs.f_fsid, statvfs.f_flag,
! 1756: statvfs.f_namemax);
! 1757: if(!tmp) {
! 1758: result = CURLE_OUT_OF_MEMORY;
! 1759: state(conn, SSH_SFTP_CLOSE);
! 1760: sshc->nextstate = SSH_NO_STATE;
! 1761: break;
! 1762: }
! 1763:
! 1764: result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp));
! 1765: free(tmp);
! 1766: if(result) {
! 1767: state(conn, SSH_SFTP_CLOSE);
! 1768: sshc->nextstate = SSH_NO_STATE;
! 1769: sshc->actualcode = result;
! 1770: }
! 1771: }
! 1772: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1773: break;
! 1774: }
! 1775: #endif
! 1776: case SSH_SFTP_GETINFO:
! 1777: {
! 1778: if(data->set.get_filetime) {
! 1779: state(conn, SSH_SFTP_FILETIME);
! 1780: }
! 1781: else {
! 1782: state(conn, SSH_SFTP_TRANS_INIT);
! 1783: }
! 1784: break;
! 1785: }
! 1786:
! 1787: case SSH_SFTP_FILETIME:
! 1788: {
! 1789: LIBSSH2_SFTP_ATTRIBUTES attrs;
! 1790:
! 1791: rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
! 1792: curlx_uztoui(strlen(sftp_scp->path)),
! 1793: LIBSSH2_SFTP_STAT, &attrs);
! 1794: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1795: break;
! 1796: }
! 1797: if(rc == 0) {
! 1798: data->info.filetime = attrs.mtime;
! 1799: }
! 1800:
! 1801: state(conn, SSH_SFTP_TRANS_INIT);
! 1802: break;
! 1803: }
! 1804:
! 1805: case SSH_SFTP_TRANS_INIT:
! 1806: if(data->set.upload)
! 1807: state(conn, SSH_SFTP_UPLOAD_INIT);
! 1808: else {
! 1809: if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
! 1810: state(conn, SSH_SFTP_READDIR_INIT);
! 1811: else
! 1812: state(conn, SSH_SFTP_DOWNLOAD_INIT);
! 1813: }
! 1814: break;
! 1815:
! 1816: case SSH_SFTP_UPLOAD_INIT:
! 1817: {
! 1818: unsigned long flags;
! 1819: /*
! 1820: * NOTE!!! libssh2 requires that the destination path is a full path
! 1821: * that includes the destination file and name OR ends in a "/"
! 1822: * If this is not done the destination file will be named the
! 1823: * same name as the last directory in the path.
! 1824: */
! 1825:
! 1826: if(data->state.resume_from != 0) {
! 1827: LIBSSH2_SFTP_ATTRIBUTES attrs;
! 1828: if(data->state.resume_from < 0) {
! 1829: rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
! 1830: curlx_uztoui(strlen(sftp_scp->path)),
! 1831: LIBSSH2_SFTP_STAT, &attrs);
! 1832: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 1833: break;
! 1834: }
! 1835: if(rc) {
! 1836: data->state.resume_from = 0;
! 1837: }
! 1838: else {
! 1839: curl_off_t size = attrs.filesize;
! 1840: if(size < 0) {
! 1841: failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
! 1842: return CURLE_BAD_DOWNLOAD_RESUME;
! 1843: }
! 1844: data->state.resume_from = attrs.filesize;
! 1845: }
! 1846: }
! 1847: }
! 1848:
! 1849: if(data->set.ftp_append)
! 1850: /* Try to open for append, but create if nonexisting */
! 1851: flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
! 1852: else if(data->state.resume_from > 0)
! 1853: /* If we have restart position then open for append */
! 1854: flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
! 1855: else
! 1856: /* Clear file before writing (normal behaviour) */
! 1857: flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
! 1858:
! 1859: sshc->sftp_handle =
! 1860: libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
! 1861: curlx_uztoui(strlen(sftp_scp->path)),
! 1862: flags, data->set.new_file_perms,
! 1863: LIBSSH2_SFTP_OPENFILE);
! 1864:
! 1865: if(!sshc->sftp_handle) {
! 1866: rc = libssh2_session_last_errno(sshc->ssh_session);
! 1867:
! 1868: if(LIBSSH2_ERROR_EAGAIN == rc)
! 1869: break;
! 1870:
! 1871: if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
! 1872: /* only when there was an SFTP protocol error can we extract
! 1873: the sftp error! */
! 1874: err = sftp_libssh2_last_error(sshc->sftp_session);
! 1875: else
! 1876: err = -1; /* not an sftp error at all */
! 1877:
! 1878: if(sshc->secondCreateDirs) {
! 1879: state(conn, SSH_SFTP_CLOSE);
! 1880: sshc->actualcode = err>= LIBSSH2_FX_OK?
! 1881: sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
! 1882: failf(data, "Creating the dir/file failed: %s",
! 1883: sftp_libssh2_strerror(err));
! 1884: break;
! 1885: }
! 1886: if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
! 1887: (err == LIBSSH2_FX_FAILURE) ||
! 1888: (err == LIBSSH2_FX_NO_SUCH_PATH)) &&
! 1889: (data->set.ftp_create_missing_dirs &&
! 1890: (strlen(sftp_scp->path) > 1))) {
! 1891: /* try to create the path remotely */
! 1892: rc = 0; /* clear rc and continue */
! 1893: sshc->secondCreateDirs = 1;
! 1894: state(conn, SSH_SFTP_CREATE_DIRS_INIT);
! 1895: break;
! 1896: }
! 1897: state(conn, SSH_SFTP_CLOSE);
! 1898: sshc->actualcode = err>= LIBSSH2_FX_OK?
! 1899: sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
! 1900: if(!sshc->actualcode) {
! 1901: /* Sometimes, for some reason libssh2_sftp_last_error() returns
! 1902: zero even though libssh2_sftp_open() failed previously! We need
! 1903: to work around that! */
! 1904: sshc->actualcode = CURLE_SSH;
! 1905: err = -1;
! 1906: }
! 1907: failf(data, "Upload failed: %s (%d/%d)",
! 1908: err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error",
! 1909: err, rc);
! 1910: break;
! 1911: }
! 1912:
! 1913: /* If we have a restart point then we need to seek to the correct
! 1914: position. */
! 1915: if(data->state.resume_from > 0) {
! 1916: /* Let's read off the proper amount of bytes from the input. */
! 1917: if(conn->seek_func) {
! 1918: Curl_set_in_callback(data, true);
! 1919: seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
! 1920: SEEK_SET);
! 1921: Curl_set_in_callback(data, false);
! 1922: }
! 1923:
! 1924: if(seekerr != CURL_SEEKFUNC_OK) {
! 1925: curl_off_t passed = 0;
! 1926:
! 1927: if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
! 1928: failf(data, "Could not seek stream");
! 1929: return CURLE_FTP_COULDNT_USE_REST;
! 1930: }
! 1931: /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
! 1932: do {
! 1933: size_t readthisamountnow =
! 1934: (data->state.resume_from - passed > data->set.buffer_size) ?
! 1935: (size_t)data->set.buffer_size :
! 1936: curlx_sotouz(data->state.resume_from - passed);
! 1937:
! 1938: size_t actuallyread;
! 1939: Curl_set_in_callback(data, true);
! 1940: actuallyread = data->state.fread_func(data->state.buffer, 1,
! 1941: readthisamountnow,
! 1942: data->state.in);
! 1943: Curl_set_in_callback(data, false);
! 1944:
! 1945: passed += actuallyread;
! 1946: if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
! 1947: /* this checks for greater-than only to make sure that the
! 1948: CURL_READFUNC_ABORT return code still aborts */
! 1949: failf(data, "Failed to read data");
! 1950: return CURLE_FTP_COULDNT_USE_REST;
! 1951: }
! 1952: } while(passed < data->state.resume_from);
! 1953: }
! 1954:
! 1955: /* now, decrease the size of the read */
! 1956: if(data->state.infilesize > 0) {
! 1957: data->state.infilesize -= data->state.resume_from;
! 1958: data->req.size = data->state.infilesize;
! 1959: Curl_pgrsSetUploadSize(data, data->state.infilesize);
! 1960: }
! 1961:
! 1962: SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
! 1963: }
! 1964: if(data->state.infilesize > 0) {
! 1965: data->req.size = data->state.infilesize;
! 1966: Curl_pgrsSetUploadSize(data, data->state.infilesize);
! 1967: }
! 1968: /* upload data */
! 1969: Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
! 1970:
! 1971: /* not set by Curl_setup_transfer to preserve keepon bits */
! 1972: conn->sockfd = conn->writesockfd;
! 1973:
! 1974: if(result) {
! 1975: state(conn, SSH_SFTP_CLOSE);
! 1976: sshc->actualcode = result;
! 1977: }
! 1978: else {
! 1979: /* store this original bitmask setup to use later on if we can't
! 1980: figure out a "real" bitmask */
! 1981: sshc->orig_waitfor = data->req.keepon;
! 1982:
! 1983: /* we want to use the _sending_ function even when the socket turns
! 1984: out readable as the underlying libssh2 sftp send function will deal
! 1985: with both accordingly */
! 1986: conn->cselect_bits = CURL_CSELECT_OUT;
! 1987:
! 1988: /* since we don't really wait for anything at this point, we want the
! 1989: state machine to move on as soon as possible so we set a very short
! 1990: timeout here */
! 1991: Curl_expire(data, 0, EXPIRE_RUN_NOW);
! 1992:
! 1993: state(conn, SSH_STOP);
! 1994: }
! 1995: break;
! 1996: }
! 1997:
! 1998: case SSH_SFTP_CREATE_DIRS_INIT:
! 1999: if(strlen(sftp_scp->path) > 1) {
! 2000: sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
! 2001: state(conn, SSH_SFTP_CREATE_DIRS);
! 2002: }
! 2003: else {
! 2004: state(conn, SSH_SFTP_UPLOAD_INIT);
! 2005: }
! 2006: break;
! 2007:
! 2008: case SSH_SFTP_CREATE_DIRS:
! 2009: sshc->slash_pos = strchr(sshc->slash_pos, '/');
! 2010: if(sshc->slash_pos) {
! 2011: *sshc->slash_pos = 0;
! 2012:
! 2013: infof(data, "Creating directory '%s'\n", sftp_scp->path);
! 2014: state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
! 2015: break;
! 2016: }
! 2017: state(conn, SSH_SFTP_UPLOAD_INIT);
! 2018: break;
! 2019:
! 2020: case SSH_SFTP_CREATE_DIRS_MKDIR:
! 2021: /* 'mode' - parameter is preliminary - default to 0644 */
! 2022: rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sftp_scp->path,
! 2023: curlx_uztoui(strlen(sftp_scp->path)),
! 2024: data->set.new_directory_perms);
! 2025: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2026: break;
! 2027: }
! 2028: *sshc->slash_pos = '/';
! 2029: ++sshc->slash_pos;
! 2030: if(rc < 0) {
! 2031: /*
! 2032: * Abort if failure wasn't that the dir already exists or the
! 2033: * permission was denied (creation might succeed further down the
! 2034: * path) - retry on unspecific FAILURE also
! 2035: */
! 2036: err = sftp_libssh2_last_error(sshc->sftp_session);
! 2037: if((err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
! 2038: (err != LIBSSH2_FX_FAILURE) &&
! 2039: (err != LIBSSH2_FX_PERMISSION_DENIED)) {
! 2040: result = sftp_libssh2_error_to_CURLE(err);
! 2041: state(conn, SSH_SFTP_CLOSE);
! 2042: sshc->actualcode = result?result:CURLE_SSH;
! 2043: break;
! 2044: }
! 2045: rc = 0; /* clear rc and continue */
! 2046: }
! 2047: state(conn, SSH_SFTP_CREATE_DIRS);
! 2048: break;
! 2049:
! 2050: case SSH_SFTP_READDIR_INIT:
! 2051: Curl_pgrsSetDownloadSize(data, -1);
! 2052: if(data->set.opt_no_body) {
! 2053: state(conn, SSH_STOP);
! 2054: break;
! 2055: }
! 2056:
! 2057: /*
! 2058: * This is a directory that we are trying to get, so produce a directory
! 2059: * listing
! 2060: */
! 2061: sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session,
! 2062: sftp_scp->path,
! 2063: curlx_uztoui(
! 2064: strlen(sftp_scp->path)),
! 2065: 0, 0, LIBSSH2_SFTP_OPENDIR);
! 2066: if(!sshc->sftp_handle) {
! 2067: if(libssh2_session_last_errno(sshc->ssh_session) ==
! 2068: LIBSSH2_ERROR_EAGAIN) {
! 2069: rc = LIBSSH2_ERROR_EAGAIN;
! 2070: break;
! 2071: }
! 2072: err = sftp_libssh2_last_error(sshc->sftp_session);
! 2073: failf(data, "Could not open directory for reading: %s",
! 2074: sftp_libssh2_strerror(err));
! 2075: state(conn, SSH_SFTP_CLOSE);
! 2076: result = sftp_libssh2_error_to_CURLE(err);
! 2077: sshc->actualcode = result?result:CURLE_SSH;
! 2078: break;
! 2079: }
! 2080: sshc->readdir_filename = malloc(PATH_MAX + 1);
! 2081: if(!sshc->readdir_filename) {
! 2082: state(conn, SSH_SFTP_CLOSE);
! 2083: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 2084: break;
! 2085: }
! 2086: sshc->readdir_longentry = malloc(PATH_MAX + 1);
! 2087: if(!sshc->readdir_longentry) {
! 2088: Curl_safefree(sshc->readdir_filename);
! 2089: state(conn, SSH_SFTP_CLOSE);
! 2090: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 2091: break;
! 2092: }
! 2093: state(conn, SSH_SFTP_READDIR);
! 2094: break;
! 2095:
! 2096: case SSH_SFTP_READDIR:
! 2097: rc = libssh2_sftp_readdir_ex(sshc->sftp_handle,
! 2098: sshc->readdir_filename,
! 2099: PATH_MAX,
! 2100: sshc->readdir_longentry,
! 2101: PATH_MAX,
! 2102: &sshc->readdir_attrs);
! 2103: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2104: break;
! 2105: }
! 2106: if(rc > 0) {
! 2107: sshc->readdir_len = (size_t) rc;
! 2108: sshc->readdir_filename[sshc->readdir_len] = '\0';
! 2109:
! 2110: if(data->set.ftp_list_only) {
! 2111: char *tmpLine;
! 2112:
! 2113: tmpLine = aprintf("%s\n", sshc->readdir_filename);
! 2114: if(tmpLine == NULL) {
! 2115: state(conn, SSH_SFTP_CLOSE);
! 2116: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 2117: break;
! 2118: }
! 2119: result = Curl_client_write(conn, CLIENTWRITE_BODY,
! 2120: tmpLine, sshc->readdir_len + 1);
! 2121: free(tmpLine);
! 2122:
! 2123: if(result) {
! 2124: state(conn, SSH_STOP);
! 2125: break;
! 2126: }
! 2127: /* since this counts what we send to the client, we include the
! 2128: newline in this counter */
! 2129: data->req.bytecount += sshc->readdir_len + 1;
! 2130:
! 2131: /* output debug output if that is requested */
! 2132: if(data->set.verbose) {
! 2133: Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
! 2134: sshc->readdir_len);
! 2135: }
! 2136: }
! 2137: else {
! 2138: sshc->readdir_currLen = strlen(sshc->readdir_longentry);
! 2139: sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
! 2140: sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
! 2141: if(!sshc->readdir_line) {
! 2142: Curl_safefree(sshc->readdir_filename);
! 2143: Curl_safefree(sshc->readdir_longentry);
! 2144: state(conn, SSH_SFTP_CLOSE);
! 2145: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 2146: break;
! 2147: }
! 2148:
! 2149: memcpy(sshc->readdir_line, sshc->readdir_longentry,
! 2150: sshc->readdir_currLen);
! 2151: if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
! 2152: ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
! 2153: LIBSSH2_SFTP_S_IFLNK)) {
! 2154: sshc->readdir_linkPath = malloc(PATH_MAX + 1);
! 2155: if(sshc->readdir_linkPath == NULL) {
! 2156: Curl_safefree(sshc->readdir_filename);
! 2157: Curl_safefree(sshc->readdir_longentry);
! 2158: state(conn, SSH_SFTP_CLOSE);
! 2159: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 2160: break;
! 2161: }
! 2162:
! 2163: msnprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
! 2164: sshc->readdir_filename);
! 2165: state(conn, SSH_SFTP_READDIR_LINK);
! 2166: break;
! 2167: }
! 2168: state(conn, SSH_SFTP_READDIR_BOTTOM);
! 2169: break;
! 2170: }
! 2171: }
! 2172: else if(rc == 0) {
! 2173: Curl_safefree(sshc->readdir_filename);
! 2174: Curl_safefree(sshc->readdir_longentry);
! 2175: state(conn, SSH_SFTP_READDIR_DONE);
! 2176: break;
! 2177: }
! 2178: else if(rc < 0) {
! 2179: err = sftp_libssh2_last_error(sshc->sftp_session);
! 2180: result = sftp_libssh2_error_to_CURLE(err);
! 2181: sshc->actualcode = result?result:CURLE_SSH;
! 2182: failf(data, "Could not open remote file for reading: %s :: %d",
! 2183: sftp_libssh2_strerror(err),
! 2184: libssh2_session_last_errno(sshc->ssh_session));
! 2185: Curl_safefree(sshc->readdir_filename);
! 2186: Curl_safefree(sshc->readdir_longentry);
! 2187: state(conn, SSH_SFTP_CLOSE);
! 2188: break;
! 2189: }
! 2190: break;
! 2191:
! 2192: case SSH_SFTP_READDIR_LINK:
! 2193: rc =
! 2194: libssh2_sftp_symlink_ex(sshc->sftp_session,
! 2195: sshc->readdir_linkPath,
! 2196: curlx_uztoui(strlen(sshc->readdir_linkPath)),
! 2197: sshc->readdir_filename,
! 2198: PATH_MAX, LIBSSH2_SFTP_READLINK);
! 2199: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2200: break;
! 2201: }
! 2202: sshc->readdir_len = (size_t) rc;
! 2203: Curl_safefree(sshc->readdir_linkPath);
! 2204:
! 2205: /* get room for the filename and extra output */
! 2206: sshc->readdir_totalLen += 4 + sshc->readdir_len;
! 2207: new_readdir_line = Curl_saferealloc(sshc->readdir_line,
! 2208: sshc->readdir_totalLen);
! 2209: if(!new_readdir_line) {
! 2210: sshc->readdir_line = NULL;
! 2211: Curl_safefree(sshc->readdir_filename);
! 2212: Curl_safefree(sshc->readdir_longentry);
! 2213: state(conn, SSH_SFTP_CLOSE);
! 2214: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 2215: break;
! 2216: }
! 2217: sshc->readdir_line = new_readdir_line;
! 2218:
! 2219: sshc->readdir_currLen += msnprintf(sshc->readdir_line +
! 2220: sshc->readdir_currLen,
! 2221: sshc->readdir_totalLen -
! 2222: sshc->readdir_currLen,
! 2223: " -> %s",
! 2224: sshc->readdir_filename);
! 2225:
! 2226: state(conn, SSH_SFTP_READDIR_BOTTOM);
! 2227: break;
! 2228:
! 2229: case SSH_SFTP_READDIR_BOTTOM:
! 2230: sshc->readdir_currLen += msnprintf(sshc->readdir_line +
! 2231: sshc->readdir_currLen,
! 2232: sshc->readdir_totalLen -
! 2233: sshc->readdir_currLen, "\n");
! 2234: result = Curl_client_write(conn, CLIENTWRITE_BODY,
! 2235: sshc->readdir_line,
! 2236: sshc->readdir_currLen);
! 2237:
! 2238: if(!result) {
! 2239:
! 2240: /* output debug output if that is requested */
! 2241: if(data->set.verbose) {
! 2242: Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
! 2243: sshc->readdir_currLen);
! 2244: }
! 2245: data->req.bytecount += sshc->readdir_currLen;
! 2246: }
! 2247: Curl_safefree(sshc->readdir_line);
! 2248: if(result) {
! 2249: state(conn, SSH_STOP);
! 2250: }
! 2251: else
! 2252: state(conn, SSH_SFTP_READDIR);
! 2253: break;
! 2254:
! 2255: case SSH_SFTP_READDIR_DONE:
! 2256: if(libssh2_sftp_closedir(sshc->sftp_handle) ==
! 2257: LIBSSH2_ERROR_EAGAIN) {
! 2258: rc = LIBSSH2_ERROR_EAGAIN;
! 2259: break;
! 2260: }
! 2261: sshc->sftp_handle = NULL;
! 2262: Curl_safefree(sshc->readdir_filename);
! 2263: Curl_safefree(sshc->readdir_longentry);
! 2264:
! 2265: /* no data to transfer */
! 2266: Curl_setup_transfer(data, -1, -1, FALSE, -1);
! 2267: state(conn, SSH_STOP);
! 2268: break;
! 2269:
! 2270: case SSH_SFTP_DOWNLOAD_INIT:
! 2271: /*
! 2272: * Work on getting the specified file
! 2273: */
! 2274: sshc->sftp_handle =
! 2275: libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
! 2276: curlx_uztoui(strlen(sftp_scp->path)),
! 2277: LIBSSH2_FXF_READ, data->set.new_file_perms,
! 2278: LIBSSH2_SFTP_OPENFILE);
! 2279: if(!sshc->sftp_handle) {
! 2280: if(libssh2_session_last_errno(sshc->ssh_session) ==
! 2281: LIBSSH2_ERROR_EAGAIN) {
! 2282: rc = LIBSSH2_ERROR_EAGAIN;
! 2283: break;
! 2284: }
! 2285: err = sftp_libssh2_last_error(sshc->sftp_session);
! 2286: failf(data, "Could not open remote file for reading: %s",
! 2287: sftp_libssh2_strerror(err));
! 2288: state(conn, SSH_SFTP_CLOSE);
! 2289: result = sftp_libssh2_error_to_CURLE(err);
! 2290: sshc->actualcode = result?result:CURLE_SSH;
! 2291: break;
! 2292: }
! 2293: state(conn, SSH_SFTP_DOWNLOAD_STAT);
! 2294: break;
! 2295:
! 2296: case SSH_SFTP_DOWNLOAD_STAT:
! 2297: {
! 2298: LIBSSH2_SFTP_ATTRIBUTES attrs;
! 2299:
! 2300: rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
! 2301: curlx_uztoui(strlen(sftp_scp->path)),
! 2302: LIBSSH2_SFTP_STAT, &attrs);
! 2303: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2304: break;
! 2305: }
! 2306: if(rc ||
! 2307: !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) ||
! 2308: (attrs.filesize == 0)) {
! 2309: /*
! 2310: * libssh2_sftp_open() didn't return an error, so maybe the server
! 2311: * just doesn't support stat()
! 2312: * OR the server doesn't return a file size with a stat()
! 2313: * OR file size is 0
! 2314: */
! 2315: data->req.size = -1;
! 2316: data->req.maxdownload = -1;
! 2317: Curl_pgrsSetDownloadSize(data, -1);
! 2318: }
! 2319: else {
! 2320: curl_off_t size = attrs.filesize;
! 2321:
! 2322: if(size < 0) {
! 2323: failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
! 2324: return CURLE_BAD_DOWNLOAD_RESUME;
! 2325: }
! 2326: if(conn->data->state.use_range) {
! 2327: curl_off_t from, to;
! 2328: char *ptr;
! 2329: char *ptr2;
! 2330: CURLofft to_t;
! 2331: CURLofft from_t;
! 2332:
! 2333: from_t = curlx_strtoofft(conn->data->state.range, &ptr, 0, &from);
! 2334: if(from_t == CURL_OFFT_FLOW)
! 2335: return CURLE_RANGE_ERROR;
! 2336: while(*ptr && (ISSPACE(*ptr) || (*ptr == '-')))
! 2337: ptr++;
! 2338: to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
! 2339: if(to_t == CURL_OFFT_FLOW)
! 2340: return CURLE_RANGE_ERROR;
! 2341: if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
! 2342: || (to >= size)) {
! 2343: to = size - 1;
! 2344: }
! 2345: if(from_t) {
! 2346: /* from is relative to end of file */
! 2347: from = size - to;
! 2348: to = size - 1;
! 2349: }
! 2350: if(from > size) {
! 2351: failf(data, "Offset (%"
! 2352: CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
! 2353: CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize);
! 2354: return CURLE_BAD_DOWNLOAD_RESUME;
! 2355: }
! 2356: if(from > to) {
! 2357: from = to;
! 2358: size = 0;
! 2359: }
! 2360: else {
! 2361: size = to - from + 1;
! 2362: }
! 2363:
! 2364: SFTP_SEEK(conn->proto.sshc.sftp_handle, from);
! 2365: }
! 2366: data->req.size = size;
! 2367: data->req.maxdownload = size;
! 2368: Curl_pgrsSetDownloadSize(data, size);
! 2369: }
! 2370:
! 2371: /* We can resume if we can seek to the resume position */
! 2372: if(data->state.resume_from) {
! 2373: if(data->state.resume_from < 0) {
! 2374: /* We're supposed to download the last abs(from) bytes */
! 2375: if((curl_off_t)attrs.filesize < -data->state.resume_from) {
! 2376: failf(data, "Offset (%"
! 2377: CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
! 2378: CURL_FORMAT_CURL_OFF_T ")",
! 2379: data->state.resume_from, attrs.filesize);
! 2380: return CURLE_BAD_DOWNLOAD_RESUME;
! 2381: }
! 2382: /* download from where? */
! 2383: data->state.resume_from += attrs.filesize;
! 2384: }
! 2385: else {
! 2386: if((curl_off_t)attrs.filesize < data->state.resume_from) {
! 2387: failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
! 2388: ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
! 2389: data->state.resume_from, attrs.filesize);
! 2390: return CURLE_BAD_DOWNLOAD_RESUME;
! 2391: }
! 2392: }
! 2393: /* Now store the number of bytes we are expected to download */
! 2394: data->req.size = attrs.filesize - data->state.resume_from;
! 2395: data->req.maxdownload = attrs.filesize - data->state.resume_from;
! 2396: Curl_pgrsSetDownloadSize(data,
! 2397: attrs.filesize - data->state.resume_from);
! 2398: SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
! 2399: }
! 2400: }
! 2401:
! 2402: /* Setup the actual download */
! 2403: if(data->req.size == 0) {
! 2404: /* no data to transfer */
! 2405: Curl_setup_transfer(data, -1, -1, FALSE, -1);
! 2406: infof(data, "File already completely downloaded\n");
! 2407: state(conn, SSH_STOP);
! 2408: break;
! 2409: }
! 2410: Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
! 2411:
! 2412: /* not set by Curl_setup_transfer to preserve keepon bits */
! 2413: conn->writesockfd = conn->sockfd;
! 2414:
! 2415: /* we want to use the _receiving_ function even when the socket turns
! 2416: out writableable as the underlying libssh2 recv function will deal
! 2417: with both accordingly */
! 2418: conn->cselect_bits = CURL_CSELECT_IN;
! 2419:
! 2420: if(result) {
! 2421: /* this should never occur; the close state should be entered
! 2422: at the time the error occurs */
! 2423: state(conn, SSH_SFTP_CLOSE);
! 2424: sshc->actualcode = result;
! 2425: }
! 2426: else {
! 2427: state(conn, SSH_STOP);
! 2428: }
! 2429: break;
! 2430:
! 2431: case SSH_SFTP_CLOSE:
! 2432: if(sshc->sftp_handle) {
! 2433: rc = libssh2_sftp_close(sshc->sftp_handle);
! 2434: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2435: break;
! 2436: }
! 2437: if(rc < 0) {
! 2438: char *err_msg = NULL;
! 2439: (void)libssh2_session_last_error(sshc->ssh_session,
! 2440: &err_msg, NULL, 0);
! 2441: infof(data, "Failed to close libssh2 file: %d %s\n", rc, err_msg);
! 2442: }
! 2443: sshc->sftp_handle = NULL;
! 2444: }
! 2445:
! 2446: Curl_safefree(sftp_scp->path);
! 2447:
! 2448: DEBUGF(infof(data, "SFTP DONE done\n"));
! 2449:
! 2450: /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
! 2451: After nextstate is executed, the control should come back to
! 2452: SSH_SFTP_CLOSE to pass the correct result back */
! 2453: if(sshc->nextstate != SSH_NO_STATE &&
! 2454: sshc->nextstate != SSH_SFTP_CLOSE) {
! 2455: state(conn, sshc->nextstate);
! 2456: sshc->nextstate = SSH_SFTP_CLOSE;
! 2457: }
! 2458: else {
! 2459: state(conn, SSH_STOP);
! 2460: result = sshc->actualcode;
! 2461: }
! 2462: break;
! 2463:
! 2464: case SSH_SFTP_SHUTDOWN:
! 2465: /* during times we get here due to a broken transfer and then the
! 2466: sftp_handle might not have been taken down so make sure that is done
! 2467: before we proceed */
! 2468:
! 2469: if(sshc->sftp_handle) {
! 2470: rc = libssh2_sftp_close(sshc->sftp_handle);
! 2471: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2472: break;
! 2473: }
! 2474: if(rc < 0) {
! 2475: char *err_msg = NULL;
! 2476: (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
! 2477: NULL, 0);
! 2478: infof(data, "Failed to close libssh2 file: %d %s\n", rc, err_msg);
! 2479: }
! 2480: sshc->sftp_handle = NULL;
! 2481: }
! 2482: if(sshc->sftp_session) {
! 2483: rc = libssh2_sftp_shutdown(sshc->sftp_session);
! 2484: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2485: break;
! 2486: }
! 2487: if(rc < 0) {
! 2488: infof(data, "Failed to stop libssh2 sftp subsystem\n");
! 2489: }
! 2490: sshc->sftp_session = NULL;
! 2491: }
! 2492:
! 2493: Curl_safefree(sshc->homedir);
! 2494: conn->data->state.most_recent_ftp_entrypath = NULL;
! 2495:
! 2496: state(conn, SSH_SESSION_DISCONNECT);
! 2497: break;
! 2498:
! 2499: case SSH_SCP_TRANS_INIT:
! 2500: result = Curl_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
! 2501: if(result) {
! 2502: sshc->actualcode = result;
! 2503: state(conn, SSH_STOP);
! 2504: break;
! 2505: }
! 2506:
! 2507: if(data->set.upload) {
! 2508: if(data->state.infilesize < 0) {
! 2509: failf(data, "SCP requires a known file size for upload");
! 2510: sshc->actualcode = CURLE_UPLOAD_FAILED;
! 2511: state(conn, SSH_SCP_CHANNEL_FREE);
! 2512: break;
! 2513: }
! 2514: state(conn, SSH_SCP_UPLOAD_INIT);
! 2515: }
! 2516: else {
! 2517: state(conn, SSH_SCP_DOWNLOAD_INIT);
! 2518: }
! 2519: break;
! 2520:
! 2521: case SSH_SCP_UPLOAD_INIT:
! 2522: /*
! 2523: * libssh2 requires that the destination path is a full path that
! 2524: * includes the destination file and name OR ends in a "/" . If this is
! 2525: * not done the destination file will be named the same name as the last
! 2526: * directory in the path.
! 2527: */
! 2528: sshc->ssh_channel =
! 2529: SCP_SEND(sshc->ssh_session, sftp_scp->path, data->set.new_file_perms,
! 2530: data->state.infilesize);
! 2531: if(!sshc->ssh_channel) {
! 2532: int ssh_err;
! 2533: char *err_msg = NULL;
! 2534:
! 2535: if(libssh2_session_last_errno(sshc->ssh_session) ==
! 2536: LIBSSH2_ERROR_EAGAIN) {
! 2537: rc = LIBSSH2_ERROR_EAGAIN;
! 2538: break;
! 2539: }
! 2540:
! 2541: ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
! 2542: &err_msg, NULL, 0));
! 2543: failf(conn->data, "%s", err_msg);
! 2544: state(conn, SSH_SCP_CHANNEL_FREE);
! 2545: sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
! 2546: /* Map generic errors to upload failed */
! 2547: if(sshc->actualcode == CURLE_SSH ||
! 2548: sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND)
! 2549: sshc->actualcode = CURLE_UPLOAD_FAILED;
! 2550: break;
! 2551: }
! 2552:
! 2553: /* upload data */
! 2554: Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET);
! 2555:
! 2556: /* not set by Curl_setup_transfer to preserve keepon bits */
! 2557: conn->sockfd = conn->writesockfd;
! 2558:
! 2559: if(result) {
! 2560: state(conn, SSH_SCP_CHANNEL_FREE);
! 2561: sshc->actualcode = result;
! 2562: }
! 2563: else {
! 2564: /* store this original bitmask setup to use later on if we can't
! 2565: figure out a "real" bitmask */
! 2566: sshc->orig_waitfor = data->req.keepon;
! 2567:
! 2568: /* we want to use the _sending_ function even when the socket turns
! 2569: out readable as the underlying libssh2 scp send function will deal
! 2570: with both accordingly */
! 2571: conn->cselect_bits = CURL_CSELECT_OUT;
! 2572:
! 2573: state(conn, SSH_STOP);
! 2574: }
! 2575: break;
! 2576:
! 2577: case SSH_SCP_DOWNLOAD_INIT:
! 2578: {
! 2579: curl_off_t bytecount;
! 2580:
! 2581: /*
! 2582: * We must check the remote file; if it is a directory no values will
! 2583: * be set in sb
! 2584: */
! 2585:
! 2586: /*
! 2587: * If support for >2GB files exists, use it.
! 2588: */
! 2589:
! 2590: /* get a fresh new channel from the ssh layer */
! 2591: #if LIBSSH2_VERSION_NUM < 0x010700
! 2592: struct stat sb;
! 2593: memset(&sb, 0, sizeof(struct stat));
! 2594: sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
! 2595: sftp_scp->path, &sb);
! 2596: #else
! 2597: libssh2_struct_stat sb;
! 2598: memset(&sb, 0, sizeof(libssh2_struct_stat));
! 2599: sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
! 2600: sftp_scp->path, &sb);
! 2601: #endif
! 2602:
! 2603: if(!sshc->ssh_channel) {
! 2604: int ssh_err;
! 2605: char *err_msg = NULL;
! 2606:
! 2607: if(libssh2_session_last_errno(sshc->ssh_session) ==
! 2608: LIBSSH2_ERROR_EAGAIN) {
! 2609: rc = LIBSSH2_ERROR_EAGAIN;
! 2610: break;
! 2611: }
! 2612:
! 2613:
! 2614: ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
! 2615: &err_msg, NULL, 0));
! 2616: failf(conn->data, "%s", err_msg);
! 2617: state(conn, SSH_SCP_CHANNEL_FREE);
! 2618: sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
! 2619: break;
! 2620: }
! 2621:
! 2622: /* download data */
! 2623: bytecount = (curl_off_t)sb.st_size;
! 2624: data->req.maxdownload = (curl_off_t)sb.st_size;
! 2625: Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
! 2626:
! 2627: /* not set by Curl_setup_transfer to preserve keepon bits */
! 2628: conn->writesockfd = conn->sockfd;
! 2629:
! 2630: /* we want to use the _receiving_ function even when the socket turns
! 2631: out writableable as the underlying libssh2 recv function will deal
! 2632: with both accordingly */
! 2633: conn->cselect_bits = CURL_CSELECT_IN;
! 2634:
! 2635: if(result) {
! 2636: state(conn, SSH_SCP_CHANNEL_FREE);
! 2637: sshc->actualcode = result;
! 2638: }
! 2639: else
! 2640: state(conn, SSH_STOP);
! 2641: }
! 2642: break;
! 2643:
! 2644: case SSH_SCP_DONE:
! 2645: if(data->set.upload)
! 2646: state(conn, SSH_SCP_SEND_EOF);
! 2647: else
! 2648: state(conn, SSH_SCP_CHANNEL_FREE);
! 2649: break;
! 2650:
! 2651: case SSH_SCP_SEND_EOF:
! 2652: if(sshc->ssh_channel) {
! 2653: rc = libssh2_channel_send_eof(sshc->ssh_channel);
! 2654: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2655: break;
! 2656: }
! 2657: if(rc) {
! 2658: char *err_msg = NULL;
! 2659: (void)libssh2_session_last_error(sshc->ssh_session,
! 2660: &err_msg, NULL, 0);
! 2661: infof(data, "Failed to send libssh2 channel EOF: %d %s\n",
! 2662: rc, err_msg);
! 2663: }
! 2664: }
! 2665: state(conn, SSH_SCP_WAIT_EOF);
! 2666: break;
! 2667:
! 2668: case SSH_SCP_WAIT_EOF:
! 2669: if(sshc->ssh_channel) {
! 2670: rc = libssh2_channel_wait_eof(sshc->ssh_channel);
! 2671: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2672: break;
! 2673: }
! 2674: if(rc) {
! 2675: char *err_msg = NULL;
! 2676: (void)libssh2_session_last_error(sshc->ssh_session,
! 2677: &err_msg, NULL, 0);
! 2678: infof(data, "Failed to get channel EOF: %d %s\n", rc, err_msg);
! 2679: }
! 2680: }
! 2681: state(conn, SSH_SCP_WAIT_CLOSE);
! 2682: break;
! 2683:
! 2684: case SSH_SCP_WAIT_CLOSE:
! 2685: if(sshc->ssh_channel) {
! 2686: rc = libssh2_channel_wait_closed(sshc->ssh_channel);
! 2687: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2688: break;
! 2689: }
! 2690: if(rc) {
! 2691: char *err_msg = NULL;
! 2692: (void)libssh2_session_last_error(sshc->ssh_session,
! 2693: &err_msg, NULL, 0);
! 2694: infof(data, "Channel failed to close: %d %s\n", rc, err_msg);
! 2695: }
! 2696: }
! 2697: state(conn, SSH_SCP_CHANNEL_FREE);
! 2698: break;
! 2699:
! 2700: case SSH_SCP_CHANNEL_FREE:
! 2701: if(sshc->ssh_channel) {
! 2702: rc = libssh2_channel_free(sshc->ssh_channel);
! 2703: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2704: break;
! 2705: }
! 2706: if(rc < 0) {
! 2707: char *err_msg = NULL;
! 2708: (void)libssh2_session_last_error(sshc->ssh_session,
! 2709: &err_msg, NULL, 0);
! 2710: infof(data, "Failed to free libssh2 scp subsystem: %d %s\n",
! 2711: rc, err_msg);
! 2712: }
! 2713: sshc->ssh_channel = NULL;
! 2714: }
! 2715: DEBUGF(infof(data, "SCP DONE phase complete\n"));
! 2716: #if 0 /* PREV */
! 2717: state(conn, SSH_SESSION_DISCONNECT);
! 2718: #endif
! 2719: state(conn, SSH_STOP);
! 2720: result = sshc->actualcode;
! 2721: break;
! 2722:
! 2723: case SSH_SESSION_DISCONNECT:
! 2724: /* during weird times when we've been prematurely aborted, the channel
! 2725: is still alive when we reach this state and we MUST kill the channel
! 2726: properly first */
! 2727: if(sshc->ssh_channel) {
! 2728: rc = libssh2_channel_free(sshc->ssh_channel);
! 2729: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2730: break;
! 2731: }
! 2732: if(rc < 0) {
! 2733: char *err_msg = NULL;
! 2734: (void)libssh2_session_last_error(sshc->ssh_session,
! 2735: &err_msg, NULL, 0);
! 2736: infof(data, "Failed to free libssh2 scp subsystem: %d %s\n",
! 2737: rc, err_msg);
! 2738: }
! 2739: sshc->ssh_channel = NULL;
! 2740: }
! 2741:
! 2742: if(sshc->ssh_session) {
! 2743: rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
! 2744: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2745: break;
! 2746: }
! 2747: if(rc < 0) {
! 2748: char *err_msg = NULL;
! 2749: (void)libssh2_session_last_error(sshc->ssh_session,
! 2750: &err_msg, NULL, 0);
! 2751: infof(data, "Failed to disconnect libssh2 session: %d %s\n",
! 2752: rc, err_msg);
! 2753: }
! 2754: }
! 2755:
! 2756: Curl_safefree(sshc->homedir);
! 2757: conn->data->state.most_recent_ftp_entrypath = NULL;
! 2758:
! 2759: state(conn, SSH_SESSION_FREE);
! 2760: break;
! 2761:
! 2762: case SSH_SESSION_FREE:
! 2763: #ifdef HAVE_LIBSSH2_KNOWNHOST_API
! 2764: if(sshc->kh) {
! 2765: libssh2_knownhost_free(sshc->kh);
! 2766: sshc->kh = NULL;
! 2767: }
! 2768: #endif
! 2769:
! 2770: #ifdef HAVE_LIBSSH2_AGENT_API
! 2771: if(sshc->ssh_agent) {
! 2772: rc = libssh2_agent_disconnect(sshc->ssh_agent);
! 2773: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2774: break;
! 2775: }
! 2776: if(rc < 0) {
! 2777: char *err_msg = NULL;
! 2778: (void)libssh2_session_last_error(sshc->ssh_session,
! 2779: &err_msg, NULL, 0);
! 2780: infof(data, "Failed to disconnect from libssh2 agent: %d %s\n",
! 2781: rc, err_msg);
! 2782: }
! 2783: libssh2_agent_free(sshc->ssh_agent);
! 2784: sshc->ssh_agent = NULL;
! 2785:
! 2786: /* NB: there is no need to free identities, they are part of internal
! 2787: agent stuff */
! 2788: sshc->sshagent_identity = NULL;
! 2789: sshc->sshagent_prev_identity = NULL;
! 2790: }
! 2791: #endif
! 2792:
! 2793: if(sshc->ssh_session) {
! 2794: rc = libssh2_session_free(sshc->ssh_session);
! 2795: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2796: break;
! 2797: }
! 2798: if(rc < 0) {
! 2799: char *err_msg = NULL;
! 2800: (void)libssh2_session_last_error(sshc->ssh_session,
! 2801: &err_msg, NULL, 0);
! 2802: infof(data, "Failed to free libssh2 session: %d %s\n", rc, err_msg);
! 2803: }
! 2804: sshc->ssh_session = NULL;
! 2805: }
! 2806:
! 2807: /* worst-case scenario cleanup */
! 2808:
! 2809: DEBUGASSERT(sshc->ssh_session == NULL);
! 2810: DEBUGASSERT(sshc->ssh_channel == NULL);
! 2811: DEBUGASSERT(sshc->sftp_session == NULL);
! 2812: DEBUGASSERT(sshc->sftp_handle == NULL);
! 2813: #ifdef HAVE_LIBSSH2_KNOWNHOST_API
! 2814: DEBUGASSERT(sshc->kh == NULL);
! 2815: #endif
! 2816: #ifdef HAVE_LIBSSH2_AGENT_API
! 2817: DEBUGASSERT(sshc->ssh_agent == NULL);
! 2818: #endif
! 2819:
! 2820: Curl_safefree(sshc->rsa_pub);
! 2821: Curl_safefree(sshc->rsa);
! 2822:
! 2823: Curl_safefree(sshc->quote_path1);
! 2824: Curl_safefree(sshc->quote_path2);
! 2825:
! 2826: Curl_safefree(sshc->homedir);
! 2827:
! 2828: Curl_safefree(sshc->readdir_filename);
! 2829: Curl_safefree(sshc->readdir_longentry);
! 2830: Curl_safefree(sshc->readdir_line);
! 2831: Curl_safefree(sshc->readdir_linkPath);
! 2832:
! 2833: /* the code we are about to return */
! 2834: result = sshc->actualcode;
! 2835:
! 2836: memset(sshc, 0, sizeof(struct ssh_conn));
! 2837:
! 2838: connclose(conn, "SSH session free");
! 2839: sshc->state = SSH_SESSION_FREE; /* current */
! 2840: sshc->nextstate = SSH_NO_STATE;
! 2841: state(conn, SSH_STOP);
! 2842: break;
! 2843:
! 2844: case SSH_QUIT:
! 2845: /* fallthrough, just stop! */
! 2846: default:
! 2847: /* internal error */
! 2848: sshc->nextstate = SSH_NO_STATE;
! 2849: state(conn, SSH_STOP);
! 2850: break;
! 2851: }
! 2852:
! 2853: } while(!rc && (sshc->state != SSH_STOP));
! 2854:
! 2855: if(rc == LIBSSH2_ERROR_EAGAIN) {
! 2856: /* we would block, we need to wait for the socket to be ready (in the
! 2857: right direction too)! */
! 2858: *block = TRUE;
! 2859: }
! 2860:
! 2861: return result;
! 2862: }
! 2863:
! 2864: /* called by the multi interface to figure out what socket(s) to wait for and
! 2865: for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
! 2866: static int ssh_perform_getsock(const struct connectdata *conn,
! 2867: curl_socket_t *sock)
! 2868: {
! 2869: #ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
! 2870: int bitmap = GETSOCK_BLANK;
! 2871:
! 2872: sock[0] = conn->sock[FIRSTSOCKET];
! 2873:
! 2874: if(conn->waitfor & KEEP_RECV)
! 2875: bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
! 2876:
! 2877: if(conn->waitfor & KEEP_SEND)
! 2878: bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
! 2879:
! 2880: return bitmap;
! 2881: #else
! 2882: /* if we don't know the direction we can use the generic *_getsock()
! 2883: function even for the protocol_connect and doing states */
! 2884: return Curl_single_getsock(conn, sock);
! 2885: #endif
! 2886: }
! 2887:
! 2888: /* Generic function called by the multi interface to figure out what socket(s)
! 2889: to wait for and for what actions during the DOING and PROTOCONNECT states*/
! 2890: static int ssh_getsock(struct connectdata *conn,
! 2891: curl_socket_t *sock)
! 2892: {
! 2893: #ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
! 2894: (void)conn;
! 2895: (void)sock;
! 2896: /* if we don't know any direction we can just play along as we used to and
! 2897: not provide any sensible info */
! 2898: return GETSOCK_BLANK;
! 2899: #else
! 2900: /* if we know the direction we can use the generic *_getsock() function even
! 2901: for the protocol_connect and doing states */
! 2902: return ssh_perform_getsock(conn, sock);
! 2903: #endif
! 2904: }
! 2905:
! 2906: #ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
! 2907: /*
! 2908: * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this
! 2909: * function is used to figure out in what direction and stores this info so
! 2910: * that the multi interface can take advantage of it. Make sure to call this
! 2911: * function in all cases so that when it _doesn't_ return EAGAIN we can
! 2912: * restore the default wait bits.
! 2913: */
! 2914: static void ssh_block2waitfor(struct connectdata *conn, bool block)
! 2915: {
! 2916: struct ssh_conn *sshc = &conn->proto.sshc;
! 2917: int dir = 0;
! 2918: if(block) {
! 2919: dir = libssh2_session_block_directions(sshc->ssh_session);
! 2920: if(dir) {
! 2921: /* translate the libssh2 define bits into our own bit defines */
! 2922: conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
! 2923: ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0);
! 2924: }
! 2925: }
! 2926: if(!dir)
! 2927: /* It didn't block or libssh2 didn't reveal in which direction, put back
! 2928: the original set */
! 2929: conn->waitfor = sshc->orig_waitfor;
! 2930: }
! 2931: #else
! 2932: /* no libssh2 directional support so we simply don't know */
! 2933: #define ssh_block2waitfor(x,y) Curl_nop_stmt
! 2934: #endif
! 2935:
! 2936: /* called repeatedly until done from multi.c */
! 2937: static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
! 2938: {
! 2939: struct ssh_conn *sshc = &conn->proto.sshc;
! 2940: CURLcode result = CURLE_OK;
! 2941: bool block; /* we store the status and use that to provide a ssh_getsock()
! 2942: implementation */
! 2943: do {
! 2944: result = ssh_statemach_act(conn, &block);
! 2945: *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
! 2946: /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
! 2947: try again */
! 2948: } while(!result && !*done && !block);
! 2949: ssh_block2waitfor(conn, block);
! 2950:
! 2951: return result;
! 2952: }
! 2953:
! 2954: static CURLcode ssh_block_statemach(struct connectdata *conn,
! 2955: bool disconnect)
! 2956: {
! 2957: struct ssh_conn *sshc = &conn->proto.sshc;
! 2958: CURLcode result = CURLE_OK;
! 2959: struct Curl_easy *data = conn->data;
! 2960:
! 2961: while((sshc->state != SSH_STOP) && !result) {
! 2962: bool block;
! 2963: timediff_t left = 1000;
! 2964: struct curltime now = Curl_now();
! 2965:
! 2966: result = ssh_statemach_act(conn, &block);
! 2967: if(result)
! 2968: break;
! 2969:
! 2970: if(!disconnect) {
! 2971: if(Curl_pgrsUpdate(conn))
! 2972: return CURLE_ABORTED_BY_CALLBACK;
! 2973:
! 2974: result = Curl_speedcheck(data, now);
! 2975: if(result)
! 2976: break;
! 2977:
! 2978: left = Curl_timeleft(data, NULL, FALSE);
! 2979: if(left < 0) {
! 2980: failf(data, "Operation timed out");
! 2981: return CURLE_OPERATION_TIMEDOUT;
! 2982: }
! 2983: }
! 2984:
! 2985: #ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
! 2986: if(block) {
! 2987: int dir = libssh2_session_block_directions(sshc->ssh_session);
! 2988: curl_socket_t sock = conn->sock[FIRSTSOCKET];
! 2989: curl_socket_t fd_read = CURL_SOCKET_BAD;
! 2990: curl_socket_t fd_write = CURL_SOCKET_BAD;
! 2991: if(LIBSSH2_SESSION_BLOCK_INBOUND & dir)
! 2992: fd_read = sock;
! 2993: if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir)
! 2994: fd_write = sock;
! 2995: /* wait for the socket to become ready */
! 2996: (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
! 2997: left>1000?1000:(time_t)left);
! 2998: }
! 2999: #endif
! 3000:
! 3001: }
! 3002:
! 3003: return result;
! 3004: }
! 3005:
! 3006: /*
! 3007: * SSH setup and connection
! 3008: */
! 3009: static CURLcode ssh_setup_connection(struct connectdata *conn)
! 3010: {
! 3011: struct SSHPROTO *ssh;
! 3012:
! 3013: conn->data->req.protop = ssh = calloc(1, sizeof(struct SSHPROTO));
! 3014: if(!ssh)
! 3015: return CURLE_OUT_OF_MEMORY;
! 3016:
! 3017: return CURLE_OK;
! 3018: }
! 3019:
! 3020: static Curl_recv scp_recv, sftp_recv;
! 3021: static Curl_send scp_send, sftp_send;
! 3022:
! 3023: /*
! 3024: * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
! 3025: * do protocol-specific actions at connect-time.
! 3026: */
! 3027: static CURLcode ssh_connect(struct connectdata *conn, bool *done)
! 3028: {
! 3029: #ifdef CURL_LIBSSH2_DEBUG
! 3030: curl_socket_t sock;
! 3031: #endif
! 3032: struct ssh_conn *ssh;
! 3033: CURLcode result;
! 3034: struct Curl_easy *data = conn->data;
! 3035:
! 3036: /* initialize per-handle data if not already */
! 3037: if(!data->req.protop)
! 3038: ssh_setup_connection(conn);
! 3039:
! 3040: /* We default to persistent connections. We set this already in this connect
! 3041: function to make the re-use checks properly be able to check this bit. */
! 3042: connkeep(conn, "SSH default");
! 3043:
! 3044: if(conn->handler->protocol & CURLPROTO_SCP) {
! 3045: conn->recv[FIRSTSOCKET] = scp_recv;
! 3046: conn->send[FIRSTSOCKET] = scp_send;
! 3047: }
! 3048: else {
! 3049: conn->recv[FIRSTSOCKET] = sftp_recv;
! 3050: conn->send[FIRSTSOCKET] = sftp_send;
! 3051: }
! 3052: ssh = &conn->proto.sshc;
! 3053:
! 3054: #ifdef CURL_LIBSSH2_DEBUG
! 3055: if(conn->user) {
! 3056: infof(data, "User: %s\n", conn->user);
! 3057: }
! 3058: if(conn->passwd) {
! 3059: infof(data, "Password: %s\n", conn->passwd);
! 3060: }
! 3061: sock = conn->sock[FIRSTSOCKET];
! 3062: #endif /* CURL_LIBSSH2_DEBUG */
! 3063:
! 3064: ssh->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
! 3065: my_libssh2_free,
! 3066: my_libssh2_realloc, conn);
! 3067: if(ssh->ssh_session == NULL) {
! 3068: failf(data, "Failure initialising ssh session");
! 3069: return CURLE_FAILED_INIT;
! 3070: }
! 3071:
! 3072: if(data->set.ssh_compression) {
! 3073: #if LIBSSH2_VERSION_NUM >= 0x010208
! 3074: if(libssh2_session_flag(ssh->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0)
! 3075: #endif
! 3076: infof(data, "Failed to enable compression for ssh session\n");
! 3077: }
! 3078:
! 3079: #ifdef HAVE_LIBSSH2_KNOWNHOST_API
! 3080: if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
! 3081: int rc;
! 3082: ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
! 3083: if(!ssh->kh) {
! 3084: libssh2_session_free(ssh->ssh_session);
! 3085: return CURLE_FAILED_INIT;
! 3086: }
! 3087:
! 3088: /* read all known hosts from there */
! 3089: rc = libssh2_knownhost_readfile(ssh->kh,
! 3090: data->set.str[STRING_SSH_KNOWNHOSTS],
! 3091: LIBSSH2_KNOWNHOST_FILE_OPENSSH);
! 3092: if(rc < 0)
! 3093: infof(data, "Failed to read known hosts from %s\n",
! 3094: data->set.str[STRING_SSH_KNOWNHOSTS]);
! 3095: }
! 3096: #endif /* HAVE_LIBSSH2_KNOWNHOST_API */
! 3097:
! 3098: #ifdef CURL_LIBSSH2_DEBUG
! 3099: libssh2_trace(ssh->ssh_session, ~0);
! 3100: infof(data, "SSH socket: %d\n", (int)sock);
! 3101: #endif /* CURL_LIBSSH2_DEBUG */
! 3102:
! 3103: state(conn, SSH_INIT);
! 3104:
! 3105: result = ssh_multi_statemach(conn, done);
! 3106:
! 3107: return result;
! 3108: }
! 3109:
! 3110: /*
! 3111: ***********************************************************************
! 3112: *
! 3113: * scp_perform()
! 3114: *
! 3115: * This is the actual DO function for SCP. Get a file according to
! 3116: * the options previously setup.
! 3117: */
! 3118:
! 3119: static
! 3120: CURLcode scp_perform(struct connectdata *conn,
! 3121: bool *connected,
! 3122: bool *dophase_done)
! 3123: {
! 3124: CURLcode result = CURLE_OK;
! 3125:
! 3126: DEBUGF(infof(conn->data, "DO phase starts\n"));
! 3127:
! 3128: *dophase_done = FALSE; /* not done yet */
! 3129:
! 3130: /* start the first command in the DO phase */
! 3131: state(conn, SSH_SCP_TRANS_INIT);
! 3132:
! 3133: /* run the state-machine */
! 3134: result = ssh_multi_statemach(conn, dophase_done);
! 3135:
! 3136: *connected = conn->bits.tcpconnect[FIRSTSOCKET];
! 3137:
! 3138: if(*dophase_done) {
! 3139: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 3140: }
! 3141:
! 3142: return result;
! 3143: }
! 3144:
! 3145: /* called from multi.c while DOing */
! 3146: static CURLcode scp_doing(struct connectdata *conn,
! 3147: bool *dophase_done)
! 3148: {
! 3149: CURLcode result;
! 3150: result = ssh_multi_statemach(conn, dophase_done);
! 3151:
! 3152: if(*dophase_done) {
! 3153: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 3154: }
! 3155: return result;
! 3156: }
! 3157:
! 3158: /*
! 3159: * The DO function is generic for both protocols. There was previously two
! 3160: * separate ones but this way means less duplicated code.
! 3161: */
! 3162:
! 3163: static CURLcode ssh_do(struct connectdata *conn, bool *done)
! 3164: {
! 3165: CURLcode result;
! 3166: bool connected = 0;
! 3167: struct Curl_easy *data = conn->data;
! 3168: struct ssh_conn *sshc = &conn->proto.sshc;
! 3169:
! 3170: *done = FALSE; /* default to false */
! 3171:
! 3172: data->req.size = -1; /* make sure this is unknown at this point */
! 3173:
! 3174: sshc->actualcode = CURLE_OK; /* reset error code */
! 3175: sshc->secondCreateDirs = 0; /* reset the create dir attempt state
! 3176: variable */
! 3177:
! 3178: Curl_pgrsSetUploadCounter(data, 0);
! 3179: Curl_pgrsSetDownloadCounter(data, 0);
! 3180: Curl_pgrsSetUploadSize(data, -1);
! 3181: Curl_pgrsSetDownloadSize(data, -1);
! 3182:
! 3183: if(conn->handler->protocol & CURLPROTO_SCP)
! 3184: result = scp_perform(conn, &connected, done);
! 3185: else
! 3186: result = sftp_perform(conn, &connected, done);
! 3187:
! 3188: return result;
! 3189: }
! 3190:
! 3191: /* BLOCKING, but the function is using the state machine so the only reason
! 3192: this is still blocking is that the multi interface code has no support for
! 3193: disconnecting operations that takes a while */
! 3194: static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection)
! 3195: {
! 3196: CURLcode result = CURLE_OK;
! 3197: struct ssh_conn *ssh = &conn->proto.sshc;
! 3198: (void) dead_connection;
! 3199:
! 3200: if(ssh->ssh_session) {
! 3201: /* only if there's a session still around to use! */
! 3202:
! 3203: state(conn, SSH_SESSION_DISCONNECT);
! 3204:
! 3205: result = ssh_block_statemach(conn, TRUE);
! 3206: }
! 3207:
! 3208: return result;
! 3209: }
! 3210:
! 3211: /* generic done function for both SCP and SFTP called from their specific
! 3212: done functions */
! 3213: static CURLcode ssh_done(struct connectdata *conn, CURLcode status)
! 3214: {
! 3215: CURLcode result = CURLE_OK;
! 3216: struct SSHPROTO *sftp_scp = conn->data->req.protop;
! 3217:
! 3218: if(!status) {
! 3219: /* run the state-machine */
! 3220: result = ssh_block_statemach(conn, FALSE);
! 3221: }
! 3222: else
! 3223: result = status;
! 3224:
! 3225: if(sftp_scp)
! 3226: Curl_safefree(sftp_scp->path);
! 3227: if(Curl_pgrsDone(conn))
! 3228: return CURLE_ABORTED_BY_CALLBACK;
! 3229:
! 3230: conn->data->req.keepon = 0; /* clear all bits */
! 3231: return result;
! 3232: }
! 3233:
! 3234:
! 3235: static CURLcode scp_done(struct connectdata *conn, CURLcode status,
! 3236: bool premature)
! 3237: {
! 3238: (void)premature; /* not used */
! 3239:
! 3240: if(!status)
! 3241: state(conn, SSH_SCP_DONE);
! 3242:
! 3243: return ssh_done(conn, status);
! 3244:
! 3245: }
! 3246:
! 3247: static ssize_t scp_send(struct connectdata *conn, int sockindex,
! 3248: const void *mem, size_t len, CURLcode *err)
! 3249: {
! 3250: ssize_t nwrite;
! 3251: (void)sockindex; /* we only support SCP on the fixed known primary socket */
! 3252:
! 3253: /* libssh2_channel_write() returns int! */
! 3254: nwrite = (ssize_t)
! 3255: libssh2_channel_write(conn->proto.sshc.ssh_channel, mem, len);
! 3256:
! 3257: ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
! 3258:
! 3259: if(nwrite == LIBSSH2_ERROR_EAGAIN) {
! 3260: *err = CURLE_AGAIN;
! 3261: nwrite = 0;
! 3262: }
! 3263: else if(nwrite < LIBSSH2_ERROR_NONE) {
! 3264: *err = libssh2_session_error_to_CURLE((int)nwrite);
! 3265: nwrite = -1;
! 3266: }
! 3267:
! 3268: return nwrite;
! 3269: }
! 3270:
! 3271: static ssize_t scp_recv(struct connectdata *conn, int sockindex,
! 3272: char *mem, size_t len, CURLcode *err)
! 3273: {
! 3274: ssize_t nread;
! 3275: (void)sockindex; /* we only support SCP on the fixed known primary socket */
! 3276:
! 3277: /* libssh2_channel_read() returns int */
! 3278: nread = (ssize_t)
! 3279: libssh2_channel_read(conn->proto.sshc.ssh_channel, mem, len);
! 3280:
! 3281: ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
! 3282: if(nread == LIBSSH2_ERROR_EAGAIN) {
! 3283: *err = CURLE_AGAIN;
! 3284: nread = -1;
! 3285: }
! 3286:
! 3287: return nread;
! 3288: }
! 3289:
! 3290: /*
! 3291: * =============== SFTP ===============
! 3292: */
! 3293:
! 3294: /*
! 3295: ***********************************************************************
! 3296: *
! 3297: * sftp_perform()
! 3298: *
! 3299: * This is the actual DO function for SFTP. Get a file/directory according to
! 3300: * the options previously setup.
! 3301: */
! 3302:
! 3303: static
! 3304: CURLcode sftp_perform(struct connectdata *conn,
! 3305: bool *connected,
! 3306: bool *dophase_done)
! 3307: {
! 3308: CURLcode result = CURLE_OK;
! 3309:
! 3310: DEBUGF(infof(conn->data, "DO phase starts\n"));
! 3311:
! 3312: *dophase_done = FALSE; /* not done yet */
! 3313:
! 3314: /* start the first command in the DO phase */
! 3315: state(conn, SSH_SFTP_QUOTE_INIT);
! 3316:
! 3317: /* run the state-machine */
! 3318: result = ssh_multi_statemach(conn, dophase_done);
! 3319:
! 3320: *connected = conn->bits.tcpconnect[FIRSTSOCKET];
! 3321:
! 3322: if(*dophase_done) {
! 3323: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 3324: }
! 3325:
! 3326: return result;
! 3327: }
! 3328:
! 3329: /* called from multi.c while DOing */
! 3330: static CURLcode sftp_doing(struct connectdata *conn,
! 3331: bool *dophase_done)
! 3332: {
! 3333: CURLcode result = ssh_multi_statemach(conn, dophase_done);
! 3334:
! 3335: if(*dophase_done) {
! 3336: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 3337: }
! 3338: return result;
! 3339: }
! 3340:
! 3341: /* BLOCKING, but the function is using the state machine so the only reason
! 3342: this is still blocking is that the multi interface code has no support for
! 3343: disconnecting operations that takes a while */
! 3344: static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection)
! 3345: {
! 3346: CURLcode result = CURLE_OK;
! 3347: (void) dead_connection;
! 3348:
! 3349: DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n"));
! 3350:
! 3351: if(conn->proto.sshc.ssh_session) {
! 3352: /* only if there's a session still around to use! */
! 3353: state(conn, SSH_SFTP_SHUTDOWN);
! 3354: result = ssh_block_statemach(conn, TRUE);
! 3355: }
! 3356:
! 3357: DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n"));
! 3358:
! 3359: return result;
! 3360:
! 3361: }
! 3362:
! 3363: static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
! 3364: bool premature)
! 3365: {
! 3366: struct ssh_conn *sshc = &conn->proto.sshc;
! 3367:
! 3368: if(!status) {
! 3369: /* Post quote commands are executed after the SFTP_CLOSE state to avoid
! 3370: errors that could happen due to open file handles during POSTQUOTE
! 3371: operation */
! 3372: if(!premature && conn->data->set.postquote && !conn->bits.retry)
! 3373: sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
! 3374: state(conn, SSH_SFTP_CLOSE);
! 3375: }
! 3376: return ssh_done(conn, status);
! 3377: }
! 3378:
! 3379: /* return number of sent bytes */
! 3380: static ssize_t sftp_send(struct connectdata *conn, int sockindex,
! 3381: const void *mem, size_t len, CURLcode *err)
! 3382: {
! 3383: ssize_t nwrite; /* libssh2_sftp_write() used to return size_t in 0.14
! 3384: but is changed to ssize_t in 0.15. These days we don't
! 3385: support libssh2 0.15*/
! 3386: (void)sockindex;
! 3387:
! 3388: nwrite = libssh2_sftp_write(conn->proto.sshc.sftp_handle, mem, len);
! 3389:
! 3390: ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
! 3391:
! 3392: if(nwrite == LIBSSH2_ERROR_EAGAIN) {
! 3393: *err = CURLE_AGAIN;
! 3394: nwrite = 0;
! 3395: }
! 3396: else if(nwrite < LIBSSH2_ERROR_NONE) {
! 3397: *err = libssh2_session_error_to_CURLE((int)nwrite);
! 3398: nwrite = -1;
! 3399: }
! 3400:
! 3401: return nwrite;
! 3402: }
! 3403:
! 3404: /*
! 3405: * Return number of received (decrypted) bytes
! 3406: * or <0 on error
! 3407: */
! 3408: static ssize_t sftp_recv(struct connectdata *conn, int sockindex,
! 3409: char *mem, size_t len, CURLcode *err)
! 3410: {
! 3411: ssize_t nread;
! 3412: (void)sockindex;
! 3413:
! 3414: nread = libssh2_sftp_read(conn->proto.sshc.sftp_handle, mem, len);
! 3415:
! 3416: ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
! 3417:
! 3418: if(nread == LIBSSH2_ERROR_EAGAIN) {
! 3419: *err = CURLE_AGAIN;
! 3420: nread = -1;
! 3421:
! 3422: }
! 3423: else if(nread < 0) {
! 3424: *err = libssh2_session_error_to_CURLE((int)nread);
! 3425: }
! 3426: return nread;
! 3427: }
! 3428:
! 3429: static const char *sftp_libssh2_strerror(int err)
! 3430: {
! 3431: switch(err) {
! 3432: case LIBSSH2_FX_NO_SUCH_FILE:
! 3433: return "No such file or directory";
! 3434:
! 3435: case LIBSSH2_FX_PERMISSION_DENIED:
! 3436: return "Permission denied";
! 3437:
! 3438: case LIBSSH2_FX_FAILURE:
! 3439: return "Operation failed";
! 3440:
! 3441: case LIBSSH2_FX_BAD_MESSAGE:
! 3442: return "Bad message from SFTP server";
! 3443:
! 3444: case LIBSSH2_FX_NO_CONNECTION:
! 3445: return "Not connected to SFTP server";
! 3446:
! 3447: case LIBSSH2_FX_CONNECTION_LOST:
! 3448: return "Connection to SFTP server lost";
! 3449:
! 3450: case LIBSSH2_FX_OP_UNSUPPORTED:
! 3451: return "Operation not supported by SFTP server";
! 3452:
! 3453: case LIBSSH2_FX_INVALID_HANDLE:
! 3454: return "Invalid handle";
! 3455:
! 3456: case LIBSSH2_FX_NO_SUCH_PATH:
! 3457: return "No such file or directory";
! 3458:
! 3459: case LIBSSH2_FX_FILE_ALREADY_EXISTS:
! 3460: return "File already exists";
! 3461:
! 3462: case LIBSSH2_FX_WRITE_PROTECT:
! 3463: return "File is write protected";
! 3464:
! 3465: case LIBSSH2_FX_NO_MEDIA:
! 3466: return "No media";
! 3467:
! 3468: case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
! 3469: return "Disk full";
! 3470:
! 3471: case LIBSSH2_FX_QUOTA_EXCEEDED:
! 3472: return "User quota exceeded";
! 3473:
! 3474: case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
! 3475: return "Unknown principle";
! 3476:
! 3477: case LIBSSH2_FX_LOCK_CONFlICT:
! 3478: return "File lock conflict";
! 3479:
! 3480: case LIBSSH2_FX_DIR_NOT_EMPTY:
! 3481: return "Directory not empty";
! 3482:
! 3483: case LIBSSH2_FX_NOT_A_DIRECTORY:
! 3484: return "Not a directory";
! 3485:
! 3486: case LIBSSH2_FX_INVALID_FILENAME:
! 3487: return "Invalid filename";
! 3488:
! 3489: case LIBSSH2_FX_LINK_LOOP:
! 3490: return "Link points to itself";
! 3491: }
! 3492: return "Unknown error in libssh2";
! 3493: }
! 3494:
! 3495: CURLcode Curl_ssh_init(void)
! 3496: {
! 3497: #ifdef HAVE_LIBSSH2_INIT
! 3498: if(libssh2_init(0)) {
! 3499: DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
! 3500: return CURLE_FAILED_INIT;
! 3501: }
! 3502: #endif
! 3503: return CURLE_OK;
! 3504: }
! 3505:
! 3506: void Curl_ssh_cleanup(void)
! 3507: {
! 3508: #ifdef HAVE_LIBSSH2_EXIT
! 3509: (void)libssh2_exit();
! 3510: #endif
! 3511: }
! 3512:
! 3513: size_t Curl_ssh_version(char *buffer, size_t buflen)
! 3514: {
! 3515: return msnprintf(buffer, buflen, "libssh2/%s", LIBSSH2_VERSION);
! 3516: }
! 3517:
! 3518: #endif /* USE_LIBSSH2 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>