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