1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5 /*
6 * scp - secure remote copy. This is basically patched BSD rcp which
7 * uses ssh to do the data transfer (instead of using rcmd).
8 *
9 * NOTE: This version should NOT be suid root. (This uses ssh to
10 * do the transfer and ssh has the necessary privileges.)
11 *
12 * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
13 *
14 * As far as I am concerned, the code I have written for this software
15 * can be used freely for any purpose. Any derived versions of this
16 * software must be clearly marked as such, and if the derived work is
17 * incompatible with the protocol description in the RFC file, it must be
18 * called by a name other than "ssh" or "Secure Shell".
19 */
20 /*
21 * Copyright (c) 1999 Theo de Raadt. All rights reserved.
22 * Copyright (c) 1999 Aaron Campbell. All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
34 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
36 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
37 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
39 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
40 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
42 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 */
44
45 /*
46 * Parts from:
47 *
48 * Copyright (c) 1983, 1990, 1992, 1993, 1995
49 * The Regents of the University of California. All rights reserved.
50 *
51 * Redistribution and use in source and binary forms, with or without
52 * modification, are permitted provided that the following conditions
53 * are met:
54 * 1. Redistributions of source code must retain the above copyright
55 * notice, this list of conditions and the following disclaimer.
56 * 2. Redistributions in binary form must reproduce the above copyright
57 * notice, this list of conditions and the following disclaimer in the
58 * documentation and/or other materials provided with the distribution.
59 * 3. All advertising materials mentioning features or use of this software
60 * must display the following acknowledgement:
61 * This product includes software developed by the University of
62 * California, Berkeley and its contributors.
63 * 4. Neither the name of the University nor the names of its contributors
64 * may be used to endorse or promote products derived from this software
65 * without specific prior written permission.
66 *
67 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
68 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
69 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
70 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
71 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
72 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
73 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
74 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
75 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
76 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
77 * SUCH DAMAGE.
78 *
79 */
80
81 #include "includes.h"
82 RCSID("$OpenBSD: scp.c,v 1.91 2002/06/19 00:27:55 deraadt Exp $");
83
84 #include "xmalloc.h"
85 #include "atomicio.h"
86 #include "pathnames.h"
87 #include "log.h"
88 #include "misc.h"
89
90 #ifdef HAVE___PROGNAME
91 extern char *__progname;
92 #else
93 char *__progname;
94 #endif
95
96 /* For progressmeter() -- number of seconds before xfer considered "stalled" */
97 #define STALLTIME 5
98 /* alarm() interval for updating progress meter */
99 #define PROGRESSTIME 1
100
101 /* Visual statistics about files as they are transferred. */
102 void progressmeter(int);
103
104 /* Returns width of the terminal (for progress meter calculations). */
105 int getttywidth(void);
106 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout,
107 int argc);
108
109 /* Struct for addargs */
110 arglist args;
111
112 /* Time a transfer started. */
113 static struct timeval start;
114
115 /* Number of bytes of current file transferred so far. */
116 volatile off_t statbytes;
117
118 /* Total size of current file. */
119 off_t totalbytes = 0;
120
121 /* Name of current file being transferred. */
122 char *curfile;
123
124 /* This is set to non-zero to enable verbose mode. */
125 int verbose_mode = 0;
126
127 /* This is set to zero if the progressmeter is not desired. */
128 int showprogress = 1;
129
130 /* This is the program to execute for the secured connection. ("ssh" or -S) */
131 char *ssh_program = _PATH_SSH_PROGRAM;
132
133 /* This is used to store the pid of ssh_program */
134 static pid_t do_cmd_pid = -1;
135
136 static void
killchild(int signo)137 killchild(int signo)
138 {
139 if (do_cmd_pid > 1) {
140 kill(do_cmd_pid, signo ? signo : SIGTERM);
141 waitpid(do_cmd_pid, NULL, 0);
142 }
143
144 if (signo)
145 _exit(1);
146 exit(1);
147 }
148
149 /*
150 * Run a command via fork(2)/exec(2). This can be a local-to-local copy via
151 * cp(1) or one side of a remote-to-remote copy. We must not use system(3) here
152 * because we don't want filenames to go through a command expansion in the
153 * underlying shell. Note that the user can create a filename that is a piece of
154 * shell code itself and this must not be executed.
155 */
156 static int
do_local_cmd(arglist * a)157 do_local_cmd(arglist *a)
158 {
159 uint_t i;
160 int status;
161 pid_t pid;
162
163 if (a->num == 0)
164 fatal("do_local_cmd: no arguments");
165
166 if (verbose_mode) {
167 fprintf(stderr, gettext("Executing:"));
168 for (i = 0; i < a->num; i++)
169 fprintf(stderr, " %s", a->list[i]);
170 fprintf(stderr, "\n");
171 }
172 if ((pid = fork()) == -1)
173 fatal("do_local_cmd: fork: %s", strerror(errno));
174
175 if (pid == 0) {
176 execvp(a->list[0], a->list);
177 perror(a->list[0]);
178 exit(1);
179 }
180
181 do_cmd_pid = pid;
182 signal(SIGTERM, killchild);
183 signal(SIGINT, killchild);
184 signal(SIGHUP, killchild);
185
186 while (waitpid(pid, &status, 0) == -1)
187 if (errno != EINTR)
188 fatal("do_local_cmd: waitpid: %s", strerror(errno));
189
190 do_cmd_pid = -1;
191
192 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
193 return (-1);
194
195 return (0);
196 }
197
198 /*
199 * This function executes the given command as the specified user on the
200 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This
201 * assigns the input and output file descriptors on success.
202 */
203
204 int
do_cmd(char * host,char * remuser,char * cmd,int * fdin,int * fdout,int argc)205 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
206 {
207 int pin[2], pout[2], reserved[2];
208
209 if (verbose_mode)
210 fprintf(stderr,
211 gettext("Executing: program %s host %s, "
212 "user %s, command %s\n"),
213 ssh_program, host,
214 remuser ? remuser : gettext("(unspecified)"), cmd);
215
216 /*
217 * Reserve two descriptors so that the real pipes won't get
218 * descriptors 0 and 1 because that will screw up dup2 below.
219 */
220 pipe(reserved);
221
222 /* Create a socket pair for communicating with ssh. */
223 if (pipe(pin) < 0)
224 fatal("pipe: %s", strerror(errno));
225 if (pipe(pout) < 0)
226 fatal("pipe: %s", strerror(errno));
227
228 /* Free the reserved descriptors. */
229 close(reserved[0]);
230 close(reserved[1]);
231
232 /* For a child to execute the command on the remote host using ssh. */
233 if ((do_cmd_pid = fork()) == 0) {
234 /* Child. */
235 close(pin[1]);
236 close(pout[0]);
237 dup2(pin[0], 0);
238 dup2(pout[1], 1);
239 close(pin[0]);
240 close(pout[1]);
241
242 args.list[0] = ssh_program;
243 if (remuser != NULL)
244 addargs(&args, "-l%s", remuser);
245 addargs(&args, "%s", host);
246 addargs(&args, "%s", cmd);
247
248 execvp(ssh_program, args.list);
249 perror(ssh_program);
250 exit(1);
251 } else if (do_cmd_pid == (pid_t)-1) {
252 /* fork() failed */
253 fatal("fork: %s", strerror(errno));
254 }
255
256 /* Parent. Close the other side, and return the local side. */
257 close(pin[0]);
258 *fdout = pin[1];
259 close(pout[1]);
260 *fdin = pout[0];
261 return (0);
262 }
263
264 typedef struct {
265 int cnt;
266 char *buf;
267 } BUF;
268
269 BUF *allocbuf(BUF *, int, int);
270 void lostconn(int);
271 void nospace(void);
272 int okname(char *);
273 void run_err(const char *, ...);
274 void verifydir(char *);
275
276 struct passwd *pwd;
277 uid_t userid;
278 int errs, remin, remout;
279 int pflag, iamremote, iamrecursive, targetshouldbedirectory;
280
281 #define CMDNEEDS 64
282 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
283
284 int response(void);
285 void rsource(char *, struct stat *);
286 void sink(int, char *[]);
287 void source(int, char *[]);
288 void tolocal(int, char *[]);
289 void toremote(char *, int, char *[]);
290 void usage(void);
291
292 int
main(argc,argv)293 main(argc, argv)
294 int argc;
295 char *argv[];
296 {
297 int ch, fflag, tflag, status;
298 char *targ;
299 extern char *optarg;
300 extern int optind;
301
302 __progname = get_progname(argv[0]);
303
304 g11n_setlocale(LC_ALL, "");
305
306 args.list = NULL;
307 addargs(&args, "ssh"); /* overwritten with ssh_program */
308 addargs(&args, "-x");
309 addargs(&args, "-oForwardAgent no");
310 addargs(&args, "-oClearAllForwardings yes");
311
312 fflag = tflag = 0;
313 while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1)
314 switch (ch) {
315 /* User-visible flags. */
316 case '4':
317 case '6':
318 case 'C':
319 addargs(&args, "-%c", ch);
320 break;
321 case 'o':
322 case 'c':
323 case 'i':
324 case 'F':
325 addargs(&args, "-%c%s", ch, optarg);
326 break;
327 case 'P':
328 addargs(&args, "-p%s", optarg);
329 break;
330 case 'B':
331 addargs(&args, "-oBatchmode yes");
332 break;
333 case 'p':
334 pflag = 1;
335 break;
336 case 'r':
337 iamrecursive = 1;
338 break;
339 case 'S':
340 ssh_program = xstrdup(optarg);
341 break;
342 case 'v':
343 addargs(&args, "-v");
344 verbose_mode = 1;
345 break;
346 case 'q':
347 showprogress = 0;
348 break;
349
350 /* Server options. */
351 case 'd':
352 targetshouldbedirectory = 1;
353 break;
354 case 'f': /* "from" */
355 iamremote = 1;
356 fflag = 1;
357 break;
358 case 't': /* "to" */
359 iamremote = 1;
360 tflag = 1;
361 #ifdef HAVE_CYGWIN
362 setmode(0, O_BINARY);
363 #endif
364 break;
365 default:
366 usage();
367 }
368 argc -= optind;
369 argv += optind;
370
371 if ((pwd = getpwuid(userid = getuid())) == NULL)
372 fatal("unknown user %d", (int)userid);
373
374 if (!isatty(STDERR_FILENO))
375 showprogress = 0;
376
377 remin = STDIN_FILENO;
378 remout = STDOUT_FILENO;
379
380 if (fflag) {
381 /* Follow "protocol", send data. */
382 (void) response();
383 source(argc, argv);
384 exit(errs != 0);
385 }
386 if (tflag) {
387 /* Receive data. */
388 sink(argc, argv);
389 exit(errs != 0);
390 }
391 if (argc < 2)
392 usage();
393 if (argc > 2)
394 targetshouldbedirectory = 1;
395
396 remin = remout = -1;
397 do_cmd_pid = (pid_t)-1;
398
399 /* Command to be executed on remote system using "ssh". */
400 (void) snprintf(cmd, sizeof (cmd), "scp%s%s%s%s",
401 verbose_mode ? " -v" : "",
402 iamrecursive ? " -r" : "", pflag ? " -p" : "",
403 targetshouldbedirectory ? " -d" : "");
404
405 (void) signal(SIGPIPE, lostconn);
406
407 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */
408 toremote(targ, argc, argv);
409 else {
410 if (targetshouldbedirectory)
411 verifydir(argv[argc - 1]);
412 tolocal(argc, argv); /* Dest is local host. */
413 }
414 /*
415 * Finally check the exit status of the ssh process, if one was forked
416 * and no error has occurred yet
417 */
418 if (do_cmd_pid != (pid_t)-1 && errs == 0) {
419 if (remin != -1) {
420 (void) close(remin);
421 }
422 if (remout != -1) {
423 (void) close(remout);
424 }
425 if (waitpid(do_cmd_pid, &status, 0) == -1) {
426 errs = 1;
427 } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
428 errs = 1;
429 }
430 }
431
432 return (errs != 0);
433 }
434
435 void
toremote(targ,argc,argv)436 toremote(targ, argc, argv)
437 char *targ, *argv[];
438 int argc;
439 {
440 int i, len;
441 char *bp, *host, *src, *suser, *thost, *tuser, *arg;
442 arglist alist;
443
444 memset(&alist, '\0', sizeof (alist));
445 alist.list = NULL;
446
447 *targ++ = 0;
448 if (*targ == 0)
449 targ = ".";
450
451 arg = xstrdup(argv[argc - 1]);
452 if ((thost = strchr(arg, '@'))) {
453 /* user@host */
454 *thost++ = 0;
455 tuser = arg;
456 if (*tuser == '\0')
457 tuser = NULL;
458 else if (!okname(tuser))
459 exit(1);
460 } else {
461 thost = arg;
462 tuser = NULL;
463 }
464
465 if (tuser != NULL && !okname(tuser)) {
466 xfree(arg);
467 return;
468 }
469
470 for (i = 0; i < argc - 1; i++) {
471 src = colon(argv[i]);
472 if (src) { /* remote to remote */
473 freeargs(&alist);
474 addargs(&alist, "%s", ssh_program);
475 if (verbose_mode)
476 addargs(&alist, "-v");
477 addargs(&alist, "-x");
478 addargs(&alist, "-oClearAllForwardings yes");
479 addargs(&alist, "-n");
480
481 *src++ = 0;
482 if (*src == 0)
483 src = ".";
484 host = strchr(argv[i], '@');
485
486 if (host) {
487 *host++ = 0;
488 host = cleanhostname(host);
489 suser = argv[i];
490 if (*suser == '\0')
491 suser = pwd->pw_name;
492 else if (!okname(suser))
493 continue;
494 addargs(&alist, "-l");
495 addargs(&alist, "%s", suser);
496 } else {
497 host = cleanhostname(argv[i]);
498 }
499 addargs(&alist, "%s", host);
500 addargs(&alist, "%s", cmd);
501 addargs(&alist, "%s", src);
502 addargs(&alist, "%s%s%s:%s",
503 tuser ? tuser : "", tuser ? "@" : "",
504 thost, targ);
505 if (do_local_cmd(&alist) != 0)
506 errs = 1;
507 } else { /* local to remote */
508 if (remin == -1) {
509 len = strlen(targ) + CMDNEEDS + 20;
510 bp = xmalloc(len);
511 (void) snprintf(bp, len, "%s -t %s", cmd, targ);
512 host = cleanhostname(thost);
513 if (do_cmd(host, tuser, bp, &remin,
514 &remout, argc) < 0)
515 exit(1);
516 if (response() < 0)
517 exit(1);
518 (void) xfree(bp);
519 }
520 source(1, argv + i);
521 }
522 }
523 }
524
525 void
tolocal(argc,argv)526 tolocal(argc, argv)
527 int argc;
528 char *argv[];
529 {
530 int i, len;
531 char *bp, *host, *src, *suser;
532 arglist alist;
533
534 memset(&alist, '\0', sizeof (alist));
535 alist.list = NULL;
536
537 for (i = 0; i < argc - 1; i++) {
538 if (!(src = colon(argv[i]))) { /* Local to local. */
539 freeargs(&alist);
540 addargs(&alist, "%s", _PATH_CP);
541 if (iamrecursive)
542 addargs(&alist, "-r");
543 if (pflag)
544 addargs(&alist, "-p");
545 addargs(&alist, "%s", argv[i]);
546 addargs(&alist, "%s", argv[argc-1]);
547 if (do_local_cmd(&alist))
548 ++errs;
549 continue;
550 }
551 *src++ = 0;
552 if (*src == 0)
553 src = ".";
554 if ((host = strchr(argv[i], '@')) == NULL) {
555 host = argv[i];
556 suser = NULL;
557 } else {
558 *host++ = 0;
559 suser = argv[i];
560 if (*suser == '\0')
561 suser = pwd->pw_name;
562 else if (!okname(suser))
563 continue;
564 }
565 host = cleanhostname(host);
566 len = strlen(src) + CMDNEEDS + 20;
567 bp = xmalloc(len);
568 (void) snprintf(bp, len, "%s -f %s", cmd, src);
569 if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
570 (void) xfree(bp);
571 ++errs;
572 continue;
573 }
574 xfree(bp);
575 sink(1, argv + argc - 1);
576 (void) close(remin);
577 remin = remout = -1;
578 }
579 }
580
581 void
source(argc,argv)582 source(argc, argv)
583 int argc;
584 char *argv[];
585 {
586 struct stat stb;
587 static BUF buffer;
588 BUF *bp;
589 off_t i, amt, result;
590 int fd, haderr, indx;
591 char *last, *name, buf[2048];
592 int len;
593
594 for (indx = 0; indx < argc; ++indx) {
595 name = argv[indx];
596 statbytes = 0;
597 len = strlen(name);
598 while (len > 1 && name[len-1] == '/')
599 name[--len] = '\0';
600 if (strchr(name, '\n') != NULL) {
601 run_err("%s: skipping, filename contains a newline",
602 name);
603 goto next;
604 }
605 if ((fd = open(name, O_RDONLY, 0)) < 0)
606 goto syserr;
607 if (fstat(fd, &stb) < 0) {
608 syserr: run_err("%s: %s", name, strerror(errno));
609 goto next;
610 }
611 switch (stb.st_mode & S_IFMT) {
612 case S_IFREG:
613 break;
614 case S_IFDIR:
615 if (iamrecursive) {
616 rsource(name, &stb);
617 goto next;
618 }
619 /* FALLTHROUGH */
620 default:
621 run_err("%s: not a regular file", name);
622 goto next;
623 }
624 if ((last = strrchr(name, '/')) == NULL)
625 last = name;
626 else
627 ++last;
628 curfile = last;
629 if (pflag) {
630 /*
631 * Make it compatible with possible future
632 * versions expecting microseconds.
633 */
634 (void) snprintf(buf, sizeof (buf), "T%lu 0 %lu 0\n",
635 (ulong_t)stb.st_mtime,
636 (ulong_t)stb.st_atime);
637 (void) atomicio(write, remout, buf, strlen(buf));
638 if (response() < 0)
639 goto next;
640 }
641 #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
642 #ifdef HAVE_LONG_LONG_INT
643 snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
644 (uint_t)(stb.st_mode & FILEMODEMASK),
645 (long long)stb.st_size, last);
646 #else
647 /* XXX: Handle integer overflow? */
648 snprintf(buf, sizeof (buf), "C%04o %lu %s\n",
649 (uint_t)(stb.st_mode & FILEMODEMASK),
650 (ulong_t)stb.st_size, last);
651 #endif
652 if (verbose_mode) {
653 fprintf(stderr, gettext("Sending file modes: %s"), buf);
654 fflush(stderr);
655 }
656 (void) atomicio(write, remout, buf, strlen(buf));
657 if (response() < 0)
658 goto next;
659 if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
660 next: (void) close(fd);
661 continue;
662 }
663 if (showprogress) {
664 totalbytes = stb.st_size;
665 progressmeter(-1);
666 }
667 /* Keep writing after an error so that we stay sync'd up. */
668 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
669 amt = bp->cnt;
670 if (i + amt > stb.st_size)
671 amt = stb.st_size - i;
672 if (!haderr) {
673 result = atomicio(read, fd, bp->buf, amt);
674 if (result != amt)
675 haderr = result >= 0 ? EIO : errno;
676 }
677 if (haderr)
678 (void) atomicio(write, remout, bp->buf, amt);
679 else {
680 result = atomicio(write, remout, bp->buf, amt);
681 if (result != amt)
682 haderr = result >= 0 ? EIO : errno;
683 statbytes += result;
684 }
685 }
686 if (showprogress)
687 progressmeter(1);
688
689 if (close(fd) < 0 && !haderr)
690 haderr = errno;
691 if (!haderr)
692 (void) atomicio(write, remout, "", 1);
693 else
694 run_err("%s: %s", name, strerror(haderr));
695 (void) response();
696 }
697 }
698
699 void
rsource(name,statp)700 rsource(name, statp)
701 char *name;
702 struct stat *statp;
703 {
704 DIR *dirp;
705 struct dirent *dp;
706 char *last, *vect[1], path[1100];
707
708 if (!(dirp = opendir(name))) {
709 run_err("%s: %s", name, strerror(errno));
710 return;
711 }
712 last = strrchr(name, '/');
713 if (last == 0)
714 last = name;
715 else
716 last++;
717 if (pflag) {
718 (void) snprintf(path, sizeof (path), "T%lu 0 %lu 0\n",
719 (ulong_t)statp->st_mtime,
720 (ulong_t)statp->st_atime);
721 (void) atomicio(write, remout, path, strlen(path));
722 if (response() < 0) {
723 closedir(dirp);
724 return;
725 }
726 }
727 (void) snprintf(path, sizeof (path), "D%04o %d %.1024s\n",
728 (uint_t)(statp->st_mode & FILEMODEMASK), 0, last);
729 if (verbose_mode)
730 fprintf(stderr, gettext("Entering directory: %s"), path);
731 (void) atomicio(write, remout, path, strlen(path));
732 if (response() < 0) {
733 closedir(dirp);
734 return;
735 }
736 while ((dp = readdir(dirp)) != NULL) {
737 if (dp->d_ino == 0)
738 continue;
739 if ((strcmp(dp->d_name, ".") == 0) ||
740 (strcmp(dp->d_name, "..") == 0))
741 continue;
742 if (strlen(name) + 1 + strlen(dp->d_name) >=
743 sizeof (path) - 1) {
744 run_err("%s/%s: name too long", name, dp->d_name);
745 continue;
746 }
747 (void) snprintf(path, sizeof (path), "%s/%s", name, dp->d_name);
748 vect[0] = path;
749 source(1, vect);
750 }
751 (void) closedir(dirp);
752 (void) atomicio(write, remout, "E\n", 2);
753 (void) response();
754 }
755
756 void
sink(argc,argv)757 sink(argc, argv)
758 int argc;
759 char *argv[];
760 {
761 static BUF buffer;
762 struct stat stb;
763 enum {
764 YES, NO, DISPLAYED
765 } wrerr;
766 BUF *bp;
767 off_t i, j;
768 int amt, count, exists, first, mask, mode, ofd, omode;
769 off_t size;
770 int setimes, targisdir, wrerrno = 0;
771 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
772 struct timeval tv[2];
773
774 #define atime tv[0]
775 #define mtime tv[1]
776 #define SCREWUP(str) { why = str; goto screwup; }
777
778 setimes = targisdir = 0;
779 mask = umask(0);
780 if (!pflag)
781 (void) umask(mask);
782 if (argc != 1) {
783 run_err("ambiguous target");
784 exit(1);
785 }
786 targ = *argv;
787 if (targetshouldbedirectory)
788 verifydir(targ);
789
790 (void) atomicio(write, remout, "", 1);
791 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
792 targisdir = 1;
793 for (first = 1; ; first = 0) {
794 cp = buf;
795 if (atomicio(read, remin, cp, 1) <= 0)
796 return;
797 if (*cp++ == '\n')
798 SCREWUP("unexpected <newline>")
799 do {
800 if (atomicio(read, remin, &ch, sizeof (ch)) !=
801 sizeof (ch))
802 SCREWUP("lost connection")
803 *cp++ = ch;
804 } while (cp < &buf[sizeof (buf) - 1] && ch != '\n');
805 *cp = 0;
806
807 if (buf[0] == '\01' || buf[0] == '\02') {
808 if (iamremote == 0)
809 (void) atomicio(write, STDERR_FILENO,
810 buf + 1, strlen(buf + 1));
811 if (buf[0] == '\02')
812 exit(1);
813 ++errs;
814 continue;
815 }
816 if (buf[0] == 'E') {
817 (void) atomicio(write, remout, "", 1);
818 return;
819 }
820 if (ch == '\n')
821 *--cp = 0;
822
823 cp = buf;
824 if (*cp == 'T') {
825 setimes++;
826 cp++;
827 mtime.tv_sec = strtol(cp, &cp, 10);
828 if (!cp || *cp++ != ' ')
829 SCREWUP("mtime.sec not delimited")
830 mtime.tv_usec = strtol(cp, &cp, 10);
831 if (!cp || *cp++ != ' ')
832 SCREWUP("mtime.usec not delimited")
833 atime.tv_sec = strtol(cp, &cp, 10);
834 if (!cp || *cp++ != ' ')
835 SCREWUP("atime.sec not delimited")
836 atime.tv_usec = strtol(cp, &cp, 10);
837 if (!cp || *cp++ != '\0')
838 SCREWUP("atime.usec not delimited")
839 (void) atomicio(write, remout, "", 1);
840 continue;
841 }
842 if (*cp != 'C' && *cp != 'D') {
843 /*
844 * Check for the case "rcp remote:foo\* local:bar".
845 * In this case, the line "No match." can be returned
846 * by the shell before the rcp command on the remote is
847 * executed so the ^Aerror_message convention isn't
848 * followed.
849 */
850 if (first) {
851 run_err("%s", cp);
852 exit(1);
853 }
854 SCREWUP("expected control record")
855 }
856 mode = 0;
857 for (++cp; cp < buf + 5; cp++) {
858 if (*cp < '0' || *cp > '7')
859 SCREWUP("bad mode")
860 mode = (mode << 3) | (*cp - '0');
861 }
862 if (*cp++ != ' ')
863 SCREWUP("mode not delimited")
864
865 for (size = 0; isdigit(*cp); )
866 size = size * 10 + (*cp++ - '0');
867 if (*cp++ != ' ')
868 SCREWUP("size not delimited")
869 if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
870 run_err("error: unexpected filename: %s", cp);
871 exit(1);
872 }
873 if (targisdir) {
874 static char *namebuf;
875 static int cursize;
876 size_t need;
877
878 need = strlen(targ) + strlen(cp) + 250;
879 if (need > cursize) {
880 if (namebuf)
881 xfree(namebuf);
882 namebuf = xmalloc(need);
883 cursize = need;
884 }
885 (void) snprintf(namebuf, need, "%s%s%s", targ,
886 strcmp(targ, "/") ? "/" : "", cp);
887 np = namebuf;
888 } else
889 np = targ;
890 curfile = cp;
891 exists = stat(np, &stb) == 0;
892 if (buf[0] == 'D') {
893 int mod_flag = pflag;
894 if (!iamrecursive)
895 SCREWUP("received directory without -r");
896 if (exists) {
897 if (!S_ISDIR(stb.st_mode)) {
898 errno = ENOTDIR;
899 goto bad;
900 }
901 if (pflag)
902 (void) chmod(np, mode);
903 } else {
904 /*
905 * Handle copying from a read-only
906 * directory
907 */
908 mod_flag = 1;
909 if (mkdir(np, mode | S_IRWXU) < 0)
910 goto bad;
911 }
912 vect[0] = xstrdup(np);
913 sink(1, vect);
914 if (setimes) {
915 setimes = 0;
916 if (utimes(vect[0], tv) < 0)
917 run_err("%s: set times: %s",
918 vect[0], strerror(errno));
919 }
920 if (mod_flag)
921 (void) chmod(vect[0], mode);
922 if (vect[0])
923 xfree(vect[0]);
924 continue;
925 }
926 omode = mode;
927 mode |= S_IWRITE;
928 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
929 bad: run_err("%s: %s", np, strerror(errno));
930 continue;
931 }
932 (void) atomicio(write, remout, "", 1);
933 if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
934 (void) close(ofd);
935 continue;
936 }
937 wrerr = NO;
938
939 if (showprogress) {
940 totalbytes = size;
941 progressmeter(-1);
942 }
943 statbytes = 0;
944 for (i = 0; i < size; i += bp->cnt) {
945 amt = bp->cnt;
946 cp = bp->buf;
947 if (i + amt > size)
948 amt = size - i;
949 count = amt;
950 do {
951 j = read(remin, cp, amt);
952 if (j == -1 && (errno == EINTR ||
953 errno == EAGAIN)) {
954 continue;
955 } else if (j <= 0) {
956 run_err("%s", j ? strerror(errno) :
957 "dropped connection");
958 exit(1);
959 }
960 amt -= j;
961 cp += j;
962 statbytes += j;
963 } while (amt > 0);
964 /* Keep reading so we stay sync'd up. */
965 if (wrerr == NO) {
966 j = atomicio(write, ofd, bp->buf,
967 count);
968 if (j != count) {
969 wrerr = YES;
970 wrerrno = j >= 0 ? EIO : errno;
971 }
972 }
973 }
974 if (showprogress)
975 progressmeter(1);
976 if (ftruncate(ofd, size)) {
977 run_err("%s: truncate: %s", np, strerror(errno));
978 wrerr = DISPLAYED;
979 }
980 if (pflag) {
981 if (exists || omode != mode)
982 #ifdef HAVE_FCHMOD
983 if (fchmod(ofd, omode)) {
984 #else /* HAVE_FCHMOD */
985 if (chmod(np, omode)) {
986 #endif /* HAVE_FCHMOD */
987 run_err("%s: set mode: %s",
988 np, strerror(errno));
989 wrerr = DISPLAYED;
990 }
991 } else {
992 if (!exists && omode != mode)
993 #ifdef HAVE_FCHMOD
994 if (fchmod(ofd, omode & ~mask)) {
995 #else /* HAVE_FCHMOD */
996 if (chmod(np, omode & ~mask)) {
997 #endif /* HAVE_FCHMOD */
998 run_err("%s: set mode: %s",
999 np, strerror(errno));
1000 wrerr = DISPLAYED;
1001 }
1002 }
1003 if (close(ofd) == -1) {
1004 wrerr = YES;
1005 wrerrno = errno;
1006 }
1007 (void) response();
1008 if (setimes && wrerr == NO) {
1009 setimes = 0;
1010 if (utimes(np, tv) < 0) {
1011 run_err("%s: set times: %s",
1012 np, strerror(errno));
1013 wrerr = DISPLAYED;
1014 }
1015 }
1016 switch (wrerr) {
1017 case YES:
1018 run_err("%s: %s", np, strerror(wrerrno));
1019 break;
1020 case NO:
1021 (void) atomicio(write, remout, "", 1);
1022 break;
1023 case DISPLAYED:
1024 break;
1025 }
1026 }
1027 screwup:
1028 run_err("protocol error: %s", why);
1029 exit(1);
1030 }
1031
1032 int
response(void)1033 response(void)
1034 {
1035 char ch, *cp, resp, rbuf[2048];
1036
1037 if (atomicio(read, remin, &resp, sizeof (resp)) != sizeof (resp))
1038 lostconn(0);
1039
1040 cp = rbuf;
1041 switch (resp) {
1042 case 0: /* ok */
1043 return (0);
1044 default:
1045 *cp++ = resp;
1046 /* FALLTHROUGH */
1047 case 1: /* error, followed by error msg */
1048 case 2: /* fatal error, "" */
1049 do {
1050 if (atomicio(read, remin, &ch, sizeof (ch)) !=
1051 sizeof (ch))
1052 lostconn(0);
1053 *cp++ = ch;
1054 } while (cp < &rbuf[sizeof (rbuf) - 1] && ch != '\n');
1055
1056 if (!iamremote)
1057 (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf);
1058 ++errs;
1059 if (resp == 1)
1060 return (-1);
1061 exit(1);
1062 }
1063 /* NOTREACHED */
1064 }
1065
1066 void
usage(void)1067 usage(void)
1068 {
1069 (void) fprintf(stderr,
1070 gettext(
1071 "Usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n"
1072 " [-c cipher] [-i identity] [-o option]\n"
1073 " [[user@]host1:]file1 [...] "
1074 "[[user@]host2:]file2\n"));
1075 exit(1);
1076 }
1077
1078 /* PRINTFLIKE1 */
1079 void
run_err(const char * fmt,...)1080 run_err(const char *fmt, ...)
1081 {
1082 static FILE *fp;
1083 va_list ap;
1084
1085 ++errs;
1086
1087 if (!iamremote) {
1088 va_start(ap, fmt);
1089 vfprintf(stderr, fmt, ap);
1090 va_end(ap);
1091 fprintf(stderr, "\n");
1092 }
1093
1094 if (fp == NULL && !(fp = fdopen(remout, "w")))
1095 return;
1096
1097 (void) fprintf(fp, "%c", 0x01);
1098 (void) fprintf(fp, "scp: ");
1099 va_start(ap, fmt);
1100 (void) vfprintf(fp, fmt, ap);
1101 va_end(ap);
1102 (void) fprintf(fp, "\n");
1103 (void) fflush(fp);
1104
1105 }
1106
1107 void
verifydir(cp)1108 verifydir(cp)
1109 char *cp;
1110 {
1111 struct stat stb;
1112
1113 if (!stat(cp, &stb)) {
1114 if (S_ISDIR(stb.st_mode))
1115 return;
1116 errno = ENOTDIR;
1117 }
1118 run_err("%s: %s", cp, strerror(errno));
1119 exit(1);
1120 }
1121
1122 int
okname(cp0)1123 okname(cp0)
1124 char *cp0;
1125 {
1126 int c;
1127 char *cp;
1128
1129 cp = cp0;
1130 do {
1131 c = (int)*cp;
1132 if (c & 0200)
1133 goto bad;
1134 if (!isalpha(c) && !isdigit(c)) {
1135 switch (c) {
1136 case '\'':
1137 case '"':
1138 case '`':
1139 case ' ':
1140 case '#':
1141 goto bad;
1142 default:
1143 break;
1144 }
1145 }
1146 } while (*++cp);
1147 return (1);
1148
1149 bad: fprintf(stderr, gettext("%s: invalid user name\n"), cp0);
1150 return (0);
1151 }
1152
1153 BUF *
allocbuf(bp,fd,blksize)1154 allocbuf(bp, fd, blksize)
1155 BUF *bp;
1156 int fd, blksize;
1157 {
1158 size_t size;
1159 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1160 struct stat stb;
1161
1162 if (fstat(fd, &stb) < 0) {
1163 run_err("fstat: %s", strerror(errno));
1164 return (0);
1165 }
1166 if (stb.st_blksize == 0)
1167 size = blksize;
1168 else
1169 size = blksize + (stb.st_blksize - blksize % stb.st_blksize) %
1170 stb.st_blksize;
1171 #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1172 size = blksize;
1173 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1174 if (bp->cnt >= size)
1175 return (bp);
1176 if (bp->buf == NULL)
1177 bp->buf = xmalloc(size);
1178 else
1179 bp->buf = xrealloc(bp->buf, size);
1180 memset(bp->buf, 0, size);
1181 bp->cnt = size;
1182 return (bp);
1183 }
1184
1185 void
lostconn(signo)1186 lostconn(signo)
1187 int signo;
1188 {
1189 if (!iamremote)
1190 write(STDERR_FILENO, "lost connection\n", 16);
1191 if (signo)
1192 _exit(1);
1193 else
1194 exit(1);
1195 }
1196
1197 static void
updateprogressmeter(int ignore)1198 updateprogressmeter(int ignore)
1199 {
1200 int save_errno = errno;
1201
1202 progressmeter(0);
1203 signal(SIGALRM, updateprogressmeter);
1204 alarm(PROGRESSTIME);
1205 errno = save_errno;
1206 }
1207
1208 static int
foregroundproc(void)1209 foregroundproc(void)
1210 {
1211 static pid_t pgrp = -1;
1212 int ctty_pgrp;
1213
1214 if (pgrp == -1)
1215 pgrp = getpgrp();
1216
1217 #ifdef HAVE_TCGETPGRP
1218 return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 &&
1219 ctty_pgrp == pgrp);
1220 #else
1221 return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
1222 ctty_pgrp == pgrp));
1223 #endif
1224 }
1225
1226 void
progressmeter(int flag)1227 progressmeter(int flag)
1228 {
1229 static const char prefixes[] = " KMGTP";
1230 static struct timeval lastupdate;
1231 static off_t lastsize;
1232 struct timeval now, td, wait;
1233 off_t cursize, abbrevsize;
1234 double elapsed;
1235 int ratio, barlength, i, remaining;
1236 char buf[512];
1237
1238 if (flag == -1) {
1239 (void) gettimeofday(&start, (struct timezone *)0);
1240 lastupdate = start;
1241 lastsize = 0;
1242 }
1243 if (foregroundproc() == 0)
1244 return;
1245
1246 (void) gettimeofday(&now, (struct timezone *)0);
1247 cursize = statbytes;
1248 if (totalbytes != 0) {
1249 ratio = (int)(100.0 * cursize / totalbytes);
1250 ratio = MAX(ratio, 0);
1251 ratio = MIN(ratio, 100);
1252 } else
1253 ratio = 100;
1254
1255 snprintf(buf, sizeof (buf), "\r%-20.20s %3d%% ", curfile, ratio);
1256
1257 barlength = getttywidth() - 51;
1258 if (barlength > 0) {
1259 i = barlength * ratio / 100;
1260 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1261 "|%.*s%*s|", i,
1262 "*******************************************************"
1263 "*******************************************************"
1264 "*******************************************************"
1265 "*******************************************************"
1266 "*******************************************************"
1267 "*******************************************************"
1268 "*******************************************************",
1269 barlength - i, "");
1270 }
1271 i = 0;
1272 abbrevsize = cursize;
1273 while (abbrevsize >= 100000 && i < strlen(prefixes) - 1) {
1274 i++;
1275 abbrevsize >>= 10;
1276 }
1277 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), " %5lu %c%c ",
1278 (unsigned long) abbrevsize, prefixes[i],
1279 prefixes[i] == ' ' ? ' ' : 'B');
1280
1281 timersub(&now, &lastupdate, &wait);
1282 if (cursize > lastsize) {
1283 lastupdate = now;
1284 lastsize = cursize;
1285 if (wait.tv_sec >= STALLTIME) {
1286 start.tv_sec += wait.tv_sec;
1287 start.tv_usec += wait.tv_usec;
1288 }
1289 wait.tv_sec = 0;
1290 }
1291 timersub(&now, &start, &td);
1292 elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
1293
1294 if (flag != 1 &&
1295 (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) {
1296 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1297 " --:-- ETA");
1298 } else if (wait.tv_sec >= STALLTIME) {
1299 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1300 " - stalled -");
1301 } else {
1302 if (flag != 1)
1303 remaining = (int)(totalbytes / (statbytes / elapsed) -
1304 elapsed);
1305 else
1306 remaining = (int)elapsed;
1307
1308 i = remaining / 3600;
1309 if (i)
1310 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1311 "%2d:", i);
1312 else
1313 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1314 " ");
1315 i = remaining % 3600;
1316 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1317 "%02d:%02d%s", i / 60, i % 60,
1318 (flag != 1) ? " ETA" : " ");
1319 }
1320 atomicio(write, fileno(stdout), buf, strlen(buf));
1321
1322 if (flag == -1) {
1323 mysignal(SIGALRM, updateprogressmeter);
1324 alarm(PROGRESSTIME);
1325 } else if (flag == 1) {
1326 alarm(0);
1327 atomicio(write, fileno(stdout), "\n", 1);
1328 statbytes = 0;
1329 }
1330 }
1331
1332 int
getttywidth(void)1333 getttywidth(void)
1334 {
1335 struct winsize winsize;
1336
1337 if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
1338 return (winsize.ws_col ? winsize.ws_col : 80);
1339 else
1340 return (80);
1341 }
1342