X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/25a6efd88122b006270eeac932d24bd489158aba..3c03ad3fd98fbdb39daf56ff2a6afbcaf94e25e7:/scp.c diff --git a/scp.c b/scp.c index 616dd378..f69fd05f 100644 --- a/scp.c +++ b/scp.c @@ -52,11 +52,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -75,7 +71,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: scp.c,v 1.98 2003/01/10 10:29:35 djm Exp $"); +RCSID("$OpenBSD: scp.c,v 1.119 2005/01/24 10:22:06 dtucker Exp $"); #include "xmalloc.h" #include "atomicio.h" @@ -84,15 +80,16 @@ RCSID("$OpenBSD: scp.c,v 1.98 2003/01/10 10:29:35 djm Exp $"); #include "misc.h" #include "progressmeter.h" -#ifdef HAVE___PROGNAME extern char *__progname; -#else -char *__progname; -#endif + +void bwlimit(int); /* Struct for addargs */ arglist args; +/* Bandwidth limit */ +off_t limit_rate = 0; + /* Name of current file being transferred. */ char *curfile; @@ -106,7 +103,18 @@ int showprogress = 1; char *ssh_program = _PATH_SSH_PROGRAM; /* This is used to store the pid of ssh_program */ -pid_t do_cmd_pid; +pid_t do_cmd_pid = -1; + +static void +killchild(int signo) +{ + if (do_cmd_pid > 1) { + kill(do_cmd_pid, signo); + waitpid(do_cmd_pid, NULL, 0); + } + + _exit(1); +} /* * This function executes the given command as the specified user on the @@ -141,7 +149,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) close(reserved[0]); close(reserved[1]); - /* For a child to execute the command on the remote host using ssh. */ + /* Fork a child to execute the command on the remote host using ssh. */ do_cmd_pid = fork(); if (do_cmd_pid == 0) { /* Child. */ @@ -169,6 +177,9 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) *fdout = pin[1]; close(pout[1]); *fdin = pout[0]; + signal(SIGTERM, killchild); + signal(SIGINT, killchild); + signal(SIGHUP, killchild); return 0; } @@ -201,16 +212,15 @@ void toremote(char *, int, char *[]); void usage(void); int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char **argv) { int ch, fflag, tflag, status; - char *targ; + double speed; + char *targ, *endp; extern char *optarg; extern int optind; - __progname = get_progname(argv[0]); + __progname = ssh_get_progname(argv[0]); args.list = NULL; addargs(&args, "ssh"); /* overwritten with ssh_program */ @@ -219,9 +229,11 @@ main(argc, argv) addargs(&args, "-oClearAllForwardings yes"); fflag = tflag = 0; - while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1) + while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) switch (ch) { /* User-visible flags. */ + case '1': + case '2': case '4': case '6': case 'C': @@ -239,6 +251,12 @@ main(argc, argv) case 'B': addargs(&args, "-oBatchmode yes"); break; + case 'l': + speed = strtod(optarg, &endp); + if (speed <= 0 || *endp != '\0') + usage(); + limit_rate = speed * 1024; + break; case 'p': pflag = 1; break; @@ -253,6 +271,7 @@ main(argc, argv) verbose_mode = 1; break; case 'q': + addargs(&args, "-q"); showprogress = 0; break; @@ -278,7 +297,7 @@ main(argc, argv) argv += optind; if ((pwd = getpwuid(userid = getuid())) == NULL) - fatal("unknown user %d", (int) userid); + fatal("unknown user %u", (u_int) userid); if (!isatty(STDERR_FILENO)) showprogress = 0; @@ -339,9 +358,7 @@ main(argc, argv) } void -toremote(targ, argc, argv) - char *targ, *argv[]; - int argc; +toremote(char *targ, int argc, char **argv) { int i, len; char *bp, *host, *src, *suser, *thost, *tuser; @@ -356,8 +373,6 @@ toremote(targ, argc, argv) tuser = argv[argc - 1]; if (*tuser == '\0') tuser = NULL; - else if (!okname(tuser)) - exit(1); } else { thost = argv[argc - 1]; tuser = NULL; @@ -383,8 +398,14 @@ toremote(targ, argc, argv) suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; - else if (!okname(suser)) + else if (!okname(suser)) { + xfree(bp); + continue; + } + if (tuser && !okname(tuser)) { + xfree(bp); continue; + } snprintf(bp, len, "%s%s %s -n " "-l %s %s %s %s '%s%s%s:%s'", @@ -404,7 +425,8 @@ toremote(targ, argc, argv) } if (verbose_mode) fprintf(stderr, "Executing: %s\n", bp); - (void) system(bp); + if (system(bp) != 0) + errs = 1; (void) xfree(bp); } else { /* local to remote */ if (remin == -1) { @@ -425,9 +447,7 @@ toremote(targ, argc, argv) } void -tolocal(argc, argv) - int argc; - char *argv[]; +tolocal(int argc, char **argv) { int i, len; char *bp, *host, *src, *suser; @@ -458,8 +478,6 @@ tolocal(argc, argv) suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; - else if (!okname(suser)) - continue; } host = cleanhostname(host); len = strlen(src) + CMDNEEDS + 20; @@ -478,9 +496,7 @@ tolocal(argc, argv) } void -source(argc, argv) - int argc; - char *argv[]; +source(int argc, char **argv) { struct stat stb; static BUF buffer; @@ -533,25 +549,18 @@ syserr: run_err("%s: %s", name, strerror(errno)); (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", (u_long) stb.st_mtime, (u_long) stb.st_atime); - (void) atomicio(write, remout, buf, strlen(buf)); + (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; } #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) -#ifdef HAVE_LONG_LONG_INT snprintf(buf, sizeof buf, "C%04o %lld %s\n", (u_int) (stb.st_mode & FILEMODEMASK), - (long long)stb.st_size, last); -#else - /* XXX: Handle integer overflow? */ - snprintf(buf, sizeof buf, "C%04o %lu %s\n", - (u_int) (stb.st_mode & FILEMODEMASK), - (u_long) stb.st_size, last); -#endif + (int64_t)stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); } - (void) atomicio(write, remout, buf, strlen(buf)); + (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { @@ -571,13 +580,15 @@ next: (void) close(fd); haderr = result >= 0 ? EIO : errno; } if (haderr) - (void) atomicio(write, remout, bp->buf, amt); + (void) atomicio(vwrite, remout, bp->buf, amt); else { - result = atomicio(write, remout, bp->buf, amt); + result = atomicio(vwrite, remout, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; statbytes += result; } + if (limit_rate) + bwlimit(amt); } if (showprogress) stop_progress_meter(); @@ -585,7 +596,7 @@ next: (void) close(fd); if (close(fd) < 0 && !haderr) haderr = errno; if (!haderr) - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); @@ -593,9 +604,7 @@ next: (void) close(fd); } void -rsource(name, statp) - char *name; - struct stat *statp; +rsource(char *name, struct stat *statp) { DIR *dirp; struct dirent *dp; @@ -614,7 +623,7 @@ rsource(name, statp) (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", (u_long) statp->st_mtime, (u_long) statp->st_atime); - (void) atomicio(write, remout, path, strlen(path)); + (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -624,7 +633,7 @@ rsource(name, statp) (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); - (void) atomicio(write, remout, path, strlen(path)); + (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -643,14 +652,66 @@ rsource(name, statp) source(1, vect); } (void) closedir(dirp); - (void) atomicio(write, remout, "E\n", 2); + (void) atomicio(vwrite, remout, "E\n", 2); (void) response(); } void -sink(argc, argv) - int argc; - char *argv[]; +bwlimit(int amount) +{ + static struct timeval bwstart, bwend; + static int lamt, thresh = 16384; + u_int64_t waitlen; + struct timespec ts, rm; + + if (!timerisset(&bwstart)) { + gettimeofday(&bwstart, NULL); + return; + } + + lamt += amount; + if (lamt < thresh) + return; + + gettimeofday(&bwend, NULL); + timersub(&bwend, &bwstart, &bwend); + if (!timerisset(&bwend)) + return; + + lamt *= 8; + waitlen = (double)1000000L * lamt / limit_rate; + + bwstart.tv_sec = waitlen / 1000000L; + bwstart.tv_usec = waitlen % 1000000L; + + if (timercmp(&bwstart, &bwend, >)) { + timersub(&bwstart, &bwend, &bwend); + + /* Adjust the wait time */ + if (bwend.tv_sec) { + thresh /= 2; + if (thresh < 2048) + thresh = 2048; + } else if (bwend.tv_usec < 100) { + thresh *= 2; + if (thresh > 32768) + thresh = 32768; + } + + TIMEVAL_TO_TIMESPEC(&bwend, &ts); + while (nanosleep(&ts, &rm) == -1) { + if (errno != EINTR) + break; + ts = rm; + } + } + + lamt = 0; + gettimeofday(&bwstart, NULL); +} + +void +sink(int argc, char **argv) { static BUF buffer; struct stat stb; @@ -667,7 +728,7 @@ sink(argc, argv) #define atime tv[0] #define mtime tv[1] -#define SCREWUP(str) do { why = str; goto screwup; } while (0) +#define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; mask = umask(0); @@ -681,7 +742,7 @@ sink(argc, argv) if (targetshouldbedirectory) verifydir(targ); - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { @@ -696,10 +757,12 @@ sink(argc, argv) *cp++ = ch; } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; + if (verbose_mode) + fprintf(stderr, "Sink: %s", buf); if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) - (void) atomicio(write, STDERR_FILENO, + (void) atomicio(vwrite, STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); @@ -707,7 +770,7 @@ sink(argc, argv) continue; } if (buf[0] == 'E') { - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); return; } if (ch == '\n') @@ -729,7 +792,7 @@ sink(argc, argv) atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0') SCREWUP("atime.usec not delimited"); - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { @@ -759,6 +822,10 @@ sink(argc, argv) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); + if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { + run_err("error: unexpected filename: %s", cp); + exit(1); + } if (targisdir) { static char *namebuf; static int cursize; @@ -780,6 +847,8 @@ sink(argc, argv) exists = stat(np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = pflag; + if (!iamrecursive) + SCREWUP("received directory without -r"); if (exists) { if (!S_ISDIR(stb.st_mode)) { errno = ENOTDIR; @@ -814,7 +883,7 @@ sink(argc, argv) bad: run_err("%s: %s", np, strerror(errno)); continue; } - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { (void) close(ofd); continue; @@ -831,11 +900,8 @@ bad: run_err("%s: %s", np, strerror(errno)); amt = size - i; count += amt; do { - j = read(remin, cp, amt); - if (j == -1 && (errno == EINTR || - errno == EAGAIN)) { - continue; - } else if (j <= 0) { + j = atomicio(read, remin, cp, amt); + if (j <= 0) { run_err("%s", j ? strerror(errno) : "dropped connection"); exit(1); @@ -844,10 +910,14 @@ bad: run_err("%s: %s", np, strerror(errno)); cp += j; statbytes += j; } while (amt > 0); + + if (limit_rate) + bwlimit(4096); + if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { - j = atomicio(write, ofd, bp->buf, count); + j = atomicio(vwrite, ofd, bp->buf, count); if (j != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; @@ -860,7 +930,7 @@ bad: run_err("%s: %s", np, strerror(errno)); if (showprogress) stop_progress_meter(); if (count != 0 && wrerr == NO && - (j = atomicio(write, ofd, bp->buf, count)) != count) { + (j = atomicio(vwrite, ofd, bp->buf, count)) != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } @@ -871,21 +941,25 @@ bad: run_err("%s: %s", np, strerror(errno)); if (pflag) { if (exists || omode != mode) #ifdef HAVE_FCHMOD - if (fchmod(ofd, omode)) + if (fchmod(ofd, omode)) { #else /* HAVE_FCHMOD */ - if (chmod(np, omode)) + if (chmod(np, omode)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); + wrerr = DISPLAYED; + } } else { if (!exists && omode != mode) #ifdef HAVE_FCHMOD - if (fchmod(ofd, omode & ~mask)) + if (fchmod(ofd, omode & ~mask)) { #else /* HAVE_FCHMOD */ - if (chmod(np, omode & ~mask)) + if (chmod(np, omode & ~mask)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); + wrerr = DISPLAYED; + } } if (close(ofd) == -1) { wrerr = YES; @@ -905,7 +979,7 @@ bad: run_err("%s: %s", np, strerror(errno)); run_err("%s: %s", np, strerror(wrerrno)); break; case NO: - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); break; case DISPLAYED: break; @@ -940,7 +1014,7 @@ response(void) } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) - (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf); + (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); @@ -953,8 +1027,8 @@ void usage(void) { (void) fprintf(stderr, - "usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n" - " [-c cipher] [-i identity] [-o option]\n" + "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" + " [-l limit] [-o ssh_option] [-P port] [-S program]\n" " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); exit(1); } @@ -985,8 +1059,7 @@ run_err(const char *fmt,...) } void -verifydir(cp) - char *cp; +verifydir(char *cp) { struct stat stb; @@ -1000,8 +1073,7 @@ verifydir(cp) } int -okname(cp0) - char *cp0; +okname(char *cp0) { int c; char *cp; @@ -1011,9 +1083,18 @@ okname(cp0) c = (int)*cp; if (c & 0200) goto bad; - if (!isalpha(c) && !isdigit(c) && - c != '@' && c != '_' && c != '-' && c != '.' && c != '+') - goto bad; + if (!isalpha(c) && !isdigit(c)) { + switch (c) { + case '\'': + case '"': + case '`': + case ' ': + case '#': + goto bad; + default: + break; + } + } } while (*++cp); return (1); @@ -1022,9 +1103,7 @@ bad: fprintf(stderr, "%s: invalid user name\n", cp0); } BUF * -allocbuf(bp, fd, blksize) - BUF *bp; - int fd, blksize; +allocbuf(BUF *bp, int fd, int blksize) { size_t size; #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE @@ -1052,8 +1131,7 @@ allocbuf(bp, fd, blksize) } void -lostconn(signo) - int signo; +lostconn(int signo) { if (!iamremote) write(STDERR_FILENO, "lost connection\n", 16);