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