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