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