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