Changes to update Tomato RAF.
[tomato.git] / release / src / router / dropbear / scp.c
blob961165afab3ca066a973faa52eebc264636a4ef1
1 /*
2 * scp - secure remote copy. This is basically patched BSD rcp which
3 * uses ssh to do the data transfer (instead of using rcmd).
5 * NOTE: This version should NOT be suid root. (This uses ssh to
6 * do the transfer and ssh has the necessary privileges.)
8 * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
10 * As far as I am concerned, the code I have written for this software
11 * can be used freely for any purpose. Any derived versions of this
12 * software must be clearly marked as such, and if the derived work is
13 * incompatible with the protocol description in the RFC file, it must be
14 * called by a name other than "ssh" or "Secure Shell".
17 * Copyright (c) 1999 Theo de Raadt. All rights reserved.
18 * Copyright (c) 1999 Aaron Campbell. All rights reserved.
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
22 * are met:
23 * 1. Redistributions of source code must retain the above copyright
24 * notice, this list of conditions and the following disclaimer.
25 * 2. Redistributions in binary form must reproduce the above copyright
26 * notice, this list of conditions and the following disclaimer in the
27 * documentation and/or other materials provided with the distribution.
29 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
30 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
31 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
32 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
33 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
34 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
38 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 * Parts from:
44 * Copyright (c) 1983, 1990, 1992, 1993, 1995
45 * The Regents of the University of California. All rights reserved.
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
49 * are met:
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 * 3. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
73 #include "includes.h"
74 /*RCSID("$OpenBSD: scp.c,v 1.130 2006/01/31 10:35:43 djm Exp $");*/
76 #include "atomicio.h"
77 #include "compat.h"
78 #include "scpmisc.h"
79 #include "progressmeter.h"
81 void bwlimit(int);
83 /* Struct for addargs */
84 arglist args;
86 /* Bandwidth limit */
87 off_t limit_rate = 0;
89 /* Name of current file being transferred. */
90 char *curfile;
92 /* This is set to non-zero to enable verbose mode. */
93 int verbose_mode = 0;
95 /* This is set to zero if the progressmeter is not desired. */
96 int showprogress = 1;
98 /* This is the program to execute for the secured connection. ("ssh" or -S) */
99 char *ssh_program = _PATH_SSH_PROGRAM;
101 /* This is used to store the pid of ssh_program */
102 pid_t do_cmd_pid = -1;
104 static void
105 killchild(int signo)
107 if (do_cmd_pid > 1) {
108 kill(do_cmd_pid, signo ? signo : SIGTERM);
109 waitpid(do_cmd_pid, NULL, 0);
112 if (signo)
113 _exit(1);
114 exit(1);
117 static int
118 do_local_cmd(arglist *a)
120 u_int i;
121 int status;
122 pid_t pid;
124 if (a->num == 0)
125 fatal("do_local_cmd: no arguments");
127 if (verbose_mode) {
128 fprintf(stderr, "Executing:");
129 for (i = 0; i < a->num; i++)
130 fprintf(stderr, " %s", a->list[i]);
131 fprintf(stderr, "\n");
133 #ifdef __uClinux__
134 pid = vfork();
135 #else
136 pid = fork();
137 #endif /* __uClinux__ */
138 if (pid == -1)
139 fatal("do_local_cmd: fork: %s", strerror(errno));
141 if (pid == 0) {
142 execvp(a->list[0], a->list);
143 perror(a->list[0]);
144 #ifdef __uClinux__
145 _exit(1);
146 #else
147 exit(1);
148 #endif /* __uClinux__ */
151 do_cmd_pid = pid;
152 signal(SIGTERM, killchild);
153 signal(SIGINT, killchild);
154 signal(SIGHUP, killchild);
156 while (waitpid(pid, &status, 0) == -1)
157 if (errno != EINTR)
158 fatal("do_local_cmd: waitpid: %s", strerror(errno));
160 do_cmd_pid = -1;
162 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
163 return (-1);
165 return (0);
169 * This function executes the given command as the specified user on the
170 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This
171 * assigns the input and output file descriptors on success.
175 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
177 int pin[2], pout[2], reserved[2];
179 if (verbose_mode)
180 fprintf(stderr,
181 "Executing: program %s host %s, user %s, command %s\n",
182 ssh_program, host,
183 remuser ? remuser : "(unspecified)", cmd);
186 * Reserve two descriptors so that the real pipes won't get
187 * descriptors 0 and 1 because that will screw up dup2 below.
189 pipe(reserved);
191 /* Create a socket pair for communicating with ssh. */
192 if (pipe(pin) < 0)
193 fatal("pipe: %s", strerror(errno));
194 if (pipe(pout) < 0)
195 fatal("pipe: %s", strerror(errno));
197 /* Free the reserved descriptors. */
198 close(reserved[0]);
199 close(reserved[1]);
201 /* uClinux needs to build the args here before vforking,
202 otherwise we do it later on. */
203 #ifdef __uClinux__
204 replacearg(&args, 0, "%s", ssh_program);
205 if (remuser != NULL)
206 addargs(&args, "-l%s", remuser);
207 addargs(&args, "%s", host);
208 addargs(&args, "%s", cmd);
209 #endif /* __uClinux__ */
211 /* Fork a child to execute the command on the remote host using ssh. */
212 #ifdef __uClinux__
213 do_cmd_pid = vfork();
214 #else
215 do_cmd_pid = fork();
216 #endif /* __uClinux__ */
218 if (do_cmd_pid == 0) {
219 /* Child. */
220 close(pin[1]);
221 close(pout[0]);
222 dup2(pin[0], 0);
223 dup2(pout[1], 1);
224 close(pin[0]);
225 close(pout[1]);
227 #ifndef __uClinux__
228 replacearg(&args, 0, "%s", ssh_program);
229 if (remuser != NULL)
230 addargs(&args, "-l%s", remuser);
231 addargs(&args, "%s", host);
232 addargs(&args, "%s", cmd);
233 #endif /* __uClinux__ */
235 execvp(ssh_program, args.list);
236 perror(ssh_program);
237 #ifndef __uClinux__
238 exit(1);
239 #else
240 _exit(1);
241 #endif /* __uClinux__ */
242 } else if (do_cmd_pid == -1) {
243 fatal("fork: %s", strerror(errno));
247 #ifdef __uClinux__
248 /* clean up command */
249 /* pop cmd */
250 xfree(args.list[args.num-1]);
251 args.list[args.num-1]=NULL;
252 args.num--;
253 /* pop host */
254 xfree(args.list[args.num-1]);
255 args.list[args.num-1]=NULL;
256 args.num--;
257 /* pop user */
258 if (remuser != NULL) {
259 xfree(args.list[args.num-1]);
260 args.list[args.num-1]=NULL;
261 args.num--;
263 #endif /* __uClinux__ */
265 /* Parent. Close the other side, and return the local side. */
266 close(pin[0]);
267 *fdout = pin[1];
268 close(pout[1]);
269 *fdin = pout[0];
270 signal(SIGTERM, killchild);
271 signal(SIGINT, killchild);
272 signal(SIGHUP, killchild);
273 return 0;
276 typedef struct {
277 size_t cnt;
278 char *buf;
279 } BUF;
281 BUF *allocbuf(BUF *, int, int);
282 void lostconn(int);
283 void nospace(void);
284 int okname(char *);
285 void run_err(const char *,...);
286 void verifydir(char *);
288 struct passwd *pwd;
289 uid_t userid;
290 int errs, remin, remout;
291 int pflag, iamremote, iamrecursive, targetshouldbedirectory;
293 #define CMDNEEDS 64
294 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
296 int response(void);
297 void rsource(char *, struct stat *);
298 void sink(int, char *[]);
299 void source(int, char *[]);
300 void tolocal(int, char *[]);
301 void toremote(char *, int, char *[]);
302 void usage(void);
304 #if defined(DBMULTI_scp) || !defined(DROPBEAR_MULTI)
305 #if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI)
306 int scp_main(int argc, char **argv)
307 #else
309 main(int argc, char **argv)
310 #endif
312 int ch, fflag, tflag, status;
313 double speed;
314 char *targ, *endp;
315 extern char *optarg;
316 extern int optind;
318 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
319 sanitise_stdfd();
321 memset(&args, '\0', sizeof(args));
322 args.list = NULL;
323 addargs(&args, "%s", ssh_program);
325 fflag = tflag = 0;
326 while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1)
327 switch (ch) {
328 /* User-visible flags. */
329 case '1':
330 case '2':
331 case '4':
332 case '6':
333 case 'C':
334 addargs(&args, "-%c", ch);
335 break;
336 case 'o':
337 case 'c':
338 case 'i':
339 case 'F':
340 addargs(&args, "-%c%s", ch, optarg);
341 break;
342 case 'P':
343 addargs(&args, "-p%s", optarg);
344 break;
345 case 'B':
346 fprintf(stderr, "Note: -B option is disabled in this version of scp");
347 break;
348 case 'l':
349 speed = strtod(optarg, &endp);
350 if (speed <= 0 || *endp != '\0')
351 usage();
352 limit_rate = speed * 1024;
353 break;
354 case 'p':
355 pflag = 1;
356 break;
357 case 'r':
358 iamrecursive = 1;
359 break;
360 case 'S':
361 ssh_program = xstrdup(optarg);
362 break;
363 case 'v':
364 addargs(&args, "-v");
365 verbose_mode = 1;
366 break;
367 #ifdef PROGRESS_METER
368 case 'q':
369 addargs(&args, "-q");
370 showprogress = 0;
371 break;
372 #endif
374 /* Server options. */
375 case 'd':
376 targetshouldbedirectory = 1;
377 break;
378 case 'f': /* "from" */
379 iamremote = 1;
380 fflag = 1;
381 break;
382 case 't': /* "to" */
383 iamremote = 1;
384 tflag = 1;
385 #ifdef HAVE_CYGWIN
386 setmode(0, O_BINARY);
387 #endif
388 break;
389 default:
390 usage();
392 argc -= optind;
393 argv += optind;
395 if ((pwd = getpwuid(userid = getuid())) == NULL)
396 fatal("unknown user %u", (u_int) userid);
398 if (!isatty(STDERR_FILENO))
399 showprogress = 0;
401 remin = STDIN_FILENO;
402 remout = STDOUT_FILENO;
404 if (fflag) {
405 /* Follow "protocol", send data. */
406 (void) response();
407 source(argc, argv);
408 exit(errs != 0);
410 if (tflag) {
411 /* Receive data. */
412 sink(argc, argv);
413 exit(errs != 0);
415 if (argc < 2)
416 usage();
417 if (argc > 2)
418 targetshouldbedirectory = 1;
420 remin = remout = -1;
421 do_cmd_pid = -1;
422 /* Command to be executed on remote system using "ssh". */
423 (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
424 verbose_mode ? " -v" : "",
425 iamrecursive ? " -r" : "", pflag ? " -p" : "",
426 targetshouldbedirectory ? " -d" : "");
428 (void) signal(SIGPIPE, lostconn);
430 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */
431 toremote(targ, argc, argv);
432 else {
433 if (targetshouldbedirectory)
434 verifydir(argv[argc - 1]);
435 tolocal(argc, argv); /* Dest is local host. */
438 * Finally check the exit status of the ssh process, if one was forked
439 * and no error has occured yet
441 if (do_cmd_pid != -1 && errs == 0) {
442 if (remin != -1)
443 (void) close(remin);
444 if (remout != -1)
445 (void) close(remout);
446 if (waitpid(do_cmd_pid, &status, 0) == -1)
447 errs = 1;
448 else {
449 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
450 errs = 1;
453 exit(errs != 0);
455 #endif /* DBMULTI_scp stuff */
457 void
458 toremote(char *targ, int argc, char **argv)
460 int i, len;
461 char *bp, *host, *src, *suser, *thost, *tuser, *arg;
462 arglist alist;
464 memset(&alist, '\0', sizeof(alist));
465 alist.list = NULL;
467 *targ++ = 0;
468 if (*targ == 0)
469 targ = ".";
471 arg = xstrdup(argv[argc - 1]);
472 if ((thost = strrchr(arg, '@'))) {
473 /* user@host */
474 *thost++ = 0;
475 tuser = arg;
476 if (*tuser == '\0')
477 tuser = NULL;
478 } else {
479 thost = arg;
480 tuser = NULL;
483 if (tuser != NULL && !okname(tuser)) {
484 xfree(arg);
485 return;
488 for (i = 0; i < argc - 1; i++) {
489 src = colon(argv[i]);
490 if (src) { /* remote to remote */
491 freeargs(&alist);
492 addargs(&alist, "%s", ssh_program);
493 if (verbose_mode)
494 addargs(&alist, "-v");
495 #if 0
496 // Disabled since dbclient won't understand them
497 // and scp works fine without them.
498 addargs(&alist, "-x");
499 addargs(&alist, "-oClearAllForwardings yes");
500 addargs(&alist, "-n");
501 #endif
503 *src++ = 0;
504 if (*src == 0)
505 src = ".";
506 host = strrchr(argv[i], '@');
508 if (host) {
509 *host++ = 0;
510 host = cleanhostname(host);
511 suser = argv[i];
512 if (*suser == '\0')
513 suser = pwd->pw_name;
514 else if (!okname(suser))
515 continue;
516 addargs(&alist, "-l");
517 addargs(&alist, "%s", suser);
518 } else {
519 host = cleanhostname(argv[i]);
521 addargs(&alist, "%s", host);
522 addargs(&alist, "%s", cmd);
523 addargs(&alist, "%s", src);
524 addargs(&alist, "%s%s%s:%s",
525 tuser ? tuser : "", tuser ? "@" : "",
526 thost, targ);
527 if (do_local_cmd(&alist) != 0)
528 errs = 1;
529 } else { /* local to remote */
530 if (remin == -1) {
531 len = strlen(targ) + CMDNEEDS + 20;
532 bp = xmalloc(len);
533 (void) snprintf(bp, len, "%s -t %s", cmd, targ);
534 host = cleanhostname(thost);
535 if (do_cmd(host, tuser, bp, &remin,
536 &remout, argc) < 0)
537 exit(1);
538 if (response() < 0)
539 exit(1);
540 (void) xfree(bp);
542 source(1, argv + i);
547 void
548 tolocal(int argc, char **argv)
550 int i, len;
551 char *bp, *host, *src, *suser;
552 arglist alist;
554 memset(&alist, '\0', sizeof(alist));
555 alist.list = NULL;
557 for (i = 0; i < argc - 1; i++) {
558 if (!(src = colon(argv[i]))) { /* Local to local. */
559 freeargs(&alist);
560 addargs(&alist, "%s", _PATH_CP);
561 if (iamrecursive)
562 addargs(&alist, "-r");
563 if (pflag)
564 addargs(&alist, "-p");
565 addargs(&alist, "%s", argv[i]);
566 addargs(&alist, "%s", argv[argc-1]);
567 if (do_local_cmd(&alist))
568 ++errs;
569 continue;
571 *src++ = 0;
572 if (*src == 0)
573 src = ".";
574 if ((host = strrchr(argv[i], '@')) == NULL) {
575 host = argv[i];
576 suser = NULL;
577 } else {
578 *host++ = 0;
579 suser = argv[i];
580 if (*suser == '\0')
581 suser = pwd->pw_name;
583 host = cleanhostname(host);
584 len = strlen(src) + CMDNEEDS + 20;
585 bp = xmalloc(len);
586 (void) snprintf(bp, len, "%s -f %s", cmd, src);
587 if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
588 (void) xfree(bp);
589 ++errs;
590 continue;
592 xfree(bp);
593 sink(1, argv + argc - 1);
594 (void) close(remin);
595 remin = remout = -1;
599 void
600 source(int argc, char **argv)
602 struct stat stb;
603 static BUF buffer;
604 BUF *bp;
605 off_t i, amt, statbytes;
606 size_t result;
607 int fd = -1, haderr, indx;
608 char *last, *name, buf[2048];
609 int len;
611 for (indx = 0; indx < argc; ++indx) {
612 name = argv[indx];
613 statbytes = 0;
614 len = strlen(name);
615 while (len > 1 && name[len-1] == '/')
616 name[--len] = '\0';
617 if (strchr(name, '\n') != NULL) {
618 run_err("%s: skipping, filename contains a newline",
619 name);
620 goto next;
622 if ((fd = open(name, O_RDONLY, 0)) < 0)
623 goto syserr;
624 if (fstat(fd, &stb) < 0) {
625 syserr: run_err("%s: %s", name, strerror(errno));
626 goto next;
628 switch (stb.st_mode & S_IFMT) {
629 case S_IFREG:
630 break;
631 case S_IFDIR:
632 if (iamrecursive) {
633 rsource(name, &stb);
634 goto next;
636 /* FALLTHROUGH */
637 default:
638 run_err("%s: not a regular file", name);
639 goto next;
641 if ((last = strrchr(name, '/')) == NULL)
642 last = name;
643 else
644 ++last;
645 curfile = last;
646 if (pflag) {
648 * Make it compatible with possible future
649 * versions expecting microseconds.
651 (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n",
652 (u_long) stb.st_mtime,
653 (u_long) stb.st_atime);
654 (void) atomicio(vwrite, remout, buf, strlen(buf));
655 if (response() < 0)
656 goto next;
658 #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
659 snprintf(buf, sizeof buf, "C%04o %lld %s\n",
660 (u_int) (stb.st_mode & FILEMODEMASK),
661 (long long)stb.st_size, last);
662 if (verbose_mode) {
663 fprintf(stderr, "Sending file modes: %s", buf);
665 (void) atomicio(vwrite, remout, buf, strlen(buf));
666 if (response() < 0)
667 goto next;
668 if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
669 next: if (fd != -1) {
670 (void) close(fd);
671 fd = -1;
673 continue;
675 #if PROGRESS_METER
676 if (showprogress)
677 start_progress_meter(curfile, stb.st_size, &statbytes);
678 #endif
679 /* Keep writing after an error so that we stay sync'd up. */
680 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
681 amt = bp->cnt;
682 if (i + amt > stb.st_size)
683 amt = stb.st_size - i;
684 if (!haderr) {
685 result = atomicio(read, fd, bp->buf, amt);
686 if (result != amt)
687 haderr = errno;
689 if (haderr)
690 (void) atomicio(vwrite, remout, bp->buf, amt);
691 else {
692 result = atomicio(vwrite, remout, bp->buf, amt);
693 if (result != amt)
694 haderr = errno;
695 statbytes += result;
697 if (limit_rate)
698 bwlimit(amt);
700 #ifdef PROGRESS_METER
701 if (showprogress)
702 stop_progress_meter();
703 #endif
705 if (fd != -1) {
706 if (close(fd) < 0 && !haderr)
707 haderr = errno;
708 fd = -1;
710 if (!haderr)
711 (void) atomicio(vwrite, remout, "", 1);
712 else
713 run_err("%s: %s", name, strerror(haderr));
714 (void) response();
718 void
719 rsource(char *name, struct stat *statp)
721 DIR *dirp;
722 struct dirent *dp;
723 char *last, *vect[1], path[1100];
725 if (!(dirp = opendir(name))) {
726 run_err("%s: %s", name, strerror(errno));
727 return;
729 last = strrchr(name, '/');
730 if (last == 0)
731 last = name;
732 else
733 last++;
734 if (pflag) {
735 (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n",
736 (u_long) statp->st_mtime,
737 (u_long) statp->st_atime);
738 (void) atomicio(vwrite, remout, path, strlen(path));
739 if (response() < 0) {
740 closedir(dirp);
741 return;
744 (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n",
745 (u_int) (statp->st_mode & FILEMODEMASK), 0, last);
746 if (verbose_mode)
747 fprintf(stderr, "Entering directory: %s", path);
748 (void) atomicio(vwrite, remout, path, strlen(path));
749 if (response() < 0) {
750 closedir(dirp);
751 return;
753 while ((dp = readdir(dirp)) != NULL) {
754 if (dp->d_ino == 0)
755 continue;
756 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
757 continue;
758 if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) {
759 run_err("%s/%s: name too long", name, dp->d_name);
760 continue;
762 (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name);
763 vect[0] = path;
764 source(1, vect);
766 (void) closedir(dirp);
767 (void) atomicio(vwrite, remout, "E\n", 2);
768 (void) response();
771 void
772 bwlimit(int amount)
774 static struct timeval bwstart, bwend;
775 static int lamt, thresh = 16384;
776 uint64_t waitlen;
777 struct timespec ts, rm;
779 if (!timerisset(&bwstart)) {
780 gettimeofday(&bwstart, NULL);
781 return;
784 lamt += amount;
785 if (lamt < thresh)
786 return;
788 gettimeofday(&bwend, NULL);
789 timersub(&bwend, &bwstart, &bwend);
790 if (!timerisset(&bwend))
791 return;
793 lamt *= 8;
794 waitlen = (double)1000000L * lamt / limit_rate;
796 bwstart.tv_sec = waitlen / 1000000L;
797 bwstart.tv_usec = waitlen % 1000000L;
799 if (timercmp(&bwstart, &bwend, >)) {
800 timersub(&bwstart, &bwend, &bwend);
802 /* Adjust the wait time */
803 if (bwend.tv_sec) {
804 thresh /= 2;
805 if (thresh < 2048)
806 thresh = 2048;
807 } else if (bwend.tv_usec < 100) {
808 thresh *= 2;
809 if (thresh > 32768)
810 thresh = 32768;
813 TIMEVAL_TO_TIMESPEC(&bwend, &ts);
814 while (nanosleep(&ts, &rm) == -1) {
815 if (errno != EINTR)
816 break;
817 ts = rm;
821 lamt = 0;
822 gettimeofday(&bwstart, NULL);
825 void
826 sink(int argc, char **argv)
828 static BUF buffer;
829 struct stat stb;
830 enum {
831 YES, NO, DISPLAYED
832 } wrerr;
833 BUF *bp;
834 off_t i;
835 size_t j, count;
836 int amt, exists, first, mask, mode, ofd, omode;
837 off_t size, statbytes;
838 int setimes, targisdir, wrerrno = 0;
839 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
840 struct timeval tv[2];
842 #define atime tv[0]
843 #define mtime tv[1]
844 #define SCREWUP(str) { why = str; goto screwup; }
846 setimes = targisdir = 0;
847 mask = umask(0);
848 if (!pflag)
849 (void) umask(mask);
850 if (argc != 1) {
851 run_err("ambiguous target");
852 exit(1);
854 targ = *argv;
855 if (targetshouldbedirectory)
856 verifydir(targ);
858 (void) atomicio(vwrite, remout, "", 1);
859 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
860 targisdir = 1;
861 for (first = 1;; first = 0) {
862 cp = buf;
863 if (atomicio(read, remin, cp, 1) != 1)
864 return;
865 if (*cp++ == '\n')
866 SCREWUP("unexpected <newline>");
867 do {
868 if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch))
869 SCREWUP("lost connection");
870 *cp++ = ch;
871 } while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
872 *cp = 0;
873 if (verbose_mode)
874 fprintf(stderr, "Sink: %s", buf);
876 if (buf[0] == '\01' || buf[0] == '\02') {
877 if (iamremote == 0)
878 (void) atomicio(vwrite, STDERR_FILENO,
879 buf + 1, strlen(buf + 1));
880 if (buf[0] == '\02')
881 exit(1);
882 ++errs;
883 continue;
885 if (buf[0] == 'E') {
886 (void) atomicio(vwrite, remout, "", 1);
887 return;
889 if (ch == '\n')
890 *--cp = 0;
892 cp = buf;
893 if (*cp == 'T') {
894 setimes++;
895 cp++;
896 mtime.tv_sec = strtol(cp, &cp, 10);
897 if (!cp || *cp++ != ' ')
898 SCREWUP("mtime.sec not delimited");
899 mtime.tv_usec = strtol(cp, &cp, 10);
900 if (!cp || *cp++ != ' ')
901 SCREWUP("mtime.usec not delimited");
902 atime.tv_sec = strtol(cp, &cp, 10);
903 if (!cp || *cp++ != ' ')
904 SCREWUP("atime.sec not delimited");
905 atime.tv_usec = strtol(cp, &cp, 10);
906 if (!cp || *cp++ != '\0')
907 SCREWUP("atime.usec not delimited");
908 (void) atomicio(vwrite, remout, "", 1);
909 continue;
911 if (*cp != 'C' && *cp != 'D') {
913 * Check for the case "rcp remote:foo\* local:bar".
914 * In this case, the line "No match." can be returned
915 * by the shell before the rcp command on the remote is
916 * executed so the ^Aerror_message convention isn't
917 * followed.
919 if (first) {
920 run_err("%s", cp);
921 exit(1);
923 SCREWUP("expected control record");
925 mode = 0;
926 for (++cp; cp < buf + 5; cp++) {
927 if (*cp < '0' || *cp > '7')
928 SCREWUP("bad mode");
929 mode = (mode << 3) | (*cp - '0');
931 if (*cp++ != ' ')
932 SCREWUP("mode not delimited");
934 for (size = 0; isdigit(*cp);)
935 size = size * 10 + (*cp++ - '0');
936 if (*cp++ != ' ')
937 SCREWUP("size not delimited");
938 if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
939 run_err("error: unexpected filename: %s", cp);
940 exit(1);
942 if (targisdir) {
943 static char *namebuf;
944 static size_t cursize;
945 size_t need;
947 need = strlen(targ) + strlen(cp) + 250;
948 if (need > cursize) {
949 if (namebuf)
950 xfree(namebuf);
951 namebuf = xmalloc(need);
952 cursize = need;
954 (void) snprintf(namebuf, need, "%s%s%s", targ,
955 strcmp(targ, "/") ? "/" : "", cp);
956 np = namebuf;
957 } else
958 np = targ;
959 curfile = cp;
960 exists = stat(np, &stb) == 0;
961 if (buf[0] == 'D') {
962 int mod_flag = pflag;
963 if (!iamrecursive)
964 SCREWUP("received directory without -r");
965 if (exists) {
966 if (!S_ISDIR(stb.st_mode)) {
967 errno = ENOTDIR;
968 goto bad;
970 if (pflag)
971 (void) chmod(np, mode);
972 } else {
973 /* Handle copying from a read-only
974 directory */
975 mod_flag = 1;
976 if (mkdir(np, mode | S_IRWXU) < 0)
977 goto bad;
979 vect[0] = xstrdup(np);
980 sink(1, vect);
981 if (setimes) {
982 setimes = 0;
983 if (utimes(vect[0], tv) < 0)
984 run_err("%s: set times: %s",
985 vect[0], strerror(errno));
987 if (mod_flag)
988 (void) chmod(vect[0], mode);
989 if (vect[0])
990 xfree(vect[0]);
991 continue;
993 omode = mode;
994 mode |= S_IWRITE;
995 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
996 bad: run_err("%s: %s", np, strerror(errno));
997 continue;
999 (void) atomicio(vwrite, remout, "", 1);
1000 if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
1001 (void) close(ofd);
1002 continue;
1004 cp = bp->buf;
1005 wrerr = NO;
1007 statbytes = 0;
1008 #ifdef PROGRESS_METER
1009 if (showprogress)
1010 start_progress_meter(curfile, size, &statbytes);
1011 #endif
1012 for (count = i = 0; i < size; i += 4096) {
1013 amt = 4096;
1014 if (i + amt > size)
1015 amt = size - i;
1016 count += amt;
1017 do {
1018 j = atomicio(read, remin, cp, amt);
1019 if (j == 0) {
1020 run_err("%s", j ? strerror(errno) :
1021 "dropped connection");
1022 exit(1);
1024 amt -= j;
1025 cp += j;
1026 statbytes += j;
1027 } while (amt > 0);
1029 if (limit_rate)
1030 bwlimit(4096);
1032 if (count == bp->cnt) {
1033 /* Keep reading so we stay sync'd up. */
1034 if (wrerr == NO) {
1035 if (atomicio(vwrite, ofd, bp->buf,
1036 count) != count) {
1037 wrerr = YES;
1038 wrerrno = errno;
1041 count = 0;
1042 cp = bp->buf;
1045 #ifdef PROGRESS_METER
1046 if (showprogress)
1047 stop_progress_meter();
1048 #endif
1049 if (count != 0 && wrerr == NO &&
1050 atomicio(vwrite, ofd, bp->buf, count) != count) {
1051 wrerr = YES;
1052 wrerrno = errno;
1054 if (wrerr == NO && ftruncate(ofd, size) != 0) {
1055 run_err("%s: truncate: %s", np, strerror(errno));
1056 wrerr = DISPLAYED;
1058 if (pflag) {
1059 if (exists || omode != mode)
1060 #ifdef HAVE_FCHMOD
1061 if (fchmod(ofd, omode)) {
1062 #else /* HAVE_FCHMOD */
1063 if (chmod(np, omode)) {
1064 #endif /* HAVE_FCHMOD */
1065 run_err("%s: set mode: %s",
1066 np, strerror(errno));
1067 wrerr = DISPLAYED;
1069 } else {
1070 if (!exists && omode != mode)
1071 #ifdef HAVE_FCHMOD
1072 if (fchmod(ofd, omode & ~mask)) {
1073 #else /* HAVE_FCHMOD */
1074 if (chmod(np, omode & ~mask)) {
1075 #endif /* HAVE_FCHMOD */
1076 run_err("%s: set mode: %s",
1077 np, strerror(errno));
1078 wrerr = DISPLAYED;
1081 if (close(ofd) == -1) {
1082 wrerr = YES;
1083 wrerrno = errno;
1085 (void) response();
1086 if (setimes && wrerr == NO) {
1087 setimes = 0;
1088 if (utimes(np, tv) < 0) {
1089 run_err("%s: set times: %s",
1090 np, strerror(errno));
1091 wrerr = DISPLAYED;
1094 switch (wrerr) {
1095 case YES:
1096 run_err("%s: %s", np, strerror(wrerrno));
1097 break;
1098 case NO:
1099 (void) atomicio(vwrite, remout, "", 1);
1100 break;
1101 case DISPLAYED:
1102 break;
1105 screwup:
1106 run_err("protocol error: %s", why);
1107 exit(1);
1111 response(void)
1113 char ch, *cp, resp, rbuf[2048];
1115 if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp))
1116 lostconn(0);
1118 cp = rbuf;
1119 switch (resp) {
1120 case 0: /* ok */
1121 return (0);
1122 default:
1123 *cp++ = resp;
1124 /* FALLTHROUGH */
1125 case 1: /* error, followed by error msg */
1126 case 2: /* fatal error, "" */
1127 do {
1128 if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch))
1129 lostconn(0);
1130 *cp++ = ch;
1131 } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
1133 if (!iamremote)
1134 (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf);
1135 ++errs;
1136 if (resp == 1)
1137 return (-1);
1138 exit(1);
1140 /* NOTREACHED */
1143 void
1144 usage(void)
1146 (void) fprintf(stderr,
1147 "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
1148 " [-l limit] [-o ssh_option] [-P port] [-S program]\n"
1149 " [[user@]host1:]file1 [...] [[user@]host2:]file2\n");
1150 exit(1);
1153 void
1154 run_err(const char *fmt,...)
1156 static FILE *fp;
1157 va_list ap;
1159 ++errs;
1160 if (fp == NULL && !(fp = fdopen(remout, "w")))
1161 return;
1162 (void) fprintf(fp, "%c", 0x01);
1163 (void) fprintf(fp, "scp: ");
1164 va_start(ap, fmt);
1165 (void) vfprintf(fp, fmt, ap);
1166 va_end(ap);
1167 (void) fprintf(fp, "\n");
1168 (void) fflush(fp);
1170 if (!iamremote) {
1171 va_start(ap, fmt);
1172 vfprintf(stderr, fmt, ap);
1173 va_end(ap);
1174 fprintf(stderr, "\n");
1178 void
1179 verifydir(char *cp)
1181 struct stat stb;
1183 if (!stat(cp, &stb)) {
1184 if (S_ISDIR(stb.st_mode))
1185 return;
1186 errno = ENOTDIR;
1188 run_err("%s: %s", cp, strerror(errno));
1189 killchild(0);
1193 okname(char *cp0)
1195 int c;
1196 char *cp;
1198 cp = cp0;
1199 do {
1200 c = (int)*cp;
1201 if (c & 0200)
1202 goto bad;
1203 if (!isalpha(c) && !isdigit(c)) {
1204 switch (c) {
1205 case '\'':
1206 case '"':
1207 case '`':
1208 case ' ':
1209 case '#':
1210 goto bad;
1211 default:
1212 break;
1215 } while (*++cp);
1216 return (1);
1218 bad: fprintf(stderr, "%s: invalid user name\n", cp0);
1219 return (0);
1222 BUF *
1223 allocbuf(BUF *bp, int fd, int blksize)
1225 size_t size;
1226 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1227 struct stat stb;
1229 if (fstat(fd, &stb) < 0) {
1230 run_err("fstat: %s", strerror(errno));
1231 return (0);
1233 size = roundup(stb.st_blksize, blksize);
1234 if (size == 0)
1235 size = blksize;
1236 #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1237 size = blksize;
1238 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1239 if (bp->cnt >= size)
1240 return (bp);
1241 if (bp->buf == NULL)
1242 bp->buf = xmalloc(size);
1243 else
1244 bp->buf = xrealloc(bp->buf, size);
1245 memset(bp->buf, 0, size);
1246 bp->cnt = size;
1247 return (bp);
1250 void
1251 lostconn(int signo)
1253 if (!iamremote)
1254 write(STDERR_FILENO, "lost connection\n", 16);
1255 if (signo)
1256 _exit(1);
1257 else
1258 exit(1);