X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/4a693cfc5c3ee0e639bbee0215345e921715ab04..e99bb8bfc8d2c1a47b6ae90ef43683d191c30f66:/pscp.c diff --git a/pscp.c b/pscp.c index a0910c7a..4c6aba73 100644 --- a/pscp.c +++ b/pscp.c @@ -45,6 +45,7 @@ static int using_sftp = 0; static Backend *back; static void *backhandle; static Conf *conf; +int sent_eof = FALSE; static void source(char *src); static void rsource(char *src); @@ -214,6 +215,19 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } +int from_backend_eof(void *frontend) +{ + /* + * We expect to be the party deciding when to close the + * connection, so if we see EOF before we sent it ourselves, we + * should panic. + */ + if (!sent_eof) { + connection_fatal(frontend, + "Received unexpected end-of-file from server"); + } + return FALSE; +} static int ssh_scp_recv(unsigned char *buf, int len) { outptr = buf; @@ -298,6 +312,7 @@ static void bump(char *fmt, ...) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } @@ -305,6 +320,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) @@ -450,7 +488,7 @@ static void do_cmd(char *host, char *user, char *cmd) if (try_scp) { /* Fallback is to use the provided scp command. */ fallback_cmd_is_sftp = 0; - conf_set_str(conf, CONF_remote_cmd2, "sftp"); + conf_set_str(conf, CONF_remote_cmd2, cmd); conf_set_int(conf, CONF_ssh_subsys2, FALSE); } else { /* Since we're not going to try SCP, we may as well try @@ -670,7 +708,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; @@ -682,10 +720,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()); @@ -695,10 +732,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) @@ -721,16 +757,16 @@ 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. @@ -771,7 +807,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; @@ -781,10 +817,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; @@ -830,12 +865,13 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime) } } -int scp_send_filename(char *name, uint64 size, int modes) +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) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); @@ -843,11 +879,14 @@ int scp_send_filename(char *name, uint64 size, int modes) fullname = dupstr(scp_sftp_remotepath); } - sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE | - SSH_FXF_CREAT | SSH_FXF_TRUNC)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + attrs.flags = 0; + PUT_PERMISSIONS(attrs, permissions); + + 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", @@ -864,7 +903,9 @@ int scp_send_filename(char *name, uint64 size, int modes) char buf[40]; char sizestr[40]; uint64_decimal(size, sizestr); - sprintf(buf, "C%04o %s ", modes, sizestr); + if (permissions < 0) + permissions = 0644; + sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr); back->send(backhandle, buf, strlen(buf)); back->send(backhandle, name, strlen(name)); back->send(backhandle, "\n", 1); @@ -885,7 +926,7 @@ 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) { + if (ret <= 0) { tell_user(stderr, "error while writing: %s\n", fxp_error()); errs++; return 1; @@ -920,12 +961,17 @@ 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\n", fxp_error()); + errs++; + return 1; + } } xfer_cleanup(scp_sftp_xfer); @@ -936,19 +982,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()); 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 { @@ -978,7 +1022,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) { @@ -994,20 +1038,18 @@ 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)) { @@ -1144,7 +1186,7 @@ struct scp_sink_action { int action; /* FILE, DIR, ENDDIR */ char *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ - int mode; /* access mode (not ENDDIR) */ + long permissions; /* access permissions (not ENDDIR) */ uint64 size; /* file size (not ENDDIR) */ int settime; /* 1 if atime and mtime are filled */ unsigned long atime, mtime; /* access times for the file */ @@ -1157,7 +1199,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) { @@ -1226,10 +1268,9 @@ 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, @@ -1282,10 +1323,9 @@ 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", @@ -1299,16 +1339,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", 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++; @@ -1340,10 +1384,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; @@ -1370,7 +1413,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; act->size = uint64_make(0,0); /* duhh, it's a directory */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1392,7 +1435,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->size = attrs.size; } else act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1474,7 +1517,8 @@ int scp_get_sink_action(struct scp_sink_action *act) { char sizestr[40]; - if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2) + if (sscanf(act->buf, "%lo %s %n", &act->permissions, + sizestr, &i) != 2) bump("Protocol error: Illegal file descriptor format"); act->size = uint64_from_decimal(sizestr); act->name = act->buf + i; @@ -1487,12 +1531,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)); - 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", @@ -1521,8 +1564,7 @@ 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()); errs++; return -1; @@ -1553,7 +1595,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 @@ -1563,19 +1605,23 @@ 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()); + 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); @@ -1609,6 +1655,7 @@ static void source(char *src) { uint64 size; unsigned long mtime, atime; + long permissions; char *last; RFile *f; int attr; @@ -1656,7 +1703,7 @@ static void source(char *src) if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; - f = open_existing_file(src, &size, &mtime, &atime); + f = open_existing_file(src, &size, &mtime, &atime, &permissions); if (f == NULL) { run_err("%s: Cannot open file", src); return; @@ -1671,7 +1718,7 @@ 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, 0644)) + if (scp_send_filename(last, size, permissions)) return; stat_bytes = uint64_make(0,0); @@ -1888,7 +1935,7 @@ static void sink(char *targ, char *src) continue; } - f = open_new_file(destfname); + f = open_new_file(destfname, act.permissions); if (f == NULL) { run_err("%s: Cannot create file", destfname); continue; @@ -2258,9 +2305,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; @@ -2304,6 +2354,7 @@ int psftp_main(int argc, char *argv[]) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } random_save_seed();