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