xref: /original-bsd/bin/rcp/rcp.c (revision 8251a00e)
1 #ifndef lint
2 static char sccsid[] = "@(#)rcp.c	4.8 83/08/12";
3 #endif
4 
5 /*
6  * rcp
7  */
8 #include <sys/param.h>
9 #include <sys/stat.h>
10 #include <sys/ioctl.h>
11 
12 #include <netinet/in.h>
13 
14 #include <stdio.h>
15 #include <signal.h>
16 #include <pwd.h>
17 #include <ctype.h>
18 #include <errno.h>
19 
20 int	rem;
21 char	*colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf();
22 int	errs;
23 int	lostconn();
24 int	iamremote;
25 
26 int	errno;
27 char	*sys_errlist[];
28 int	iamremote, targetshouldbedirectory;
29 int	iamrecursive;
30 struct	passwd *pwd;
31 struct	passwd *getpwuid();
32 
33 /*VARARGS*/
34 int	error();
35 
36 #define	ga()	 	(void) write(rem, "", 1)
37 
38 main(argc, argv)
39 	int argc;
40 	char **argv;
41 {
42 	char *targ, *host, *src;
43 	char *suser, *tuser;
44 	int i;
45 	char buf[BUFSIZ], cmd[16];
46 
47 	setpwent();
48 	pwd = getpwuid(getuid());
49 	endpwent();
50 	if (pwd == 0) {
51 		fprintf(stderr, "who are you?\n");
52 		exit(1);
53 	}
54 	argc--, argv++;
55 	if (argc > 0 && !strcmp(*argv, "-r")) {
56 		iamrecursive++;
57 		argc--, argv++;
58 	}
59 	if (argc > 0 && !strcmp(*argv, "-d")) {
60 		targetshouldbedirectory = 1;
61 		argc--, argv++;
62 	}
63 	if (argc > 0 && !strcmp(*argv, "-f")) {
64 		argc--, argv++; iamremote = 1;
65 		(void) response();
66 		(void) setuid(getuid());
67 		source(argc, argv);
68 		exit(errs);
69 	}
70 	if (argc > 0 && !strcmp(*argv, "-t")) {
71 		argc--, argv++; iamremote = 1;
72 		(void) setuid(getuid());
73 		sink(argc, argv);
74 		exit(errs);
75 	}
76 	rem = -1;
77 	if (argc > 2)
78 		targetshouldbedirectory = 1;
79 	(void) sprintf(cmd, "rcp%s%s",
80 	    iamrecursive ? " -r" : "", targetshouldbedirectory ? " -d" : "");
81 	signal(SIGPIPE, lostconn);
82 	targ = colon(argv[argc - 1]);
83 	if (targ) {
84 		*targ++ = 0;
85 		if (*targ == 0)
86 			targ = ".";
87 		tuser = rindex(argv[argc - 1], '.');
88 		if (tuser) {
89 			*tuser++ = 0;
90 			if (!okname(tuser))
91 				exit(1);
92 		} else
93 			tuser = pwd->pw_name;
94 		for (i = 0; i < argc - 1; i++) {
95 			src = colon(argv[i]);
96 			if (src) {
97 				*src++ = 0;
98 				if (*src == 0)
99 					src = ".";
100 				suser = rindex(argv[i], '.');
101 				if (suser) {
102 					*suser++ = 0;
103 					if (!okname(suser))
104 						continue;
105 		(void) sprintf(buf, "rsh %s -L %s -n %s %s '%s:%s'",
106 					    argv[i], suser, cmd,
107 					    src, argv[argc - 1], targ);
108 				} else
109 		(void) sprintf(buf, "rsh %s -n %s %s '%s:%s'",
110 					    argv[i], cmd,
111 					    src, argv[argc - 1], targ);
112 				(void) susystem(buf);
113 			} else {
114 				if (rem == -1) {
115 					(void) sprintf(buf, "%s -t %s",
116 					    cmd, targ);
117 					host = argv[argc - 1];
118 					rem = rcmd(&host, IPPORT_CMDSERVER,
119 					    pwd->pw_name, tuser,
120 					    buf, 0);
121 					if (rem < 0)
122 						exit(1);
123 					if (response() < 0)
124 						exit(1);
125 				}
126 				source(1, argv+i);
127 			}
128 		}
129 	} else {
130 		if (targetshouldbedirectory)
131 			verifydir(argv[argc - 1]);
132 		for (i = 0; i < argc - 1; i++) {
133 			src = colon(argv[i]);
134 			if (src == 0) {
135 				(void) sprintf(buf, "/bin/cp%s %s %s",
136 				    iamrecursive ? " -r" : "",
137 				    argv[i], argv[argc - 1]);
138 				(void) susystem(buf);
139 			} else {
140 				*src++ = 0;
141 				if (*src == 0)
142 					src = ".";
143 				suser = rindex(argv[i], '.');
144 				if (suser) {
145 					*suser++ = 0;
146 					if (!okname(suser))
147 						continue;
148 				} else
149 					suser = pwd->pw_name;
150 				(void) sprintf(buf, "%s -f %s", cmd, src);
151 				host = argv[i];
152 				rem = rcmd(&host, IPPORT_CMDSERVER,
153 				    pwd->pw_name, suser,
154 				    buf, 0);
155 				if (rem < 0)
156 					exit(1);
157 				sink(1, argv+argc-1);
158 				(void) close(rem);
159 				rem = -1;
160 			}
161 		}
162 	}
163 	exit(errs);
164 }
165 
166 verifydir(cp)
167 	char *cp;
168 {
169 	struct stat stb;
170 
171 	if (stat(cp, &stb) < 0)
172 		goto bad;
173 	if ((stb.st_mode & S_IFMT) == S_IFDIR)
174 		return;
175 	errno = ENOTDIR;
176 bad:
177 	error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
178 	exit(1);
179 }
180 
181 char *
182 colon(cp)
183 	char *cp;
184 {
185 
186 	while (*cp) {
187 		if (*cp == ':')
188 			return (cp);
189 		if (*cp == '/')
190 			return (0);
191 		cp++;
192 	}
193 	return (0);
194 }
195 
196 okname(cp0)
197 	char *cp0;
198 {
199 	register char *cp = cp0;
200 	register int c;
201 
202 	do {
203 		c = *cp;
204 		if (c & 0200)
205 			goto bad;
206 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
207 			goto bad;
208 		cp++;
209 	} while (*cp);
210 	return (1);
211 bad:
212 	fprintf(stderr, "rcp: invalid user name %s\n", cp0);
213 	return (0);
214 }
215 
216 susystem(buf)
217 	char *buf;
218 {
219 
220 	if (fork() == 0) {
221 		(void) setuid(getuid());
222 		(void) system(buf);
223 		_exit(0);
224 	} else
225 		(void) wait((int *)0);
226 }
227 
228 source(argc, argv)
229 	int argc;
230 	char **argv;
231 {
232 	char *last, *name;
233 	struct stat stb;
234 	char buf[BUFSIZ];
235 	int x, sizerr, f;
236 	off_t i;
237 
238 	for (x = 0; x < argc; x++) {
239 		name = argv[x];
240 		if (access(name, 4) < 0 || (f = open(name, 0)) < 0) {
241 			error("rcp: %s: %s\n", name, sys_errlist[errno]);
242 			continue;
243 		}
244 		if (fstat(f, &stb) < 0)
245 			goto notreg;
246 		switch (stb.st_mode&S_IFMT) {
247 
248 		case S_IFREG:
249 			break;
250 
251 		case S_IFDIR:
252 			if (iamrecursive) {
253 				(void) close(f);
254 				rsource(name, (int)stb.st_mode);
255 				continue;
256 			}
257 			/* fall into ... */
258 		default:
259 notreg:
260 			(void) close(f);
261 			error("rcp: %s: not a plain file\n", name);
262 			continue;
263 		}
264 		last = rindex(name, '/');
265 		if (last == 0)
266 			last = name;
267 		else
268 			last++;
269 		(void) sprintf(buf, "C%04o %D %s\n",
270 		    stb.st_mode&07777, stb.st_size, last);
271 		(void) write(rem, buf, strlen(buf));
272 		if (response() < 0) {
273 			(void) close(f);
274 			continue;
275 		}
276 		sizerr = 0;
277 		for (i = 0; i < stb.st_size; i += BUFSIZ) {
278 			int amt = BUFSIZ;
279 			if (i + amt > stb.st_size)
280 				amt = stb.st_size - i;
281 			if (sizerr == 0 && read(f, buf, amt) != amt)
282 				sizerr = 1;
283 			(void) write(rem, buf, amt);
284 		}
285 		(void) close(f);
286 		if (sizerr == 0)
287 			ga();
288 		else
289 			error("rcp: %s: file changed size\n", name);
290 		(void) response();
291 	}
292 }
293 
294 #include <sys/dir.h>
295 
296 rsource(name, mode)
297 	char *name;
298 	int mode;
299 {
300 	DIR *d = opendir(name);
301 	char *last;
302 	struct direct *dp;
303 	char buf[BUFSIZ];
304 	char *bufv[1];
305 
306 	if (d == 0) {
307 		error("%s: %s\n", name, sys_errlist[errno]);
308 		return;
309 	}
310 	last = rindex(name, '/');
311 	if (last == 0)
312 		last = name;
313 	else
314 		last++;
315 	(void) sprintf(buf, "D%04o %d %s\n", mode&07777, 0, last);
316 	(void) write(rem, buf, strlen(buf));
317 	if (response() < 0) {
318 		closedir(d);
319 		return;
320 	}
321 	while (dp = readdir(d)) {
322 		if (dp->d_ino == 0)
323 			continue;
324 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
325 			continue;
326 		if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
327 			error("%s/%s: Name too long.\n", name, dp->d_name);
328 			continue;
329 		}
330 		(void) sprintf(buf, "%s/%s", name, dp->d_name);
331 		bufv[0] = buf;
332 		source(1, bufv);
333 	}
334 	closedir(d);
335 	(void) write(rem, "E\n", 2);
336 	(void) response();
337 }
338 
339 response()
340 {
341 	char resp, c, rbuf[BUFSIZ], *cp = rbuf;
342 
343 	if (read(rem, &resp, 1) != 1)
344 		lostconn();
345 	switch (resp) {
346 
347 	case 0:
348 		return (0);
349 
350 	default:
351 		*cp++ = resp;
352 		/* fall into... */
353 	case 1:
354 	case 2:
355 		do {
356 			if (read(rem, &c, 1) != 1)
357 				lostconn();
358 			*cp++ = c;
359 		} while (cp < &rbuf[BUFSIZ] && c != '\n');
360 		if (iamremote == 0)
361 			(void) write(2, rbuf, cp - rbuf);
362 		errs++;
363 		if (resp == 1)
364 			return (-1);
365 		exit(1);
366 	}
367 	/*NOTREACHED*/
368 }
369 
370 lostconn()
371 {
372 
373 	if (iamremote == 0)
374 		fprintf(stderr, "rcp: lost connection\n");
375 	exit(1);
376 }
377 
378 sink(argc, argv)
379 	int argc;
380 	char **argv;
381 {
382 	char *targ;
383 	char cmdbuf[BUFSIZ], nambuf[BUFSIZ], buf[BUFSIZ], *cp;
384 	int of, mode, wrerr, exists, first;
385 	off_t i, size;
386 	char *whopp;
387 	struct stat stb; int targisdir = 0;
388 #define	SCREWUP(str)	{ whopp = str; goto screwup; }
389 	int mask = umask(0);
390 	char *myargv[1];
391 
392 	umask(mask);
393 	if (argc > 1) {
394 		error("rcp: ambiguous target\n");
395 		exit(1);
396 	}
397 	targ = *argv;
398 	if (targetshouldbedirectory)
399 		verifydir(targ);
400 	ga();
401 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
402 		targisdir = 1;
403 	for (first = 1; ; first = 0) {
404 		cp = cmdbuf;
405 		if (read(rem, cp, 1) <= 0)
406 			return;
407 		if (*cp++ == '\n')
408 			SCREWUP("unexpected '\\n'");
409 		do {
410 			if (read(rem, cp, 1) != 1)
411 				SCREWUP("lost connection");
412 		} while (*cp++ != '\n');
413 		*cp = 0;
414 		if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
415 			if (iamremote == 0)
416 				(void) write(2, cmdbuf+1, strlen(cmdbuf+1));
417 			if (cmdbuf[0] == '\02')
418 				exit(1);
419 			errs++;
420 			continue;
421 		}
422 		*--cp = 0;
423 		cp = cmdbuf;
424 		if (*cp == 'E') {
425 			ga();
426 			return;
427 		}
428 		if (*cp != 'C' && *cp != 'D') {
429 			/*
430 			 * Check for the case "rcp remote:foo\* local:bar".
431 			 * In this case, the line "No match." can be returned
432 			 * by the shell before the rcp command on the remote is
433 			 * executed so the ^Aerror_message convention isn't
434 			 * followed.
435 			 */
436 			if (first) {
437 				error("%s\n", cp);
438 				exit(1);
439 			}
440 			SCREWUP("expected control record");
441 		}
442 		cp++;
443 		mode = 0;
444 		for (; cp < cmdbuf+5; cp++) {
445 			if (*cp < '0' || *cp > '7')
446 				SCREWUP("bad mode");
447 			mode = (mode << 3) | (*cp - '0');
448 		}
449 		if (*cp++ != ' ')
450 			SCREWUP("mode not delimited");
451 		size = 0;
452 		while (*cp >= '0' && *cp <= '9')
453 			size = size * 10 + (*cp++ - '0');
454 		if (*cp++ != ' ')
455 			SCREWUP("size not delimited");
456 		if (targisdir)
457 			(void) sprintf(nambuf, "%s%s%s", targ,
458 			    *targ ? "/" : "", cp);
459 		else
460 			(void) strcpy(nambuf, targ);
461 		exists = stat(nambuf, &stb) == 0;
462 		if (exists && access(nambuf, 2) < 0)
463 			goto bad2;
464 		{ char *slash = rindex(nambuf, '/'), *dir;
465 		  if (slash == 0) {
466 			slash = "/";
467 			dir = ".";
468 		  } else {
469 			*slash = 0;
470 			dir = nambuf;
471 		  }
472 		  if (exists == 0 && access(dir, 2) < 0)
473 			goto bad;
474 		  *slash = '/';
475 		  if (cmdbuf[0] == 'D') {
476 			if (stat(nambuf, &stb) == 0) {
477 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
478 					errno = ENOTDIR;
479 					goto bad;
480 				}
481 			} else if (mkdir(nambuf, mode) < 0)
482 				goto bad;
483 			myargv[0] = nambuf;
484 			sink(1, myargv);
485 			continue;
486 		  }
487 		  if ((of = creat(nambuf, mode)) < 0) {
488 	bad:
489 			*slash = '/';
490 	bad2:
491 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
492 			continue;
493 		  }
494 		}
495 		if (exists == 0) {
496 			(void) stat(nambuf, &stb);
497 			(void) chown(nambuf, pwd->pw_uid, stb.st_gid);
498 			(void) chmod(nambuf, mode &~ mask);
499 		}
500 		ga();
501 		wrerr = 0;
502 		for (i = 0; i < size; i += BUFSIZ) {
503 			int amt = BUFSIZ;
504 			char *cp = buf;
505 
506 			if (i + amt > size)
507 				amt = size - i;
508 			do {
509 				int j = read(rem, cp, amt);
510 
511 				if (j <= 0)
512 					exit(1);
513 				amt -= j;
514 				cp += j;
515 			} while (amt > 0);
516 			amt = BUFSIZ;
517 			if (i + amt > size)
518 				amt = size - i;
519 			if (wrerr == 0 && write(of, buf, amt) != amt)
520 				wrerr++;
521 		}
522 		(void) close(of);
523 		(void) response();
524 		if (wrerr)
525 			error("rcp: %s: %s\n", cp, sys_errlist[errno]);
526 		else
527 			ga();
528 	}
529 screwup:
530 	error("rcp: protocol screwup: %s\n", whopp);
531 	exit(1);
532 }
533 
534 /*VARARGS*/
535 error(fmt, a1, a2, a3, a4, a5)
536 	char *fmt;
537 	int a1, a2, a3, a4, a5;
538 {
539 	char buf[BUFSIZ], *cp = buf;
540 
541 	errs++;
542 	*cp++ = 1;
543 	(void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
544 	(void) write(rem, buf, strlen(buf));
545 	if (iamremote == 0)
546 		(void) write(2, buf+1, strlen(buf+1));
547 }
548 
549 mkdir(name, mode)
550 	char *name;
551 	int mode;
552 {
553 	char *argv[4];
554 	int pid, rc;
555 
556 	argv[0] = "mkdir";
557 	argv[1] = name;
558 	argv[2] = 0;
559 	pid = fork();
560 	if (pid < 0) {
561 		perror("cp");
562 		return (1);
563 	}
564 	if (pid) {
565 		while (wait(&rc) != pid)
566 			continue;
567 		if (rc == 0)
568 			if (chmod(name, mode) < 0) {
569 				perror(name);
570 				rc = 1;
571 			}
572 		return (rc);
573 	}
574 	(void) setuid(getuid());
575 	execv("/bin/mkdir", argv);
576 	execv("/usr/bin/mkdir", argv);
577 	perror("mkdir");
578 	_exit(1);
579 	/*NOTREACHED*/
580 }
581 
582