X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/2285d016cf0103f03e05e53e22025282c104a164..HEAD:/pscp.c diff --git a/pscp.c b/pscp.c index 9259dd08..32099323 100644 --- a/pscp.c +++ b/pscp.c @@ -25,6 +25,7 @@ #include "ssh.h" #include "sftp.h" #include "storage.h" +#include "int64.h" static int list = 0; static int verbose = 0; @@ -35,21 +36,24 @@ static int statistics = 1; static int prev_stats_len = 0; static int scp_unsafe_mode = 0; static int errs = 0; -static int gui_mode = 0; static int try_scp = 1; 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; -static Config cfg; +static Conf *conf; +int sent_eof = FALSE; static void source(char *src); static void rsource(char *src); static void sink(char *targ, char *src); +const char *const appname = "PSCP"; + /* * The maximum amount of queued data we accept before we stop and * wait for the server to process some. @@ -69,10 +73,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) static void tell_char(FILE * stream, char c) { - if (!gui_mode) - fputc(c, stream); - else - gui_send_char(stream == stderr, c); + fputc(c, stream); } static void tell_str(FILE * stream, char *str) @@ -112,9 +113,6 @@ void fatalbox(char *fmt, ...) sfree(str2); errs++; - if (gui_mode) - gui_send_errcount(list, errs); - cleanup_exit(1); } void modalfatalbox(char *fmt, ...) @@ -130,11 +128,21 @@ void modalfatalbox(char *fmt, ...) sfree(str2); errs++; - if (gui_mode) - gui_send_errcount(list, errs); - 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; @@ -148,9 +156,6 @@ void connection_fatal(void *frontend, char *fmt, ...) sfree(str2); errs++; - if (gui_mode) - gui_send_errcount(list, errs); - cleanup_exit(1); } @@ -188,16 +193,11 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen) */ if (is_stderr) { if (len > 0) - fwrite(data, 1, len, stderr); + if (fwrite(data, 1, len, stderr) < len) + /* oh well */; return 0; } - /* - * If this is before the real session begins, just return. - */ - if (!outptr) - return 0; - if ((outlen > 0) && (len > 0)) { unsigned used = outlen; if (used > len) @@ -213,8 +213,6 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen) if (pendsize < pendlen + len) { pendsize = pendlen + len + 4096; pending = sresize(pending, pendsize, unsigned char); - if (!pending) - fatalbox("Out of memory"); } memcpy(pending + pendlen, p, len); pendlen += len; @@ -222,6 +220,29 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen) return 0; } +int from_backend_untrusted(void *frontend_handle, const char *data, int len) +{ + /* + * No "untrusted" output should get here (the way the code is + * currently, it's all diverted by FLAG_STDERR). + */ + assert(!"Unexpected call to from_backend_untrusted()"); + return 0; /* not reached */ +} +int from_backend_eof(void *frontend) +{ + /* + * 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. The exception is if we're using old-style scp and + * downloading rather than uploading. + */ + if ((using_sftp || uploading) && !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; @@ -250,7 +271,7 @@ static int ssh_scp_recv(unsigned char *buf, int len) } while (outlen > 0) { - if (ssh_sftp_loop_iteration() < 0) + if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0) return 0; /* doom */ } @@ -263,8 +284,14 @@ static int ssh_scp_recv(unsigned char *buf, int len) static void ssh_scp_init(void) { while (!back->sendok(backhandle)) { - if (ssh_sftp_loop_iteration() < 0) + if (back->exitcode(backhandle) >= 0) { + errs++; + return; + } + if (ssh_sftp_loop_iteration() < 0) { + errs++; return; /* doom */ + } } /* Work out which backend we ended up using. */ @@ -297,19 +324,40 @@ static void bump(char *fmt, ...) sfree(str2); errs++; - if (back != NULL && back->socket(backhandle) != NULL) { + if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } - if (gui_mode) - gui_send_errcount(list, errs); - cleanup_exit(1); } /* + * 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) @@ -338,88 +386,90 @@ static void do_cmd(char *host, char *user, char *cmd) */ if (!loaded_session) { /* Try to load settings for `host' into a temporary config */ - Config cfg2; - cfg2.host[0] = '\0'; - do_defaults(host, &cfg2); - if (cfg2.host[0] != '\0') { + Conf *conf2 = conf_new(); + conf_set_str(conf2, CONF_host, ""); + do_defaults(host, conf2); + if (conf_get_str(conf2, CONF_host)[0] != '\0') { /* Settings present and include hostname */ /* Re-load data into the real config. */ - do_defaults(host, &cfg); + do_defaults(host, conf); } else { /* Session doesn't exist or mention a hostname. */ /* Use `host' as a bare hostname. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } } else { /* Patch in hostname `host' to session details. */ - strncpy(cfg.host, host, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; + conf_set_str(conf, CONF_host, host); } /* * Force use of SSH. (If they got the protocol wrong we assume the * port is useless too.) */ - if (cfg.protocol != PROT_SSH) { - cfg.protocol = PROT_SSH; - cfg.port = 22; + if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { + conf_set_int(conf, CONF_protocol, PROT_SSH); + conf_set_int(conf, CONF_port, 22); } /* * Enact command-line overrides. */ - cmdline_run_saved(&cfg); + cmdline_run_saved(conf); /* - * Trim leading whitespace off the hostname if it's there. + * Muck about with the hostname in various ways. */ { - int space = strspn(cfg.host, " \t"); - memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); - } + char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); + char *host = hostbuf; + char *p, *q; + + /* + * Trim leading whitespace. + */ + host += strspn(host, " \t"); - /* See if host is of the form user@host */ - if (cfg.host[0] != '\0') { - char *atsign = strrchr(cfg.host, '@'); - /* Make sure we're not overflowing the user field */ - if (atsign) { - if (atsign - cfg.host < sizeof cfg.username) { - strncpy(cfg.username, cfg.host, atsign - cfg.host); - cfg.username[atsign - cfg.host] = '\0'; + /* + * See if host is of the form user@host, and separate out + * the username if so. + */ + if (host[0] != '\0') { + char *atsign = strrchr(host, '@'); + if (atsign) { + *atsign = '\0'; + conf_set_str(conf, CONF_username, host); + host = atsign + 1; } - memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); } - } - /* - * Remove any remaining whitespace from the hostname. - */ - { - int p1 = 0, p2 = 0; - while (cfg.host[p2] != '\0') { - if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { - cfg.host[p1] = cfg.host[p2]; - p1++; - } - p2++; + /* + * Remove any remaining whitespace. + */ + p = hostbuf; + q = host; + while (*q) { + if (*q != ' ' && *q != '\t') + *p++ = *q; + q++; } - cfg.host[p1] = '\0'; + *p = '\0'; + + conf_set_str(conf, CONF_host, hostbuf); + sfree(hostbuf); } /* Set username */ if (user != NULL && user[0] != '\0') { - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; - } else if (cfg.username[0] == '\0') { + conf_set_str(conf, CONF_username, user); + } else if (conf_get_str(conf, CONF_username)[0] == '\0') { user = get_username(); if (!user) bump("Empty user name"); else { if (verbose) tell_user(stderr, "Guessing user name: %s", user); - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; + conf_set_str(conf, CONF_username, user); sfree(user); } } @@ -429,9 +479,14 @@ static void do_cmd(char *host, char *user, char *cmd) * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ - cfg.x11_forward = 0; - cfg.agentfwd = 0; - cfg.portfwd[0] = cfg.portfwd[1] = '\0'; + conf_set_int(conf, CONF_x11_forward, 0); + conf_set_int(conf, CONF_agentfwd, 0); + conf_set_int(conf, CONF_ssh_simple, TRUE); + { + char *key; + while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL) + conf_del_str_str(conf, CONF_portfwd, key); + } /* * Set up main and possibly fallback command depending on @@ -439,56 +494,61 @@ static void do_cmd(char *host, char *user, char *cmd) * Attempt to start the SFTP subsystem as a first choice, * falling back to the provided scp command if that fails. */ - cfg.remote_cmd_ptr2 = NULL; + conf_set_str(conf, CONF_remote_cmd2, ""); if (try_sftp) { /* First choice is SFTP subsystem. */ main_cmd_is_sftp = 1; - strcpy(cfg.remote_cmd, "sftp"); - cfg.ssh_subsys = TRUE; + conf_set_str(conf, CONF_remote_cmd, "sftp"); + conf_set_int(conf, CONF_ssh_subsys, TRUE); if (try_scp) { /* Fallback is to use the provided scp command. */ fallback_cmd_is_sftp = 0; - cfg.remote_cmd_ptr2 = cmd; - cfg.ssh_subsys2 = FALSE; + 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 * harder to find an SFTP server, since in the current * implementation we have a spare slot. */ fallback_cmd_is_sftp = 1; /* see psftp.c for full explanation of this kludge */ - cfg.remote_cmd_ptr2 = - "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" - "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" - "exec sftp-server"; - cfg.ssh_subsys2 = FALSE; + conf_set_str(conf, CONF_remote_cmd2, + "test -x /usr/lib/sftp-server &&" + " exec /usr/lib/sftp-server\n" + "test -x /usr/local/lib/sftp-server &&" + " exec /usr/local/lib/sftp-server\n" + "exec sftp-server"); + conf_set_int(conf, CONF_ssh_subsys2, FALSE); } } else { /* Don't try SFTP at all; just try the scp command. */ main_cmd_is_sftp = 0; - cfg.remote_cmd_ptr = cmd; - cfg.ssh_subsys = FALSE; + conf_set_str(conf, CONF_remote_cmd, cmd); + conf_set_int(conf, CONF_ssh_subsys, FALSE); } - cfg.nopty = TRUE; + conf_set_int(conf, CONF_nopty, TRUE); back = &ssh_backend; - err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, - 0, cfg.tcp_keepalives); + err = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, 0, + conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) bump("ssh_init: %s", err); - logctx = log_init(NULL, &cfg); + logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); console_provide_logctx(logctx); ssh_scp_init(); - if (verbose && realhost != NULL) - tell_user(stderr, "Connected to %s\n", realhost); + if (verbose && realhost != NULL && errs == 0) + tell_user(stderr, "Connected to %s", realhost); sfree(realhost); } /* * Update statistic information about current file. */ -static void print_stats(char *name, unsigned long size, unsigned long done, +static void print_stats(char *name, uint64 size, uint64 done, time_t start, time_t now) { float ratebs; @@ -497,34 +557,42 @@ static void print_stats(char *name, unsigned long size, unsigned long done, int pct; int len; int elap; + double donedbl; + double sizedbl; elap = (unsigned long) difftime(now, start); if (now > start) - ratebs = (float) done / elap; + ratebs = (float) (uint64_to_double(done) / elap); else - ratebs = (float) done; + ratebs = (float) uint64_to_double(done); if (ratebs < 1.0) - eta = size - done; - else - eta = (unsigned long) ((size - done) / ratebs); + eta = (unsigned long) (uint64_to_double(uint64_subtract(size, done))); + else { + eta = (unsigned long) + ((uint64_to_double(uint64_subtract(size, done)) / ratebs)); + } + etastr = dupprintf("%02ld:%02ld:%02ld", eta / 3600, (eta % 3600) / 60, eta % 60); - pct = (int) (100 * (done * 1.0 / size)); + donedbl = uint64_to_double(done); + sizedbl = uint64_to_double(size); + pct = (int) (100 * (donedbl * 1.0 / sizedbl)); - if (gui_mode) { - gui_update_stats(name, size, pct, elap, done, eta, - (unsigned long) ratebs); - } else { - len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%", - name, done / 1024, ratebs / 1024.0, etastr, pct); + { + char donekb[40]; + /* divide by 1024 to provide kB */ + uint64_decimal(uint64_shift_right(done, 10), donekb); + len = printf("\r%-25.25s | %s kB | %5.1f kB/s | ETA: %8s | %3d%%", + name, + donekb, ratebs / 1024.0, etastr, pct); if (len < prev_stats_len) printf("%*s", prev_stats_len - len, ""); prev_stats_len = len; - if (done == size) + if (uint64_compare(done, size) == 0) printf("\n"); fflush(stdout); @@ -539,26 +607,23 @@ static void print_stats(char *name, unsigned long size, unsigned long done, */ static char *colon(char *str) { - /* Check and process IPv6 literal addresses - * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */ - char *ipv6 = strchr(str, '['); - if (ipv6) { - str = strchr(str, ']'); - if (str) { - /* Terminate on the closing bracket */ - *str++ = '\0'; - return (str); - } - return (NULL); - } - /* We ignore a leading colon, since the hostname cannot be empty. We also ignore a colon as second character because of filenames like f:myfile.txt. */ - if (str[0] == '\0' || str[0] == ':' || str[1] == ':') + if (str[0] == '\0' || str[0] == ':' || + (str[0] != '[' && str[1] == ':')) return (NULL); - while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\') + while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\') { + if (*str == '[') { + /* Skip over IPv6 literal addresses + * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */ + char *ipv6_end = strchr(str, ']'); + if (ipv6_end) { + str = ipv6_end; + } + } str++; + } if (*str == ':') return (str); else @@ -625,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++; @@ -658,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; @@ -670,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()); @@ -683,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) @@ -709,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); } } @@ -751,7 +816,7 @@ static struct fxp_handle *scp_sftp_filehandle; static struct fxp_xfer *scp_sftp_xfer; static uint64 scp_sftp_fileoffset; -void scp_source_setup(char *target, int shouldbedir) +int scp_source_setup(char *target, int shouldbedir) { if (using_sftp) { /* @@ -759,20 +824,19 @@ void 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; if (!fxp_init()) { tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); errs++; - return; + 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; @@ -789,6 +853,7 @@ void scp_source_setup(char *target, int shouldbedir) } else { (void) response(); } + return 0; } int scp_send_errmsg(char *str) @@ -817,12 +882,13 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime) } } -int scp_send_filename(char *name, unsigned long 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); @@ -830,15 +896,19 @@ int scp_send_filename(char *name, unsigned long 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", fullname, fxp_error()); + sfree(fullname); errs++; return 1; } @@ -849,7 +919,11 @@ int scp_send_filename(char *name, unsigned long size, int modes) return 0; } else { char buf[40]; - sprintf(buf, "C%04o %lu ", modes, size); + char sizestr[40]; + uint64_decimal(size, 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); @@ -870,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; } @@ -905,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); @@ -921,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 { @@ -963,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) { @@ -979,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; } @@ -1053,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); @@ -1129,8 +1212,8 @@ 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) */ - unsigned long size; /* file size (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 */ }; @@ -1142,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) { @@ -1211,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; } @@ -1267,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++; @@ -1284,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++; @@ -1317,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]; @@ -1325,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; @@ -1354,8 +1439,8 @@ int scp_get_sink_action(struct scp_sink_action *act) act->action = SCP_SINK_DIR; act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; - act->size = 0; /* duhh, it's a directory */ - act->mode = 07777 & attrs.permissions; + act->size = uint64_make(0,0); /* duhh, it's a directory */ + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1374,14 +1459,10 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) { - if (uint64_compare(attrs.size, - uint64_make(0, ULONG_MAX)) > 0) { - act->size = ULONG_MAX; /* *boggle* */ - } else - act->size = attrs.size.lo; + act->size = attrs.size; } else - act->size = ULONG_MAX; /* no idea */ - act->mode = 07777 & attrs.permissions; + act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */ + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1425,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 */ @@ -1460,10 +1541,16 @@ int scp_get_sink_action(struct scp_sink_action *act) * If we get here, we must have seen SCP_SINK_FILE or * SCP_SINK_DIR. */ - if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2) - bump("Protocol error: Illegal file descriptor format"); - act->name = act->buf + i; - return 0; + { + char sizestr[40]; + + 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; + return 0; + } } } @@ -1471,12 +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)); - 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", @@ -1505,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; } @@ -1537,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 @@ -1547,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); @@ -1578,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); @@ -1591,13 +1684,14 @@ static void run_err(const char *fmt, ...) */ static void source(char *src) { - unsigned long size; + uint64 size; unsigned long mtime, atime; + long permissions; char *last; RFile *f; int attr; - unsigned long i; - unsigned long stat_bytes; + uint64 i; + uint64 stat_bytes; time_t stat_starttime, stat_lasttime; attr = file_type(src); @@ -1640,31 +1734,40 @@ 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; } if (preserve) { - if (scp_send_filetimes(mtime, atime)) + if (scp_send_filetimes(mtime, atime)) { + close_rfile(f); return; + } } - if (verbose) - tell_user(stderr, "Sending file %s, size=%lu", last, size); - if (scp_send_filename(last, size, 0644)) + if (verbose) { + char sizestr[40]; + uint64_decimal(size, sizestr); + tell_user(stderr, "Sending file %s, size=%s", last, sizestr); + } + if (scp_send_filename(last, size, permissions)) { + close_rfile(f); return; + } - stat_bytes = 0; + stat_bytes = uint64_make(0,0); stat_starttime = time(NULL); stat_lasttime = 0; - for (i = 0; i < size; i += 4096) { + for (i = uint64_make(0,0); + uint64_compare(i,size) < 0; + i = uint64_add32(i,4096)) { char transbuf[4096]; int j, k = 4096; - if (i + k > size) - k = size - i; + if (uint64_compare(uint64_add32(i, k),size) > 0) /* i + k > size */ + k = (uint64_subtract(size, i)).lo; /* k = size - i; */ if ((j = read_from_file(f, transbuf, k)) != k) { if (statistics) printf("\n"); @@ -1674,8 +1777,9 @@ static void source(char *src) bump("%s: Network error occurred", src); if (statistics) { - stat_bytes += k; - if (time(NULL) != stat_lasttime || i + k == size) { + stat_bytes = uint64_add32(stat_bytes, k); + if (time(NULL) != stat_lasttime || + (uint64_compare(uint64_add32(i, k), size) == 0)) { stat_lasttime = time(NULL); print_stats(last, size, stat_bytes, stat_starttime, stat_lasttime); @@ -1742,9 +1846,9 @@ static void sink(char *targ, char *src) int exists; int attr; WFile *f; - unsigned long received; + uint64 received; int wrerror = 0; - unsigned long stat_bytes; + uint64 stat_bytes; time_t stat_starttime, stat_lasttime; char *stat_name; @@ -1853,42 +1957,49 @@ 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); + 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 = 0; + stat_bytes = uint64_make(0, 0); stat_starttime = time(NULL); stat_lasttime = 0; stat_name = stripslashes(destfname, 1); - received = 0; - while (received < act.size) { - char transbuf[4096]; - unsigned long blksize; + received = uint64_make(0, 0); + while (uint64_compare(received,act.size) < 0) { + char transbuf[32768]; + uint64 blksize; int read; - blksize = 4096; - if (blksize > (act.size - received)) - blksize = act.size - received; - read = scp_recv_filedata(transbuf, (int)blksize); + blksize = uint64_make(0, 32768); + if (uint64_compare(blksize,uint64_subtract(act.size,received)) > 0) + blksize = uint64_subtract(act.size,received); + read = scp_recv_filedata(transbuf, (int)blksize.lo); if (read <= 0) bump("Lost connection"); if (wrerror) @@ -1903,15 +2014,15 @@ static void sink(char *targ, char *src) continue; } if (statistics) { - stat_bytes += read; + stat_bytes = uint64_add32(stat_bytes,read); if (time(NULL) > stat_lasttime || - received + read == act.size) { + uint64_compare(uint64_add32(received, read), act.size) == 0) { stat_lasttime = time(NULL); print_stats(stat_name, act.size, stat_bytes, stat_starttime, stat_lasttime); } } - received += read; + received = uint64_add32(received, read); } if (act.settime) { set_file_times(f, act.mtime, act.atime); @@ -1920,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(); @@ -1937,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 */ @@ -1980,7 +2094,8 @@ static void toremote(int argc, char *argv[]) do_cmd(host, user, cmd); sfree(cmd); - scp_source_setup(targ, targetshouldbedirectory); + if (scp_source_setup(targ, targetshouldbedirectory)) + return; for (i = 0; i < argc - 1; i++) { src = argv[i]; @@ -2025,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"); @@ -2082,7 +2199,7 @@ static void get_dir_list(int argc, char *argv[]) host = src; src = colon(src); if (src == NULL) - bump("Local to local copy not supported"); + bump("Local file listing not supported"); *src++ = '\0'; if (*src == '\0') src = "."; @@ -2153,6 +2270,8 @@ static void usage(void) printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for authentication\n"); + printf(" -noagent disable use of Pageant\n"); + printf(" -agent enable use of Pageant\n"); printf(" -batch disable all interactive prompts\n"); printf(" -unsafe allow server-side wildcards (DANGEROUS)\n"); printf(" -sftp force use of SFTP protocol\n"); @@ -2204,18 +2323,18 @@ int psftp_main(int argc, char *argv[]) #endif ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; - ssh_get_line = &console_get_line; sk_init(); /* Load Default Settings before doing anything else. */ - do_defaults(NULL, &cfg); + conf = conf_new(); + do_defaults(NULL, conf); loaded_session = FALSE; for (i = 1; i < argc; i++) { int ret; if (argv[i][0] != '-') break; - ret = cmdline_process_param(argv[i], i+1socket(backhandle) != NULL) { + 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(); - if (gui_mode) - gui_send_errcount(list, errs); - cmdline_cleanup(); console_provide_logctx(NULL); back->free(backhandle);