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