xref: /original-bsd/bin/rcp/rcp.c (revision 0fc6f013)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)rcp.c	5.4 (Berkeley) 09/12/85";
15 #endif not lint
16 
17 /*
18  * rcp
19  */
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <sys/ioctl.h>
24 
25 #include <netinet/in.h>
26 
27 #include <stdio.h>
28 #include <signal.h>
29 #include <pwd.h>
30 #include <ctype.h>
31 #include <netdb.h>
32 #include <errno.h>
33 
34 int	rem;
35 char	*colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf();
36 int	errs;
37 int	lostconn();
38 int	errno;
39 char	*sys_errlist[];
40 int	iamremote, targetshouldbedirectory;
41 int	iamrecursive;
42 int	pflag;
43 struct	passwd *pwd;
44 struct	passwd *getpwuid();
45 int	userid;
46 int	port;
47 
48 struct buffer {
49 	int	cnt;
50 	char	*buf;
51 } *allocbuf();
52 
53 /*VARARGS*/
54 int	error();
55 
56 #define	ga()	 	(void) write(rem, "", 1)
57 
58 main(argc, argv)
59 	int argc;
60 	char **argv;
61 {
62 	char *targ, *host, *src;
63 	char *suser, *tuser, *thost;
64 	int i;
65 	char buf[BUFSIZ], cmd[16];
66 	struct servent *sp;
67 
68 	sp = getservbyname("shell", "tcp");
69 	if (sp == NULL) {
70 		fprintf(stderr, "rcp: shell/tcp: unknown service\n");
71 		exit(1);
72 	}
73 	port = sp->s_port;
74 	pwd = getpwuid(userid = getuid());
75 	if (pwd == 0) {
76 		fprintf(stderr, "who are you?\n");
77 		exit(1);
78 	}
79 
80 	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
81 		(*argv)++;
82 		while (**argv) switch (*(*argv)++) {
83 
84 		    case 'r':
85 			iamrecursive++;
86 			break;
87 
88 		    case 'p':		/* preserve mtimes and atimes */
89 			pflag++;
90 			break;
91 
92 		    /* The rest of these are not for users. */
93 		    case 'd':
94 			targetshouldbedirectory = 1;
95 			break;
96 
97 		    case 'f':		/* "from" */
98 			iamremote = 1;
99 			(void) response();
100 			(void) setuid(userid);
101 			source(--argc, ++argv);
102 			exit(errs);
103 
104 		    case 't':		/* "to" */
105 			iamremote = 1;
106 			(void) setuid(userid);
107 			sink(--argc, ++argv);
108 			exit(errs);
109 
110 		    default:
111 			fprintf(stderr,
112 		"Usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n");
113 			exit(1);
114 		}
115 	}
116 	rem = -1;
117 	if (argc > 2)
118 		targetshouldbedirectory = 1;
119 	(void) sprintf(cmd, "rcp%s%s%s",
120 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
121 	    targetshouldbedirectory ? " -d" : "");
122 	(void) signal(SIGPIPE, lostconn);
123 	targ = colon(argv[argc - 1]);
124 	if (targ) {				/* ... to remote */
125 		*targ++ = 0;
126 		if (*targ == 0)
127 			targ = ".";
128 		thost = index(argv[argc - 1], '@');
129 		if (thost) {
130 			*thost++ = 0;
131 			tuser = argv[argc - 1];
132 			if (*tuser == '\0')
133 				tuser = NULL;
134 			else if (!okname(tuser))
135 				exit(1);
136 		} else {
137 			thost = argv[argc - 1];
138 			tuser = NULL;
139 		}
140 		for (i = 0; i < argc - 1; i++) {
141 			src = colon(argv[i]);
142 			if (src) {		/* remote to remote */
143 				*src++ = 0;
144 				if (*src == 0)
145 					src = ".";
146 				host = index(argv[i], '@');
147 				if (host) {
148 					*host++ = 0;
149 					suser = argv[i];
150 					if (*suser == '\0')
151 						suser = pwd->pw_name;
152 					else if (!okname(suser))
153 						continue;
154 		(void) sprintf(buf, "rsh %s -l %s -n %s %s '%s%s%s:%s'",
155 					    host, suser, cmd, src, tuser,
156 					    tuser ? "@" : "",
157 					    thost, targ);
158 				} else
159 		(void) sprintf(buf, "rsh %s -n %s %s '%s%s%s:%s'",
160 					    argv[i], cmd, src, tuser,
161 					    tuser ? "@" : "",
162 					    thost, targ);
163 				(void) susystem(buf);
164 			} else {		/* local to remote */
165 				if (rem == -1) {
166 					(void) sprintf(buf, "%s -t %s",
167 					    cmd, targ);
168 					host = thost;
169 					rem = rcmd(&host, port, pwd->pw_name,
170 					    tuser ? tuser : pwd->pw_name,
171 					    buf, 0);
172 					if (rem < 0)
173 						exit(1);
174 					if (response() < 0)
175 						exit(1);
176 					(void) setuid(userid);
177 				}
178 				source(1, argv+i);
179 			}
180 		}
181 	} else {				/* ... to local */
182 		if (targetshouldbedirectory)
183 			verifydir(argv[argc - 1]);
184 		for (i = 0; i < argc - 1; i++) {
185 			src = colon(argv[i]);
186 			if (src == 0) {		/* local to local */
187 				(void) sprintf(buf, "/bin/cp%s%s %s %s",
188 				    iamrecursive ? " -r" : "",
189 				    pflag ? " -p" : "",
190 				    argv[i], argv[argc - 1]);
191 				(void) susystem(buf);
192 			} else {		/* remote to local */
193 				*src++ = 0;
194 				if (*src == 0)
195 					src = ".";
196 				host = index(argv[i], '@');
197 				if (host) {
198 					*host++ = 0;
199 					suser = argv[i];
200 					if (*suser == '\0')
201 						suser = pwd->pw_name;
202 					else if (!okname(suser))
203 						continue;
204 				} else {
205 					host = argv[i];
206 					suser = pwd->pw_name;
207 				}
208 				(void) sprintf(buf, "%s -f %s", cmd, src);
209 				rem = rcmd(&host, port, pwd->pw_name, suser,
210 				    buf, 0);
211 				if (rem < 0)
212 					continue;
213 				(void) setreuid(0, userid);
214 				sink(1, argv+argc-1);
215 				(void) setreuid(userid, 0);
216 				(void) close(rem);
217 				rem = -1;
218 			}
219 		}
220 	}
221 	exit(errs);
222 }
223 
224 verifydir(cp)
225 	char *cp;
226 {
227 	struct stat stb;
228 
229 	if (stat(cp, &stb) >= 0) {
230 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
231 			return;
232 		errno = ENOTDIR;
233 	}
234 	error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
235 	exit(1);
236 }
237 
238 char *
239 colon(cp)
240 	char *cp;
241 {
242 
243 	while (*cp) {
244 		if (*cp == ':')
245 			return (cp);
246 		if (*cp == '/')
247 			return (0);
248 		cp++;
249 	}
250 	return (0);
251 }
252 
253 okname(cp0)
254 	char *cp0;
255 {
256 	register char *cp = cp0;
257 	register int c;
258 
259 	do {
260 		c = *cp;
261 		if (c & 0200)
262 			goto bad;
263 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
264 			goto bad;
265 		cp++;
266 	} while (*cp);
267 	return (1);
268 bad:
269 	fprintf(stderr, "rcp: invalid user name %s\n", cp0);
270 	return (0);
271 }
272 
273 susystem(s)
274 	char *s;
275 {
276 	int status, pid, w;
277 	register int (*istat)(), (*qstat)();
278 
279 	if ((pid = vfork()) == 0) {
280 		(void) setuid(userid);
281 		execl("/bin/sh", "sh", "-c", s, (char *)0);
282 		_exit(127);
283 	}
284 	istat = signal(SIGINT, SIG_IGN);
285 	qstat = signal(SIGQUIT, SIG_IGN);
286 	while ((w = wait(&status)) != pid && w != -1)
287 		;
288 	if (w == -1)
289 		status = -1;
290 	(void) signal(SIGINT, istat);
291 	(void) signal(SIGQUIT, qstat);
292 	return (status);
293 }
294 
295 source(argc, argv)
296 	int argc;
297 	char **argv;
298 {
299 	char *last, *name;
300 	struct stat stb;
301 	static struct buffer buffer;
302 	struct buffer *bp;
303 	int x, sizerr, f, amt;
304 	off_t i;
305 	char buf[BUFSIZ];
306 
307 	for (x = 0; x < argc; x++) {
308 		name = argv[x];
309 		if ((f = open(name, 0)) < 0) {
310 			error("rcp: %s: %s\n", name, sys_errlist[errno]);
311 			continue;
312 		}
313 		if (fstat(f, &stb) < 0)
314 			goto notreg;
315 		switch (stb.st_mode&S_IFMT) {
316 
317 		case S_IFREG:
318 			break;
319 
320 		case S_IFDIR:
321 			if (iamrecursive) {
322 				(void) close(f);
323 				rsource(name, &stb);
324 				continue;
325 			}
326 			/* fall into ... */
327 		default:
328 notreg:
329 			(void) close(f);
330 			error("rcp: %s: not a plain file\n", name);
331 			continue;
332 		}
333 		last = rindex(name, '/');
334 		if (last == 0)
335 			last = name;
336 		else
337 			last++;
338 		if (pflag) {
339 			/*
340 			 * Make it compatible with possible future
341 			 * versions expecting microseconds.
342 			 */
343 			(void) sprintf(buf, "T%ld 0 %ld 0\n",
344 			    stb.st_mtime, stb.st_atime);
345 			(void) write(rem, buf, strlen(buf));
346 			if (response() < 0) {
347 				(void) close(f);
348 				continue;
349 			}
350 		}
351 		(void) sprintf(buf, "C%04o %ld %s\n",
352 		    stb.st_mode&07777, stb.st_size, last);
353 		(void) write(rem, buf, strlen(buf));
354 		if (response() < 0) {
355 			(void) close(f);
356 			continue;
357 		}
358 		if ((bp = allocbuf(&buffer, f, BUFSIZ)) < 0) {
359 			(void) close(f);
360 			continue;
361 		}
362 		sizerr = 0;
363 		for (i = 0; i < stb.st_size; i += bp->cnt) {
364 			amt = bp->cnt;
365 			if (i + amt > stb.st_size)
366 				amt = stb.st_size - i;
367 			if (sizerr == 0 && read(f, bp->buf, amt) != amt)
368 				sizerr = 1;
369 			(void) write(rem, bp->buf, amt);
370 		}
371 		(void) close(f);
372 		if (sizerr == 0)
373 			ga();
374 		else
375 			error("rcp: %s: file changed size\n", name);
376 		(void) response();
377 	}
378 }
379 
380 #include <sys/dir.h>
381 
382 rsource(name, statp)
383 	char *name;
384 	struct stat *statp;
385 {
386 	DIR *d = opendir(name);
387 	char *last;
388 	struct direct *dp;
389 	char buf[BUFSIZ];
390 	char *bufv[1];
391 
392 	if (d == 0) {
393 		error("rcp: %s: %s\n", name, sys_errlist[errno]);
394 		return;
395 	}
396 	last = rindex(name, '/');
397 	if (last == 0)
398 		last = name;
399 	else
400 		last++;
401 	if (pflag) {
402 		(void) sprintf(buf, "T%ld 0 %ld 0\n",
403 		    statp->st_mtime, statp->st_atime);
404 		(void) write(rem, buf, strlen(buf));
405 		if (response() < 0) {
406 			closedir(d);
407 			return;
408 		}
409 	}
410 	(void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
411 	(void) write(rem, buf, strlen(buf));
412 	if (response() < 0) {
413 		closedir(d);
414 		return;
415 	}
416 	while (dp = readdir(d)) {
417 		if (dp->d_ino == 0)
418 			continue;
419 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
420 			continue;
421 		if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
422 			error("%s/%s: Name too long.\n", name, dp->d_name);
423 			continue;
424 		}
425 		(void) sprintf(buf, "%s/%s", name, dp->d_name);
426 		bufv[0] = buf;
427 		source(1, bufv);
428 	}
429 	closedir(d);
430 	(void) write(rem, "E\n", 2);
431 	(void) response();
432 }
433 
434 response()
435 {
436 	char resp, c, rbuf[BUFSIZ], *cp = rbuf;
437 
438 	if (read(rem, &resp, 1) != 1)
439 		lostconn();
440 	switch (resp) {
441 
442 	case 0:				/* ok */
443 		return (0);
444 
445 	default:
446 		*cp++ = resp;
447 		/* fall into... */
448 	case 1:				/* error, followed by err msg */
449 	case 2:				/* fatal error, "" */
450 		do {
451 			if (read(rem, &c, 1) != 1)
452 				lostconn();
453 			*cp++ = c;
454 		} while (cp < &rbuf[BUFSIZ] && c != '\n');
455 		if (iamremote == 0)
456 			(void) write(2, rbuf, cp - rbuf);
457 		errs++;
458 		if (resp == 1)
459 			return (-1);
460 		exit(1);
461 	}
462 	/*NOTREACHED*/
463 }
464 
465 lostconn()
466 {
467 
468 	if (iamremote == 0)
469 		fprintf(stderr, "rcp: lost connection\n");
470 	exit(1);
471 }
472 
473 sink(argc, argv)
474 	int argc;
475 	char **argv;
476 {
477 	off_t i, j;
478 	char *targ, *whopp, *cp;
479 	int of, mode, wrerr, exists, first, count, amt, size;
480 	struct buffer *bp;
481 	static struct buffer buffer;
482 	struct stat stb;
483 	int targisdir = 0;
484 	int mask = umask(0);
485 	char *myargv[1];
486 	char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
487 	int setimes = 0;
488 	struct timeval tv[2];
489 #define atime	tv[0]
490 #define mtime	tv[1]
491 #define	SCREWUP(str)	{ whopp = str; goto screwup; }
492 
493 	if (!pflag)
494 		(void) umask(mask);
495 	if (argc != 1) {
496 		error("rcp: ambiguous target\n");
497 		exit(1);
498 	}
499 	targ = *argv;
500 	if (targetshouldbedirectory)
501 		verifydir(targ);
502 	ga();
503 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
504 		targisdir = 1;
505 	for (first = 1; ; first = 0) {
506 		cp = cmdbuf;
507 		if (read(rem, cp, 1) <= 0)
508 			return;
509 		if (*cp++ == '\n')
510 			SCREWUP("unexpected '\\n'");
511 		do {
512 			if (read(rem, cp, 1) != 1)
513 				SCREWUP("lost connection");
514 		} while (*cp++ != '\n');
515 		*cp = 0;
516 		if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
517 			if (iamremote == 0)
518 				(void) write(2, cmdbuf+1, strlen(cmdbuf+1));
519 			if (cmdbuf[0] == '\02')
520 				exit(1);
521 			errs++;
522 			continue;
523 		}
524 		*--cp = 0;
525 		cp = cmdbuf;
526 		if (*cp == 'E') {
527 			ga();
528 			return;
529 		}
530 
531 #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
532 		if (*cp == 'T') {
533 			setimes++;
534 			cp++;
535 			getnum(mtime.tv_sec);
536 			if (*cp++ != ' ')
537 				SCREWUP("mtime.sec not delimited");
538 			getnum(mtime.tv_usec);
539 			if (*cp++ != ' ')
540 				SCREWUP("mtime.usec not delimited");
541 			getnum(atime.tv_sec);
542 			if (*cp++ != ' ')
543 				SCREWUP("atime.sec not delimited");
544 			getnum(atime.tv_usec);
545 			if (*cp++ != '\0')
546 				SCREWUP("atime.usec not delimited");
547 			ga();
548 			continue;
549 		}
550 		if (*cp != 'C' && *cp != 'D') {
551 			/*
552 			 * Check for the case "rcp remote:foo\* local:bar".
553 			 * In this case, the line "No match." can be returned
554 			 * by the shell before the rcp command on the remote is
555 			 * executed so the ^Aerror_message convention isn't
556 			 * followed.
557 			 */
558 			if (first) {
559 				error("%s\n", cp);
560 				exit(1);
561 			}
562 			SCREWUP("expected control record");
563 		}
564 		cp++;
565 		mode = 0;
566 		for (; cp < cmdbuf+5; cp++) {
567 			if (*cp < '0' || *cp > '7')
568 				SCREWUP("bad mode");
569 			mode = (mode << 3) | (*cp - '0');
570 		}
571 		if (*cp++ != ' ')
572 			SCREWUP("mode not delimited");
573 		size = 0;
574 		while (isdigit(*cp))
575 			size = size * 10 + (*cp++ - '0');
576 		if (*cp++ != ' ')
577 			SCREWUP("size not delimited");
578 		if (targisdir)
579 			(void) sprintf(nambuf, "%s%s%s", targ,
580 			    *targ ? "/" : "", cp);
581 		else
582 			(void) strcpy(nambuf, targ);
583 		exists = stat(nambuf, &stb) == 0;
584 		if (cmdbuf[0] == 'D') {
585 			if (exists) {
586 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
587 					errno = ENOTDIR;
588 					goto bad;
589 				}
590 				if (pflag)
591 					(void) chmod(nambuf, mode);
592 			} else if (mkdir(nambuf, mode) < 0)
593 				goto bad;
594 			myargv[0] = nambuf;
595 			sink(1, myargv);
596 			if (setimes) {
597 				setimes = 0;
598 				if (utimes(nambuf, tv) < 0)
599 					error("rcp: can't set times on %s: %s\n",
600 					    nambuf, sys_errlist[errno]);
601 			}
602 			continue;
603 		}
604 		if ((of = creat(nambuf, mode)) < 0) {
605 	bad:
606 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
607 			continue;
608 		}
609 		if (exists && pflag)
610 			(void) fchmod(of, mode);
611 		ga();
612 		if ((bp = allocbuf(&buffer, of, BUFSIZ)) < 0) {
613 			(void) close(of);
614 			continue;
615 		}
616 		cp = bp->buf;
617 		count = 0;
618 		wrerr = 0;
619 		for (i = 0; i < size; i += BUFSIZ) {
620 			amt = BUFSIZ;
621 			if (i + amt > size)
622 				amt = size - i;
623 			count += amt;
624 			do {
625 				j = read(rem, cp, amt);
626 				if (j <= 0) {
627 					if (j == 0)
628 					    error("rcp: dropped connection");
629 					else
630 					    error("rcp: %s\n",
631 						sys_errlist[errno]);
632 					exit(1);
633 				}
634 				amt -= j;
635 				cp += j;
636 			} while (amt > 0);
637 			if (count == bp->cnt) {
638 				if (wrerr == 0 &&
639 				    write(of, bp->buf, count) != count)
640 					wrerr++;
641 				count = 0;
642 				cp = bp->buf;
643 			}
644 		}
645 		if (count != 0 && wrerr == 0 &&
646 		    write(of, bp->buf, count) != count)
647 			wrerr++;
648 		(void) close(of);
649 		(void) response();
650 		if (setimes) {
651 			setimes = 0;
652 			if (utimes(nambuf, tv) < 0)
653 				error("rcp: can't set times on %s: %s\n",
654 				    nambuf, sys_errlist[errno]);
655 		}
656 		if (wrerr)
657 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
658 		else
659 			ga();
660 	}
661 screwup:
662 	error("rcp: protocol screwup: %s\n", whopp);
663 	exit(1);
664 }
665 
666 struct buffer *
667 allocbuf(bp, fd, blksize)
668 	struct buffer *bp;
669 	int fd, blksize;
670 {
671 	struct stat stb;
672 	int size;
673 
674 	if (fstat(fd, &stb) < 0) {
675 		error("rcp: fstat: %s\n", sys_errlist[errno]);
676 		return ((struct buffer *)-1);
677 	}
678 	size = roundup(stb.st_blksize, blksize);
679 	if (size == 0)
680 		size = blksize;
681 	if (bp->cnt < size) {
682 		if (bp->buf != 0)
683 			free(bp->buf);
684 		bp->buf = (char *)malloc((unsigned) size);
685 		if (bp->buf == 0) {
686 			error("rcp: malloc: out of memory\n");
687 			return ((struct buffer *)-1);
688 		}
689 	}
690 	bp->cnt = size;
691 	return (bp);
692 }
693 
694 /*VARARGS1*/
695 error(fmt, a1, a2, a3, a4, a5)
696 	char *fmt;
697 	int a1, a2, a3, a4, a5;
698 {
699 	char buf[BUFSIZ], *cp = buf;
700 
701 	errs++;
702 	*cp++ = 1;
703 	(void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
704 	(void) write(rem, buf, strlen(buf));
705 	if (iamremote == 0)
706 		(void) write(2, buf+1, strlen(buf+1));
707 }
708