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