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