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