xref: /original-bsd/bin/rcp/rcp.c (revision b3b53e97)
1 #ifndef lint
2 static char sccsid[] = "@(#)rcp.c	4.1 82/04/02";
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, i;
227 
228 	for (x = 0; x < argc; x++) {
229 		name = argv[x];
230 		if (access(name, 4) < 0 || (f = open(name, 0)) < 0) {
231 			error("rcp: %s: %s\n", name, sys_errlist[errno]);
232 			continue;
233 		}
234 		if (fstat(f, &stb) < 0)
235 			goto notreg;
236 		switch (stb.st_mode&S_IFMT) {
237 
238 		case S_IFREG:
239 			break;
240 
241 		case S_IFDIR:
242 			if (iamrecursive) {
243 				(void) close(f);
244 				rsource(name, (int)stb.st_mode);
245 				continue;
246 			}
247 			/* fall into ... */
248 		default:
249 notreg:
250 			(void) close(f);
251 			error("rcp: %s: not a plain file\n", name);
252 			continue;
253 		}
254 		last = rindex(name, '/');
255 		if (last == 0)
256 			last = name;
257 		else
258 			last++;
259 		(void) sprintf(buf, "C%04o %d %s\n",
260 		    stb.st_mode&07777, stb.st_size, last);
261 		(void) write(rem, buf, strlen(buf));
262 		if (response() < 0) {
263 			(void) close(f);
264 			continue;
265 		}
266 		sizerr = 0;
267 		for (i = 0; i < stb.st_size; i += BUFSIZ) {
268 			int amt = BUFSIZ;
269 			if (i + amt > stb.st_size)
270 				amt = stb.st_size - i;
271 			if (sizerr == 0 && read(f, buf, amt) != amt)
272 				sizerr = 1;
273 			(void) write(rem, buf, amt);
274 		}
275 		(void) close(f);
276 		if (sizerr == 0)
277 			ga();
278 		else
279 			error("rcp: %s: file changed size\n", name);
280 		(void) response();
281 	}
282 }
283 
284 #include <ndir.h>
285 
286 rsource(name, mode)
287 	char *name;
288 	int mode;
289 {
290 	DIR *d = opendir(name);
291 	char *last;
292 	struct direct *dp;
293 	char buf[BUFSIZ];
294 	char *bufv[1];
295 
296 	if (d == 0) {
297 		error("%s: %s\n", name, sys_errlist[errno]);
298 		return;
299 	}
300 	last = rindex(name, '/');
301 	if (last == 0)
302 		last = name;
303 	else
304 		last++;
305 	(void) sprintf(buf, "D%04o %d %s\n", mode&07777, 0, last);
306 	(void) write(rem, buf, strlen(buf));
307 	if (response() < 0) {
308 		closedir(d);
309 		return;
310 	}
311 	while (dp = readdir(d)) {
312 		if (dp->d_ino == 0)
313 			continue;
314 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
315 			continue;
316 		if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
317 			error("%s/%s: Name too long.\n", name, dp->d_name);
318 			continue;
319 		}
320 		(void) sprintf(buf, "%s/%s", name, dp->d_name);
321 		bufv[0] = buf;
322 		source(1, bufv);
323 	}
324 	closedir(d);
325 	(void) write(rem, "E\n", 2);
326 	(void) response();
327 }
328 
329 response()
330 {
331 	char resp, c, rbuf[BUFSIZ], *cp = rbuf;
332 
333 	if (read(rem, &resp, 1) != 1)
334 		lostconn();
335 	switch (resp) {
336 
337 	case 0:
338 		return (0);
339 
340 	default:
341 		*cp++ = resp;
342 		/* fall into... */
343 	case 1:
344 	case 2:
345 		do {
346 			if (read(rem, &c, 1) != 1)
347 				lostconn();
348 			*cp++ = c;
349 		} while (cp < &rbuf[BUFSIZ] && c != '\n');
350 		if (iamremote == 0)
351 			(void) write(2, rbuf, cp - rbuf);
352 		errs++;
353 		if (resp == 1)
354 			return (-1);
355 		exit(1);
356 	}
357 	/*NOTREACHED*/
358 }
359 
360 lostconn()
361 {
362 
363 	if (iamremote == 0)
364 		fprintf(stderr, "rcp: lost connection\n");
365 	exit(1);
366 }
367 
368 sink(argc, argv)
369 	int argc;
370 	char **argv;
371 {
372 	char *targ;
373 	char cmdbuf[BUFSIZ], nambuf[BUFSIZ], buf[BUFSIZ], *cp;
374 	int of, mode, i, size, wrerr, exists;
375 	char *whopp;
376 	struct stat stb; int targisdir = 0;
377 #define	SCREWUP(str)	{ whopp = str; goto screwup; }
378 	int mask = umask(0);
379 	char *myargv[1];
380 
381 	umask(mask);
382 	if (argc > 1) {
383 		error("rcp: ambiguous target\n");
384 		exit(1);
385 	}
386 	targ = *argv;
387 	if (targetshouldbedirectory)
388 		verifydir(targ);
389 	ga();
390 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
391 		targisdir = 1;
392 	for (;;) {
393 		cp = cmdbuf;
394 		if (read(rem, cp, 1) <= 0)
395 			return;
396 		if (*cp++ == '\n')
397 			SCREWUP("unexpected '\\n'");
398 		do {
399 			if (read(rem, cp, 1) != 1)
400 				SCREWUP("lost connection");
401 		} while (*cp++ != '\n');
402 		*cp = 0;
403 		if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
404 			if (iamremote == 0)
405 				(void) write(2, cmdbuf, strlen(cmdbuf));
406 			if (cmdbuf[0] == '\02')
407 				exit(1);
408 			errs++;
409 			continue;
410 		}
411 		*--cp = 0;
412 		cp = cmdbuf;
413 		if (*cp == 'E') {
414 			ga();
415 			return;
416 		}
417 		if (*cp != 'C' && *cp != 'D')
418 			SCREWUP("expected control record");
419 		cp++;
420 		mode = 0;
421 		for (; cp < cmdbuf+5; cp++) {
422 			if (*cp < '0' || *cp > '7')
423 				SCREWUP("bad mode");
424 			mode = (mode << 3) | (*cp - '0');
425 		}
426 		if (*cp++ != ' ')
427 			SCREWUP("mode not delimited");
428 		size = 0;
429 		while (*cp >= '0' && *cp <= '9')
430 			size = size * 10 + (*cp++ - '0');
431 		if (*cp++ != ' ')
432 			SCREWUP("size not delimited");
433 		if (targisdir)
434 			(void) sprintf(nambuf, "%s%s%s", targ,
435 			    *targ ? "/" : "", cp);
436 		else
437 			(void) strcpy(nambuf, targ);
438 		exists = stat(nambuf, &stb) == 0;
439 		if (exists && access(nambuf, 2) < 0)
440 			goto bad2;
441 		{ char *slash = rindex(nambuf, '/'), *dir;
442 		  if (slash == 0) {
443 			slash = "/";
444 			dir = ".";
445 		  } else {
446 			*slash = 0;
447 			dir = nambuf;
448 		  }
449 		  if (exists == 0 && access(dir, 2) < 0)
450 			goto bad;
451 		  *slash = '/';
452 		  if (cmdbuf[0] == 'D') {
453 			if (stat(nambuf, &stb) == 0) {
454 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
455 					errno = ENOTDIR;
456 					goto bad;
457 				}
458 			} else if (mkdir(nambuf, mode) < 0)
459 				goto bad;
460 			myargv[0] = nambuf;
461 			sink(1, myargv);
462 			continue;
463 		  }
464 		  if ((of = creat(nambuf, mode)) < 0) {
465 	bad:
466 			*slash = '/';
467 	bad2:
468 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
469 			continue;
470 		  }
471 		}
472 		if (exists == 0) {
473 			(void) stat(nambuf, &stb);
474 			(void) chown(nambuf, pwd->pw_uid, stb.st_gid);
475 			(void) chmod(nambuf, mode &~ mask);
476 		}
477 		ga();
478 		wrerr = 0;
479 		for (i = 0; i < size; i += BUFSIZ) {
480 			int amt = BUFSIZ;
481 			char *cp = buf;
482 
483 			if (i + amt > size)
484 				amt = size - i;
485 			do {
486 				int j = read(rem, cp, amt);
487 
488 				if (j <= 0)
489 					exit(1);
490 				amt -= j;
491 				cp += j;
492 			} while (amt > 0);
493 			amt = BUFSIZ;
494 			if (i + amt > size)
495 				amt = size - i;
496 			if (wrerr == 0 && write(of, buf, amt) != amt)
497 				wrerr++;
498 		}
499 		(void) close(of);
500 		(void) response();
501 		if (wrerr)
502 			error("rcp: %s: %s\n", cp, sys_errlist[errno]);
503 		else
504 			ga();
505 	}
506 screwup:
507 	error("rcp: protocol screwup: %s\n", whopp);
508 	exit(1);
509 }
510 
511 /*VARARGS*/
512 error(fmt, a1, a2, a3, a4, a5)
513 	char *fmt;
514 	int a1, a2, a3, a4, a5;
515 {
516 	char buf[BUFSIZ], *cp = buf;
517 
518 	errs++;
519 	*cp++ = 1;
520 	(void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
521 	(void) write(rem, buf, strlen(buf));
522 	if (iamremote == 0)
523 		(void) write(2, buf+1, strlen(buf+1));
524 }
525 
526 mkdir(name, mode)
527 	char *name;
528 	int mode;
529 {
530 	char *argv[4];
531 	int pid, rc;
532 
533 	argv[0] = "mkdir";
534 	argv[1] = name;
535 	argv[2] = 0;
536 	pid = fork();
537 	if (pid < 0) {
538 		perror("cp");
539 		return (1);
540 	}
541 	if (pid) {
542 		while (wait(&rc) != pid)
543 			continue;
544 		if (rc == 0)
545 			if (chmod(name, mode) < 0) {
546 				perror(name);
547 				rc = 1;
548 			}
549 		return (rc);
550 	}
551 	(void) setuid(getuid());
552 	execv("/bin/mkdir", argv);
553 	execv("/usr/bin/mkdir", argv);
554 	perror("mkdir");
555 	_exit(1);
556 	/*NOTREACHED*/
557 }
558