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