Home | History | Annotate | Download | only in scp
      1 /*
      2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 /*
      6  * scp - secure remote copy.  This is basically patched BSD rcp which
      7  * uses ssh to do the data transfer (instead of using rcmd).
      8  *
      9  * NOTE: This version should NOT be suid root.  (This uses ssh to
     10  * do the transfer and ssh has the necessary privileges.)
     11  *
     12  * 1995 Timo Rinne <tri (at) iki.fi>, Tatu Ylonen <ylo (at) cs.hut.fi>
     13  *
     14  * As far as I am concerned, the code I have written for this software
     15  * can be used freely for any purpose.  Any derived versions of this
     16  * software must be clearly marked as such, and if the derived work is
     17  * incompatible with the protocol description in the RFC file, it must be
     18  * called by a name other than "ssh" or "Secure Shell".
     19  */
     20 /*
     21  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
     22  * Copyright (c) 1999 Aaron Campbell.  All rights reserved.
     23  *
     24  * Redistribution and use in source and binary forms, with or without
     25  * modification, are permitted provided that the following conditions
     26  * are met:
     27  * 1. Redistributions of source code must retain the above copyright
     28  *    notice, this list of conditions and the following disclaimer.
     29  * 2. Redistributions in binary form must reproduce the above copyright
     30  *    notice, this list of conditions and the following disclaimer in the
     31  *    documentation and/or other materials provided with the distribution.
     32  *
     33  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     34  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     36  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     37  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     38  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     39  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     40  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     41  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     42  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     43  */
     44 
     45 /*
     46  * Parts from:
     47  *
     48  * Copyright (c) 1983, 1990, 1992, 1993, 1995
     49  *	The Regents of the University of California.  All rights reserved.
     50  *
     51  * Redistribution and use in source and binary forms, with or without
     52  * modification, are permitted provided that the following conditions
     53  * are met:
     54  * 1. Redistributions of source code must retain the above copyright
     55  *    notice, this list of conditions and the following disclaimer.
     56  * 2. Redistributions in binary form must reproduce the above copyright
     57  *    notice, this list of conditions and the following disclaimer in the
     58  *    documentation and/or other materials provided with the distribution.
     59  * 3. All advertising materials mentioning features or use of this software
     60  *    must display the following acknowledgement:
     61  *	This product includes software developed by the University of
     62  *	California, Berkeley and its contributors.
     63  * 4. Neither the name of the University nor the names of its contributors
     64  *    may be used to endorse or promote products derived from this software
     65  *    without specific prior written permission.
     66  *
     67  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     68  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     69  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     70  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     71  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     72  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     73  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     74  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     75  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     76  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     77  * SUCH DAMAGE.
     78  *
     79  */
     80 
     81 #include "includes.h"
     82 RCSID("$OpenBSD: scp.c,v 1.91 2002/06/19 00:27:55 deraadt Exp $");
     83 
     84 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     85 
     86 #include "xmalloc.h"
     87 #include "atomicio.h"
     88 #include "pathnames.h"
     89 #include "log.h"
     90 #include "misc.h"
     91 
     92 #ifdef HAVE___PROGNAME
     93 extern char *__progname;
     94 #else
     95 char *__progname;
     96 #endif
     97 
     98 /* For progressmeter() -- number of seconds before xfer considered "stalled" */
     99 #define	STALLTIME	5
    100 /* alarm() interval for updating progress meter */
    101 #define	PROGRESSTIME	1
    102 
    103 /* Visual statistics about files as they are transferred. */
    104 void progressmeter(int);
    105 
    106 /* Returns width of the terminal (for progress meter calculations). */
    107 int getttywidth(void);
    108 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout,
    109     int argc);
    110 
    111 /* Struct for addargs */
    112 arglist args;
    113 
    114 /* Time a transfer started. */
    115 static struct timeval start;
    116 
    117 /* Number of bytes of current file transferred so far. */
    118 volatile off_t statbytes;
    119 
    120 /* Total size of current file. */
    121 off_t totalbytes = 0;
    122 
    123 /* Name of current file being transferred. */
    124 char *curfile;
    125 
    126 /* This is set to non-zero to enable verbose mode. */
    127 int verbose_mode = 0;
    128 
    129 /* This is set to zero if the progressmeter is not desired. */
    130 int showprogress = 1;
    131 
    132 /* This is the program to execute for the secured connection. ("ssh" or -S) */
    133 char *ssh_program = _PATH_SSH_PROGRAM;
    134 
    135 /* This is used to store the pid of ssh_program */
    136 static pid_t do_cmd_pid = -1;
    137 
    138 static void
    139 killchild(int signo)
    140 {
    141 	if (do_cmd_pid > 1) {
    142 		kill(do_cmd_pid, signo ? signo : SIGTERM);
    143 		waitpid(do_cmd_pid, NULL, 0);
    144 	}
    145 
    146 	if (signo)
    147 		_exit(1);
    148 	exit(1);
    149 }
    150 
    151 /*
    152  * Run a command via fork(2)/exec(2). This can be a local-to-local copy via
    153  * cp(1) or one side of a remote-to-remote copy. We must not use system(3) here
    154  * because we don't want filenames to go through a command expansion in the
    155  * underlying shell. Note that the user can create a filename that is a piece of
    156  * shell code itself and this must not be executed.
    157  */
    158 static int
    159 do_local_cmd(arglist *a)
    160 {
    161 	uint_t i;
    162 	int status;
    163 	pid_t pid;
    164 
    165 	if (a->num == 0)
    166 		fatal("do_local_cmd: no arguments");
    167 
    168 	if (verbose_mode) {
    169 		fprintf(stderr, gettext("Executing:"));
    170 		for (i = 0; i < a->num; i++)
    171 			fprintf(stderr, " %s", a->list[i]);
    172 		fprintf(stderr, "\n");
    173 	}
    174 	if ((pid = fork()) == -1)
    175 		fatal("do_local_cmd: fork: %s", strerror(errno));
    176 
    177 	if (pid == 0) {
    178 		execvp(a->list[0], a->list);
    179 		perror(a->list[0]);
    180 		exit(1);
    181 	}
    182 
    183 	do_cmd_pid = pid;
    184 	signal(SIGTERM, killchild);
    185 	signal(SIGINT, killchild);
    186 	signal(SIGHUP, killchild);
    187 
    188 	while (waitpid(pid, &status, 0) == -1)
    189 		if (errno != EINTR)
    190 			fatal("do_local_cmd: waitpid: %s", strerror(errno));
    191 
    192 	do_cmd_pid = -1;
    193 
    194 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
    195 		return (-1);
    196 
    197 	return (0);
    198 }
    199 
    200 /*
    201  * This function executes the given command as the specified user on the
    202  * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
    203  * assigns the input and output file descriptors on success.
    204  */
    205 
    206 int
    207 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
    208 {
    209 	int pin[2], pout[2], reserved[2];
    210 
    211 	if (verbose_mode)
    212 		fprintf(stderr,
    213 			gettext("Executing: program %s host %s, "
    214 				"user %s, command %s\n"),
    215 			ssh_program, host,
    216 			remuser ? remuser : gettext("(unspecified)"), cmd);
    217 
    218 	/*
    219 	 * Reserve two descriptors so that the real pipes won't get
    220 	 * descriptors 0 and 1 because that will screw up dup2 below.
    221 	 */
    222 	pipe(reserved);
    223 
    224 	/* Create a socket pair for communicating with ssh. */
    225 	if (pipe(pin) < 0)
    226 		fatal("pipe: %s", strerror(errno));
    227 	if (pipe(pout) < 0)
    228 		fatal("pipe: %s", strerror(errno));
    229 
    230 	/* Free the reserved descriptors. */
    231 	close(reserved[0]);
    232 	close(reserved[1]);
    233 
    234 	/* For a child to execute the command on the remote host using ssh. */
    235 	if ((do_cmd_pid = fork()) == 0)  {
    236 		/* Child. */
    237 		close(pin[1]);
    238 		close(pout[0]);
    239 		dup2(pin[0], 0);
    240 		dup2(pout[1], 1);
    241 		close(pin[0]);
    242 		close(pout[1]);
    243 
    244 		args.list[0] = ssh_program;
    245 		if (remuser != NULL)
    246 			addargs(&args, "-l%s", remuser);
    247 		addargs(&args, "%s", host);
    248 		addargs(&args, "%s", cmd);
    249 
    250 		execvp(ssh_program, args.list);
    251 		perror(ssh_program);
    252 		exit(1);
    253 	} else if (do_cmd_pid == (pid_t)-1) {
    254 		/* fork() failed */
    255 		fatal("fork: %s", strerror(errno));
    256 	}
    257 
    258 	/* Parent.  Close the other side, and return the local side. */
    259 	close(pin[0]);
    260 	*fdout = pin[1];
    261 	close(pout[1]);
    262 	*fdin = pout[0];
    263 	return (0);
    264 }
    265 
    266 typedef struct {
    267 	int cnt;
    268 	char *buf;
    269 } BUF;
    270 
    271 BUF *allocbuf(BUF *, int, int);
    272 void lostconn(int);
    273 void nospace(void);
    274 int okname(char *);
    275 void run_err(const char *, ...);
    276 void verifydir(char *);
    277 
    278 struct passwd *pwd;
    279 uid_t userid;
    280 int errs, remin, remout;
    281 int pflag, iamremote, iamrecursive, targetshouldbedirectory;
    282 
    283 #define	CMDNEEDS	64
    284 char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
    285 
    286 int response(void);
    287 void rsource(char *, struct stat *);
    288 void sink(int, char *[]);
    289 void source(int, char *[]);
    290 void tolocal(int, char *[]);
    291 void toremote(char *, int, char *[]);
    292 void usage(void);
    293 
    294 int
    295 main(argc, argv)
    296 	int argc;
    297 	char *argv[];
    298 {
    299 	int ch, fflag, tflag, status;
    300 	char *targ;
    301 	extern char *optarg;
    302 	extern int optind;
    303 
    304 	__progname = get_progname(argv[0]);
    305 
    306 	g11n_setlocale(LC_ALL, "");
    307 
    308 	args.list = NULL;
    309 	addargs(&args, "ssh");		/* overwritten with ssh_program */
    310 	addargs(&args, "-x");
    311 	addargs(&args, "-oForwardAgent no");
    312 	addargs(&args, "-oClearAllForwardings yes");
    313 
    314 	fflag = tflag = 0;
    315 	while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1)
    316 		switch (ch) {
    317 		/* User-visible flags. */
    318 		case '4':
    319 		case '6':
    320 		case 'C':
    321 			addargs(&args, "-%c", ch);
    322 			break;
    323 		case 'o':
    324 		case 'c':
    325 		case 'i':
    326 		case 'F':
    327 			addargs(&args, "-%c%s", ch, optarg);
    328 			break;
    329 		case 'P':
    330 			addargs(&args, "-p%s", optarg);
    331 			break;
    332 		case 'B':
    333 			addargs(&args, "-oBatchmode yes");
    334 			break;
    335 		case 'p':
    336 			pflag = 1;
    337 			break;
    338 		case 'r':
    339 			iamrecursive = 1;
    340 			break;
    341 		case 'S':
    342 			ssh_program = xstrdup(optarg);
    343 			break;
    344 		case 'v':
    345 			addargs(&args, "-v");
    346 			verbose_mode = 1;
    347 			break;
    348 		case 'q':
    349 			showprogress = 0;
    350 			break;
    351 
    352 		/* Server options. */
    353 		case 'd':
    354 			targetshouldbedirectory = 1;
    355 			break;
    356 		case 'f':	/* "from" */
    357 			iamremote = 1;
    358 			fflag = 1;
    359 			break;
    360 		case 't':	/* "to" */
    361 			iamremote = 1;
    362 			tflag = 1;
    363 #ifdef HAVE_CYGWIN
    364 			setmode(0, O_BINARY);
    365 #endif
    366 			break;
    367 		default:
    368 			usage();
    369 		}
    370 	argc -= optind;
    371 	argv += optind;
    372 
    373 	if ((pwd = getpwuid(userid = getuid())) == NULL)
    374 		fatal("unknown user %d", (int)userid);
    375 
    376 	if (!isatty(STDERR_FILENO))
    377 		showprogress = 0;
    378 
    379 	remin = STDIN_FILENO;
    380 	remout = STDOUT_FILENO;
    381 
    382 	if (fflag) {
    383 		/* Follow "protocol", send data. */
    384 		(void) response();
    385 		source(argc, argv);
    386 		exit(errs != 0);
    387 	}
    388 	if (tflag) {
    389 		/* Receive data. */
    390 		sink(argc, argv);
    391 		exit(errs != 0);
    392 	}
    393 	if (argc < 2)
    394 		usage();
    395 	if (argc > 2)
    396 		targetshouldbedirectory = 1;
    397 
    398 	remin = remout = -1;
    399 	do_cmd_pid = (pid_t)-1;
    400 
    401 	/* Command to be executed on remote system using "ssh". */
    402 	(void) snprintf(cmd, sizeof (cmd), "scp%s%s%s%s",
    403 	    verbose_mode ? " -v" : "",
    404 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
    405 	    targetshouldbedirectory ? " -d" : "");
    406 
    407 	(void) signal(SIGPIPE, lostconn);
    408 
    409 	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
    410 		toremote(targ, argc, argv);
    411 	else {
    412 		if (targetshouldbedirectory)
    413 			verifydir(argv[argc - 1]);
    414 		tolocal(argc, argv);	/* Dest is local host. */
    415 	}
    416 	/*
    417 	 * Finally check the exit status of the ssh process, if one was forked
    418 	 * and no error has occurred yet
    419 	 */
    420 	if (do_cmd_pid != (pid_t)-1 && errs == 0) {
    421 		if (remin != -1) {
    422 			(void) close(remin);
    423 		}
    424 		if (remout != -1) {
    425 			(void) close(remout);
    426 		}
    427 		if (waitpid(do_cmd_pid, &status, 0) == -1) {
    428 			errs = 1;
    429 		} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    430 			errs = 1;
    431 		}
    432 	}
    433 
    434 	return (errs != 0);
    435 }
    436 
    437 void
    438 toremote(targ, argc, argv)
    439 	char *targ, *argv[];
    440 	int argc;
    441 {
    442 	int i, len;
    443 	char *bp, *host, *src, *suser, *thost, *tuser, *arg;
    444 	arglist alist;
    445 
    446 	memset(&alist, '\0', sizeof (alist));
    447 	alist.list = NULL;
    448 
    449 	*targ++ = 0;
    450 	if (*targ == 0)
    451 		targ = ".";
    452 
    453 	arg = xstrdup(argv[argc - 1]);
    454 	if ((thost = strchr(arg, '@'))) {
    455 		/* user@host */
    456 		*thost++ = 0;
    457 		tuser = arg;
    458 		if (*tuser == '\0')
    459 			tuser = NULL;
    460 		else if (!okname(tuser))
    461 			exit(1);
    462 	} else {
    463 		thost = arg;
    464 		tuser = NULL;
    465 	}
    466 
    467 	if (tuser != NULL && !okname(tuser)) {
    468 		xfree(arg);
    469 		return;
    470 	}
    471 
    472 	for (i = 0; i < argc - 1; i++) {
    473 		src = colon(argv[i]);
    474 		if (src) {	/* remote to remote */
    475 			freeargs(&alist);
    476 			addargs(&alist, "%s", ssh_program);
    477 			if (verbose_mode)
    478 				addargs(&alist, "-v");
    479 			addargs(&alist, "-x");
    480 			addargs(&alist, "-oClearAllForwardings yes");
    481 			addargs(&alist, "-n");
    482 
    483 			*src++ = 0;
    484 			if (*src == 0)
    485 				src = ".";
    486 			host = strchr(argv[i], '@');
    487 
    488 			if (host) {
    489 				*host++ = 0;
    490 				host = cleanhostname(host);
    491 				suser = argv[i];
    492 				if (*suser == '\0')
    493 					suser = pwd->pw_name;
    494 				else if (!okname(suser))
    495 					continue;
    496 				addargs(&alist, "-l");
    497 				addargs(&alist, "%s", suser);
    498 			} else {
    499 				host = cleanhostname(argv[i]);
    500 			}
    501 			addargs(&alist, "%s", host);
    502 			addargs(&alist, "%s", cmd);
    503 			addargs(&alist, "%s", src);
    504 			addargs(&alist, "%s%s%s:%s",
    505 				    tuser ? tuser : "", tuser ? "@" : "",
    506 				    thost, targ);
    507 			if (do_local_cmd(&alist) != 0)
    508 				errs = 1;
    509 		} else {	/* local to remote */
    510 			if (remin == -1) {
    511 				len = strlen(targ) + CMDNEEDS + 20;
    512 				bp = xmalloc(len);
    513 				(void) snprintf(bp, len, "%s -t %s", cmd, targ);
    514 				host = cleanhostname(thost);
    515 				if (do_cmd(host, tuser, bp, &remin,
    516 				    &remout, argc) < 0)
    517 					exit(1);
    518 				if (response() < 0)
    519 					exit(1);
    520 				(void) xfree(bp);
    521 			}
    522 			source(1, argv + i);
    523 		}
    524 	}
    525 }
    526 
    527 void
    528 tolocal(argc, argv)
    529 	int argc;
    530 	char *argv[];
    531 {
    532 	int i, len;
    533 	char *bp, *host, *src, *suser;
    534 	arglist alist;
    535 
    536 	memset(&alist, '\0', sizeof (alist));
    537 	alist.list = NULL;
    538 
    539 	for (i = 0; i < argc - 1; i++) {
    540 		if (!(src = colon(argv[i]))) {	/* Local to local. */
    541 			freeargs(&alist);
    542 			addargs(&alist, "%s", _PATH_CP);
    543 			if (iamrecursive)
    544 				addargs(&alist, "-r");
    545 			if (pflag)
    546 				addargs(&alist, "-p");
    547 			addargs(&alist, "%s", argv[i]);
    548 			addargs(&alist, "%s", argv[argc-1]);
    549 			if (do_local_cmd(&alist))
    550 				++errs;
    551 			continue;
    552 		}
    553 		*src++ = 0;
    554 		if (*src == 0)
    555 			src = ".";
    556 		if ((host = strchr(argv[i], '@')) == NULL) {
    557 			host = argv[i];
    558 			suser = NULL;
    559 		} else {
    560 			*host++ = 0;
    561 			suser = argv[i];
    562 			if (*suser == '\0')
    563 				suser = pwd->pw_name;
    564 			else if (!okname(suser))
    565 				continue;
    566 		}
    567 		host = cleanhostname(host);
    568 		len = strlen(src) + CMDNEEDS + 20;
    569 		bp = xmalloc(len);
    570 		(void) snprintf(bp, len, "%s -f %s", cmd, src);
    571 		if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
    572 			(void) xfree(bp);
    573 			++errs;
    574 			continue;
    575 		}
    576 		xfree(bp);
    577 		sink(1, argv + argc - 1);
    578 		(void) close(remin);
    579 		remin = remout = -1;
    580 	}
    581 }
    582 
    583 void
    584 source(argc, argv)
    585 	int argc;
    586 	char *argv[];
    587 {
    588 	struct stat stb;
    589 	static BUF buffer;
    590 	BUF *bp;
    591 	off_t i, amt, result;
    592 	int fd, haderr, indx;
    593 	char *last, *name, buf[2048];
    594 	int len;
    595 
    596 	for (indx = 0; indx < argc; ++indx) {
    597 		name = argv[indx];
    598 		statbytes = 0;
    599 		len = strlen(name);
    600 		while (len > 1 && name[len-1] == '/')
    601 			name[--len] = '\0';
    602 		if (strchr(name, '\n') != NULL) {
    603 			run_err("%s: skipping, filename contains a newline",
    604 			    name);
    605 			goto next;
    606 		}
    607 		if ((fd = open(name, O_RDONLY, 0)) < 0)
    608 			goto syserr;
    609 		if (fstat(fd, &stb) < 0) {
    610 syserr:			run_err("%s: %s", name, strerror(errno));
    611 			goto next;
    612 		}
    613 		switch (stb.st_mode & S_IFMT) {
    614 		case S_IFREG:
    615 			break;
    616 		case S_IFDIR:
    617 			if (iamrecursive) {
    618 				rsource(name, &stb);
    619 				goto next;
    620 			}
    621 			/* FALLTHROUGH */
    622 		default:
    623 			run_err("%s: not a regular file", name);
    624 			goto next;
    625 		}
    626 		if ((last = strrchr(name, '/')) == NULL)
    627 			last = name;
    628 		else
    629 			++last;
    630 		curfile = last;
    631 		if (pflag) {
    632 			/*
    633 			 * Make it compatible with possible future
    634 			 * versions expecting microseconds.
    635 			 */
    636 			(void) snprintf(buf, sizeof (buf), "T%lu 0 %lu 0\n",
    637 			    (ulong_t)stb.st_mtime,
    638 			    (ulong_t)stb.st_atime);
    639 			(void) atomicio(write, remout, buf, strlen(buf));
    640 			if (response() < 0)
    641 				goto next;
    642 		}
    643 #define	FILEMODEMASK	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
    644 #ifdef HAVE_LONG_LONG_INT
    645 		snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
    646 		    (uint_t)(stb.st_mode & FILEMODEMASK),
    647 		    (long long)stb.st_size, last);
    648 #else
    649 		/* XXX: Handle integer overflow? */
    650 		snprintf(buf, sizeof (buf), "C%04o %lu %s\n",
    651 		    (uint_t)(stb.st_mode & FILEMODEMASK),
    652 		    (ulong_t)stb.st_size, last);
    653 #endif
    654 		if (verbose_mode) {
    655 			fprintf(stderr, gettext("Sending file modes: %s"), buf);
    656 			fflush(stderr);
    657 		}
    658 		(void) atomicio(write, remout, buf, strlen(buf));
    659 		if (response() < 0)
    660 			goto next;
    661 		if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
    662 next:			(void) close(fd);
    663 			continue;
    664 		}
    665 		if (showprogress) {
    666 			totalbytes = stb.st_size;
    667 			progressmeter(-1);
    668 		}
    669 		/* Keep writing after an error so that we stay sync'd up. */
    670 		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
    671 			amt = bp->cnt;
    672 			if (i + amt > stb.st_size)
    673 				amt = stb.st_size - i;
    674 			if (!haderr) {
    675 				result = atomicio(read, fd, bp->buf, amt);
    676 				if (result != amt)
    677 					haderr = result >= 0 ? EIO : errno;
    678 			}
    679 			if (haderr)
    680 				(void) atomicio(write, remout, bp->buf, amt);
    681 			else {
    682 				result = atomicio(write, remout, bp->buf, amt);
    683 				if (result != amt)
    684 					haderr = result >= 0 ? EIO : errno;
    685 				statbytes += result;
    686 			}
    687 		}
    688 		if (showprogress)
    689 			progressmeter(1);
    690 
    691 		if (close(fd) < 0 && !haderr)
    692 			haderr = errno;
    693 		if (!haderr)
    694 			(void) atomicio(write, remout, "", 1);
    695 		else
    696 			run_err("%s: %s", name, strerror(haderr));
    697 		(void) response();
    698 	}
    699 }
    700 
    701 void
    702 rsource(name, statp)
    703 	char *name;
    704 	struct stat *statp;
    705 {
    706 	DIR *dirp;
    707 	struct dirent *dp;
    708 	char *last, *vect[1], path[1100];
    709 
    710 	if (!(dirp = opendir(name))) {
    711 		run_err("%s: %s", name, strerror(errno));
    712 		return;
    713 	}
    714 	last = strrchr(name, '/');
    715 	if (last == 0)
    716 		last = name;
    717 	else
    718 		last++;
    719 	if (pflag) {
    720 		(void) snprintf(path, sizeof (path), "T%lu 0 %lu 0\n",
    721 		    (ulong_t)statp->st_mtime,
    722 		    (ulong_t)statp->st_atime);
    723 		(void) atomicio(write, remout, path, strlen(path));
    724 		if (response() < 0) {
    725 			closedir(dirp);
    726 			return;
    727 		}
    728 	}
    729 	(void) snprintf(path, sizeof (path), "D%04o %d %.1024s\n",
    730 	    (uint_t)(statp->st_mode & FILEMODEMASK), 0, last);
    731 	if (verbose_mode)
    732 		fprintf(stderr, gettext("Entering directory: %s"), path);
    733 	(void) atomicio(write, remout, path, strlen(path));
    734 	if (response() < 0) {
    735 		closedir(dirp);
    736 		return;
    737 	}
    738 	while ((dp = readdir(dirp)) != NULL) {
    739 		if (dp->d_ino == 0)
    740 			continue;
    741 		if ((strcmp(dp->d_name, ".") == 0) ||
    742 		    (strcmp(dp->d_name, "..") == 0))
    743 			continue;
    744 		if (strlen(name) + 1 + strlen(dp->d_name) >=
    745 		    sizeof (path) - 1) {
    746 			run_err("%s/%s: name too long", name, dp->d_name);
    747 			continue;
    748 		}
    749 		(void) snprintf(path, sizeof (path), "%s/%s", name, dp->d_name);
    750 		vect[0] = path;
    751 		source(1, vect);
    752 	}
    753 	(void) closedir(dirp);
    754 	(void) atomicio(write, remout, "E\n", 2);
    755 	(void) response();
    756 }
    757 
    758 void
    759 sink(argc, argv)
    760 	int argc;
    761 	char *argv[];
    762 {
    763 	static BUF buffer;
    764 	struct stat stb;
    765 	enum {
    766 		YES, NO, DISPLAYED
    767 	} wrerr;
    768 	BUF *bp;
    769 	off_t i, j;
    770 	int amt, count, exists, first, mask, mode, ofd, omode;
    771 	off_t size;
    772 	int setimes, targisdir, wrerrno = 0;
    773 	char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
    774 	struct timeval tv[2];
    775 
    776 #define	atime	tv[0]
    777 #define	mtime	tv[1]
    778 #define	SCREWUP(str)	{ why = str; goto screwup; }
    779 
    780 	setimes = targisdir = 0;
    781 	mask = umask(0);
    782 	if (!pflag)
    783 		(void) umask(mask);
    784 	if (argc != 1) {
    785 		run_err("ambiguous target");
    786 		exit(1);
    787 	}
    788 	targ = *argv;
    789 	if (targetshouldbedirectory)
    790 		verifydir(targ);
    791 
    792 	(void) atomicio(write, remout, "", 1);
    793 	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
    794 		targisdir = 1;
    795 	for (first = 1; ; first = 0) {
    796 		cp = buf;
    797 		if (atomicio(read, remin, cp, 1) <= 0)
    798 			return;
    799 		if (*cp++ == '\n')
    800 			SCREWUP("unexpected <newline>")
    801 		do {
    802 			if (atomicio(read, remin, &ch, sizeof (ch)) !=
    803 			    sizeof (ch))
    804 				SCREWUP("lost connection")
    805 			*cp++ = ch;
    806 		} while (cp < &buf[sizeof (buf) - 1] && ch != '\n');
    807 		*cp = 0;
    808 
    809 		if (buf[0] == '\01' || buf[0] == '\02') {
    810 			if (iamremote == 0)
    811 				(void) atomicio(write, STDERR_FILENO,
    812 				    buf + 1, strlen(buf + 1));
    813 			if (buf[0] == '\02')
    814 				exit(1);
    815 			++errs;
    816 			continue;
    817 		}
    818 		if (buf[0] == 'E') {
    819 			(void) atomicio(write, remout, "", 1);
    820 			return;
    821 		}
    822 		if (ch == '\n')
    823 			*--cp = 0;
    824 
    825 		cp = buf;
    826 		if (*cp == 'T') {
    827 			setimes++;
    828 			cp++;
    829 			mtime.tv_sec = strtol(cp, &cp, 10);
    830 			if (!cp || *cp++ != ' ')
    831 				SCREWUP("mtime.sec not delimited")
    832 			mtime.tv_usec = strtol(cp, &cp, 10);
    833 			if (!cp || *cp++ != ' ')
    834 				SCREWUP("mtime.usec not delimited")
    835 			atime.tv_sec = strtol(cp, &cp, 10);
    836 			if (!cp || *cp++ != ' ')
    837 				SCREWUP("atime.sec not delimited")
    838 			atime.tv_usec = strtol(cp, &cp, 10);
    839 			if (!cp || *cp++ != '\0')
    840 				SCREWUP("atime.usec not delimited")
    841 			(void) atomicio(write, remout, "", 1);
    842 			continue;
    843 		}
    844 		if (*cp != 'C' && *cp != 'D') {
    845 			/*
    846 			 * Check for the case "rcp remote:foo\* local:bar".
    847 			 * In this case, the line "No match." can be returned
    848 			 * by the shell before the rcp command on the remote is
    849 			 * executed so the ^Aerror_message convention isn't
    850 			 * followed.
    851 			 */
    852 			if (first) {
    853 				run_err("%s", cp);
    854 				exit(1);
    855 			}
    856 			SCREWUP("expected control record")
    857 		}
    858 		mode = 0;
    859 		for (++cp; cp < buf + 5; cp++) {
    860 			if (*cp < '0' || *cp > '7')
    861 				SCREWUP("bad mode")
    862 			mode = (mode << 3) | (*cp - '0');
    863 		}
    864 		if (*cp++ != ' ')
    865 			SCREWUP("mode not delimited")
    866 
    867 		for (size = 0; isdigit(*cp); )
    868 			size = size * 10 + (*cp++ - '0');
    869 		if (*cp++ != ' ')
    870 			SCREWUP("size not delimited")
    871 		if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
    872 			run_err("error: unexpected filename: %s", cp);
    873 			exit(1);
    874 		}
    875 		if (targisdir) {
    876 			static char *namebuf;
    877 			static int cursize;
    878 			size_t need;
    879 
    880 			need = strlen(targ) + strlen(cp) + 250;
    881 			if (need > cursize) {
    882 				if (namebuf)
    883 					xfree(namebuf);
    884 				namebuf = xmalloc(need);
    885 				cursize = need;
    886 			}
    887 			(void) snprintf(namebuf, need, "%s%s%s", targ,
    888 			    strcmp(targ, "/") ? "/" : "", cp);
    889 			np = namebuf;
    890 		} else
    891 			np = targ;
    892 		curfile = cp;
    893 		exists = stat(np, &stb) == 0;
    894 		if (buf[0] == 'D') {
    895 			int mod_flag = pflag;
    896 			if (!iamrecursive)
    897 				SCREWUP("received directory without -r");
    898 			if (exists) {
    899 				if (!S_ISDIR(stb.st_mode)) {
    900 					errno = ENOTDIR;
    901 					goto bad;
    902 				}
    903 				if (pflag)
    904 					(void) chmod(np, mode);
    905 			} else {
    906 				/*
    907 				 * Handle copying from a read-only
    908 				 * directory
    909 				 */
    910 				mod_flag = 1;
    911 				if (mkdir(np, mode | S_IRWXU) < 0)
    912 					goto bad;
    913 			}
    914 			vect[0] = xstrdup(np);
    915 			sink(1, vect);
    916 			if (setimes) {
    917 				setimes = 0;
    918 				if (utimes(vect[0], tv) < 0)
    919 					run_err("%s: set times: %s",
    920 					    vect[0], strerror(errno));
    921 			}
    922 			if (mod_flag)
    923 				(void) chmod(vect[0], mode);
    924 			if (vect[0])
    925 				xfree(vect[0]);
    926 			continue;
    927 		}
    928 		omode = mode;
    929 		mode |= S_IWRITE;
    930 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
    931 bad:			run_err("%s: %s", np, strerror(errno));
    932 			continue;
    933 		}
    934 		(void) atomicio(write, remout, "", 1);
    935 		if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
    936 			(void) close(ofd);
    937 			continue;
    938 		}
    939 		wrerr = NO;
    940 
    941 		if (showprogress) {
    942 			totalbytes = size;
    943 			progressmeter(-1);
    944 		}
    945 		statbytes = 0;
    946 		for (i = 0; i < size; i += bp->cnt) {
    947 			amt = bp->cnt;
    948 			cp = bp->buf;
    949 			if (i + amt > size)
    950 				amt = size - i;
    951 			count = amt;
    952 			do {
    953 				j = read(remin, cp, amt);
    954 				if (j == -1 && (errno == EINTR ||
    955 				    errno == EAGAIN)) {
    956 					continue;
    957 				} else if (j <= 0) {
    958 					run_err("%s", j ? strerror(errno) :
    959 					    "dropped connection");
    960 					exit(1);
    961 				}
    962 				amt -= j;
    963 				cp += j;
    964 				statbytes += j;
    965 			} while (amt > 0);
    966 			/* Keep reading so we stay sync'd up. */
    967 			if (wrerr == NO) {
    968 				j = atomicio(write, ofd, bp->buf,
    969 				    count);
    970 				if (j != count) {
    971 					wrerr = YES;
    972 					wrerrno = j >= 0 ? EIO : errno;
    973 				}
    974 			}
    975 		}
    976 		if (showprogress)
    977 			progressmeter(1);
    978 		if (ftruncate(ofd, size)) {
    979 			run_err("%s: truncate: %s", np, strerror(errno));
    980 			wrerr = DISPLAYED;
    981 		}
    982 		if (pflag) {
    983 			if (exists || omode != mode)
    984 #ifdef HAVE_FCHMOD
    985 				if (fchmod(ofd, omode)) {
    986 #else /* HAVE_FCHMOD */
    987 				if (chmod(np, omode)) {
    988 #endif /* HAVE_FCHMOD */
    989 					run_err("%s: set mode: %s",
    990 					    np, strerror(errno));
    991 					wrerr = DISPLAYED;
    992 				}
    993 		} else {
    994 			if (!exists && omode != mode)
    995 #ifdef HAVE_FCHMOD
    996 				if (fchmod(ofd, omode & ~mask)) {
    997 #else /* HAVE_FCHMOD */
    998 				if (chmod(np, omode & ~mask)) {
    999 #endif /* HAVE_FCHMOD */
   1000 					run_err("%s: set mode: %s",
   1001 					    np, strerror(errno));
   1002 					wrerr = DISPLAYED;
   1003 				}
   1004 		}
   1005 		if (close(ofd) == -1) {
   1006 			wrerr = YES;
   1007 			wrerrno = errno;
   1008 		}
   1009 		(void) response();
   1010 		if (setimes && wrerr == NO) {
   1011 			setimes = 0;
   1012 			if (utimes(np, tv) < 0) {
   1013 				run_err("%s: set times: %s",
   1014 				    np, strerror(errno));
   1015 				wrerr = DISPLAYED;
   1016 			}
   1017 		}
   1018 		switch (wrerr) {
   1019 		case YES:
   1020 			run_err("%s: %s", np, strerror(wrerrno));
   1021 			break;
   1022 		case NO:
   1023 			(void) atomicio(write, remout, "", 1);
   1024 			break;
   1025 		case DISPLAYED:
   1026 			break;
   1027 		}
   1028 	}
   1029 screwup:
   1030 	run_err("protocol error: %s", why);
   1031 	exit(1);
   1032 }
   1033 
   1034 int
   1035 response(void)
   1036 {
   1037 	char ch, *cp, resp, rbuf[2048];
   1038 
   1039 	if (atomicio(read, remin, &resp, sizeof (resp)) != sizeof (resp))
   1040 		lostconn(0);
   1041 
   1042 	cp = rbuf;
   1043 	switch (resp) {
   1044 	case 0:		/* ok */
   1045 		return (0);
   1046 	default:
   1047 		*cp++ = resp;
   1048 		/* FALLTHROUGH */
   1049 	case 1:		/* error, followed by error msg */
   1050 	case 2:		/* fatal error, "" */
   1051 		do {
   1052 			if (atomicio(read, remin, &ch, sizeof (ch)) !=
   1053 			    sizeof (ch))
   1054 				lostconn(0);
   1055 			*cp++ = ch;
   1056 		} while (cp < &rbuf[sizeof (rbuf) - 1] && ch != '\n');