rcp.c revision 78527
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.
1372445Sassar * 3. All advertising materials mentioning features or use of this software
1472445Sassar *    must display the following acknowledgement:
1572445Sassar *	This product includes software developed by the University of
1672445Sassar *	California, Berkeley and its contributors.
1772445Sassar * 4. Neither the name of the University nor the names of its contributors
1872445Sassar *    may be used to endorse or promote products derived from this software
1972445Sassar *    without specific prior written permission.
2072445Sassar *
2172445Sassar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2272445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2372445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2472445Sassar * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2572445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2672445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2772445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2872445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2972445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3072445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3172445Sassar * SUCH DAMAGE.
3272445Sassar */
3372445Sassar
3472445Sassar#include "rcp_locl.h"
3578527Sassar#include <getarg.h>
3672445Sassar
3772445Sassar#define RSH_PROGRAM "rsh"
3872445Sassar
3972445Sassarstruct  passwd *pwd;
4072445Sassaruid_t	userid;
4172445Sassarint     errs, remin, remout;
4272445Sassarint     pflag, iamremote, iamrecursive, targetshouldbedirectory;
4372445Sassarint     doencrypt, noencrypt;
4478527Sassarint     usebroken, usekrb5, forwardtkt;
4572445Sassarchar    *port;
4672445Sassar
4772445Sassar#define	CMDNEEDS	64
4872445Sassarchar cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
4972445Sassar
5072445Sassarint	 response (void);
5172445Sassarvoid	 rsource (char *, struct stat *);
5272445Sassarvoid	 sink (int, char *[]);
5372445Sassarvoid	 source (int, char *[]);
5472445Sassarvoid	 tolocal (int, char *[]);
5572445Sassarvoid	 toremote (char *, int, char *[]);
5672445Sassar
5772445Sassarint      do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
5872445Sassar
5978527Sassarstatic int fflag, tflag;
6078527Sassar
6178527Sassarstatic int version_flag, help_flag;
6278527Sassar
6378527Sassarstruct getargs args[] = {
6478527Sassar    { NULL,	'5', arg_flag,		&usekrb5,	"use Kerberos 5 authentication" },
6578527Sassar    { NULL,	'F', arg_flag,		&forwardtkt,	"forward credentials" },
6678527Sassar    { NULL,	'K', arg_flag,		&usebroken,	"use BSD authentication" },
6778527Sassar    { NULL,	'P', arg_string,	&port,		"non-default port", "port" },
6878527Sassar    { NULL,	'p', arg_flag,		&pflag,	"preserve file permissions" },
6978527Sassar    { NULL,	'r', arg_flag,		&iamrecursive,	"recursive mode" },
7078527Sassar    { NULL,	'x', arg_flag,		&doencrypt,	"use encryption" },
7178527Sassar    { NULL,	'z', arg_flag,		&noencrypt,	"don't encrypt" },
7278527Sassar    { NULL,	'd', arg_flag,		&targetshouldbedirectory },
7378527Sassar    { NULL,	'f', arg_flag,		&fflag },
7478527Sassar    { NULL,	't', arg_flag,		&tflag },
7578527Sassar    { "version", 0,  arg_flag,		&version_flag },
7678527Sassar    { "help",	 0,  arg_flag,		&help_flag }
7778527Sassar};
7878527Sassar
7978527Sassarstatic void
8078527Sassarusage (int ret)
8178527Sassar{
8278527Sassar    arg_printusage (args,
8378527Sassar		    sizeof(args) / sizeof(args[0]),
8478527Sassar		    NULL,
8578527Sassar		    "file1 file2|file... directory");
8678527Sassar    exit (ret);
8778527Sassar}
8878527Sassar
8972445Sassarint
9078527Sassarmain(int argc, char **argv)
9172445Sassar{
9272445Sassar	char *targ;
9378527Sassar	int optind = 0;
9472445Sassar
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	}
10478527Sassar
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. */
11778527Sassar		response();
11878527Sassar		setuid(userid);
11972445Sassar		source(argc, argv);
12072445Sassar		exit(errs);
12172445Sassar	}
12272445Sassar
12372445Sassar	if (tflag) {			/* Receive data. */
12478527Sassar		setuid(userid);
12572445Sassar		sink(argc, argv);
12672445Sassar		exit(errs);
12772445Sassar	}
12872445Sassar
12972445Sassar	if (argc < 2)
13078527Sassar	    usage(1);
13172445Sassar	if (argc > 2)
13272445Sassar		targetshouldbedirectory = 1;
13372445Sassar
13472445Sassar	remin = remout = -1;
13572445Sassar	/* Command to be executed on remote system using "rsh". */
13678527Sassar	 sprintf(cmd, "rcp%s%s%s", iamrecursive ? " -r" : "",
13772445Sassar		       pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
13872445Sassar
13978527Sassar	signal(SIGPIPE, lostconn);
14072445Sassar
14172445Sassar	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
14272445Sassar		toremote(targ, argc, argv);
14372445Sassar	else {
14472445Sassar		tolocal(argc, argv);		/* Dest is local host. */
14572445Sassar		if (targetshouldbedirectory)
14672445Sassar			verifydir(argv[argc - 1]);
14772445Sassar	}
14872445Sassar	exit(errs);
14972445Sassar}
15072445Sassar
15172445Sassarvoid
15278527Sassartoremote(char *targ, int argc, char **argv)
15372445Sassar{
15472445Sassar	int i, len;
15572445Sassar	char *bp, *host, *src, *suser, *thost, *tuser;
15672445Sassar
15772445Sassar	*targ++ = 0;
15872445Sassar	if (*targ == 0)
15972445Sassar		targ = ".";
16072445Sassar
16172445Sassar	if ((thost = strchr(argv[argc - 1], '@'))) {
16272445Sassar		/* user@host */
16372445Sassar		*thost++ = 0;
16472445Sassar		tuser = argv[argc - 1];
16572445Sassar		if (*tuser == '\0')
16672445Sassar			tuser = NULL;
16772445Sassar		else if (!okname(tuser))
16872445Sassar			exit(1);
16972445Sassar	} else {
17072445Sassar		thost = argv[argc - 1];
17172445Sassar		tuser = NULL;
17272445Sassar	}
17372445Sassar
17472445Sassar	for (i = 0; i < argc - 1; i++) {
17572445Sassar		src = colon(argv[i]);
17672445Sassar		if (src) {			/* remote to remote */
17772445Sassar			*src++ = 0;
17872445Sassar			if (*src == 0)
17972445Sassar				src = ".";
18072445Sassar			host = strchr(argv[i], '@');
18172445Sassar			len = strlen(_PATH_RSH) + strlen(argv[i]) +
18272445Sassar			    strlen(src) + (tuser ? strlen(tuser) : 0) +
18372445Sassar			    strlen(thost) + strlen(targ) + CMDNEEDS + 20;
18472445Sassar			if (!(bp = malloc(len)))
18572445Sassar				err(1, "malloc");
18672445Sassar			if (host) {
18772445Sassar				*host++ = 0;
18872445Sassar				suser = argv[i];
18972445Sassar				if (*suser == '\0')
19072445Sassar					suser = pwd->pw_name;
19172445Sassar				else if (!okname(suser))
19272445Sassar					continue;
19378527Sassar				snprintf(bp, len,
19472445Sassar				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
19572445Sassar				    _PATH_RSH, host, suser, cmd, src,
19672445Sassar				    tuser ? tuser : "", tuser ? "@" : "",
19772445Sassar				    thost, targ);
19872445Sassar			} else
19978527Sassar				snprintf(bp, len,
20072445Sassar				    "exec %s %s -n %s %s '%s%s%s:%s'",
20172445Sassar				    _PATH_RSH, argv[i], cmd, src,
20272445Sassar				    tuser ? tuser : "", tuser ? "@" : "",
20372445Sassar				    thost, targ);
20478527Sassar			susystem(bp, userid);
20578527Sassar			free(bp);
20672445Sassar		} else {			/* local to remote */
20772445Sassar			if (remin == -1) {
20872445Sassar				len = strlen(targ) + CMDNEEDS + 20;
20972445Sassar				if (!(bp = malloc(len)))
21072445Sassar					err(1, "malloc");
21178527Sassar				snprintf(bp, len, "%s -t %s", cmd, targ);
21272445Sassar				host = thost;
21372445Sassar
21472445Sassar				if (do_cmd(host, tuser, bp, &remin, &remout) < 0)
21572445Sassar					exit(1);
21672445Sassar
21772445Sassar				if (response() < 0)
21872445Sassar					exit(1);
21978527Sassar				free(bp);
22078527Sassar				setuid(userid);
22172445Sassar			}
22272445Sassar			source(1, argv+i);
22372445Sassar		}
22472445Sassar	}
22572445Sassar}
22672445Sassar
22772445Sassarvoid
22878527Sassartolocal(int argc, char **argv)
22972445Sassar{
23072445Sassar	int i, len;
23172445Sassar	char *bp, *host, *src, *suser;
23272445Sassar
23372445Sassar	for (i = 0; i < argc - 1; i++) {
23472445Sassar		if (!(src = colon(argv[i]))) {		/* Local to local. */
23572445Sassar			len = strlen(_PATH_CP) + strlen(argv[i]) +
23672445Sassar			    strlen(argv[argc - 1]) + 20;
23772445Sassar			if (!(bp = malloc(len)))
23872445Sassar				err(1, "malloc");
23978527Sassar			snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
24072445Sassar			    iamrecursive ? " -PR" : "", pflag ? " -p" : "",
24172445Sassar			    argv[i], argv[argc - 1]);
24272445Sassar			if (susystem(bp, userid))
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		}
26172445Sassar		len = strlen(src) + CMDNEEDS + 20;
26272445Sassar		if ((bp = malloc(len)) == NULL)
26372445Sassar			err(1, "malloc");
26478527Sassar		snprintf(bp, len, "%s -f %s", cmd, src);
26572445Sassar		if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
26678527Sassar			free(bp);
26772445Sassar			++errs;
26872445Sassar			continue;
26972445Sassar		}
27078527Sassar		free(bp);
27172445Sassar		sink(1, argv + argc - 1);
27278527Sassar		seteuid(0);
27378527Sassar		close(remin);
27472445Sassar		remin = remout = -1;
27572445Sassar	}
27672445Sassar}
27772445Sassar
27878527Sassarstatic char *
27978527Sassarsizestr(off_t size)
28078527Sassar{
28178527Sassar    static char ss[32];
28278527Sassar    char *p;
28378527Sassar    ss[sizeof(ss) - 1] = '\0';
28478527Sassar    for(p = ss + sizeof(ss) - 2; p >= ss; p--) {
28578527Sassar	*p = '0' + size % 10;
28678527Sassar	size /= 10;
28778527Sassar	if(size == 0)
28878527Sassar	    break;
28978527Sassar    }
29078527Sassar    return ss;
29178527Sassar}
29278527Sassar
29378527Sassar
29472445Sassarvoid
29578527Sassarsource(int argc, char **argv)
29672445Sassar{
29772445Sassar	struct stat stb;
29872445Sassar	static BUF buffer;
29972445Sassar	BUF *bp;
30072445Sassar	off_t i;
30172445Sassar	int amt, fd, haderr, indx, result;
30272445Sassar	char *last, *name, buf[BUFSIZ];
30372445Sassar
30472445Sassar	for (indx = 0; indx < argc; ++indx) {
30572445Sassar                name = argv[indx];
30672445Sassar		if ((fd = open(name, O_RDONLY, 0)) < 0)
30772445Sassar			goto syserr;
30872445Sassar		if (fstat(fd, &stb)) {
30972445Sassarsyserr:			run_err("%s: %s", name, strerror(errno));
31072445Sassar			goto next;
31172445Sassar		}
31272445Sassar		switch (stb.st_mode & S_IFMT) {
31372445Sassar		case S_IFREG:
31472445Sassar			break;
31572445Sassar		case S_IFDIR:
31672445Sassar			if (iamrecursive) {
31772445Sassar				rsource(name, &stb);
31872445Sassar				goto next;
31972445Sassar			}
32072445Sassar			/* FALLTHROUGH */
32172445Sassar		default:
32272445Sassar			run_err("%s: not a regular file", name);
32372445Sassar			goto next;
32472445Sassar		}
32572445Sassar		if ((last = strrchr(name, '/')) == NULL)
32672445Sassar			last = name;
32772445Sassar		else
32872445Sassar			++last;
32972445Sassar		if (pflag) {
33072445Sassar			/*
33172445Sassar			 * Make it compatible with possible future
33272445Sassar			 * versions expecting microseconds.
33372445Sassar			 */
33478527Sassar			snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
33572445Sassar			    (long)stb.st_mtime,
33672445Sassar			    (long)stb.st_atime);
33778527Sassar			write(remout, buf, strlen(buf));
33872445Sassar			if (response() < 0)
33972445Sassar				goto next;
34072445Sassar		}
34172445Sassar#define	MODEMASK	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
34278527Sassar		snprintf(buf, sizeof(buf), "C%04o %s %s\n",
34378527Sassar			 stb.st_mode & MODEMASK, sizestr(stb.st_size), last);
34478527Sassar		write(remout, buf, strlen(buf));
34572445Sassar		if (response() < 0)
34672445Sassar			goto next;
34772445Sassar		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
34878527Sassarnext:			close(fd);
34972445Sassar			continue;
35072445Sassar		}
35172445Sassar
35272445Sassar		/* Keep writing after an error so that we stay sync'd up. */
35372445Sassar		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
35472445Sassar			amt = bp->cnt;
35572445Sassar			if (i + amt > stb.st_size)
35672445Sassar				amt = stb.st_size - i;
35772445Sassar			if (!haderr) {
35872445Sassar				result = read(fd, bp->buf, amt);
35972445Sassar				if (result != amt)
36072445Sassar					haderr = result >= 0 ? EIO : errno;
36172445Sassar			}
36272445Sassar			if (haderr)
36378527Sassar				write(remout, bp->buf, amt);
36472445Sassar			else {
36572445Sassar				result = write(remout, bp->buf, amt);
36672445Sassar				if (result != amt)
36772445Sassar					haderr = result >= 0 ? EIO : errno;
36872445Sassar			}
36972445Sassar		}
37072445Sassar		if (close(fd) && !haderr)
37172445Sassar			haderr = errno;
37272445Sassar		if (!haderr)
37378527Sassar			write(remout, "", 1);
37472445Sassar		else
37572445Sassar			run_err("%s: %s", name, strerror(haderr));
37678527Sassar		response();
37772445Sassar	}
37872445Sassar}
37972445Sassar
38072445Sassarvoid
38178527Sassarrsource(char *name, struct stat *statp)
38272445Sassar{
38372445Sassar	DIR *dirp;
38472445Sassar	struct dirent *dp;
38572445Sassar	char *last, *vect[1], path[MAXPATHLEN];
38672445Sassar
38772445Sassar	if (!(dirp = opendir(name))) {
38872445Sassar		run_err("%s: %s", name, strerror(errno));
38972445Sassar		return;
39072445Sassar	}
39172445Sassar	last = strrchr(name, '/');
39272445Sassar	if (last == 0)
39372445Sassar		last = name;
39472445Sassar	else
39572445Sassar		last++;
39672445Sassar	if (pflag) {
39778527Sassar		snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
39872445Sassar		    (long)statp->st_mtime,
39972445Sassar		    (long)statp->st_atime);
40078527Sassar		write(remout, path, strlen(path));
40172445Sassar		if (response() < 0) {
40272445Sassar			closedir(dirp);
40372445Sassar			return;
40472445Sassar		}
40572445Sassar	}
40678527Sassar	snprintf(path, sizeof(path),
40772445Sassar	    "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
40878527Sassar	write(remout, path, strlen(path));
40972445Sassar	if (response() < 0) {
41072445Sassar		closedir(dirp);
41172445Sassar		return;
41272445Sassar	}
41372445Sassar	while ((dp = readdir(dirp))) {
41472445Sassar		if (dp->d_ino == 0)
41572445Sassar			continue;
41672445Sassar		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
41772445Sassar			continue;
41872445Sassar		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
41972445Sassar			run_err("%s/%s: name too long", name, dp->d_name);
42072445Sassar			continue;
42172445Sassar		}
42278527Sassar		snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
42372445Sassar		vect[0] = path;
42472445Sassar		source(1, vect);
42572445Sassar	}
42678527Sassar	closedir(dirp);
42778527Sassar	write(remout, "E\n", 2);
42878527Sassar	response();
42972445Sassar}
43072445Sassar
43172445Sassarvoid
43278527Sassarsink(int argc, char **argv)
43372445Sassar{
43472445Sassar	static BUF buffer;
43572445Sassar	struct stat stb;
43672445Sassar	struct timeval tv[2];
43772445Sassar	enum { YES, NO, DISPLAYED } wrerr;
43872445Sassar	BUF *bp;
43972445Sassar	off_t i, j, size;
44072445Sassar	int amt, count, exists, first, mask, mode, ofd, omode;
44172445Sassar	int setimes, targisdir, wrerrno = 0;
44272445Sassar	char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ];
44372445Sassar
44472445Sassar#define	atime	tv[0]
44572445Sassar#define	mtime	tv[1]
44672445Sassar#define	SCREWUP(str)	{ why = str; goto screwup; }
44772445Sassar
44872445Sassar	setimes = targisdir = 0;
44972445Sassar	mask = umask(0);
45072445Sassar	if (!pflag)
45178527Sassar		umask(mask);
45272445Sassar	if (argc != 1) {
45372445Sassar		run_err("ambiguous target");
45472445Sassar		exit(1);
45572445Sassar	}
45672445Sassar	targ = *argv;
45772445Sassar	if (targetshouldbedirectory)
45872445Sassar		verifydir(targ);
45978527Sassar	write(remout, "", 1);
46072445Sassar	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
46172445Sassar		targisdir = 1;
46272445Sassar	for (first = 1;; first = 0) {
46372445Sassar		cp = buf;
46472445Sassar		if (read(remin, cp, 1) <= 0)
46572445Sassar			return;
46672445Sassar		if (*cp++ == '\n')
46772445Sassar			SCREWUP("unexpected <newline>");
46872445Sassar		do {
46972445Sassar			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
47072445Sassar				SCREWUP("lost connection");
47172445Sassar			*cp++ = ch;
47272445Sassar		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
47372445Sassar		*cp = 0;
47472445Sassar
47572445Sassar		if (buf[0] == '\01' || buf[0] == '\02') {
47672445Sassar			if (iamremote == 0)
47778527Sassar				write(STDERR_FILENO,
47872445Sassar				    buf + 1, strlen(buf + 1));
47972445Sassar			if (buf[0] == '\02')
48072445Sassar				exit(1);
48172445Sassar			++errs;
48272445Sassar			continue;
48372445Sassar		}
48472445Sassar		if (buf[0] == 'E') {
48578527Sassar			write(remout, "", 1);
48672445Sassar			return;
48772445Sassar		}
48872445Sassar
48972445Sassar		if (ch == '\n')
49072445Sassar			*--cp = 0;
49172445Sassar
49272445Sassar		cp = buf;
49372445Sassar		if (*cp == 'T') {
49472445Sassar			setimes++;
49572445Sassar			cp++;
49672445Sassar			mtime.tv_sec = strtol(cp, &cp, 10);
49772445Sassar			if (!cp || *cp++ != ' ')
49872445Sassar				SCREWUP("mtime.sec not delimited");
49972445Sassar			mtime.tv_usec = strtol(cp, &cp, 10);
50072445Sassar			if (!cp || *cp++ != ' ')
50172445Sassar				SCREWUP("mtime.usec not delimited");
50272445Sassar			atime.tv_sec = strtol(cp, &cp, 10);
50372445Sassar			if (!cp || *cp++ != ' ')
50472445Sassar				SCREWUP("atime.sec not delimited");
50572445Sassar			atime.tv_usec = strtol(cp, &cp, 10);
50672445Sassar			if (!cp || *cp++ != '\0')
50772445Sassar				SCREWUP("atime.usec not delimited");
50878527Sassar			write(remout, "", 1);
50972445Sassar			continue;
51072445Sassar		}
51172445Sassar		if (*cp != 'C' && *cp != 'D') {
51272445Sassar			/*
51372445Sassar			 * Check for the case "rcp remote:foo\* local:bar".
51472445Sassar			 * In this case, the line "No match." can be returned
51572445Sassar			 * by the shell before the rcp command on the remote is
51672445Sassar			 * executed so the ^Aerror_message convention isn't
51772445Sassar			 * followed.
51872445Sassar			 */
51972445Sassar			if (first) {
52072445Sassar				run_err("%s", cp);
52172445Sassar				exit(1);
52272445Sassar			}
52372445Sassar			SCREWUP("expected control record");
52472445Sassar		}
52572445Sassar		mode = 0;
52672445Sassar		for (++cp; cp < buf + 5; cp++) {
52772445Sassar			if (*cp < '0' || *cp > '7')
52872445Sassar				SCREWUP("bad mode");
52972445Sassar			mode = (mode << 3) | (*cp - '0');
53072445Sassar		}
53172445Sassar		if (*cp++ != ' ')
53272445Sassar			SCREWUP("mode not delimited");
53372445Sassar
53472445Sassar		for (size = 0; isdigit(*cp);)
53572445Sassar			size = size * 10 + (*cp++ - '0');
53672445Sassar		if (*cp++ != ' ')
53772445Sassar			SCREWUP("size not delimited");
53872445Sassar		if (targisdir) {
53972445Sassar			static char *namebuf;
54072445Sassar			static int cursize;
54172445Sassar			size_t need;
54272445Sassar
54372445Sassar			need = strlen(targ) + strlen(cp) + 250;
54472445Sassar			if (need > cursize) {
54572445Sassar				if (!(namebuf = malloc(need)))
54672445Sassar					run_err("%s", strerror(errno));
54772445Sassar			}
54878527Sassar			snprintf(namebuf, need, "%s%s%s", targ,
54972445Sassar			    *targ ? "/" : "", cp);
55072445Sassar			np = namebuf;
55172445Sassar		} else
55272445Sassar			np = targ;
55372445Sassar		exists = stat(np, &stb) == 0;
55472445Sassar		if (buf[0] == 'D') {
55572445Sassar			int mod_flag = pflag;
55672445Sassar			if (exists) {
55772445Sassar				if (!S_ISDIR(stb.st_mode)) {
55872445Sassar					errno = ENOTDIR;
55972445Sassar					goto bad;
56072445Sassar				}
56172445Sassar				if (pflag)
56278527Sassar					chmod(np, mode);
56372445Sassar			} else {
56472445Sassar				/* Handle copying from a read-only directory */
56572445Sassar				mod_flag = 1;
56672445Sassar				if (mkdir(np, mode | S_IRWXU) < 0)
56772445Sassar					goto bad;
56872445Sassar			}
56972445Sassar			vect[0] = np;
57072445Sassar			sink(1, vect);
57172445Sassar			if (setimes) {
57272445Sassar				setimes = 0;
57372445Sassar				if (utimes(np, tv) < 0)
57472445Sassar				    run_err("%s: set times: %s",
57572445Sassar					np, strerror(errno));
57672445Sassar			}
57772445Sassar			if (mod_flag)
57878527Sassar				chmod(np, mode);
57972445Sassar			continue;
58072445Sassar		}
58172445Sassar		omode = mode;
58272445Sassar		mode |= S_IWRITE;
58372445Sassar		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
58472445Sassarbad:			run_err("%s: %s", np, strerror(errno));
58572445Sassar			continue;
58672445Sassar		}
58778527Sassar		write(remout, "", 1);
58872445Sassar		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
58978527Sassar			close(ofd);
59072445Sassar			continue;
59172445Sassar		}
59272445Sassar		cp = bp->buf;
59372445Sassar		wrerr = NO;
59472445Sassar		for (count = i = 0; i < size; i += BUFSIZ) {
59572445Sassar			amt = BUFSIZ;
59672445Sassar			if (i + amt > size)
59772445Sassar				amt = size - i;
59872445Sassar			count += amt;
59978527Sassar			if((j = net_read(remin, cp, amt)) != amt) {
60078527Sassar			    run_err("%s", j ? strerror(errno) :
60178527Sassar				    "dropped connection");
60278527Sassar			    exit(1);
60378527Sassar			}
60478527Sassar			amt -= j;
60578527Sassar			cp += j;
60672445Sassar			if (count == bp->cnt) {
60772445Sassar				/* Keep reading so we stay sync'd up. */
60872445Sassar				if (wrerr == NO) {
60972445Sassar					j = write(ofd, bp->buf, count);
61072445Sassar					if (j != count) {
61172445Sassar						wrerr = YES;
61272445Sassar						wrerrno = j >= 0 ? EIO : errno;
61372445Sassar					}
61472445Sassar				}
61572445Sassar				count = 0;
61672445Sassar				cp = bp->buf;
61772445Sassar			}
61872445Sassar		}
61972445Sassar		if (count != 0 && wrerr == NO &&
62072445Sassar		    (j = write(ofd, bp->buf, count)) != count) {
62172445Sassar			wrerr = YES;
62272445Sassar			wrerrno = j >= 0 ? EIO : errno;
62372445Sassar		}
62472445Sassar		if (ftruncate(ofd, size)) {
62572445Sassar			run_err("%s: truncate: %s", np, strerror(errno));
62672445Sassar			wrerr = DISPLAYED;
62772445Sassar		}
62872445Sassar		if (pflag) {
62972445Sassar			if (exists || omode != mode)
63072445Sassar				if (fchmod(ofd, omode))
63172445Sassar					run_err("%s: set mode: %s",
63272445Sassar					    np, strerror(errno));
63372445Sassar		} else {
63472445Sassar			if (!exists && omode != mode)
63572445Sassar				if (fchmod(ofd, omode & ~mask))
63672445Sassar					run_err("%s: set mode: %s",
63772445Sassar					    np, strerror(errno));
63872445Sassar		}
63978527Sassar		close(ofd);
64078527Sassar		response();
64172445Sassar		if (setimes && wrerr == NO) {
64272445Sassar			setimes = 0;
64372445Sassar			if (utimes(np, tv) < 0) {
64472445Sassar				run_err("%s: set times: %s",
64572445Sassar				    np, strerror(errno));
64672445Sassar				wrerr = DISPLAYED;
64772445Sassar			}
64872445Sassar		}
64972445Sassar		switch(wrerr) {
65072445Sassar		case YES:
65172445Sassar			run_err("%s: %s", np, strerror(wrerrno));
65272445Sassar			break;
65372445Sassar		case NO:
65478527Sassar			write(remout, "", 1);
65572445Sassar			break;
65672445Sassar		case DISPLAYED:
65772445Sassar			break;
65872445Sassar		}
65972445Sassar	}
66072445Sassarscrewup:
66172445Sassar	run_err("protocol error: %s", why);
66272445Sassar	exit(1);
66372445Sassar}
66472445Sassar
66572445Sassarint
66678527Sassarresponse(void)
66772445Sassar{
66872445Sassar	char ch, *cp, resp, rbuf[BUFSIZ];
66972445Sassar
67072445Sassar	if (read(remin, &resp, sizeof(resp)) != sizeof(resp))
67172445Sassar		lostconn(0);
67272445Sassar
67372445Sassar	cp = rbuf;
67472445Sassar	switch(resp) {
67572445Sassar	case 0:				/* ok */
67672445Sassar		return (0);
67772445Sassar	default:
67872445Sassar		*cp++ = resp;
67972445Sassar		/* FALLTHROUGH */
68072445Sassar	case 1:				/* error, followed by error msg */
68172445Sassar	case 2:				/* fatal error, "" */
68272445Sassar		do {
68372445Sassar			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
68472445Sassar				lostconn(0);
68572445Sassar			*cp++ = ch;
68672445Sassar		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
68772445Sassar
68872445Sassar		if (!iamremote)
68978527Sassar			write(STDERR_FILENO, rbuf, cp - rbuf);
69072445Sassar		++errs;
69172445Sassar		if (resp == 1)
69272445Sassar			return (-1);
69372445Sassar		exit(1);
69472445Sassar	}
69572445Sassar	/* NOTREACHED */
69672445Sassar}
69772445Sassar
69872445Sassar#include <stdarg.h>
69972445Sassar
70072445Sassarvoid
70172445Sassarrun_err(const char *fmt, ...)
70272445Sassar{
70372445Sassar	static FILE *fp;
70472445Sassar	va_list ap;
70572445Sassar	va_start(ap, fmt);
70672445Sassar
70772445Sassar	++errs;
70872445Sassar	if (fp == NULL && !(fp = fdopen(remout, "w")))
70972445Sassar		return;
71078527Sassar	fprintf(fp, "%c", 0x01);
71178527Sassar	fprintf(fp, "rcp: ");
71278527Sassar	vfprintf(fp, fmt, ap);
71378527Sassar	fprintf(fp, "\n");
71478527Sassar	fflush(fp);
71572445Sassar
71672445Sassar	if (!iamremote)
71772445Sassar		vwarnx(fmt, ap);
71872445Sassar
71972445Sassar	va_end(ap);
72072445Sassar}
72172445Sassar
72272445Sassar/*
72372445Sassar * This function executes the given command as the specified user on the
72472445Sassar * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
72572445Sassar * assigns the input and output file descriptors on success.
72672445Sassar *
72772445Sassar * If it cannot create necessary pipes it exits with error message.
72872445Sassar */
72972445Sassar
73072445Sassarint
73172445Sassardo_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
73272445Sassar{
73372445Sassar	int pin[2], pout[2], reserved[2];
73472445Sassar
73572445Sassar	/*
73672445Sassar	 * Reserve two descriptors so that the real pipes won't get
73772445Sassar	 * descriptors 0 and 1 because that will screw up dup2 below.
73872445Sassar	 */
73972445Sassar	pipe(reserved);
74072445Sassar
74172445Sassar	/* Create a socket pair for communicating with rsh. */
74272445Sassar	if (pipe(pin) < 0) {
74372445Sassar		perror("pipe");
74472445Sassar		exit(255);
74572445Sassar	}
74672445Sassar	if (pipe(pout) < 0) {
74772445Sassar		perror("pipe");
74872445Sassar		exit(255);
74972445Sassar	}
75072445Sassar
75172445Sassar	/* Free the reserved descriptors. */
75272445Sassar	close(reserved[0]);
75372445Sassar	close(reserved[1]);
75472445Sassar
75572445Sassar	/* For a child to execute the command on the remote host using rsh. */
75672445Sassar	if (fork() == 0) {
75772445Sassar		char *args[100];
75872445Sassar		unsigned int i;
75972445Sassar
76072445Sassar		/* Child. */
76172445Sassar		close(pin[1]);
76272445Sassar		close(pout[0]);
76372445Sassar		dup2(pin[0], 0);
76472445Sassar		dup2(pout[1], 1);
76572445Sassar		close(pin[0]);
76672445Sassar		close(pout[1]);
76772445Sassar
76872445Sassar		i = 0;
76972445Sassar		args[i++] = RSH_PROGRAM;
77072445Sassar		if (usekrb5)
77172445Sassar			args[i++] = "-5";
77272445Sassar		if (usebroken)
77372445Sassar			args[i++] = "-K";
77472445Sassar		if (doencrypt)
77572445Sassar			args[i++] = "-x";
77678527Sassar		if (forwardtkt)
77778527Sassar			args[i++] = "-F";
77872445Sassar		if (noencrypt)
77972445Sassar			args[i++] = "-z";
78072445Sassar		if (port != NULL) {
78172445Sassar			args[i++] = "-p";
78272445Sassar			args[i++] = port;
78372445Sassar		}
78472445Sassar		if (remuser != NULL) {
78572445Sassar			args[i++] = "-l";
78672445Sassar			args[i++] = remuser;
78772445Sassar		}
78872445Sassar		args[i++] = host;
78972445Sassar		args[i++] = cmd;
79072445Sassar		args[i++] = NULL;
79172445Sassar
79272445Sassar		execvp(RSH_PROGRAM, args);
79372445Sassar		perror(RSH_PROGRAM);
79472445Sassar		exit(1);
79572445Sassar	}
79672445Sassar	/* Parent.  Close the other side, and return the local side. */
79772445Sassar	close(pin[0]);
79872445Sassar	*fdout = pin[1];
79972445Sassar	close(pout[1]);
80072445Sassar	*fdin = pout[0];
80172445Sassar	return 0;
80272445Sassar}
803