X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/bc06669bcf2c0d331e231d18c0e14023433f9e79..HEAD:/pscp.c diff --git a/pscp.c b/pscp.c index 833e46cd..32099323 100644 --- a/pscp.c +++ b/pscp.c @@ -41,6 +41,7 @@ static int try_sftp = 1; static int main_cmd_is_sftp = 0; static int fallback_cmd_is_sftp = 0; static int using_sftp = 0; +static int uploading = 0; static Backend *back; static void *backhandle; @@ -129,6 +130,19 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +void nonfatal(char *fmt, ...) +{ + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Error: ", str, "\n", NULL); + sfree(str); + va_end(ap); + tell_str(stderr, str2); + sfree(str2); + errs++; +} void connection_fatal(void *frontend, char *fmt, ...) { char *str, *str2; @@ -218,11 +232,12 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) int from_backend_eof(void *frontend) { /* - * We expect to be the party deciding when to close the + * We usually expect to be the party deciding when to close the * connection, so if we see EOF before we sent it ourselves, we - * should panic. + * should panic. The exception is if we're using old-style scp and + * downloading rather than uploading. */ - if (!sent_eof) { + if ((using_sftp || uploading) && !sent_eof) { connection_fatal(frontend, "Received unexpected end-of-file from server"); } @@ -320,6 +335,29 @@ static void bump(char *fmt, ...) } /* + * Wait for the reply to a single SFTP request. Parallels the same + * function in psftp.c (but isn't centralised into sftp.c because the + * latter module handles SFTP only and shouldn't assume that SFTP is + * the only thing going on by calling connection_fatal). + */ +struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) +{ + struct sftp_packet *pktin; + struct sftp_request *rreq; + + sftp_register(req); + pktin = sftp_recv(); + if (pktin == NULL) + connection_fatal(NULL, "did not receive SFTP response packet " + "from server"); + rreq = sftp_find_request(pktin); + if (rreq != req) + connection_fatal(NULL, "unable to understand SFTP response packet " + "from server: %s", fxp_error()); + return pktin; +} + +/* * Open an SSH connection to user@host and execute cmd. */ static void do_cmd(char *host, char *user, char *cmd) @@ -503,7 +541,7 @@ static void do_cmd(char *host, char *user, char *cmd) console_provide_logctx(logctx); ssh_scp_init(); if (verbose && realhost != NULL && errs == 0) - tell_user(stderr, "Connected to %s\n", realhost); + tell_user(stderr, "Connected to %s", realhost); sfree(realhost); } @@ -652,7 +690,7 @@ static int response(void) } while (p < sizeof(rbuf) && ch != '\n'); rbuf[p - 1] = '\0'; if (resp == 1) - tell_user(stderr, "%s\n", rbuf); + tell_user(stderr, "%s", rbuf); else bump("%s", rbuf); errs++; @@ -685,7 +723,7 @@ void scp_sftp_listdir(char *dirname) struct fxp_names *names; struct fxp_name *ournames; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int nnames, namesize; int i; @@ -697,10 +735,9 @@ void scp_sftp_listdir(char *dirname) printf("Listing directory %s\n", dirname); - sftp_register(req = fxp_opendir_send(dirname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(dirname); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { printf("Unable to open %s: %s\n", dirname, fxp_error()); @@ -710,10 +747,9 @@ void scp_sftp_listdir(char *dirname) while (1) { - sftp_register(req = fxp_readdir_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirh); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) @@ -736,22 +772,24 @@ void scp_sftp_listdir(char *dirname) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); /* * And print them. */ for (i = 0; i < nnames; i++) printf("%s\n", ournames[i].longname); + + sfree(ournames); } } @@ -786,7 +824,7 @@ int scp_source_setup(char *target, int shouldbedir) * directory. */ struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_attrs attrs; int ret; @@ -796,10 +834,9 @@ int scp_source_setup(char *target, int shouldbedir) return 1; } - sftp_register(req = fxp_stat_send(target)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(target); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) scp_sftp_targetisdir = 0; @@ -850,7 +887,7 @@ int scp_send_filename(char *name, uint64 size, int permissions) if (using_sftp) { char *fullname; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_attrs attrs; if (scp_sftp_targetisdir) { @@ -862,16 +899,16 @@ int scp_send_filename(char *name, uint64 size, int permissions) attrs.flags = 0; PUT_PERMISSIONS(attrs, permissions); - sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE | - SSH_FXF_CREAT | SSH_FXF_TRUNC, - &attrs)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + req = fxp_open_send(fullname, + SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, + &attrs); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", fullname, fxp_error()); + sfree(fullname); errs++; return 1; } @@ -907,8 +944,10 @@ int scp_send_filedata(char *data, int len) while (!xfer_upload_ready(scp_sftp_xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); - if (!ret) { - tell_user(stderr, "error while writing: %s\n", fxp_error()); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return 1; } @@ -942,12 +981,19 @@ int scp_send_finish(void) if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; while (!xfer_done(scp_sftp_xfer)) { pktin = sftp_recv(); - xfer_upload_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return 1; + } } xfer_cleanup(scp_sftp_xfer); @@ -958,19 +1004,17 @@ int scp_send_finish(void) attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME; attrs.atime = scp_sftp_atime; attrs.mtime = scp_sftp_mtime; - sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_fsetstat_recv(pktin, rreq); + req = fxp_fsetstat_send(scp_sftp_filehandle, attrs); + pktin = sftp_wait_for_reply(req); + ret = fxp_fsetstat_recv(pktin, req); if (!ret) { - tell_user(stderr, "unable to set file times: %s\n", fxp_error()); + tell_user(stderr, "unable to set file times: %s", fxp_error()); errs++; } } - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); scp_has_times = 0; return 0; } else { @@ -1000,7 +1044,7 @@ int scp_send_dirname(char *name, int modes) char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (scp_sftp_targetisdir) { @@ -1016,25 +1060,24 @@ int scp_send_dirname(char *name, int modes) * exists and is a directory we will assume we were either * successful or it didn't matter. */ - sftp_register(req = fxp_mkdir_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_mkdir_recv(pktin, req); if (!ret) err = fxp_error(); else err = "server reported no error"; - sftp_register(req = fxp_stat_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { tell_user(stderr, "unable to create directory %s: %s", fullname, err); + sfree(fullname); errs++; return 1; } @@ -1090,6 +1133,9 @@ int scp_sink_setup(char *source, int preserve, int recursive) if (!wc_unescape(newsource, source)) { /* Yes, here we go; it's a wildcard. Bah. */ char *dupsource, *lastpart, *dirpart, *wildcard; + + sfree(newsource); + dupsource = dupstr(source); lastpart = stripslashes(dupsource, 0); wildcard = dupstr(lastpart); @@ -1179,7 +1225,7 @@ int scp_get_sink_action(struct scp_sink_action *act) int must_free_fname; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (!scp_sftp_dirstack_head) { @@ -1248,14 +1294,14 @@ int scp_get_sink_action(struct scp_sink_action *act) * Now we have a filename. Stat it, and see if it's a file * or a directory. */ - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { tell_user(stderr, "unable to identify %s: %s", fname, ret ? "file type not supplied" : fxp_error()); + if (must_free_fname) sfree(fname); errs++; return 1; } @@ -1304,13 +1350,12 @@ int scp_get_sink_action(struct scp_sink_action *act) * list), we must push the other (target,namelist) pair * on a stack. */ - sftp_register(req = fxp_opendir_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirhandle = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(fname); + pktin = sftp_wait_for_reply(req); + dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { - tell_user(stderr, "scp: unable to open directory %s: %s", + tell_user(stderr, "pscp: unable to open directory %s: %s", fname, fxp_error()); if (must_free_fname) sfree(fname); errs++; @@ -1321,16 +1366,20 @@ int scp_get_sink_action(struct scp_sink_action *act) while (1) { int i; - sftp_register(req = fxp_readdir_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirhandle); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; - tell_user(stderr, "scp: reading directory %s: %s\n", + tell_user(stderr, "pscp: reading directory %s: %s", fname, fxp_error()); + + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); + if (must_free_fname) sfree(fname); sfree(ournames); errs++; @@ -1354,7 +1403,7 @@ int scp_get_sink_action(struct scp_sink_action *act) */ } else if (!vet_filename(names->names[i].filename)) { tell_user(stderr, "ignoring potentially dangerous server-" - "supplied filename '%s'\n", + "supplied filename '%s'", names->names[i].filename); } else ournames[nnames++] = names->names[i]; @@ -1362,10 +1411,9 @@ int scp_get_sink_action(struct scp_sink_action *act) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); newitem = snew(struct scp_sftp_dirstack); newitem->next = scp_sftp_dirstack_head; @@ -1458,7 +1506,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf[i - 1] = '\0'; switch (action) { case '\01': /* error */ - tell_user(stderr, "%s\n", act->buf); + tell_user(stderr, "%s", act->buf); errs++; continue; /* go round again */ case '\02': /* fatal error */ @@ -1510,13 +1558,11 @@ int scp_accept_filexfer(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; - sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ, - NULL)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ, NULL); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", @@ -1545,9 +1591,10 @@ int scp_recv_filedata(char *data, int len) xfer_download_queue(scp_sftp_xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); - - if (ret < 0) { + if (ret <= 0) { tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return -1; } @@ -1577,7 +1624,7 @@ int scp_finish_filerecv(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; /* * Ensure that xfer_done() will work correctly, so we can @@ -1587,19 +1634,25 @@ int scp_finish_filerecv(void) xfer_set_error(scp_sftp_xfer); while (!xfer_done(scp_sftp_xfer)) { void *vbuf; - int len; + int ret, len; pktin = sftp_recv(); - xfer_download_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return -1; + } if (xfer_download_data(scp_sftp_xfer, &vbuf, &len)) sfree(vbuf); } xfer_cleanup(scp_sftp_xfer); - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } else { back->send(backhandle, "", 1); @@ -1618,7 +1671,7 @@ static void run_err(const char *fmt, ...) va_start(ap, fmt); errs++; str = dupvprintf(fmt, ap); - str2 = dupcat("scp: ", str, "\n", NULL); + str2 = dupcat("pscp: ", str, "\n", NULL); sfree(str); scp_send_errmsg(str2); tell_user(stderr, "%s", str2); @@ -1687,8 +1740,10 @@ static void source(char *src) return; } if (preserve) { - if (scp_send_filetimes(mtime, atime)) + if (scp_send_filetimes(mtime, atime)) { + close_rfile(f); return; + } } if (verbose) { @@ -1696,8 +1751,10 @@ static void source(char *src) uint64_decimal(size, sizestr); tell_user(stderr, "Sending file %s, size=%s", last, sizestr); } - if (scp_send_filename(last, size, permissions)) + if (scp_send_filename(last, size, permissions)) { + close_rfile(f); return; + } stat_bytes = uint64_make(0,0); stat_starttime = time(NULL); @@ -1900,27 +1957,34 @@ static void sink(char *targ, char *src) if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); + sfree(destfname); continue; } if (!exists) { if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); + sfree(destfname); continue; } } sink(destfname, NULL); /* can we set the timestamp for directories ? */ + sfree(destfname); continue; } f = open_new_file(destfname, act.permissions); if (f == NULL) { run_err("%s: Cannot create file", destfname); + sfree(destfname); continue; } - if (scp_accept_filexfer()) + if (scp_accept_filexfer()) { + sfree(destfname); + close_wfile(f); return; + } stat_bytes = uint64_make(0, 0); stat_starttime = time(NULL); @@ -1967,6 +2031,7 @@ static void sink(char *targ, char *src) close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); + sfree(destfname); continue; } (void) scp_finish_filerecv(); @@ -1984,6 +2049,8 @@ static void toremote(int argc, char *argv[]) char *cmd; int i, wc_type; + uploading = 1; + targ = argv[argc - 1]; /* Separate host from filename */ @@ -2073,6 +2140,8 @@ static void tolocal(int argc, char *argv[]) char *src, *targ, *host, *user; char *cmd; + uploading = 0; + if (argc != 2) bump("More than one remote source not supported"); @@ -2283,9 +2352,12 @@ int psftp_main(int argc, char *argv[]) preserve = 1; } else if (strcmp(argv[i], "-q") == 0) { statistics = 0; - } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) { + } else if (strcmp(argv[i], "-h") == 0 || + strcmp(argv[i], "-?") == 0 || + strcmp(argv[i], "--help") == 0) { usage(); - } else if (strcmp(argv[i], "-V") == 0) { + } else if (strcmp(argv[i], "-V") == 0 || + strcmp(argv[i], "--version") == 0) { version(); } else if (strcmp(argv[i], "-ls") == 0) { list = 1;