rcp.c revision 90926
1283424Sdchagin/*
2283424Sdchagin * Copyright (c) 1983, 1990, 1992, 1993
3283424Sdchagin *	The Regents of the University of California.  All rights reserved.
4283424Sdchagin *
5283424Sdchagin * Redistribution and use in source and binary forms, with or without
6283424Sdchagin * modification, are permitted provided that the following conditions
7283424Sdchagin * are met:
8283424Sdchagin * 1. Redistributions of source code must retain the above copyright
9283424Sdchagin *    notice, this list of conditions and the following disclaimer.
10283424Sdchagin * 2. Redistributions in binary form must reproduce the above copyright
11283424Sdchagin *    notice, this list of conditions and the following disclaimer in the
12283424Sdchagin *    documentation and/or other materials provided with the distribution.
13283424Sdchagin * 3. All advertising materials mentioning features or use of this software
14283424Sdchagin *    must display the following acknowledgement:
15283424Sdchagin *	This product includes software developed by the University of
16283424Sdchagin *	California, Berkeley and its contributors.
17283424Sdchagin * 4. Neither the name of the University nor the names of its contributors
18283424Sdchagin *    may be used to endorse or promote products derived from this software
19283424Sdchagin *    without specific prior written permission.
20283424Sdchagin *
21283424Sdchagin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22283424Sdchagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23283424Sdchagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24283424Sdchagin * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25283424Sdchagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26283424Sdchagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27283424Sdchagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28283424Sdchagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29283424Sdchagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30283424Sdchagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31283424Sdchagin * SUCH DAMAGE.
32283424Sdchagin */
33283424Sdchagin
34283424Sdchagin#include "rcp_locl.h"
35283424Sdchagin#include <getarg.h>
36283424Sdchagin
37283424Sdchagin#define RSH_PROGRAM "rsh"
38283424Sdchagin
39283424Sdchaginstruct  passwd *pwd;
40283424Sdchaginuid_t	userid;
41283424Sdchaginint     errs, remin, remout;
42283424Sdchaginint     pflag, iamremote, iamrecursive, targetshouldbedirectory;
43283424Sdchaginint     doencrypt, noencrypt;
44283424Sdchaginint     usebroken, usekrb5, forwardtkt;
45283424Sdchaginchar    *port;
46283424Sdchagin
47283424Sdchagin#define	CMDNEEDS	64
48283424Sdchaginchar cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
49283424Sdchagin
50283424Sdchaginint	 response (void);
51283424Sdchaginvoid	 rsource (char *, struct stat *);
52283424Sdchaginvoid	 sink (int, char *[]);
53283424Sdchaginvoid	 source (int, char *[]);
54283424Sdchaginvoid	 tolocal (int, char *[]);
55283424Sdchaginvoid	 toremote (char *, int, char *[]);
56283424Sdchagin
57283424Sdchaginint      do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
58283424Sdchagin
59283424Sdchaginstatic int fflag, tflag;
60283424Sdchagin
61283424Sdchaginstatic int version_flag, help_flag;
62283424Sdchagin
63283424Sdchaginstruct getargs args[] = {
64283424Sdchagin    { NULL,	'5', arg_flag,		&usekrb5,	"use Kerberos 5 authentication" },
65283424Sdchagin    { NULL,	'F', arg_flag,		&forwardtkt,	"forward credentials" },
66283424Sdchagin    { NULL,	'K', arg_flag,		&usebroken,	"use BSD authentication" },
67283424Sdchagin    { NULL,	'P', arg_string,	&port,		"non-default port", "port" },
68283424Sdchagin    { NULL,	'p', arg_flag,		&pflag,	"preserve file permissions" },
69283424Sdchagin    { NULL,	'r', arg_flag,		&iamrecursive,	"recursive mode" },
70283424Sdchagin    { NULL,	'x', arg_flag,		&doencrypt,	"use encryption" },
71283424Sdchagin    { NULL,	'z', arg_flag,		&noencrypt,	"don't encrypt" },
72283424Sdchagin    { NULL,	'd', arg_flag,		&targetshouldbedirectory },
73283424Sdchagin    { NULL,	'f', arg_flag,		&fflag },
74283424Sdchagin    { NULL,	't', arg_flag,		&tflag },
75283424Sdchagin    { "version", 0,  arg_flag,		&version_flag },
76283424Sdchagin    { "help",	 0,  arg_flag,		&help_flag }
77283424Sdchagin};
78283424Sdchagin
79283424Sdchaginstatic void
80283424Sdchaginusage (int ret)
81283424Sdchagin{
82283424Sdchagin    arg_printusage (args,
83283424Sdchagin		    sizeof(args) / sizeof(args[0]),
84283424Sdchagin		    NULL,
85283424Sdchagin		    "file1 file2|file... directory");
86283424Sdchagin    exit (ret);
87283424Sdchagin}
88283424Sdchagin
89283424Sdchaginint
90283424Sdchaginmain(int argc, char **argv)
91283424Sdchagin{
92283424Sdchagin	char *targ;
93283424Sdchagin	int optind = 0;
94283424Sdchagin
95283424Sdchagin	setprogname(argv[0]);
96283424Sdchagin	if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
97283424Sdchagin		    &optind))
98283424Sdchagin	    usage (1);
99283424Sdchagin	if(help_flag)
100283424Sdchagin	    usage(0);
101283424Sdchagin	if (version_flag) {
102283424Sdchagin	    print_version (NULL);
103283424Sdchagin	    return 0;
104283424Sdchagin	}
105283424Sdchagin
106283424Sdchagin	iamremote = (fflag || tflag);
107283424Sdchagin
108283424Sdchagin	argc -= optind;
109283424Sdchagin	argv += optind;
110283424Sdchagin
111283424Sdchagin	if ((pwd = getpwuid(userid = getuid())) == NULL)
112283424Sdchagin		errx(1, "unknown user %d", (int)userid);
113283424Sdchagin
114283424Sdchagin	remin = STDIN_FILENO;		/* XXX */
115283424Sdchagin	remout = STDOUT_FILENO;
116283424Sdchagin
117283424Sdchagin	if (fflag) {			/* Follow "protocol", send data. */
118283424Sdchagin		response();
119283424Sdchagin		setuid(userid);
120283424Sdchagin		source(argc, argv);
121283424Sdchagin		exit(errs);
122283424Sdchagin	}
123283424Sdchagin
124283424Sdchagin	if (tflag) {			/* Receive data. */
125283424Sdchagin		setuid(userid);
126283424Sdchagin		sink(argc, argv);
127283424Sdchagin		exit(errs);
128283424Sdchagin	}
129283424Sdchagin
130283424Sdchagin	if (argc < 2)
131283424Sdchagin	    usage(1);
132283424Sdchagin	if (argc > 2)
133283424Sdchagin		targetshouldbedirectory = 1;
134283424Sdchagin
135283424Sdchagin	remin = remout = -1;
136283424Sdchagin	/* Command to be executed on remote system using "rsh". */
137283424Sdchagin	snprintf(cmd, sizeof(cmd),
138283424Sdchagin		 "rcp%s%s%s", iamrecursive ? " -r" : "",
139283424Sdchagin		 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
140283424Sdchagin
141283424Sdchagin	signal(SIGPIPE, lostconn);
142283424Sdchagin
143283424Sdchagin	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
144283424Sdchagin		toremote(targ, argc, argv);
145283424Sdchagin	else {
146283424Sdchagin		tolocal(argc, argv);		/* Dest is local host. */
147283424Sdchagin		if (targetshouldbedirectory)
148283424Sdchagin			verifydir(argv[argc - 1]);
149283424Sdchagin	}
150283424Sdchagin	exit(errs);
151283424Sdchagin}
152283424Sdchagin
153283424Sdchaginvoid
154283424Sdchagintoremote(char *targ, int argc, char **argv)
155283424Sdchagin{
156283424Sdchagin	int i;
157283424Sdchagin	char *bp, *host, *src, *suser, *thost, *tuser;
158283424Sdchagin
159283424Sdchagin	*targ++ = 0;
160283424Sdchagin	if (*targ == 0)
161283424Sdchagin		targ = ".";
162283424Sdchagin
163283424Sdchagin	if ((thost = strchr(argv[argc - 1], '@'))) {
164283424Sdchagin		/* user@host */
165283424Sdchagin		*thost++ = 0;
166283424Sdchagin		tuser = argv[argc - 1];
167283424Sdchagin		if (*tuser == '\0')
168283424Sdchagin			tuser = NULL;
169283424Sdchagin		else if (!okname(tuser))
170283424Sdchagin			exit(1);
171283424Sdchagin	} else {
172283424Sdchagin		thost = argv[argc - 1];
173283424Sdchagin		tuser = NULL;
174283424Sdchagin	}
175283424Sdchagin
176283424Sdchagin	for (i = 0; i < argc - 1; i++) {
177283424Sdchagin		src = colon(argv[i]);
178283424Sdchagin		if (src) {			/* remote to remote */
179283424Sdchagin			*src++ = 0;
180283424Sdchagin			if (*src == 0)
181283424Sdchagin				src = ".";
182283424Sdchagin			host = strchr(argv[i], '@');
183283424Sdchagin			if (host) {
184283424Sdchagin				*host++ = '\0';
185283424Sdchagin				suser = argv[i];
186283424Sdchagin				if (*suser == '\0')
187283424Sdchagin					suser = pwd->pw_name;
188283424Sdchagin				else if (!okname(suser))
189283424Sdchagin					continue;
190283424Sdchagin				asprintf(&bp,
191283424Sdchagin				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
192283424Sdchagin				    _PATH_RSH, host, suser, cmd, src,
193283424Sdchagin				    tuser ? tuser : "", tuser ? "@" : "",
194283424Sdchagin				    thost, targ);
195283424Sdchagin			} else {
196283424Sdchagin				asprintf(&bp,
197283424Sdchagin				    "exec %s %s -n %s %s '%s%s%s:%s'",
198283424Sdchagin				    _PATH_RSH, argv[i], cmd, src,
199283424Sdchagin				    tuser ? tuser : "", tuser ? "@" : "",
200283424Sdchagin				    thost, targ);
201283424Sdchagin			}
202283424Sdchagin			if (bp == NULL)
203283424Sdchagin				err (1, "malloc");
204283424Sdchagin			susystem(bp, userid);
205283424Sdchagin			free(bp);
206283424Sdchagin		} else {			/* local to remote */
207283424Sdchagin			if (remin == -1) {
208283424Sdchagin				asprintf(&bp, "%s -t %s", cmd, targ);
209283424Sdchagin				if (bp == NULL)
210283424Sdchagin					err (1, "malloc");
211283424Sdchagin				host = thost;
212283424Sdchagin
213283424Sdchagin				if (do_cmd(host, tuser, bp, &remin, &remout) < 0)
214283424Sdchagin					exit(1);
215283424Sdchagin
216283424Sdchagin				if (response() < 0)
217283424Sdchagin					exit(1);
218283424Sdchagin				free(bp);
219283424Sdchagin				setuid(userid);
220283424Sdchagin			}
221283424Sdchagin			source(1, argv+i);
222283424Sdchagin		}
223283424Sdchagin	}
224283424Sdchagin}
225283424Sdchagin
226283424Sdchaginvoid
227283424Sdchagintolocal(int argc, char **argv)
228283424Sdchagin{
229283424Sdchagin	int i;
230283424Sdchagin	char *bp, *host, *src, *suser;
231283424Sdchagin
232283424Sdchagin	for (i = 0; i < argc - 1; i++) {
233283424Sdchagin		if (!(src = colon(argv[i]))) {		/* Local to local. */
234283424Sdchagin			asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP,
235283424Sdchagin			    iamrecursive ? " -PR" : "", pflag ? " -p" : "",
236283424Sdchagin			    argv[i], argv[argc - 1]);
237283424Sdchagin			if (bp == NULL)
238283424Sdchagin				err (1, "malloc");
239283424Sdchagin			if (susystem(bp, userid))
240283424Sdchagin				++errs;
241283424Sdchagin			free(bp);
242283424Sdchagin			continue;
243283424Sdchagin		}
244283424Sdchagin		*src++ = 0;
245283424Sdchagin		if (*src == 0)
246283424Sdchagin			src = ".";
247283424Sdchagin		if ((host = strchr(argv[i], '@')) == NULL) {
248283424Sdchagin			host = argv[i];
249283424Sdchagin			suser = pwd->pw_name;
250283424Sdchagin		} else {
251283424Sdchagin			*host++ = 0;
252283424Sdchagin			suser = argv[i];
253283424Sdchagin			if (*suser == '\0')
254283424Sdchagin				suser = pwd->pw_name;
255283424Sdchagin			else if (!okname(suser))
256283424Sdchagin				continue;
257283424Sdchagin		}
258283424Sdchagin		asprintf(&bp, "%s -f %s", cmd, src);
259283424Sdchagin		if (bp == NULL)
260283424Sdchagin			err (1, "malloc");
261283424Sdchagin		if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
262283424Sdchagin			free(bp);
263283424Sdchagin			++errs;
264283424Sdchagin			continue;
265283424Sdchagin		}
266283424Sdchagin		free(bp);
267283424Sdchagin		sink(1, argv + argc - 1);
268283424Sdchagin		seteuid(0);
269283424Sdchagin		close(remin);
270283424Sdchagin		remin = remout = -1;
271283424Sdchagin	}
272283424Sdchagin}
273283424Sdchagin
274283424Sdchaginvoid
275283424Sdchaginsource(int argc, char **argv)
276283424Sdchagin{
277283424Sdchagin	struct stat stb;
278283424Sdchagin	static BUF buffer;
279283424Sdchagin	BUF *bp;
280283424Sdchagin	off_t i;
281283424Sdchagin	int amt, fd, haderr, indx, result;
282283424Sdchagin	char *last, *name, buf[BUFSIZ];
283283424Sdchagin
284283424Sdchagin	for (indx = 0; indx < argc; ++indx) {
285283424Sdchagin                name = argv[indx];
286283424Sdchagin		if ((fd = open(name, O_RDONLY, 0)) < 0)
287283424Sdchagin			goto syserr;
288283424Sdchagin		if (fstat(fd, &stb)) {
289283424Sdchaginsyserr:			run_err("%s: %s", name, strerror(errno));
290283424Sdchagin			goto next;
291283424Sdchagin		}
292283424Sdchagin		switch (stb.st_mode & S_IFMT) {
293283424Sdchagin		case S_IFREG:
294283424Sdchagin			break;
295283424Sdchagin		case S_IFDIR:
296283424Sdchagin			if (iamrecursive) {
297283424Sdchagin				rsource(name, &stb);
298283424Sdchagin				goto next;
299283424Sdchagin			}
300283424Sdchagin			/* FALLTHROUGH */
301283424Sdchagin		default:
302283424Sdchagin			run_err("%s: not a regular file", name);
303283424Sdchagin			goto next;
304283424Sdchagin		}
305283424Sdchagin		if ((last = strrchr(name, '/')) == NULL)
306283424Sdchagin			last = name;
307283424Sdchagin		else
308283424Sdchagin			++last;
309283424Sdchagin		if (pflag) {
310283424Sdchagin			/*
311283424Sdchagin			 * Make it compatible with possible future
312283424Sdchagin			 * versions expecting microseconds.
313283424Sdchagin			 */
314283424Sdchagin			snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
315283424Sdchagin			    (long)stb.st_mtime,
316283424Sdchagin			    (long)stb.st_atime);
317283424Sdchagin			write(remout, buf, strlen(buf));
318283424Sdchagin			if (response() < 0)
319283424Sdchagin				goto next;
320283424Sdchagin		}
321283424Sdchagin#define	MODEMASK	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
322283424Sdchagin		snprintf(buf, sizeof(buf), "C%04o %lu %s\n",
323283424Sdchagin			 stb.st_mode & MODEMASK,
324283424Sdchagin			 (unsigned long)stb.st_size,
325283424Sdchagin			 last);
326283424Sdchagin		write(remout, buf, strlen(buf));
327283424Sdchagin		if (response() < 0)
328283424Sdchagin			goto next;
329283424Sdchagin		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
330283424Sdchaginnext:			close(fd);
331283424Sdchagin			continue;
332283424Sdchagin		}
333283424Sdchagin
334283424Sdchagin		/* Keep writing after an error so that we stay sync'd up. */
335283424Sdchagin		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
336283424Sdchagin			amt = bp->cnt;
337283424Sdchagin			if (i + amt > stb.st_size)
338283424Sdchagin				amt = stb.st_size - i;
339283424Sdchagin			if (!haderr) {
340283424Sdchagin				result = read(fd, bp->buf, amt);
341283424Sdchagin				if (result != amt)
342283424Sdchagin					haderr = result >= 0 ? EIO : errno;
343283424Sdchagin			}
344283424Sdchagin			if (haderr)
345283424Sdchagin				write(remout, bp->buf, amt);
346283424Sdchagin			else {
347283424Sdchagin				result = write(remout, bp->buf, amt);
348283424Sdchagin				if (result != amt)
349283424Sdchagin					haderr = result >= 0 ? EIO : errno;
350283424Sdchagin			}
351283424Sdchagin		}
352283424Sdchagin		if (close(fd) && !haderr)
353283424Sdchagin			haderr = errno;
354283424Sdchagin		if (!haderr)
355283424Sdchagin			write(remout, "", 1);
356283424Sdchagin		else
357283424Sdchagin			run_err("%s: %s", name, strerror(haderr));
358283424Sdchagin		response();
359283424Sdchagin	}
360283424Sdchagin}
361283424Sdchagin
362283424Sdchaginvoid
363283424Sdchaginrsource(char *name, struct stat *statp)
364283424Sdchagin{
365283424Sdchagin	DIR *dirp;
366283424Sdchagin	struct dirent *dp;
367283424Sdchagin	char *last, *vect[1], path[MAXPATHLEN];
368283424Sdchagin
369283424Sdchagin	if (!(dirp = opendir(name))) {
370283424Sdchagin		run_err("%s: %s", name, strerror(errno));
371283424Sdchagin		return;
372283424Sdchagin	}
373283424Sdchagin	last = strrchr(name, '/');
374283424Sdchagin	if (last == 0)
375283424Sdchagin		last = name;
376283424Sdchagin	else
377283424Sdchagin		last++;
378283424Sdchagin	if (pflag) {
379283424Sdchagin		snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
380283424Sdchagin		    (long)statp->st_mtime,
381283424Sdchagin		    (long)statp->st_atime);
382283424Sdchagin		write(remout, path, strlen(path));
383283424Sdchagin		if (response() < 0) {
384283424Sdchagin			closedir(dirp);
385283424Sdchagin			return;
386283424Sdchagin		}
387283424Sdchagin	}
388283424Sdchagin	snprintf(path, sizeof(path),
389283424Sdchagin	    "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
390283424Sdchagin	write(remout, path, strlen(path));
391283424Sdchagin	if (response() < 0) {
392283424Sdchagin		closedir(dirp);
393283424Sdchagin		return;
394283424Sdchagin	}
395283424Sdchagin	while ((dp = readdir(dirp))) {
396283424Sdchagin		if (dp->d_ino == 0)
397283424Sdchagin			continue;
398283424Sdchagin		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
399283424Sdchagin			continue;
400283424Sdchagin		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
401283424Sdchagin			run_err("%s/%s: name too long", name, dp->d_name);
402283424Sdchagin			continue;
403283424Sdchagin		}
404283424Sdchagin		snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
405283424Sdchagin		vect[0] = path;
406283424Sdchagin		source(1, vect);
407283424Sdchagin	}
408283424Sdchagin	closedir(dirp);
409283424Sdchagin	write(remout, "E\n", 2);
410283424Sdchagin	response();
411283424Sdchagin}
412283424Sdchagin
413283424Sdchaginvoid
414283424Sdchaginsink(int argc, char **argv)
415283424Sdchagin{
416283424Sdchagin	static BUF buffer;
417283424Sdchagin	struct stat stb;
418283424Sdchagin	struct timeval tv[2];
419283424Sdchagin	enum { YES, NO, DISPLAYED } wrerr;
420283424Sdchagin	BUF *bp;
421283424Sdchagin	off_t i, j, size;
422283424Sdchagin	int amt, count, exists, first, mask, mode, ofd, omode;
423283424Sdchagin	int setimes, targisdir, wrerrno = 0;
424283424Sdchagin	char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ];
425283424Sdchagin
426283424Sdchagin#define	atime	tv[0]
427283424Sdchagin#define	mtime	tv[1]
428283424Sdchagin#define	SCREWUP(str)	{ why = str; goto screwup; }
429283424Sdchagin
430283424Sdchagin	setimes = targisdir = 0;
431283424Sdchagin	mask = umask(0);
432283424Sdchagin	if (!pflag)
433		umask(mask);
434	if (argc != 1) {
435		run_err("ambiguous target");
436		exit(1);
437	}
438	targ = *argv;
439	if (targetshouldbedirectory)
440		verifydir(targ);
441	write(remout, "", 1);
442	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
443		targisdir = 1;
444	for (first = 1;; first = 0) {
445		cp = buf;
446		if (read(remin, cp, 1) <= 0)
447			return;
448		if (*cp++ == '\n')
449			SCREWUP("unexpected <newline>");
450		do {
451			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
452				SCREWUP("lost connection");
453			*cp++ = ch;
454		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
455		*cp = 0;
456
457		if (buf[0] == '\01' || buf[0] == '\02') {
458			if (iamremote == 0)
459				write(STDERR_FILENO,
460				    buf + 1, strlen(buf + 1));
461			if (buf[0] == '\02')
462				exit(1);
463			++errs;
464			continue;
465		}
466		if (buf[0] == 'E') {
467			write(remout, "", 1);
468			return;
469		}
470
471		if (ch == '\n')
472			*--cp = 0;
473
474		cp = buf;
475		if (*cp == 'T') {
476			setimes++;
477			cp++;
478			mtime.tv_sec = strtol(cp, &cp, 10);
479			if (!cp || *cp++ != ' ')
480				SCREWUP("mtime.sec not delimited");
481			mtime.tv_usec = strtol(cp, &cp, 10);
482			if (!cp || *cp++ != ' ')
483				SCREWUP("mtime.usec not delimited");
484			atime.tv_sec = strtol(cp, &cp, 10);
485			if (!cp || *cp++ != ' ')
486				SCREWUP("atime.sec not delimited");
487			atime.tv_usec = strtol(cp, &cp, 10);
488			if (!cp || *cp++ != '\0')
489				SCREWUP("atime.usec not delimited");
490			write(remout, "", 1);
491			continue;
492		}
493		if (*cp != 'C' && *cp != 'D') {
494			/*
495			 * Check for the case "rcp remote:foo\* local:bar".
496			 * In this case, the line "No match." can be returned
497			 * by the shell before the rcp command on the remote is
498			 * executed so the ^Aerror_message convention isn't
499			 * followed.
500			 */
501			if (first) {
502				run_err("%s", cp);
503				exit(1);
504			}
505			SCREWUP("expected control record");
506		}
507		mode = 0;
508		for (++cp; cp < buf + 5; cp++) {
509			if (*cp < '0' || *cp > '7')
510				SCREWUP("bad mode");
511			mode = (mode << 3) | (*cp - '0');
512		}
513		if (*cp++ != ' ')
514			SCREWUP("mode not delimited");
515
516		for (size = 0; isdigit((unsigned char)*cp);)
517			size = size * 10 + (*cp++ - '0');
518		if (*cp++ != ' ')
519			SCREWUP("size not delimited");
520		if (targisdir) {
521			static char *namebuf;
522			static int cursize;
523			size_t need;
524
525			need = strlen(targ) + strlen(cp) + 250;
526			if (need > cursize) {
527				if (!(namebuf = malloc(need)))
528					run_err("%s", strerror(errno));
529			}
530			snprintf(namebuf, need, "%s%s%s", targ,
531			    *targ ? "/" : "", cp);
532			np = namebuf;
533		} else
534			np = targ;
535		exists = stat(np, &stb) == 0;
536		if (buf[0] == 'D') {
537			int mod_flag = pflag;
538			if (exists) {
539				if (!S_ISDIR(stb.st_mode)) {
540					errno = ENOTDIR;
541					goto bad;
542				}
543				if (pflag)
544					chmod(np, mode);
545			} else {
546				/* Handle copying from a read-only directory */
547				mod_flag = 1;
548				if (mkdir(np, mode | S_IRWXU) < 0)
549					goto bad;
550			}
551			vect[0] = np;
552			sink(1, vect);
553			if (setimes) {
554				setimes = 0;
555				if (utimes(np, tv) < 0)
556				    run_err("%s: set times: %s",
557					np, strerror(errno));
558			}
559			if (mod_flag)
560				chmod(np, mode);
561			continue;
562		}
563		omode = mode;
564		mode |= S_IWRITE;
565		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
566bad:			run_err("%s: %s", np, strerror(errno));
567			continue;
568		}
569		write(remout, "", 1);
570		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
571			close(ofd);
572			continue;
573		}
574		cp = bp->buf;
575		wrerr = NO;
576		for (count = i = 0; i < size; i += BUFSIZ) {
577			amt = BUFSIZ;
578			if (i + amt > size)
579				amt = size - i;
580			count += amt;
581			if((j = net_read(remin, cp, amt)) != amt) {
582			    run_err("%s", j ? strerror(errno) :
583				    "dropped connection");
584			    exit(1);
585			}
586			amt -= j;
587			cp += j;
588			if (count == bp->cnt) {
589				/* Keep reading so we stay sync'd up. */
590				if (wrerr == NO) {
591					j = write(ofd, bp->buf, count);
592					if (j != count) {
593						wrerr = YES;
594						wrerrno = j >= 0 ? EIO : errno;
595					}
596				}
597				count = 0;
598				cp = bp->buf;
599			}
600		}
601		if (count != 0 && wrerr == NO &&
602		    (j = write(ofd, bp->buf, count)) != count) {
603			wrerr = YES;
604			wrerrno = j >= 0 ? EIO : errno;
605		}
606		if (ftruncate(ofd, size)) {
607			run_err("%s: truncate: %s", np, strerror(errno));
608			wrerr = DISPLAYED;
609		}
610		if (pflag) {
611			if (exists || omode != mode)
612				if (fchmod(ofd, omode))
613					run_err("%s: set mode: %s",
614					    np, strerror(errno));
615		} else {
616			if (!exists && omode != mode)
617				if (fchmod(ofd, omode & ~mask))
618					run_err("%s: set mode: %s",
619					    np, strerror(errno));
620		}
621		close(ofd);
622		response();
623		if (setimes && wrerr == NO) {
624			setimes = 0;
625			if (utimes(np, tv) < 0) {
626				run_err("%s: set times: %s",
627				    np, strerror(errno));
628				wrerr = DISPLAYED;
629			}
630		}
631		switch(wrerr) {
632		case YES:
633			run_err("%s: %s", np, strerror(wrerrno));
634			break;
635		case NO:
636			write(remout, "", 1);
637			break;
638		case DISPLAYED:
639			break;
640		}
641	}
642screwup:
643	run_err("protocol error: %s", why);
644	exit(1);
645}
646
647int
648response(void)
649{
650	char ch, *cp, resp, rbuf[BUFSIZ];
651
652	if (read(remin, &resp, sizeof(resp)) != sizeof(resp))
653		lostconn(0);
654
655	cp = rbuf;
656	switch(resp) {
657	case 0:				/* ok */
658		return (0);
659	default:
660		*cp++ = resp;
661		/* FALLTHROUGH */
662	case 1:				/* error, followed by error msg */
663	case 2:				/* fatal error, "" */
664		do {
665			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
666				lostconn(0);
667			*cp++ = ch;
668		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
669
670		if (!iamremote)
671			write(STDERR_FILENO, rbuf, cp - rbuf);
672		++errs;
673		if (resp == 1)
674			return (-1);
675		exit(1);
676	}
677	/* NOTREACHED */
678}
679
680#include <stdarg.h>
681
682void
683run_err(const char *fmt, ...)
684{
685	static FILE *fp;
686	va_list ap;
687
688	++errs;
689	if (fp == NULL && !(fp = fdopen(remout, "w")))
690		return;
691	va_start(ap, fmt);
692	fprintf(fp, "%c", 0x01);
693	fprintf(fp, "rcp: ");
694	vfprintf(fp, fmt, ap);
695	fprintf(fp, "\n");
696	fflush(fp);
697	va_end(ap);
698
699	if (!iamremote) {
700	    va_start(ap, fmt);
701	    vwarnx(fmt, ap);
702	    va_end(ap);
703	}
704}
705
706/*
707 * This function executes the given command as the specified user on the
708 * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
709 * assigns the input and output file descriptors on success.
710 *
711 * If it cannot create necessary pipes it exits with error message.
712 */
713
714int
715do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
716{
717	int pin[2], pout[2], reserved[2];
718
719	/*
720	 * Reserve two descriptors so that the real pipes won't get
721	 * descriptors 0 and 1 because that will screw up dup2 below.
722	 */
723	pipe(reserved);
724
725	/* Create a socket pair for communicating with rsh. */
726	if (pipe(pin) < 0) {
727		perror("pipe");
728		exit(255);
729	}
730	if (pipe(pout) < 0) {
731		perror("pipe");
732		exit(255);
733	}
734
735	/* Free the reserved descriptors. */
736	close(reserved[0]);
737	close(reserved[1]);
738
739	/* For a child to execute the command on the remote host using rsh. */
740	if (fork() == 0) {
741		char *args[100];
742		unsigned int i;
743
744		/* Child. */
745		close(pin[1]);
746		close(pout[0]);
747		dup2(pin[0], 0);
748		dup2(pout[1], 1);
749		close(pin[0]);
750		close(pout[1]);
751
752		i = 0;
753		args[i++] = RSH_PROGRAM;
754		if (usekrb5)
755			args[i++] = "-5";
756		if (usebroken)
757			args[i++] = "-K";
758		if (doencrypt)
759			args[i++] = "-x";
760		if (forwardtkt)
761			args[i++] = "-F";
762		if (noencrypt)
763			args[i++] = "-z";
764		if (port != NULL) {
765			args[i++] = "-p";
766			args[i++] = port;
767		}
768		if (remuser != NULL) {
769			args[i++] = "-l";
770			args[i++] = remuser;
771		}
772		args[i++] = host;
773		args[i++] = cmd;
774		args[i++] = NULL;
775
776		execvp(RSH_PROGRAM, args);
777		perror(RSH_PROGRAM);
778		exit(1);
779	}
780	/* Parent.  Close the other side, and return the local side. */
781	close(pin[0]);
782	*fdout = pin[1];
783	close(pout[1]);
784	*fdin = pout[0];
785	return 0;
786}
787