172445Sassar/*
272445Sassar * Copyright (c) 1983, 1990, 1992, 1993
372445Sassar *	The Regents of the University of California.  All rights reserved.
472445Sassar *
572445Sassar * Redistribution and use in source and binary forms, with or without
672445Sassar * modification, are permitted provided that the following conditions
772445Sassar * are met:
872445Sassar * 1. Redistributions of source code must retain the above copyright
972445Sassar *    notice, this list of conditions and the following disclaimer.
1072445Sassar * 2. Redistributions in binary form must reproduce the above copyright
1172445Sassar *    notice, this list of conditions and the following disclaimer in the
1272445Sassar *    documentation and/or other materials provided with the distribution.
13233294Sstas * 3. Neither the name of the University nor the names of its contributors
1472445Sassar *    may be used to endorse or promote products derived from this software
1572445Sassar *    without specific prior written permission.
1672445Sassar *
1772445Sassar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1872445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1972445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2072445Sassar * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2172445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2272445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2372445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2472445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2572445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2672445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2772445Sassar * SUCH DAMAGE.
2872445Sassar */
2972445Sassar
3072445Sassar#include "rcp_locl.h"
3178527Sassar#include <getarg.h>
3272445Sassar
3372445Sassar#define RSH_PROGRAM "rsh"
3472445Sassar
3572445Sassarstruct  passwd *pwd;
3672445Sassaruid_t	userid;
3772445Sassarint     errs, remin, remout;
3872445Sassarint     pflag, iamremote, iamrecursive, targetshouldbedirectory;
3972445Sassarint     doencrypt, noencrypt;
40120945Snectarint     usebroken, usekrb4, usekrb5, forwardtkt;
4172445Sassarchar    *port;
42178825Sdfrint     eflag = 0;
4372445Sassar
4472445Sassar#define	CMDNEEDS	64
4572445Sassarchar cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
4672445Sassar
4772445Sassarint	 response (void);
4872445Sassarvoid	 rsource (char *, struct stat *);
4972445Sassarvoid	 sink (int, char *[]);
5072445Sassarvoid	 source (int, char *[]);
5172445Sassarvoid	 tolocal (int, char *[]);
5272445Sassarvoid	 toremote (char *, int, char *[]);
5372445Sassar
5472445Sassarint      do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
5572445Sassar
5678527Sassarstatic int fflag, tflag;
5778527Sassar
5878527Sassarstatic int version_flag, help_flag;
5978527Sassar
6078527Sassarstruct getargs args[] = {
61120945Snectar    { NULL,	'4', arg_flag,		&usekrb4,	"use Kerberos 4 authentication" },
6278527Sassar    { NULL,	'5', arg_flag,		&usekrb5,	"use Kerberos 5 authentication" },
6378527Sassar    { NULL,	'F', arg_flag,		&forwardtkt,	"forward credentials" },
6478527Sassar    { NULL,	'K', arg_flag,		&usebroken,	"use BSD authentication" },
6578527Sassar    { NULL,	'P', arg_string,	&port,		"non-default port", "port" },
6678527Sassar    { NULL,	'p', arg_flag,		&pflag,	"preserve file permissions" },
6778527Sassar    { NULL,	'r', arg_flag,		&iamrecursive,	"recursive mode" },
6878527Sassar    { NULL,	'x', arg_flag,		&doencrypt,	"use encryption" },
6978527Sassar    { NULL,	'z', arg_flag,		&noencrypt,	"don't encrypt" },
7078527Sassar    { NULL,	'd', arg_flag,		&targetshouldbedirectory },
71178825Sdfr    { NULL,	'e', arg_flag,		&eflag, 	"passed to rsh" },
7278527Sassar    { NULL,	'f', arg_flag,		&fflag },
7378527Sassar    { NULL,	't', arg_flag,		&tflag },
7478527Sassar    { "version", 0,  arg_flag,		&version_flag },
7578527Sassar    { "help",	 0,  arg_flag,		&help_flag }
7678527Sassar};
7778527Sassar
7878527Sassarstatic void
7978527Sassarusage (int ret)
8078527Sassar{
8178527Sassar    arg_printusage (args,
8278527Sassar		    sizeof(args) / sizeof(args[0]),
8378527Sassar		    NULL,
8478527Sassar		    "file1 file2|file... directory");
8578527Sassar    exit (ret);
8678527Sassar}
8778527Sassar
8872445Sassarint
8978527Sassarmain(int argc, char **argv)
9072445Sassar{
9172445Sassar	char *targ;
9278527Sassar	int optind = 0;
9372445Sassar
9490926Snectar	setprogname(argv[0]);
9578527Sassar	if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
9678527Sassar		    &optind))
9778527Sassar	    usage (1);
9878527Sassar	if(help_flag)
9978527Sassar	    usage(0);
10078527Sassar	if (version_flag) {
10178527Sassar	    print_version (NULL);
10278527Sassar	    return 0;
10378527Sassar	}
104233294Sstas
10578527Sassar	iamremote = (fflag || tflag);
10678527Sassar
10772445Sassar	argc -= optind;
10872445Sassar	argv += optind;
10972445Sassar
11072445Sassar	if ((pwd = getpwuid(userid = getuid())) == NULL)
11172445Sassar		errx(1, "unknown user %d", (int)userid);
11272445Sassar
11372445Sassar	remin = STDIN_FILENO;		/* XXX */
11472445Sassar	remout = STDOUT_FILENO;
11572445Sassar
11672445Sassar	if (fflag) {			/* Follow "protocol", send data. */
117233294Sstas		(void)response();
11872445Sassar		source(argc, argv);
11972445Sassar		exit(errs);
12072445Sassar	}
12172445Sassar
12272445Sassar	if (tflag) {			/* Receive data. */
12372445Sassar		sink(argc, argv);
12472445Sassar		exit(errs);
12572445Sassar	}
12672445Sassar
12772445Sassar	if (argc < 2)
12878527Sassar	    usage(1);
12972445Sassar	if (argc > 2)
13072445Sassar		targetshouldbedirectory = 1;
13172445Sassar
13272445Sassar	remin = remout = -1;
13372445Sassar	/* Command to be executed on remote system using "rsh". */
13490926Snectar	snprintf(cmd, sizeof(cmd),
135233294Sstas		 "rcp%s%s%s", iamrecursive ? " -r" : "",
13690926Snectar		 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
13772445Sassar
13878527Sassar	signal(SIGPIPE, lostconn);
13972445Sassar
14072445Sassar	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
14172445Sassar		toremote(targ, argc, argv);
14272445Sassar	else {
14372445Sassar		tolocal(argc, argv);		/* Dest is local host. */
14472445Sassar		if (targetshouldbedirectory)
14572445Sassar			verifydir(argv[argc - 1]);
14672445Sassar	}
14772445Sassar	exit(errs);
14872445Sassar}
14972445Sassar
15072445Sassarvoid
15178527Sassartoremote(char *targ, int argc, char **argv)
15272445Sassar{
15390926Snectar	int i;
15472445Sassar	char *bp, *host, *src, *suser, *thost, *tuser;
15572445Sassar
15672445Sassar	*targ++ = 0;
15772445Sassar	if (*targ == 0)
15872445Sassar		targ = ".";
15972445Sassar
160233294Sstas	if ((thost = strchr(argv[argc - 1], '@')) != NULL) {
16172445Sassar		/* user@host */
16272445Sassar		*thost++ = 0;
16372445Sassar		tuser = argv[argc - 1];
16472445Sassar		if (*tuser == '\0')
16572445Sassar			tuser = NULL;
16672445Sassar		else if (!okname(tuser))
16772445Sassar			exit(1);
16872445Sassar	} else {
16972445Sassar		thost = argv[argc - 1];
17072445Sassar		tuser = NULL;
17172445Sassar	}
172233294Sstas	thost = unbracket(thost);
17372445Sassar
17472445Sassar	for (i = 0; i < argc - 1; i++) {
17572445Sassar		src = colon(argv[i]);
17672445Sassar		if (src) {			/* remote to remote */
177178825Sdfr			int ret;
17872445Sassar			*src++ = 0;
17972445Sassar			if (*src == 0)
18072445Sassar				src = ".";
18172445Sassar			host = strchr(argv[i], '@');
18272445Sassar			if (host) {
18390926Snectar				*host++ = '\0';
184233294Sstas				host = unbracket(host);
18572445Sassar				suser = argv[i];
18672445Sassar				if (*suser == '\0')
18772445Sassar					suser = pwd->pw_name;
18872445Sassar				else if (!okname(suser))
18972445Sassar					continue;
190178825Sdfr				ret = asprintf(&bp,
191178825Sdfr				    "%s%s %s -l %s -n %s %s '%s%s%s:%s'",
192233294Sstas					 _PATH_RSH, eflag ? " -e" : "",
193178825Sdfr					 host, suser, cmd, src,
19472445Sassar				    tuser ? tuser : "", tuser ? "@" : "",
19572445Sassar				    thost, targ);
19690926Snectar			} else {
197233294Sstas				host = unbracket(argv[i]);
198178825Sdfr				ret = asprintf(&bp,
199178825Sdfr					 "exec %s%s %s -n %s %s '%s%s%s:%s'",
200233294Sstas					 _PATH_RSH, eflag ? " -e" : "",
201233294Sstas					 host, cmd, src,
202178825Sdfr					 tuser ? tuser : "", tuser ? "@" : "",
203178825Sdfr					 thost, targ);
20490926Snectar			}
205178825Sdfr			if (ret == -1)
20690926Snectar				err (1, "malloc");
207233294Sstas			susystem(bp);
20878527Sassar			free(bp);
20972445Sassar		} else {			/* local to remote */
21072445Sassar			if (remin == -1) {
211178825Sdfr				if (asprintf(&bp, "%s -t %s", cmd, targ) == -1)
21290926Snectar					err (1, "malloc");
21372445Sassar				host = thost;
21472445Sassar
21572445Sassar				if (do_cmd(host, tuser, bp, &remin, &remout) < 0)
21672445Sassar					exit(1);
21772445Sassar
21872445Sassar				if (response() < 0)
21972445Sassar					exit(1);
22078527Sassar				free(bp);
22172445Sassar			}
22272445Sassar			source(1, argv+i);
22372445Sassar		}
22472445Sassar	}
22572445Sassar}
22672445Sassar
22772445Sassarvoid
22878527Sassartolocal(int argc, char **argv)
22972445Sassar{
23090926Snectar	int i;
23172445Sassar	char *bp, *host, *src, *suser;
23272445Sassar
23372445Sassar	for (i = 0; i < argc - 1; i++) {
234178825Sdfr		int ret;
235178825Sdfr
23672445Sassar		if (!(src = colon(argv[i]))) {		/* Local to local. */
237178825Sdfr			ret = asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP,
23872445Sassar			    iamrecursive ? " -PR" : "", pflag ? " -p" : "",
23972445Sassar			    argv[i], argv[argc - 1]);
240178825Sdfr			if (ret == -1)
24190926Snectar				err (1, "malloc");
242233294Sstas			if (susystem(bp))
24372445Sassar				++errs;
24478527Sassar			free(bp);
24572445Sassar			continue;
24672445Sassar		}
24772445Sassar		*src++ = 0;
24872445Sassar		if (*src == 0)
24972445Sassar			src = ".";
25072445Sassar		if ((host = strchr(argv[i], '@')) == NULL) {
25172445Sassar			host = argv[i];
25272445Sassar			suser = pwd->pw_name;
25372445Sassar		} else {
25472445Sassar			*host++ = 0;
25572445Sassar			suser = argv[i];
25672445Sassar			if (*suser == '\0')
25772445Sassar				suser = pwd->pw_name;
25872445Sassar			else if (!okname(suser))
25972445Sassar				continue;
26072445Sassar		}
261178825Sdfr		ret = asprintf(&bp, "%s -f %s", cmd, src);
262178825Sdfr		if (ret == -1)
26390926Snectar			err (1, "malloc");
26472445Sassar		if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
26578527Sassar			free(bp);
26672445Sassar			++errs;
26772445Sassar			continue;
26872445Sassar		}
26978527Sassar		free(bp);
27072445Sassar		sink(1, argv + argc - 1);
27178527Sassar		close(remin);
27272445Sassar		remin = remout = -1;
27372445Sassar	}
27472445Sassar}
27572445Sassar
27672445Sassarvoid
27778527Sassarsource(int argc, char **argv)
27872445Sassar{
27972445Sassar	struct stat stb;
28072445Sassar	static BUF buffer;
28172445Sassar	BUF *bp;
28272445Sassar	off_t i;
283233294Sstas	off_t amt;
284233294Sstas	int fd, haderr, indx, result;
28572445Sassar	char *last, *name, buf[BUFSIZ];
28672445Sassar
28772445Sassar	for (indx = 0; indx < argc; ++indx) {
28872445Sassar                name = argv[indx];
28972445Sassar		if ((fd = open(name, O_RDONLY, 0)) < 0)
29072445Sassar			goto syserr;
29172445Sassar		if (fstat(fd, &stb)) {
29272445Sassarsyserr:			run_err("%s: %s", name, strerror(errno));
29372445Sassar			goto next;
29472445Sassar		}
295233294Sstas		if (S_ISDIR(stb.st_mode) && iamrecursive) {
296233294Sstas			rsource(name, &stb);
297233294Sstas			goto next;
298233294Sstas		} else if (!S_ISREG(stb.st_mode)) {
29972445Sassar			run_err("%s: not a regular file", name);
30072445Sassar			goto next;
30172445Sassar		}
30272445Sassar		if ((last = strrchr(name, '/')) == NULL)
30372445Sassar			last = name;
30472445Sassar		else
30572445Sassar			++last;
30672445Sassar		if (pflag) {
30772445Sassar			/*
30872445Sassar			 * Make it compatible with possible future
30972445Sassar			 * versions expecting microseconds.
31072445Sassar			 */
31178527Sassar			snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
31272445Sassar			    (long)stb.st_mtime,
31372445Sassar			    (long)stb.st_atime);
31478527Sassar			write(remout, buf, strlen(buf));
31572445Sassar			if (response() < 0)
31672445Sassar				goto next;
31772445Sassar		}
318178825Sdfr#undef MODEMASK
31972445Sassar#define	MODEMASK	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
32090926Snectar		snprintf(buf, sizeof(buf), "C%04o %lu %s\n",
321233294Sstas			 (unsigned int)(stb.st_mode & MODEMASK),
32290926Snectar			 (unsigned long)stb.st_size,
32390926Snectar			 last);
32478527Sassar		write(remout, buf, strlen(buf));
32572445Sassar		if (response() < 0)
32672445Sassar			goto next;
32772445Sassar		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
32878527Sassarnext:			close(fd);
32972445Sassar			continue;
33072445Sassar		}
33172445Sassar
33272445Sassar		/* Keep writing after an error so that we stay sync'd up. */
33372445Sassar		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
33472445Sassar			amt = bp->cnt;
33572445Sassar			if (i + amt > stb.st_size)
33672445Sassar				amt = stb.st_size - i;
33772445Sassar			if (!haderr) {
338233294Sstas			        result = read(fd, bp->buf, (size_t)amt);
33972445Sassar				if (result != amt)
34072445Sassar					haderr = result >= 0 ? EIO : errno;
34172445Sassar			}
34272445Sassar			if (haderr)
34378527Sassar				write(remout, bp->buf, amt);
34472445Sassar			else {
345233294Sstas			        result = write(remout, bp->buf, (size_t)amt);
34672445Sassar				if (result != amt)
34772445Sassar					haderr = result >= 0 ? EIO : errno;
34872445Sassar			}
34972445Sassar		}
35072445Sassar		if (close(fd) && !haderr)
35172445Sassar			haderr = errno;
35272445Sassar		if (!haderr)
35378527Sassar			write(remout, "", 1);
35472445Sassar		else
35572445Sassar			run_err("%s: %s", name, strerror(haderr));
35678527Sassar		response();
35772445Sassar	}
35872445Sassar}
35972445Sassar
36072445Sassarvoid
36178527Sassarrsource(char *name, struct stat *statp)
36272445Sassar{
36372445Sassar	DIR *dirp;
36472445Sassar	struct dirent *dp;
36572445Sassar	char *last, *vect[1], path[MAXPATHLEN];
36672445Sassar
36772445Sassar	if (!(dirp = opendir(name))) {
36872445Sassar		run_err("%s: %s", name, strerror(errno));
36972445Sassar		return;
37072445Sassar	}
37172445Sassar	last = strrchr(name, '/');
37272445Sassar	if (last == 0)
37372445Sassar		last = name;
37472445Sassar	else
37572445Sassar		last++;
37672445Sassar	if (pflag) {
37778527Sassar		snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
37872445Sassar		    (long)statp->st_mtime,
37972445Sassar		    (long)statp->st_atime);
38078527Sassar		write(remout, path, strlen(path));
38172445Sassar		if (response() < 0) {
38272445Sassar			closedir(dirp);
38372445Sassar			return;
38472445Sassar		}
38572445Sassar	}
38678527Sassar	snprintf(path, sizeof(path),
387233294Sstas		 "D%04o %d %s\n",
388233294Sstas		 (unsigned int)(statp->st_mode & MODEMASK), 0, last);
38978527Sassar	write(remout, path, strlen(path));
39072445Sassar	if (response() < 0) {
39172445Sassar		closedir(dirp);
39272445Sassar		return;
39372445Sassar	}
394233294Sstas	while ((dp = readdir(dirp)) != NULL) {
39572445Sassar		if (dp->d_ino == 0)
39672445Sassar			continue;
39772445Sassar		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
39872445Sassar			continue;
39972445Sassar		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
40072445Sassar			run_err("%s/%s: name too long", name, dp->d_name);
40172445Sassar			continue;
40272445Sassar		}
40378527Sassar		snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
40472445Sassar		vect[0] = path;
40572445Sassar		source(1, vect);
40672445Sassar	}
40778527Sassar	closedir(dirp);
40878527Sassar	write(remout, "E\n", 2);
40978527Sassar	response();
41072445Sassar}
41172445Sassar
41272445Sassarvoid
41378527Sassarsink(int argc, char **argv)
41472445Sassar{
41572445Sassar	static BUF buffer;
41672445Sassar	struct stat stb;
41772445Sassar	struct timeval tv[2];
41872445Sassar	enum { YES, NO, DISPLAYED } wrerr;
41972445Sassar	BUF *bp;
42072445Sassar	off_t i, j, size;
42172445Sassar	int amt, count, exists, first, mask, mode, ofd, omode;
42272445Sassar	int setimes, targisdir, wrerrno = 0;
42372445Sassar	char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ];
42472445Sassar
42572445Sassar#define	atime	tv[0]
42672445Sassar#define	mtime	tv[1]
42772445Sassar#define	SCREWUP(str)	{ why = str; goto screwup; }
42872445Sassar
42972445Sassar	setimes = targisdir = 0;
43072445Sassar	mask = umask(0);
43172445Sassar	if (!pflag)
43278527Sassar		umask(mask);
43372445Sassar	if (argc != 1) {
43472445Sassar		run_err("ambiguous target");
43572445Sassar		exit(1);
43672445Sassar	}
43772445Sassar	targ = *argv;
43872445Sassar	if (targetshouldbedirectory)
43972445Sassar		verifydir(targ);
44078527Sassar	write(remout, "", 1);
44172445Sassar	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
44272445Sassar		targisdir = 1;
44372445Sassar	for (first = 1;; first = 0) {
44472445Sassar		cp = buf;
44572445Sassar		if (read(remin, cp, 1) <= 0)
44672445Sassar			return;
44772445Sassar		if (*cp++ == '\n')
44872445Sassar			SCREWUP("unexpected <newline>");
44972445Sassar		do {
45072445Sassar			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
45172445Sassar				SCREWUP("lost connection");
45272445Sassar			*cp++ = ch;
45372445Sassar		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
45472445Sassar		*cp = 0;
45572445Sassar
45672445Sassar		if (buf[0] == '\01' || buf[0] == '\02') {
45772445Sassar			if (iamremote == 0)
45878527Sassar				write(STDERR_FILENO,
45972445Sassar				    buf + 1, strlen(buf + 1));
46072445Sassar			if (buf[0] == '\02')
46172445Sassar				exit(1);
46272445Sassar			++errs;
46372445Sassar			continue;
46472445Sassar		}
46572445Sassar		if (buf[0] == 'E') {
46678527Sassar			write(remout, "", 1);
46772445Sassar			return;
46872445Sassar		}
46972445Sassar
47072445Sassar		if (ch == '\n')
47172445Sassar			*--cp = 0;
47272445Sassar
47372445Sassar		cp = buf;
47472445Sassar		if (*cp == 'T') {
47572445Sassar			setimes++;
47672445Sassar			cp++;
47772445Sassar			mtime.tv_sec = strtol(cp, &cp, 10);
47872445Sassar			if (!cp || *cp++ != ' ')
47972445Sassar				SCREWUP("mtime.sec not delimited");
48072445Sassar			mtime.tv_usec = strtol(cp, &cp, 10);
48172445Sassar			if (!cp || *cp++ != ' ')
48272445Sassar				SCREWUP("mtime.usec not delimited");
48372445Sassar			atime.tv_sec = strtol(cp, &cp, 10);
48472445Sassar			if (!cp || *cp++ != ' ')
48572445Sassar				SCREWUP("atime.sec not delimited");
48672445Sassar			atime.tv_usec = strtol(cp, &cp, 10);
48772445Sassar			if (!cp || *cp++ != '\0')
48872445Sassar				SCREWUP("atime.usec not delimited");
48978527Sassar			write(remout, "", 1);
49072445Sassar			continue;
49172445Sassar		}
49272445Sassar		if (*cp != 'C' && *cp != 'D') {
49372445Sassar			/*
49472445Sassar			 * Check for the case "rcp remote:foo\* local:bar".
49572445Sassar			 * In this case, the line "No match." can be returned
49672445Sassar			 * by the shell before the rcp command on the remote is
49772445Sassar			 * executed so the ^Aerror_message convention isn't
49872445Sassar			 * followed.
49972445Sassar			 */
50072445Sassar			if (first) {
50172445Sassar				run_err("%s", cp);
50272445Sassar				exit(1);
50372445Sassar			}
50472445Sassar			SCREWUP("expected control record");
50572445Sassar		}
50672445Sassar		mode = 0;
50772445Sassar		for (++cp; cp < buf + 5; cp++) {
50872445Sassar			if (*cp < '0' || *cp > '7')
50972445Sassar				SCREWUP("bad mode");
51072445Sassar			mode = (mode << 3) | (*cp - '0');
51172445Sassar		}
51272445Sassar		if (*cp++ != ' ')
51372445Sassar			SCREWUP("mode not delimited");
51472445Sassar
51590926Snectar		for (size = 0; isdigit((unsigned char)*cp);)
51672445Sassar			size = size * 10 + (*cp++ - '0');
51772445Sassar		if (*cp++ != ' ')
51872445Sassar			SCREWUP("size not delimited");
51972445Sassar		if (targisdir) {
52072445Sassar			static char *namebuf;
52172445Sassar			static int cursize;
52272445Sassar			size_t need;
52372445Sassar
52472445Sassar			need = strlen(targ) + strlen(cp) + 250;
52572445Sassar			if (need > cursize) {
52672445Sassar				if (!(namebuf = malloc(need)))
52772445Sassar					run_err("%s", strerror(errno));
52872445Sassar			}
52978527Sassar			snprintf(namebuf, need, "%s%s%s", targ,
53072445Sassar			    *targ ? "/" : "", cp);
53172445Sassar			np = namebuf;
53272445Sassar		} else
53372445Sassar			np = targ;
53472445Sassar		exists = stat(np, &stb) == 0;
53572445Sassar		if (buf[0] == 'D') {
53672445Sassar			int mod_flag = pflag;
53772445Sassar			if (exists) {
53872445Sassar				if (!S_ISDIR(stb.st_mode)) {
53972445Sassar					errno = ENOTDIR;
54072445Sassar					goto bad;
54172445Sassar				}
54272445Sassar				if (pflag)
54378527Sassar					chmod(np, mode);
54472445Sassar			} else {
54572445Sassar				/* Handle copying from a read-only directory */
54672445Sassar				mod_flag = 1;
54772445Sassar				if (mkdir(np, mode | S_IRWXU) < 0)
54872445Sassar					goto bad;
54972445Sassar			}
55072445Sassar			vect[0] = np;
55172445Sassar			sink(1, vect);
55272445Sassar			if (setimes) {
55372445Sassar				setimes = 0;
55472445Sassar				if (utimes(np, tv) < 0)
55572445Sassar				    run_err("%s: set times: %s",
55672445Sassar					np, strerror(errno));
55772445Sassar			}
55872445Sassar			if (mod_flag)
55978527Sassar				chmod(np, mode);
56072445Sassar			continue;
56172445Sassar		}
56272445Sassar		omode = mode;
56372445Sassar		mode |= S_IWRITE;
56472445Sassar		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
56572445Sassarbad:			run_err("%s: %s", np, strerror(errno));
56672445Sassar			continue;
56772445Sassar		}
56878527Sassar		write(remout, "", 1);
56972445Sassar		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
57078527Sassar			close(ofd);
57172445Sassar			continue;
57272445Sassar		}
57372445Sassar		cp = bp->buf;
57472445Sassar		wrerr = NO;
57572445Sassar		for (count = i = 0; i < size; i += BUFSIZ) {
57672445Sassar			amt = BUFSIZ;
57772445Sassar			if (i + amt > size)
57872445Sassar				amt = size - i;
57972445Sassar			count += amt;
58078527Sassar			if((j = net_read(remin, cp, amt)) != amt) {
58178527Sassar			    run_err("%s", j ? strerror(errno) :
58278527Sassar				    "dropped connection");
58378527Sassar			    exit(1);
58478527Sassar			}
58578527Sassar			amt -= j;
58678527Sassar			cp += j;
58772445Sassar			if (count == bp->cnt) {
58872445Sassar				/* Keep reading so we stay sync'd up. */
58972445Sassar				if (wrerr == NO) {
590233294Sstas					j = write(ofd, bp->buf, (size_t)count);
59172445Sassar					if (j != count) {
59272445Sassar						wrerr = YES;
59372445Sassar						wrerrno = j >= 0 ? EIO : errno;
59472445Sassar					}
59572445Sassar				}
59672445Sassar				count = 0;
59772445Sassar				cp = bp->buf;
59872445Sassar			}
59972445Sassar		}
60072445Sassar		if (count != 0 && wrerr == NO &&
601233294Sstas		    (j = write(ofd, bp->buf, (size_t)count)) != count) {
60272445Sassar			wrerr = YES;
60372445Sassar			wrerrno = j >= 0 ? EIO : errno;
60472445Sassar		}
60572445Sassar		if (ftruncate(ofd, size)) {
60672445Sassar			run_err("%s: truncate: %s", np, strerror(errno));
60772445Sassar			wrerr = DISPLAYED;
60872445Sassar		}
60972445Sassar		if (pflag) {
61072445Sassar			if (exists || omode != mode)
61172445Sassar				if (fchmod(ofd, omode))
61272445Sassar					run_err("%s: set mode: %s",
61372445Sassar					    np, strerror(errno));
61472445Sassar		} else {
61572445Sassar			if (!exists && omode != mode)
61672445Sassar				if (fchmod(ofd, omode & ~mask))
61772445Sassar					run_err("%s: set mode: %s",
61872445Sassar					    np, strerror(errno));
61972445Sassar		}
62078527Sassar		close(ofd);
62178527Sassar		response();
62272445Sassar		if (setimes && wrerr == NO) {
62372445Sassar			setimes = 0;
62472445Sassar			if (utimes(np, tv) < 0) {
62572445Sassar				run_err("%s: set times: %s",
62672445Sassar				    np, strerror(errno));
62772445Sassar				wrerr = DISPLAYED;
62872445Sassar			}
62972445Sassar		}
63072445Sassar		switch(wrerr) {
63172445Sassar		case YES:
63272445Sassar			run_err("%s: %s", np, strerror(wrerrno));
63372445Sassar			break;
63472445Sassar		case NO:
63578527Sassar			write(remout, "", 1);
63672445Sassar			break;
63772445Sassar		case DISPLAYED:
63872445Sassar			break;
63972445Sassar		}
64072445Sassar	}
64172445Sassarscrewup:
64272445Sassar	run_err("protocol error: %s", why);
64372445Sassar	exit(1);
64472445Sassar}
64572445Sassar
64672445Sassarint
64778527Sassarresponse(void)
64872445Sassar{
64972445Sassar	char ch, *cp, resp, rbuf[BUFSIZ];
65072445Sassar
65172445Sassar	if (read(remin, &resp, sizeof(resp)) != sizeof(resp))
65272445Sassar		lostconn(0);
65372445Sassar
65472445Sassar	cp = rbuf;
65572445Sassar	switch(resp) {
65672445Sassar	case 0:				/* ok */
65772445Sassar		return (0);
65872445Sassar	default:
65972445Sassar		*cp++ = resp;
66072445Sassar		/* FALLTHROUGH */
66172445Sassar	case 1:				/* error, followed by error msg */
66272445Sassar	case 2:				/* fatal error, "" */
66372445Sassar		do {
66472445Sassar			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
66572445Sassar				lostconn(0);
66672445Sassar			*cp++ = ch;
66772445Sassar		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
66872445Sassar
66972445Sassar		if (!iamremote)
67078527Sassar			write(STDERR_FILENO, rbuf, cp - rbuf);
67172445Sassar		++errs;
67272445Sassar		if (resp == 1)
67372445Sassar			return (-1);
67472445Sassar		exit(1);
67572445Sassar	}
67672445Sassar	/* NOTREACHED */
67772445Sassar}
67872445Sassar
67972445Sassar#include <stdarg.h>
68072445Sassar
68172445Sassarvoid
68272445Sassarrun_err(const char *fmt, ...)
68372445Sassar{
68472445Sassar	static FILE *fp;
68572445Sassar	va_list ap;
68672445Sassar
68772445Sassar	++errs;
68872445Sassar	if (fp == NULL && !(fp = fdopen(remout, "w")))
68972445Sassar		return;
69090926Snectar	va_start(ap, fmt);
69178527Sassar	fprintf(fp, "%c", 0x01);
69278527Sassar	fprintf(fp, "rcp: ");
69378527Sassar	vfprintf(fp, fmt, ap);
69478527Sassar	fprintf(fp, "\n");
69578527Sassar	fflush(fp);
69690926Snectar	va_end(ap);
69772445Sassar
69890926Snectar	if (!iamremote) {
69990926Snectar	    va_start(ap, fmt);
70090926Snectar	    vwarnx(fmt, ap);
70190926Snectar	    va_end(ap);
70290926Snectar	}
70372445Sassar}
70472445Sassar
70572445Sassar/*
70672445Sassar * This function executes the given command as the specified user on the
70772445Sassar * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
70872445Sassar * assigns the input and output file descriptors on success.
70972445Sassar *
71072445Sassar * If it cannot create necessary pipes it exits with error message.
71172445Sassar */
71272445Sassar
713233294Sstasint
71472445Sassardo_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
71572445Sassar{
71672445Sassar	int pin[2], pout[2], reserved[2];
71772445Sassar
71872445Sassar	/*
71972445Sassar	 * Reserve two descriptors so that the real pipes won't get
72072445Sassar	 * descriptors 0 and 1 because that will screw up dup2 below.
72172445Sassar	 */
72272445Sassar	pipe(reserved);
72372445Sassar
72472445Sassar	/* Create a socket pair for communicating with rsh. */
72572445Sassar	if (pipe(pin) < 0) {
72672445Sassar		perror("pipe");
72772445Sassar		exit(255);
72872445Sassar	}
72972445Sassar	if (pipe(pout) < 0) {
73072445Sassar		perror("pipe");
73172445Sassar		exit(255);
73272445Sassar	}
73372445Sassar
73472445Sassar	/* Free the reserved descriptors. */
73572445Sassar	close(reserved[0]);
73672445Sassar	close(reserved[1]);
73772445Sassar
73872445Sassar	/* For a child to execute the command on the remote host using rsh. */
73972445Sassar	if (fork() == 0) {
74072445Sassar		char *args[100];
74172445Sassar		unsigned int i;
74272445Sassar
74372445Sassar		/* Child. */
74472445Sassar		close(pin[1]);
74572445Sassar		close(pout[0]);
74672445Sassar		dup2(pin[0], 0);
74772445Sassar		dup2(pout[1], 1);
74872445Sassar		close(pin[0]);
74972445Sassar		close(pout[1]);
75072445Sassar
75172445Sassar		i = 0;
75272445Sassar		args[i++] = RSH_PROGRAM;
753120945Snectar		if (usekrb4)
754120945Snectar			args[i++] = "-4";
75572445Sassar		if (usekrb5)
75672445Sassar			args[i++] = "-5";
75772445Sassar		if (usebroken)
75872445Sassar			args[i++] = "-K";
75972445Sassar		if (doencrypt)
76072445Sassar			args[i++] = "-x";
76178527Sassar		if (forwardtkt)
76278527Sassar			args[i++] = "-F";
76372445Sassar		if (noencrypt)
76472445Sassar			args[i++] = "-z";
76572445Sassar		if (port != NULL) {
76672445Sassar			args[i++] = "-p";
76772445Sassar			args[i++] = port;
76872445Sassar		}
769178825Sdfr		if (eflag)
770178825Sdfr		    args[i++] = "-e";
77172445Sassar		if (remuser != NULL) {
77272445Sassar			args[i++] = "-l";
77372445Sassar			args[i++] = remuser;
77472445Sassar		}
77572445Sassar		args[i++] = host;
77672445Sassar		args[i++] = cmd;
77772445Sassar		args[i++] = NULL;
77872445Sassar
77972445Sassar		execvp(RSH_PROGRAM, args);
78072445Sassar		perror(RSH_PROGRAM);
78172445Sassar		exit(1);
78272445Sassar	}
78372445Sassar	/* Parent.  Close the other side, and return the local side. */
78472445Sassar	close(pin[0]);
78572445Sassar	*fdout = pin[1];
78672445Sassar	close(pout[1]);
78772445Sassar	*fdin = pout[0];
78872445Sassar	return 0;
78972445Sassar}
790