rcp.c revision 178825
11541Srgrimes/*
21541Srgrimes * Copyright (c) 1983, 1990, 1992, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes * 3. All advertising materials mentioning features or use of this software
141541Srgrimes *    must display the following acknowledgement:
151541Srgrimes *	This product includes software developed by the University of
161541Srgrimes *	California, Berkeley and its contributors.
171541Srgrimes * 4. Neither the name of the University nor the names of its contributors
181541Srgrimes *    may be used to endorse or promote products derived from this software
191541Srgrimes *    without specific prior written permission.
201541Srgrimes *
211541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311541Srgrimes * SUCH DAMAGE.
321541Srgrimes */
331541Srgrimes
341541Srgrimes#include "rcp_locl.h"
351541Srgrimes#include <getarg.h>
361541Srgrimes
371541Srgrimes#define RSH_PROGRAM "rsh"
381541Srgrimes
391541Srgrimesstruct  passwd *pwd;
401541Srgrimesuid_t	userid;
411541Srgrimesint     errs, remin, remout;
421541Srgrimesint     pflag, iamremote, iamrecursive, targetshouldbedirectory;
431541Srgrimesint     doencrypt, noencrypt;
441541Srgrimesint     usebroken, usekrb4, usekrb5, forwardtkt;
451541Srgrimeschar    *port;
461541Srgrimesint     eflag = 0;
471541Srgrimes
481541Srgrimes#define	CMDNEEDS	64
491541Srgrimeschar cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
501541Srgrimes
511541Srgrimesint	 response (void);
521541Srgrimesvoid	 rsource (char *, struct stat *);
531541Srgrimesvoid	 sink (int, char *[]);
541541Srgrimesvoid	 source (int, char *[]);
551541Srgrimesvoid	 tolocal (int, char *[]);
561541Srgrimesvoid	 toremote (char *, int, char *[]);
571541Srgrimes
581541Srgrimesint      do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
591541Srgrimes
601541Srgrimesstatic int fflag, tflag;
611541Srgrimes
621541Srgrimesstatic int version_flag, help_flag;
631541Srgrimes
641541Srgrimesstruct getargs args[] = {
651541Srgrimes    { NULL,	'4', arg_flag,		&usekrb4,	"use Kerberos 4 authentication" },
661541Srgrimes    { NULL,	'5', arg_flag,		&usekrb5,	"use Kerberos 5 authentication" },
671541Srgrimes    { NULL,	'F', arg_flag,		&forwardtkt,	"forward credentials" },
681541Srgrimes    { NULL,	'K', arg_flag,		&usebroken,	"use BSD authentication" },
691541Srgrimes    { NULL,	'P', arg_string,	&port,		"non-default port", "port" },
701541Srgrimes    { NULL,	'p', arg_flag,		&pflag,	"preserve file permissions" },
711541Srgrimes    { NULL,	'r', arg_flag,		&iamrecursive,	"recursive mode" },
721541Srgrimes    { NULL,	'x', arg_flag,		&doencrypt,	"use encryption" },
731541Srgrimes    { NULL,	'z', arg_flag,		&noencrypt,	"don't encrypt" },
741541Srgrimes    { NULL,	'd', arg_flag,		&targetshouldbedirectory },
751541Srgrimes    { NULL,	'e', arg_flag,		&eflag, 	"passed to rsh" },
761541Srgrimes    { NULL,	'f', arg_flag,		&fflag },
771541Srgrimes    { NULL,	't', arg_flag,		&tflag },
781541Srgrimes    { "version", 0,  arg_flag,		&version_flag },
791541Srgrimes    { "help",	 0,  arg_flag,		&help_flag }
801541Srgrimes};
811541Srgrimes
821541Srgrimesstatic void
831541Srgrimesusage (int ret)
841541Srgrimes{
851541Srgrimes    arg_printusage (args,
861541Srgrimes		    sizeof(args) / sizeof(args[0]),
871541Srgrimes		    NULL,
881541Srgrimes		    "file1 file2|file... directory");
891541Srgrimes    exit (ret);
901541Srgrimes}
911541Srgrimes
921541Srgrimesint
931541Srgrimesmain(int argc, char **argv)
941541Srgrimes{
951541Srgrimes	char *targ;
961541Srgrimes	int optind = 0;
971541Srgrimes
981541Srgrimes	setprogname(argv[0]);
991541Srgrimes	if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
1001541Srgrimes		    &optind))
1011541Srgrimes	    usage (1);
1021541Srgrimes	if(help_flag)
1031541Srgrimes	    usage(0);
1041541Srgrimes	if (version_flag) {
1051541Srgrimes	    print_version (NULL);
1061541Srgrimes	    return 0;
1071541Srgrimes	}
1081541Srgrimes
1091541Srgrimes	iamremote = (fflag || tflag);
1101541Srgrimes
1111541Srgrimes	argc -= optind;
1121541Srgrimes	argv += optind;
1131541Srgrimes
1141549Srgrimes	if ((pwd = getpwuid(userid = getuid())) == NULL)
1151541Srgrimes		errx(1, "unknown user %d", (int)userid);
1161541Srgrimes
1171541Srgrimes	remin = STDIN_FILENO;		/* XXX */
1181541Srgrimes	remout = STDOUT_FILENO;
1191541Srgrimes
1201541Srgrimes	if (fflag) {			/* Follow "protocol", send data. */
1211541Srgrimes		response();
1221541Srgrimes		if (setuid(userid) < 0)
1231541Srgrimes			errx(1, "setuid failed");
1241541Srgrimes		source(argc, argv);
1251541Srgrimes		exit(errs);
1261541Srgrimes	}
1271541Srgrimes
1281541Srgrimes	if (tflag) {			/* Receive data. */
1291541Srgrimes		if (setuid(userid) < 0)
1301541Srgrimes			errx(1, "setuid failed");
1311541Srgrimes		sink(argc, argv);
1321549Srgrimes		exit(errs);
1331541Srgrimes	}
1341541Srgrimes
1351541Srgrimes	if (argc < 2)
1361541Srgrimes	    usage(1);
1371541Srgrimes	if (argc > 2)
1381541Srgrimes		targetshouldbedirectory = 1;
1391541Srgrimes
1401541Srgrimes	remin = remout = -1;
1411541Srgrimes	/* Command to be executed on remote system using "rsh". */
1421541Srgrimes	snprintf(cmd, sizeof(cmd),
1431541Srgrimes		 "rcp%s%s%s", iamrecursive ? " -r" : "",
1441541Srgrimes		 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
1451541Srgrimes
1461541Srgrimes	signal(SIGPIPE, lostconn);
1471541Srgrimes
1481541Srgrimes	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
1491541Srgrimes		toremote(targ, argc, argv);
1501541Srgrimes	else {
1511541Srgrimes		tolocal(argc, argv);		/* Dest is local host. */
1521541Srgrimes		if (targetshouldbedirectory)
1531541Srgrimes			verifydir(argv[argc - 1]);
1541541Srgrimes	}
1551541Srgrimes	exit(errs);
1561541Srgrimes}
1571541Srgrimes
1581541Srgrimesvoid
1591541Srgrimestoremote(char *targ, int argc, char **argv)
1601541Srgrimes{
1611541Srgrimes	int i;
1621541Srgrimes	char *bp, *host, *src, *suser, *thost, *tuser;
1631541Srgrimes
1641541Srgrimes	*targ++ = 0;
1651541Srgrimes	if (*targ == 0)
1661541Srgrimes		targ = ".";
1671541Srgrimes
1681541Srgrimes	if ((thost = strchr(argv[argc - 1], '@'))) {
1691541Srgrimes		/* user@host */
1701541Srgrimes		*thost++ = 0;
1711541Srgrimes		tuser = argv[argc - 1];
1721541Srgrimes		if (*tuser == '\0')
1731541Srgrimes			tuser = NULL;
1741541Srgrimes		else if (!okname(tuser))
1751541Srgrimes			exit(1);
1761541Srgrimes	} else {
1771541Srgrimes		thost = argv[argc - 1];
1781541Srgrimes		tuser = NULL;
1791541Srgrimes	}
1801541Srgrimes
1811541Srgrimes	for (i = 0; i < argc - 1; i++) {
1821541Srgrimes		src = colon(argv[i]);
1831541Srgrimes		if (src) {			/* remote to remote */
1841541Srgrimes			int ret;
1851541Srgrimes			*src++ = 0;
1861541Srgrimes			if (*src == 0)
1871541Srgrimes				src = ".";
1881541Srgrimes			host = strchr(argv[i], '@');
1891541Srgrimes			if (host) {
1901541Srgrimes				*host++ = '\0';
1911541Srgrimes				suser = argv[i];
1921541Srgrimes				if (*suser == '\0')
1931541Srgrimes					suser = pwd->pw_name;
1941541Srgrimes				else if (!okname(suser))
1951541Srgrimes					continue;
1961541Srgrimes				ret = asprintf(&bp,
1971541Srgrimes				    "%s%s %s -l %s -n %s %s '%s%s%s:%s'",
1981541Srgrimes					 _PATH_RSH, eflag ? " -e" : "",
1991541Srgrimes					 host, suser, cmd, src,
2001541Srgrimes				    tuser ? tuser : "", tuser ? "@" : "",
2011541Srgrimes				    thost, targ);
2021541Srgrimes			} else {
2031541Srgrimes				ret = asprintf(&bp,
2041541Srgrimes					 "exec %s%s %s -n %s %s '%s%s%s:%s'",
2051541Srgrimes					 _PATH_RSH, eflag ? " -e" : "",
2061541Srgrimes					 argv[i], cmd, src,
2071541Srgrimes					 tuser ? tuser : "", tuser ? "@" : "",
2081541Srgrimes					 thost, targ);
2091541Srgrimes			}
2101541Srgrimes			if (ret == -1)
2111541Srgrimes				err (1, "malloc");
2121541Srgrimes			susystem(bp, userid);
2131541Srgrimes			free(bp);
2141541Srgrimes		} else {			/* local to remote */
2151541Srgrimes			if (remin == -1) {
2161541Srgrimes				if (asprintf(&bp, "%s -t %s", cmd, targ) == -1)
2171541Srgrimes					err (1, "malloc");
2181541Srgrimes				host = thost;
2191541Srgrimes
2201541Srgrimes				if (do_cmd(host, tuser, bp, &remin, &remout) < 0)
2211541Srgrimes					exit(1);
2221541Srgrimes
2231549Srgrimes				if (response() < 0)
2241541Srgrimes					exit(1);
2251541Srgrimes				free(bp);
2261541Srgrimes				if (setuid(userid) < 0)
2271541Srgrimes					errx(1, "setuid failed");
2281541Srgrimes			}
2291541Srgrimes			source(1, argv+i);
2301541Srgrimes		}
2311541Srgrimes	}
2321541Srgrimes}
2331541Srgrimes
2341541Srgrimesvoid
2351541Srgrimestolocal(int argc, char **argv)
2361541Srgrimes{
2371541Srgrimes	int i;
2381541Srgrimes	char *bp, *host, *src, *suser;
2391541Srgrimes
2401541Srgrimes	for (i = 0; i < argc - 1; i++) {
2411541Srgrimes		int ret;
2421541Srgrimes
2431541Srgrimes		if (!(src = colon(argv[i]))) {		/* Local to local. */
2441541Srgrimes			ret = asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP,
2451541Srgrimes			    iamrecursive ? " -PR" : "", pflag ? " -p" : "",
2461541Srgrimes			    argv[i], argv[argc - 1]);
2471541Srgrimes			if (ret == -1)
2481541Srgrimes				err (1, "malloc");
2491541Srgrimes			if (susystem(bp, userid))
2501541Srgrimes				++errs;
2511541Srgrimes			free(bp);
2521541Srgrimes			continue;
2531541Srgrimes		}
2541541Srgrimes		*src++ = 0;
2551541Srgrimes		if (*src == 0)
2561541Srgrimes			src = ".";
2571541Srgrimes		if ((host = strchr(argv[i], '@')) == NULL) {
2581541Srgrimes			host = argv[i];
2591541Srgrimes			suser = pwd->pw_name;
2601541Srgrimes		} else {
2611541Srgrimes			*host++ = 0;
2621541Srgrimes			suser = argv[i];
2631549Srgrimes			if (*suser == '\0')
2641541Srgrimes				suser = pwd->pw_name;
2651541Srgrimes			else if (!okname(suser))
2661541Srgrimes				continue;
2671541Srgrimes		}
2681541Srgrimes		ret = asprintf(&bp, "%s -f %s", cmd, src);
2691541Srgrimes		if (ret == -1)
2701541Srgrimes			err (1, "malloc");
2711541Srgrimes		if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
2721541Srgrimes			free(bp);
2731541Srgrimes			++errs;
2741541Srgrimes			continue;
2751541Srgrimes		}
2761541Srgrimes		free(bp);
2771541Srgrimes		sink(1, argv + argc - 1);
2781541Srgrimes		if (seteuid(0) < 0)
2791541Srgrimes			exit(1);
2801541Srgrimes		close(remin);
2811541Srgrimes		remin = remout = -1;
2821541Srgrimes	}
2831541Srgrimes}
2841541Srgrimes
2851541Srgrimesvoid
2861541Srgrimessource(int argc, char **argv)
2871541Srgrimes{
2881541Srgrimes	struct stat stb;
2891541Srgrimes	static BUF buffer;
2901541Srgrimes	BUF *bp;
2911541Srgrimes	off_t i;
2921541Srgrimes	int amt, fd, haderr, indx, result;
2931549Srgrimes	char *last, *name, buf[BUFSIZ];
2941541Srgrimes
2951541Srgrimes	for (indx = 0; indx < argc; ++indx) {
2961541Srgrimes                name = argv[indx];
2971541Srgrimes		if ((fd = open(name, O_RDONLY, 0)) < 0)
2981541Srgrimes			goto syserr;
2991541Srgrimes		if (fstat(fd, &stb)) {
3001541Srgrimessyserr:			run_err("%s: %s", name, strerror(errno));
3011541Srgrimes			goto next;
3021541Srgrimes		}
3031541Srgrimes		switch (stb.st_mode & S_IFMT) {
3041541Srgrimes		case S_IFREG:
3051541Srgrimes			break;
3061541Srgrimes		case S_IFDIR:
3071541Srgrimes			if (iamrecursive) {
3081541Srgrimes				rsource(name, &stb);
3091541Srgrimes				goto next;
3101541Srgrimes			}
3111541Srgrimes			/* FALLTHROUGH */
3121541Srgrimes		default:
3131541Srgrimes			run_err("%s: not a regular file", name);
3141541Srgrimes			goto next;
3151541Srgrimes		}
3161549Srgrimes		if ((last = strrchr(name, '/')) == NULL)
3171541Srgrimes			last = name;
3181541Srgrimes		else
3191541Srgrimes			++last;
3201541Srgrimes		if (pflag) {
3211541Srgrimes			/*
3221541Srgrimes			 * Make it compatible with possible future
3231541Srgrimes			 * versions expecting microseconds.
3241541Srgrimes			 */
3251541Srgrimes			snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
3261541Srgrimes			    (long)stb.st_mtime,
3271541Srgrimes			    (long)stb.st_atime);
3281541Srgrimes			write(remout, buf, strlen(buf));
3291541Srgrimes			if (response() < 0)
3301541Srgrimes				goto next;
3311541Srgrimes		}
3321541Srgrimes#undef MODEMASK
3331541Srgrimes#define	MODEMASK	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
3341541Srgrimes		snprintf(buf, sizeof(buf), "C%04o %lu %s\n",
3351541Srgrimes			 stb.st_mode & MODEMASK,
3361541Srgrimes			 (unsigned long)stb.st_size,
3371541Srgrimes			 last);
3381549Srgrimes		write(remout, buf, strlen(buf));
3391541Srgrimes		if (response() < 0)
3401541Srgrimes			goto next;
3411541Srgrimes		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
3421541Srgrimesnext:			close(fd);
3431541Srgrimes			continue;
3441541Srgrimes		}
3451541Srgrimes
3461541Srgrimes		/* Keep writing after an error so that we stay sync'd up. */
3471541Srgrimes		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
3481541Srgrimes			amt = bp->cnt;
3491541Srgrimes			if (i + amt > stb.st_size)
3501541Srgrimes				amt = stb.st_size - i;
3511541Srgrimes			if (!haderr) {
3521541Srgrimes				result = read(fd, bp->buf, amt);
3531541Srgrimes				if (result != amt)
3541541Srgrimes					haderr = result >= 0 ? EIO : errno;
3551541Srgrimes			}
3561541Srgrimes			if (haderr)
3571541Srgrimes				write(remout, bp->buf, amt);
3581541Srgrimes			else {
3591549Srgrimes				result = write(remout, bp->buf, amt);
3601541Srgrimes				if (result != amt)
3611541Srgrimes					haderr = result >= 0 ? EIO : errno;
3621541Srgrimes			}
3631541Srgrimes		}
3641541Srgrimes		if (close(fd) && !haderr)
3651541Srgrimes			haderr = errno;
3661541Srgrimes		if (!haderr)
3671541Srgrimes			write(remout, "", 1);
3681541Srgrimes		else
3691541Srgrimes			run_err("%s: %s", name, strerror(haderr));
3701549Srgrimes		response();
3711541Srgrimes	}
3721541Srgrimes}
3731541Srgrimes
3741541Srgrimesvoid
3751541Srgrimesrsource(char *name, struct stat *statp)
3761541Srgrimes{
3771541Srgrimes	DIR *dirp;
3781541Srgrimes	struct dirent *dp;
3791541Srgrimes	char *last, *vect[1], path[MAXPATHLEN];
3801541Srgrimes
3811541Srgrimes	if (!(dirp = opendir(name))) {
3821541Srgrimes		run_err("%s: %s", name, strerror(errno));
3831541Srgrimes		return;
3841549Srgrimes	}
3851541Srgrimes	last = strrchr(name, '/');
3861541Srgrimes	if (last == 0)
3871541Srgrimes		last = name;
3881541Srgrimes	else
3891541Srgrimes		last++;
3901541Srgrimes	if (pflag) {
3911541Srgrimes		snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
3921541Srgrimes		    (long)statp->st_mtime,
3931541Srgrimes		    (long)statp->st_atime);
3941541Srgrimes		write(remout, path, strlen(path));
3951541Srgrimes		if (response() < 0) {
3961541Srgrimes			closedir(dirp);
3971541Srgrimes			return;
3981541Srgrimes		}
3991541Srgrimes	}
4001541Srgrimes	snprintf(path, sizeof(path),
4011541Srgrimes	    "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
4021541Srgrimes	write(remout, path, strlen(path));
4031541Srgrimes	if (response() < 0) {
4041541Srgrimes		closedir(dirp);
4051541Srgrimes		return;
4061541Srgrimes	}
4071541Srgrimes	while ((dp = readdir(dirp))) {
4081541Srgrimes		if (dp->d_ino == 0)
4091541Srgrimes			continue;
4101541Srgrimes		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
4111541Srgrimes			continue;
4121541Srgrimes		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
4131541Srgrimes			run_err("%s/%s: name too long", name, dp->d_name);
4141541Srgrimes			continue;
4151541Srgrimes		}
4161541Srgrimes		snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
4171541Srgrimes		vect[0] = path;
4181541Srgrimes		source(1, vect);
4191541Srgrimes	}
4201549Srgrimes	closedir(dirp);
4211541Srgrimes	write(remout, "E\n", 2);
4221541Srgrimes	response();
4231541Srgrimes}
4241541Srgrimes
4251541Srgrimesvoid
4261541Srgrimessink(int argc, char **argv)
4271541Srgrimes{
4281541Srgrimes	static BUF buffer;
4291541Srgrimes	struct stat stb;
4301549Srgrimes	struct timeval tv[2];
4311541Srgrimes	enum { YES, NO, DISPLAYED } wrerr;
4321541Srgrimes	BUF *bp;
4331541Srgrimes	off_t i, j, size;
4341541Srgrimes	int amt, count, exists, first, mask, mode, ofd, omode;
4351541Srgrimes	int setimes, targisdir, wrerrno = 0;
4361549Srgrimes	char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ];
4371541Srgrimes
4381541Srgrimes#define	atime	tv[0]
4391541Srgrimes#define	mtime	tv[1]
4401541Srgrimes#define	SCREWUP(str)	{ why = str; goto screwup; }
4411541Srgrimes
4421541Srgrimes	setimes = targisdir = 0;
4431541Srgrimes	mask = umask(0);
4441549Srgrimes	if (!pflag)
4451541Srgrimes		umask(mask);
4461541Srgrimes	if (argc != 1) {
4471541Srgrimes		run_err("ambiguous target");
4481541Srgrimes		exit(1);
4491541Srgrimes	}
4501549Srgrimes	targ = *argv;
4511541Srgrimes	if (targetshouldbedirectory)
4521541Srgrimes		verifydir(targ);
4531541Srgrimes	write(remout, "", 1);
4541541Srgrimes	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
4551541Srgrimes		targisdir = 1;
4561541Srgrimes	for (first = 1;; first = 0) {
4571541Srgrimes		cp = buf;
4581541Srgrimes		if (read(remin, cp, 1) <= 0)
4591541Srgrimes			return;
4601541Srgrimes		if (*cp++ == '\n')
4611541Srgrimes			SCREWUP("unexpected <newline>");
4621541Srgrimes		do {
4631541Srgrimes			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
4641541Srgrimes				SCREWUP("lost connection");
4651541Srgrimes			*cp++ = ch;
4661541Srgrimes		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
4671541Srgrimes		*cp = 0;
4681541Srgrimes
4691541Srgrimes		if (buf[0] == '\01' || buf[0] == '\02') {
4701541Srgrimes			if (iamremote == 0)
4711541Srgrimes				write(STDERR_FILENO,
4721541Srgrimes				    buf + 1, strlen(buf + 1));
4731541Srgrimes			if (buf[0] == '\02')
4741541Srgrimes				exit(1);
4751541Srgrimes			++errs;
4761541Srgrimes			continue;
4771541Srgrimes		}
4781549Srgrimes		if (buf[0] == 'E') {
4791541Srgrimes			write(remout, "", 1);
4801541Srgrimes			return;
4811541Srgrimes		}
4821541Srgrimes
4831541Srgrimes		if (ch == '\n')
4841541Srgrimes			*--cp = 0;
4851541Srgrimes
4861541Srgrimes		cp = buf;
4871541Srgrimes		if (*cp == 'T') {
4881541Srgrimes			setimes++;
4891549Srgrimes			cp++;
4901541Srgrimes			mtime.tv_sec = strtol(cp, &cp, 10);
4911541Srgrimes			if (!cp || *cp++ != ' ')
4921541Srgrimes				SCREWUP("mtime.sec not delimited");
4931541Srgrimes			mtime.tv_usec = strtol(cp, &cp, 10);
4941541Srgrimes			if (!cp || *cp++ != ' ')
4951541Srgrimes				SCREWUP("mtime.usec not delimited");
4961541Srgrimes			atime.tv_sec = strtol(cp, &cp, 10);
4971541Srgrimes			if (!cp || *cp++ != ' ')
4981541Srgrimes				SCREWUP("atime.sec not delimited");
4991541Srgrimes			atime.tv_usec = strtol(cp, &cp, 10);
5001541Srgrimes			if (!cp || *cp++ != '\0')
5011541Srgrimes				SCREWUP("atime.usec not delimited");
5021541Srgrimes			write(remout, "", 1);
5031541Srgrimes			continue;
5041541Srgrimes		}
5051541Srgrimes		if (*cp != 'C' && *cp != 'D') {
5061549Srgrimes			/*
5071541Srgrimes			 * Check for the case "rcp remote:foo\* local:bar".
5081541Srgrimes			 * In this case, the line "No match." can be returned
5091541Srgrimes			 * by the shell before the rcp command on the remote is
5101541Srgrimes			 * executed so the ^Aerror_message convention isn't
5111541Srgrimes			 * followed.
5121541Srgrimes			 */
513			if (first) {
514				run_err("%s", cp);
515				exit(1);
516			}
517			SCREWUP("expected control record");
518		}
519		mode = 0;
520		for (++cp; cp < buf + 5; cp++) {
521			if (*cp < '0' || *cp > '7')
522				SCREWUP("bad mode");
523			mode = (mode << 3) | (*cp - '0');
524		}
525		if (*cp++ != ' ')
526			SCREWUP("mode not delimited");
527
528		for (size = 0; isdigit((unsigned char)*cp);)
529			size = size * 10 + (*cp++ - '0');
530		if (*cp++ != ' ')
531			SCREWUP("size not delimited");
532		if (targisdir) {
533			static char *namebuf;
534			static int cursize;
535			size_t need;
536
537			need = strlen(targ) + strlen(cp) + 250;
538			if (need > cursize) {
539				if (!(namebuf = malloc(need)))
540					run_err("%s", strerror(errno));
541			}
542			snprintf(namebuf, need, "%s%s%s", targ,
543			    *targ ? "/" : "", cp);
544			np = namebuf;
545		} else
546			np = targ;
547		exists = stat(np, &stb) == 0;
548		if (buf[0] == 'D') {
549			int mod_flag = pflag;
550			if (exists) {
551				if (!S_ISDIR(stb.st_mode)) {
552					errno = ENOTDIR;
553					goto bad;
554				}
555				if (pflag)
556					chmod(np, mode);
557			} else {
558				/* Handle copying from a read-only directory */
559				mod_flag = 1;
560				if (mkdir(np, mode | S_IRWXU) < 0)
561					goto bad;
562			}
563			vect[0] = np;
564			sink(1, vect);
565			if (setimes) {
566				setimes = 0;
567				if (utimes(np, tv) < 0)
568				    run_err("%s: set times: %s",
569					np, strerror(errno));
570			}
571			if (mod_flag)
572				chmod(np, mode);
573			continue;
574		}
575		omode = mode;
576		mode |= S_IWRITE;
577		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
578bad:			run_err("%s: %s", np, strerror(errno));
579			continue;
580		}
581		write(remout, "", 1);
582		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
583			close(ofd);
584			continue;
585		}
586		cp = bp->buf;
587		wrerr = NO;
588		for (count = i = 0; i < size; i += BUFSIZ) {
589			amt = BUFSIZ;
590			if (i + amt > size)
591				amt = size - i;
592			count += amt;
593			if((j = net_read(remin, cp, amt)) != amt) {
594			    run_err("%s", j ? strerror(errno) :
595				    "dropped connection");
596			    exit(1);
597			}
598			amt -= j;
599			cp += j;
600			if (count == bp->cnt) {
601				/* Keep reading so we stay sync'd up. */
602				if (wrerr == NO) {
603					j = write(ofd, bp->buf, count);
604					if (j != count) {
605						wrerr = YES;
606						wrerrno = j >= 0 ? EIO : errno;
607					}
608				}
609				count = 0;
610				cp = bp->buf;
611			}
612		}
613		if (count != 0 && wrerr == NO &&
614		    (j = write(ofd, bp->buf, count)) != count) {
615			wrerr = YES;
616			wrerrno = j >= 0 ? EIO : errno;
617		}
618		if (ftruncate(ofd, size)) {
619			run_err("%s: truncate: %s", np, strerror(errno));
620			wrerr = DISPLAYED;
621		}
622		if (pflag) {
623			if (exists || omode != mode)
624				if (fchmod(ofd, omode))
625					run_err("%s: set mode: %s",
626					    np, strerror(errno));
627		} else {
628			if (!exists && omode != mode)
629				if (fchmod(ofd, omode & ~mask))
630					run_err("%s: set mode: %s",
631					    np, strerror(errno));
632		}
633		close(ofd);
634		response();
635		if (setimes && wrerr == NO) {
636			setimes = 0;
637			if (utimes(np, tv) < 0) {
638				run_err("%s: set times: %s",
639				    np, strerror(errno));
640				wrerr = DISPLAYED;
641			}
642		}
643		switch(wrerr) {
644		case YES:
645			run_err("%s: %s", np, strerror(wrerrno));
646			break;
647		case NO:
648			write(remout, "", 1);
649			break;
650		case DISPLAYED:
651			break;
652		}
653	}
654screwup:
655	run_err("protocol error: %s", why);
656	exit(1);
657}
658
659int
660response(void)
661{
662	char ch, *cp, resp, rbuf[BUFSIZ];
663
664	if (read(remin, &resp, sizeof(resp)) != sizeof(resp))
665		lostconn(0);
666
667	cp = rbuf;
668	switch(resp) {
669	case 0:				/* ok */
670		return (0);
671	default:
672		*cp++ = resp;
673		/* FALLTHROUGH */
674	case 1:				/* error, followed by error msg */
675	case 2:				/* fatal error, "" */
676		do {
677			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
678				lostconn(0);
679			*cp++ = ch;
680		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
681
682		if (!iamremote)
683			write(STDERR_FILENO, rbuf, cp - rbuf);
684		++errs;
685		if (resp == 1)
686			return (-1);
687		exit(1);
688	}
689	/* NOTREACHED */
690}
691
692#include <stdarg.h>
693
694void
695run_err(const char *fmt, ...)
696{
697	static FILE *fp;
698	va_list ap;
699
700	++errs;
701	if (fp == NULL && !(fp = fdopen(remout, "w")))
702		return;
703	va_start(ap, fmt);
704	fprintf(fp, "%c", 0x01);
705	fprintf(fp, "rcp: ");
706	vfprintf(fp, fmt, ap);
707	fprintf(fp, "\n");
708	fflush(fp);
709	va_end(ap);
710
711	if (!iamremote) {
712	    va_start(ap, fmt);
713	    vwarnx(fmt, ap);
714	    va_end(ap);
715	}
716}
717
718/*
719 * This function executes the given command as the specified user on the
720 * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
721 * assigns the input and output file descriptors on success.
722 *
723 * If it cannot create necessary pipes it exits with error message.
724 */
725
726int
727do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
728{
729	int pin[2], pout[2], reserved[2];
730
731	/*
732	 * Reserve two descriptors so that the real pipes won't get
733	 * descriptors 0 and 1 because that will screw up dup2 below.
734	 */
735	pipe(reserved);
736
737	/* Create a socket pair for communicating with rsh. */
738	if (pipe(pin) < 0) {
739		perror("pipe");
740		exit(255);
741	}
742	if (pipe(pout) < 0) {
743		perror("pipe");
744		exit(255);
745	}
746
747	/* Free the reserved descriptors. */
748	close(reserved[0]);
749	close(reserved[1]);
750
751	/* For a child to execute the command on the remote host using rsh. */
752	if (fork() == 0) {
753		char *args[100];
754		unsigned int i;
755
756		/* Child. */
757		close(pin[1]);
758		close(pout[0]);
759		dup2(pin[0], 0);
760		dup2(pout[1], 1);
761		close(pin[0]);
762		close(pout[1]);
763
764		i = 0;
765		args[i++] = RSH_PROGRAM;
766		if (usekrb4)
767			args[i++] = "-4";
768		if (usekrb5)
769			args[i++] = "-5";
770		if (usebroken)
771			args[i++] = "-K";
772		if (doencrypt)
773			args[i++] = "-x";
774		if (forwardtkt)
775			args[i++] = "-F";
776		if (noencrypt)
777			args[i++] = "-z";
778		if (port != NULL) {
779			args[i++] = "-p";
780			args[i++] = port;
781		}
782		if (eflag)
783		    args[i++] = "-e";
784		if (remuser != NULL) {
785			args[i++] = "-l";
786			args[i++] = remuser;
787		}
788		args[i++] = host;
789		args[i++] = cmd;
790		args[i++] = NULL;
791
792		execvp(RSH_PROGRAM, args);
793		perror(RSH_PROGRAM);
794		exit(1);
795	}
796	/* Parent.  Close the other side, and return the local side. */
797	close(pin[0]);
798	*fdout = pin[1];
799	close(pout[1]);
800	*fdin = pout[0];
801	return 0;
802}
803