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