xref: /386bsd/usr/src/bin/rcp/rcp.c (revision a2142627)
1 /*
2  * Copyright (c) 1983, 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)rcp.c	5.32 (Berkeley) 2/25/91";
42 #endif /* not lint */
43 
44 /*
45  * rcp
46  */
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/time.h>
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
52 #include <sys/wait.h>
53 #include <netinet/in.h>
54 #include <netinet/in_systm.h>
55 #include <netinet/ip.h>
56 #include <dirent.h>
57 #include <fcntl.h>
58 #include <signal.h>
59 #include <pwd.h>
60 #include <netdb.h>
61 #include <errno.h>
62 #include <unistd.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <ctype.h>
67 #include "pathnames.h"
68 
69 #ifdef KERBEROS
70 #include <kerberosIV/des.h>
71 #include <kerberosIV/krb.h>
72 char	dst_realm_buf[REALM_SZ];
73 char	*dest_realm = NULL;
74 int	use_kerberos = 1;
75 CREDENTIALS 	cred;
76 Key_schedule	schedule;
77 extern	char	*krb_realmofhost();
78 #ifdef CRYPT
79 int	doencrypt = 0;
80 #define	OPTIONS	"dfk:prtx"
81 #else
82 #define	OPTIONS	"dfk:prt"
83 #endif
84 #else
85 #define	OPTIONS "dfprt"
86 #endif
87 
88 struct passwd *pwd;
89 u_short	port;
90 uid_t	userid;
91 int errs, rem;
92 int pflag, iamremote, iamrecursive, targetshouldbedirectory;
93 
94 #define	CMDNEEDS	64
95 char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
96 
97 typedef struct _buf {
98 	int	cnt;
99 	char	*buf;
100 } BUF;
101 
102 void lostconn();
103 
main(argc,argv)104 main(argc, argv)
105 	int argc;
106 	char **argv;
107 {
108 	extern int optind;
109 	extern char *optarg;
110 	struct servent *sp;
111 	int ch, fflag, tflag;
112 	char *targ, *shell, *colon();
113 
114 	fflag = tflag = 0;
115 	while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
116 		switch(ch) {
117 		/* user-visible flags */
118 		case 'p':			/* preserve access/mod times */
119 			++pflag;
120 			break;
121 		case 'r':
122 			++iamrecursive;
123 			break;
124 #ifdef	KERBEROS
125 		case 'k':
126 			strncpy(dst_realm_buf, optarg, REALM_SZ);
127 			dest_realm = dst_realm_buf;
128 			break;
129 #ifdef CRYPT
130 		case 'x':
131 			doencrypt = 1;
132 			/* des_set_key(cred.session, schedule); */
133 			break;
134 #endif
135 #endif
136 		/* rshd-invoked options (server) */
137 		case 'd':
138 			targetshouldbedirectory = 1;
139 			break;
140 		case 'f':			/* "from" */
141 			iamremote = 1;
142 			fflag = 1;
143 			break;
144 		case 't':			/* "to" */
145 			iamremote = 1;
146 			tflag = 1;
147 			break;
148 
149 		case '?':
150 		default:
151 			usage();
152 		}
153 	argc -= optind;
154 	argv += optind;
155 
156 #ifdef KERBEROS
157 #ifdef CRYPT
158 	shell = doencrypt ? "ekshell" : "kshell";
159 #else
160 	shell = "kshell";
161 #endif
162 	sp = getservbyname(shell, "tcp");
163 	if (sp == NULL) {
164 		char	msgbuf[64];
165 		use_kerberos = 0;
166 		(void)snprintf(msgbuf, sizeof(msgbuf),
167 		    "can't get entry for %s/tcp service", shell);
168 		old_warning(msgbuf);
169 		sp = getservbyname(shell = "shell", "tcp");
170 	}
171 #else
172 	sp = getservbyname(shell = "shell", "tcp");
173 #endif
174 	if (sp == NULL) {
175 		(void)fprintf(stderr, "rcp: %s/tcp: unknown service\n", shell);
176 		exit(1);
177 	}
178 	port = sp->s_port;
179 
180 	if (!(pwd = getpwuid(userid = getuid()))) {
181 		(void)fprintf(stderr, "rcp: unknown user %d.\n", (int)userid);
182 		exit(1);
183 	}
184 
185 	if (fflag) {
186 		/* follow "protocol", send data */
187 		(void)response();
188 		(void)setuid(userid);
189 		source(argc, argv);
190 		exit(errs);
191 	}
192 
193 	if (tflag) {
194 		/* receive data */
195 		(void)setuid(userid);
196 		sink(argc, argv);
197 		exit(errs);
198 	}
199 
200 	if (argc < 2)
201 		usage();
202 	if (argc > 2)
203 		targetshouldbedirectory = 1;
204 
205 	rem = -1;
206 	/* command to be executed on remote system using "rsh" */
207 #ifdef	KERBEROS
208 	(void)snprintf(cmd, sizeof(cmd),
209 	    "rcp%s%s%s%s", iamrecursive ? " -r" : "",
210 #ifdef CRYPT
211 	    ((doencrypt && use_kerberos) ? " -x" : ""),
212 #else
213 	    "",
214 #endif
215 	    pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
216 #else
217 	(void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
218 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
219 	    targetshouldbedirectory ? " -d" : "");
220 #endif
221 
222 	(void)signal(SIGPIPE, lostconn);
223 
224 	if (targ = colon(argv[argc - 1]))
225 		toremote(targ, argc, argv);	/* destination is remote host */
226 	else {
227 		tolocal(argc, argv);		/* destination is local host */
228 		if (targetshouldbedirectory)
229 			verifydir(argv[argc - 1]);
230 	}
231 	exit(errs);
232 }
233 
toremote(targ,argc,argv)234 toremote(targ, argc, argv)
235 	char *targ;
236 	int argc;
237 	char **argv;
238 {
239 	int i, len, tos;
240 	char *bp, *host, *src, *suser, *thost, *tuser;
241 	char *colon();
242 
243 	*targ++ = 0;
244 	if (*targ == 0)
245 		targ = ".";
246 
247 	if (thost = index(argv[argc - 1], '@')) {
248 		/* user@host */
249 		*thost++ = 0;
250 		tuser = argv[argc - 1];
251 		if (*tuser == '\0')
252 			tuser = NULL;
253 		else if (!okname(tuser))
254 			exit(1);
255 	} else {
256 		thost = argv[argc - 1];
257 		tuser = NULL;
258 	}
259 
260 	for (i = 0; i < argc - 1; i++) {
261 		src = colon(argv[i]);
262 		if (src) {			/* remote to remote */
263 			*src++ = 0;
264 			if (*src == 0)
265 				src = ".";
266 			host = index(argv[i], '@');
267 			len = strlen(_PATH_RSH) + strlen(argv[i]) +
268 			    strlen(src) + (tuser ? strlen(tuser) : 0) +
269 			    strlen(thost) + strlen(targ) + CMDNEEDS + 20;
270 			if (!(bp = malloc(len)))
271 				nospace();
272 			if (host) {
273 				*host++ = 0;
274 				suser = argv[i];
275 				if (*suser == '\0')
276 					suser = pwd->pw_name;
277 				else if (!okname(suser))
278 					continue;
279 				(void)snprintf(bp, len,
280 				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
281 				    _PATH_RSH, host, suser, cmd, src,
282 				    tuser ? tuser : "", tuser ? "@" : "",
283 				    thost, targ);
284 			} else
285 				(void)snprintf(bp, len,
286 				    "%s %s -n %s %s '%s%s%s:%s'",
287 				    _PATH_RSH, argv[i], cmd, src,
288 				    tuser ? tuser : "", tuser ? "@" : "",
289 				    thost, targ);
290 			(void)susystem(bp);
291 			(void)free(bp);
292 		} else {			/* local to remote */
293 			if (rem == -1) {
294 				len = strlen(targ) + CMDNEEDS + 20;
295 				if (!(bp = malloc(len)))
296 					nospace();
297 				(void)snprintf(bp, len, "%s -t %s", cmd, targ);
298 				host = thost;
299 #ifdef KERBEROS
300 				if (use_kerberos)
301 					rem = kerberos(&host, bp,
302 					    pwd->pw_name,
303 					    tuser ? tuser : pwd->pw_name);
304 				else
305 #endif
306 					rem = rcmd(&host, port, pwd->pw_name,
307 					    tuser ? tuser : pwd->pw_name,
308 					    bp, 0);
309 				if (rem < 0)
310 					exit(1);
311 				tos = IPTOS_THROUGHPUT;
312 				if (setsockopt(rem, IPPROTO_IP, IP_TOS,
313 				    (char *)&tos, sizeof(int)) < 0)
314 					perror("rcp: setsockopt TOS (ignored)");
315 				if (response() < 0)
316 					exit(1);
317 				(void)free(bp);
318 				(void)setuid(userid);
319 			}
320 			source(1, argv+i);
321 		}
322 	}
323 }
324 
tolocal(argc,argv)325 tolocal(argc, argv)
326 	int argc;
327 	char **argv;
328 {
329 	int i, len, tos;
330 	char *bp, *host, *src, *suser;
331 	char *colon();
332 
333 	for (i = 0; i < argc - 1; i++) {
334 		if (!(src = colon(argv[i]))) {	/* local to local */
335 			len = strlen(_PATH_CP) + strlen(argv[i]) +
336 			    strlen(argv[argc - 1]) + 20;
337 			if (!(bp = malloc(len)))
338 				nospace();
339 			(void)snprintf(bp, len, "%s%s%s %s %s", _PATH_CP,
340 			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
341 			    argv[i], argv[argc - 1]);
342 			(void)susystem(bp);
343 			(void)free(bp);
344 			continue;
345 		}
346 		*src++ = 0;
347 		if (*src == 0)
348 			src = ".";
349 		host = index(argv[i], '@');
350 		if (host) {
351 			*host++ = 0;
352 			suser = argv[i];
353 			if (*suser == '\0')
354 				suser = pwd->pw_name;
355 			else if (!okname(suser))
356 				continue;
357 		} else {
358 			host = argv[i];
359 			suser = pwd->pw_name;
360 		}
361 		len = strlen(src) + CMDNEEDS + 20;
362 		if (!(bp = malloc(len)))
363 			nospace();
364 		(void)snprintf(bp, len, "%s -f %s", cmd, src);
365 #ifdef KERBEROS
366 		if (use_kerberos)
367 			rem = kerberos(&host, bp, pwd->pw_name, suser);
368 		else
369 #endif
370 			rem = rcmd(&host, port, pwd->pw_name, suser, bp, 0);
371 		(void)free(bp);
372 		if (rem < 0)
373 			continue;
374 		(void)seteuid(userid);
375 		tos = IPTOS_THROUGHPUT;
376 		if (setsockopt(rem, IPPROTO_IP, IP_TOS,
377 		    (char *)&tos, sizeof(int)) < 0)
378 			perror("rcp: setsockopt TOS (ignored)");
379 		sink(1, argv + argc - 1);
380 		(void)seteuid(0);
381 		(void)close(rem);
382 		rem = -1;
383 	}
384 }
385 
verifydir(cp)386 verifydir(cp)
387 	char *cp;
388 {
389 	struct stat stb;
390 
391 	if (stat(cp, &stb) >= 0) {
392 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
393 			return;
394 		errno = ENOTDIR;
395 	}
396 	error("rcp: %s: %s.\n", cp, strerror(errno));
397 	exit(1);
398 }
399 
400 char *
colon(cp)401 colon(cp)
402 	register char *cp;
403 {
404 	for (; *cp; ++cp) {
405 		if (*cp == ':')
406 			return(cp);
407 		if (*cp == '/')
408 			return(0);
409 	}
410 	return(0);
411 }
412 
okname(cp0)413 okname(cp0)
414 	char *cp0;
415 {
416 	register char *cp = cp0;
417 	register int c;
418 
419 	do {
420 		c = *cp;
421 		if (c & 0200)
422 			goto bad;
423 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
424 			goto bad;
425 	} while (*++cp);
426 	return(1);
427 bad:
428 	(void)fprintf(stderr, "rcp: invalid user name %s\n", cp0);
429 	return(0);
430 }
431 
susystem(s)432 susystem(s)
433 	char *s;
434 {
435 	int status, pid, w;
436 	register sig_t istat, qstat;
437 
438 	if ((pid = vfork()) == 0) {
439 		(void)setuid(userid);
440 		execl(_PATH_BSHELL, "sh", "-c", s, (char *)0);
441 		_exit(127);
442 	}
443 	istat = signal(SIGINT, SIG_IGN);
444 	qstat = signal(SIGQUIT, SIG_IGN);
445 	while ((w = wait(&status)) != pid && w != -1)
446 		;
447 	if (w == -1)
448 		status = -1;
449 	(void)signal(SIGINT, istat);
450 	(void)signal(SIGQUIT, qstat);
451 	return(status);
452 }
453 
source(argc,argv)454 source(argc, argv)
455 	int argc;
456 	char **argv;
457 {
458 	struct stat stb;
459 	static BUF buffer;
460 	BUF *bp;
461 	off_t i;
462 	int x, readerr, f, amt;
463 	char *last, *name, buf[BUFSIZ];
464 	BUF *allocbuf();
465 
466 	for (x = 0; x < argc; x++) {
467 		name = argv[x];
468 		if ((f = open(name, O_RDONLY, 0)) < 0) {
469 			error("rcp: %s: %s\n", name, strerror(errno));
470 			continue;
471 		}
472 		if (fstat(f, &stb) < 0)
473 			goto notreg;
474 		switch (stb.st_mode&S_IFMT) {
475 
476 		case S_IFREG:
477 			break;
478 
479 		case S_IFDIR:
480 			if (iamrecursive) {
481 				(void)close(f);
482 				rsource(name, &stb);
483 				continue;
484 			}
485 			/* FALLTHROUGH */
486 		default:
487 notreg:			(void)close(f);
488 			error("rcp: %s: not a plain file\n", name);
489 			continue;
490 		}
491 		last = rindex(name, '/');
492 		if (last == 0)
493 			last = name;
494 		else
495 			last++;
496 		if (pflag) {
497 			/*
498 			 * Make it compatible with possible future
499 			 * versions expecting microseconds.
500 			 */
501 			(void)snprintf(buf, sizeof(buf),
502 			    "T%ld 0 %ld 0\n", stb.st_mtime, stb.st_atime);
503 			(void)write(rem, buf, (int)strlen(buf));
504 			if (response() < 0) {
505 				(void)close(f);
506 				continue;
507 			}
508 		}
509 		(void)snprintf(buf, sizeof(buf),
510 		    "C%04o %ld %s\n", stb.st_mode&07777, stb.st_size, last);
511 		(void)write(rem, buf, (int)strlen(buf));
512 		if (response() < 0) {
513 			(void)close(f);
514 			continue;
515 		}
516 		if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) {
517 			(void)close(f);
518 			continue;
519 		}
520 		readerr = 0;
521 		for (i = 0; i < stb.st_size; i += bp->cnt) {
522 			amt = bp->cnt;
523 			if (i + amt > stb.st_size)
524 				amt = stb.st_size - i;
525 			if (readerr == 0 && read(f, bp->buf, amt) != amt)
526 				readerr = errno;
527 			(void)write(rem, bp->buf, amt);
528 		}
529 		(void)close(f);
530 		if (readerr == 0)
531 			(void)write(rem, "", 1);
532 		else
533 			error("rcp: %s: %s\n", name, strerror(readerr));
534 		(void)response();
535 	}
536 }
537 
rsource(name,statp)538 rsource(name, statp)
539 	char *name;
540 	struct stat *statp;
541 {
542 	DIR *dirp;
543 	struct dirent *dp;
544 	char *last, *vect[1], path[MAXPATHLEN];
545 
546 	if (!(dirp = opendir(name))) {
547 		error("rcp: %s: %s\n", name, strerror(errno));
548 		return;
549 	}
550 	last = rindex(name, '/');
551 	if (last == 0)
552 		last = name;
553 	else
554 		last++;
555 	if (pflag) {
556 		(void)snprintf(path, sizeof(path),
557 		    "T%ld 0 %ld 0\n", statp->st_mtime, statp->st_atime);
558 		(void)write(rem, path, (int)strlen(path));
559 		if (response() < 0) {
560 			closedir(dirp);
561 			return;
562 		}
563 	}
564 	(void)snprintf(path, sizeof(path),
565 	    "D%04o %d %s\n", statp->st_mode&07777, 0, last);
566 	(void)write(rem, path, (int)strlen(path));
567 	if (response() < 0) {
568 		closedir(dirp);
569 		return;
570 	}
571 	while (dp = readdir(dirp)) {
572 		if (dp->d_ino == 0)
573 			continue;
574 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
575 			continue;
576 		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
577 			error("%s/%s: name too long.\n", name, dp->d_name);
578 			continue;
579 		}
580 		(void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
581 		vect[0] = path;
582 		source(1, vect);
583 	}
584 	closedir(dirp);
585 	(void)write(rem, "E\n", 2);
586 	(void)response();
587 }
588 
response()589 response()
590 {
591 	register char *cp;
592 	char ch, resp, rbuf[BUFSIZ];
593 
594 	if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
595 		lostconn();
596 
597 	cp = rbuf;
598 	switch(resp) {
599 	case 0:				/* ok */
600 		return(0);
601 	default:
602 		*cp++ = resp;
603 		/* FALLTHROUGH */
604 	case 1:				/* error, followed by err msg */
605 	case 2:				/* fatal error, "" */
606 		do {
607 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
608 				lostconn();
609 			*cp++ = ch;
610 		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
611 
612 		if (!iamremote)
613 			(void)write(2, rbuf, cp - rbuf);
614 		++errs;
615 		if (resp == 1)
616 			return(-1);
617 		exit(1);
618 	}
619 	/*NOTREACHED*/
620 }
621 
622 void
lostconn()623 lostconn()
624 {
625 	if (!iamremote)
626 		(void)fprintf(stderr, "rcp: lost connection\n");
627 	exit(1);
628 }
629 
sink(argc,argv)630 sink(argc, argv)
631 	int argc;
632 	char **argv;
633 {
634 	register char *cp;
635 	static BUF buffer;
636 	struct stat stb;
637 	struct timeval tv[2];
638 	enum { YES, NO, DISPLAYED } wrerr;
639 	BUF *bp, *allocbuf();
640 	off_t i, j;
641 	char ch, *targ, *why;
642 	int amt, count, exists, first, mask, mode;
643 	int ofd, setimes, size, targisdir;
644 	char *np, *vect[1], buf[BUFSIZ];
645 
646 #define	atime	tv[0]
647 #define	mtime	tv[1]
648 #define	SCREWUP(str)	{ why = str; goto screwup; }
649 
650 	setimes = targisdir = 0;
651 	mask = umask(0);
652 	if (!pflag)
653 		(void)umask(mask);
654 	if (argc != 1) {
655 		error("rcp: ambiguous target\n");
656 		exit(1);
657 	}
658 	targ = *argv;
659 	if (targetshouldbedirectory)
660 		verifydir(targ);
661 	(void)write(rem, "", 1);
662 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
663 		targisdir = 1;
664 	for (first = 1;; first = 0) {
665 		cp = buf;
666 		if (read(rem, cp, 1) <= 0)
667 			return;
668 		if (*cp++ == '\n')
669 			SCREWUP("unexpected <newline>");
670 		do {
671 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
672 				SCREWUP("lost connection");
673 			*cp++ = ch;
674 		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
675 		*cp = 0;
676 
677 		if (buf[0] == '\01' || buf[0] == '\02') {
678 			if (iamremote == 0)
679 				(void)write(2, buf + 1, (int)strlen(buf + 1));
680 			if (buf[0] == '\02')
681 				exit(1);
682 			errs++;
683 			continue;
684 		}
685 		if (buf[0] == 'E') {
686 			(void)write(rem, "", 1);
687 			return;
688 		}
689 
690 		if (ch == '\n')
691 			*--cp = 0;
692 
693 #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
694 		cp = buf;
695 		if (*cp == 'T') {
696 			setimes++;
697 			cp++;
698 			getnum(mtime.tv_sec);
699 			if (*cp++ != ' ')
700 				SCREWUP("mtime.sec not delimited");
701 			getnum(mtime.tv_usec);
702 			if (*cp++ != ' ')
703 				SCREWUP("mtime.usec not delimited");
704 			getnum(atime.tv_sec);
705 			if (*cp++ != ' ')
706 				SCREWUP("atime.sec not delimited");
707 			getnum(atime.tv_usec);
708 			if (*cp++ != '\0')
709 				SCREWUP("atime.usec not delimited");
710 			(void)write(rem, "", 1);
711 			continue;
712 		}
713 		if (*cp != 'C' && *cp != 'D') {
714 			/*
715 			 * Check for the case "rcp remote:foo\* local:bar".
716 			 * In this case, the line "No match." can be returned
717 			 * by the shell before the rcp command on the remote is
718 			 * executed so the ^Aerror_message convention isn't
719 			 * followed.
720 			 */
721 			if (first) {
722 				error("%s\n", cp);
723 				exit(1);
724 			}
725 			SCREWUP("expected control record");
726 		}
727 		mode = 0;
728 		for (++cp; cp < buf + 5; cp++) {
729 			if (*cp < '0' || *cp > '7')
730 				SCREWUP("bad mode");
731 			mode = (mode << 3) | (*cp - '0');
732 		}
733 		if (*cp++ != ' ')
734 			SCREWUP("mode not delimited");
735 		size = 0;
736 		while (isdigit(*cp))
737 			size = size * 10 + (*cp++ - '0');
738 		if (*cp++ != ' ')
739 			SCREWUP("size not delimited");
740 		if (targisdir) {
741 			static char *namebuf;
742 			static int cursize;
743 			size_t need;
744 
745 			need = strlen(targ) + strlen(cp) + 250;
746 			if (need > cursize) {
747 				if (!(namebuf = malloc(need)))
748 					error("out of memory\n");
749 			}
750 			(void)snprintf(namebuf, need, "%s%s%s", targ,
751 			    *targ ? "/" : "", cp);
752 			np = namebuf;
753 		}
754 		else
755 			np = targ;
756 		exists = stat(np, &stb) == 0;
757 		if (buf[0] == 'D') {
758 			if (exists) {
759 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
760 					errno = ENOTDIR;
761 					goto bad;
762 				}
763 				if (pflag)
764 					(void)chmod(np, mode);
765 			} else if (mkdir(np, mode) < 0)
766 				goto bad;
767 			vect[0] = np;
768 			sink(1, vect);
769 			if (setimes) {
770 				setimes = 0;
771 				if (utimes(np, tv) < 0)
772 				    error("rcp: can't set times on %s: %s\n",
773 					np, strerror(errno));
774 			}
775 			continue;
776 		}
777 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
778 bad:			error("rcp: %s: %s\n", np, strerror(errno));
779 			continue;
780 		}
781 		if (exists && pflag)
782 			(void)fchmod(ofd, mode);
783 		(void)write(rem, "", 1);
784 		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == 0) {
785 			(void)close(ofd);
786 			continue;
787 		}
788 		cp = bp->buf;
789 		count = 0;
790 		wrerr = NO;
791 		for (i = 0; i < size; i += BUFSIZ) {
792 			amt = BUFSIZ;
793 			if (i + amt > size)
794 				amt = size - i;
795 			count += amt;
796 			do {
797 				j = read(rem, cp, amt);
798 				if (j <= 0) {
799 					error("rcp: %s\n",
800 					    j ? strerror(errno) :
801 					    "dropped connection");
802 					exit(1);
803 				}
804 				amt -= j;
805 				cp += j;
806 			} while (amt > 0);
807 			if (count == bp->cnt) {
808 				if (wrerr == NO &&
809 				    write(ofd, bp->buf, count) != count)
810 					wrerr = YES;
811 				count = 0;
812 				cp = bp->buf;
813 			}
814 		}
815 		if (count != 0 && wrerr == NO &&
816 		    write(ofd, bp->buf, count) != count)
817 			wrerr = YES;
818 		if (ftruncate(ofd, size)) {
819 			error("rcp: can't truncate %s: %s\n", np,
820 			    strerror(errno));
821 			wrerr = DISPLAYED;
822 		}
823 		(void)close(ofd);
824 		(void)response();
825 		if (setimes && wrerr == NO) {
826 			setimes = 0;
827 			if (utimes(np, tv) < 0) {
828 				error("rcp: can't set times on %s: %s\n",
829 				    np, strerror(errno));
830 				wrerr = DISPLAYED;
831 			}
832 		}
833 		switch(wrerr) {
834 		case YES:
835 			error("rcp: %s: %s\n", np, strerror(errno));
836 			break;
837 		case NO:
838 			(void)write(rem, "", 1);
839 			break;
840 		case DISPLAYED:
841 			break;
842 		}
843 	}
844 screwup:
845 	error("rcp: protocol screwup: %s\n", why);
846 	exit(1);
847 }
848 
849 BUF *
allocbuf(bp,fd,blksize)850 allocbuf(bp, fd, blksize)
851 	BUF *bp;
852 	int fd, blksize;
853 {
854 	struct stat stb;
855 	size_t size;
856 
857 	if (fstat(fd, &stb) < 0) {
858 		error("rcp: fstat: %s\n", strerror(errno));
859 		return(0);
860 	}
861 	size = roundup(stb.st_blksize, blksize);
862 	if (size == 0)
863 		size = blksize;
864 	if (bp->cnt < size) {
865 		if (bp->buf != 0)
866 			free(bp->buf);
867 		bp->buf = malloc(size);
868 		if (!bp->buf) {
869 			error("rcp: malloc: out of memory\n");
870 			return(0);
871 		}
872 	}
873 	bp->cnt = size;
874 	return(bp);
875 }
876 
877 /* VARARGS1 */
error(fmt,a1,a2,a3)878 error(fmt, a1, a2, a3)
879 	char *fmt;
880 	int a1, a2, a3;
881 {
882 	static FILE *fp;
883 
884 	++errs;
885 	if (!fp && !(fp = fdopen(rem, "w")))
886 		return;
887 	(void)fprintf(fp, "%c", 0x01);
888 	(void)fprintf(fp, fmt, a1, a2, a3);
889 	(void)fflush(fp);
890 	if (!iamremote)
891 		(void)fprintf(stderr, fmt, a1, a2, a3);
892 }
893 
nospace()894 nospace()
895 {
896 	(void)fprintf(stderr, "rcp: out of memory.\n");
897 	exit(1);
898 }
899 
900 
usage()901 usage()
902 {
903 #ifdef KERBEROS
904 #ifdef CRYPT
905 	(void)fprintf(stderr, "%s\n\t%s\n",
906 	    "usage: rcp [-k realm] [-px] f1 f2",
907 	    "or: rcp [-k realm] [-rpx] f1 ... fn directory");
908 #else
909 	(void)fprintf(stderr, "%s\n\t%s\n",
910 	    "usage: rcp [-k realm] [-p] f1 f2",
911 	    "or: rcp [-k realm] [-rp] f1 ... fn directory");
912 #endif
913 #else
914 	(void)fprintf(stderr,
915 	    "usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn directory\n");
916 #endif
917 	exit(1);
918 }
919 
920 #ifdef KERBEROS
old_warning(str)921 old_warning(str)
922 	char *str;
923 {
924 	(void)fprintf(stderr, "rcp: warning: %s, using standard rcp\n", str);
925 }
926 
927 int
kerberos(host,bp,locuser,user)928 kerberos(host, bp, locuser, user)
929 
930 	char **host, *bp, *locuser, *user;
931 {
932 	struct servent *sp;
933 
934 again:
935 	if (use_kerberos) {
936 		rem = KSUCCESS;
937 		errno = 0;
938 		if (dest_realm == NULL)
939 			dest_realm = krb_realmofhost(*host);
940 
941 #ifdef CRYPT
942 		if (doencrypt)
943 			rem = krcmd_mutual(
944 				host, port,
945 				user, bp, 0,
946 		    		dest_realm,
947 				&cred, schedule);
948 		else
949 #endif
950 			rem = krcmd(
951 				host, port,
952 				user, bp, 0, dest_realm);
953 
954 		if (rem < 0) {
955 			use_kerberos = 0;
956 			sp = getservbyname("shell", "tcp");
957 			if (sp == NULL) {
958 				(void)fprintf(stderr,
959 			    	    "rcp: unknown service shell/tcp\n");
960 				exit(1);
961 			}
962 			if (errno == ECONNREFUSED)
963 				old_warning(
964 				    "remote host doesn't support Kerberos");
965 
966 			if (errno == ENOENT)
967 				old_warning(
968 				    "Can't provide Kerberos auth data");
969 			port = sp->s_port;
970 			goto again;
971 		}
972 	} else {
973 #ifdef CRYPT
974 		if (doencrypt) {
975 			fprintf(stderr,
976 			    "The -x option requires Kerberos authentication\n");
977 			exit(1);
978 		}
979 #endif
980 		rem = rcmd(host, sp->s_port, locuser, user, bp, 0);
981 	}
982 	return(rem);
983 }
984 #endif /* KERBEROS */
985