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