Commit | Line | Data |
---|---|---|
b7ff3bb3 | 1 | #ifndef lint |
48755084 | 2 | static char sccsid[] = "@(#)rcp.c 4.11 85/02/16"; |
b7ff3bb3 BJ |
3 | #endif |
4 | ||
4ca10280 SL |
5 | /* |
6 | * rcp | |
7 | */ | |
ed0072ae | 8 | #include <sys/param.h> |
b7ff3bb3 BJ |
9 | #include <sys/stat.h> |
10 | #include <sys/ioctl.h> | |
94a2d2a7 SL |
11 | |
12 | #include <netinet/in.h> | |
13 | ||
14 | #include <stdio.h> | |
15 | #include <signal.h> | |
b7ff3bb3 BJ |
16 | #include <pwd.h> |
17 | #include <ctype.h> | |
18 | #include <errno.h> | |
4ca10280 | 19 | |
b7ff3bb3 BJ |
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" : ""); | |
4ca10280 | 81 | signal(SIGPIPE, lostconn); |
b7ff3bb3 BJ |
82 | targ = colon(argv[argc - 1]); |
83 | if (targ) { | |
84 | *targ++ = 0; | |
06fb72f5 SL |
85 | if (*targ == 0) |
86 | targ = "."; | |
b7ff3bb3 BJ |
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; | |
06fb72f5 | 98 | if (*src == 0) |
195c1de2 | 99 | src = "."; |
b7ff3bb3 BJ |
100 | suser = rindex(argv[i], '.'); |
101 | if (suser) { | |
102 | *suser++ = 0; | |
103 | if (!okname(suser)) | |
104 | continue; | |
bd864c30 RC |
105 | (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s.%s:%s'", |
106 | argv[i], suser, cmd, src, | |
107 | argv[argc - 1], tuser, targ); | |
b7ff3bb3 | 108 | } else |
bd864c30 RC |
109 | (void) sprintf(buf, "rsh %s -n %s %s '%s.%s:%s'", |
110 | argv[i], cmd, src, | |
111 | argv[argc - 1], tuser, targ); | |
b7ff3bb3 BJ |
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; | |
06fb72f5 SL |
141 | if (*src == 0) |
142 | src = "."; | |
b7ff3bb3 BJ |
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 | ||
48755084 S |
216 | susystem(s) |
217 | char *s; | |
b7ff3bb3 | 218 | { |
48755084 S |
219 | int status, pid, w; |
220 | register int (*istat)(), (*qstat)(); | |
b7ff3bb3 | 221 | |
48755084 S |
222 | if ((pid = vfork()) == 0) { |
223 | setuid(getuid()); | |
224 | execl("/bin/sh", "sh", "-c", s, (char *)0); | |
225 | _exit(127); | |
226 | } | |
227 | istat = signal(SIGINT, SIG_IGN); | |
228 | qstat = signal(SIGQUIT, SIG_IGN); | |
229 | while ((w = wait(&status)) != pid && w != -1) | |
230 | ; | |
231 | if (w == -1) | |
232 | status = -1; | |
233 | signal(SIGINT, istat); | |
234 | signal(SIGQUIT, qstat); | |
235 | return (status); | |
b7ff3bb3 BJ |
236 | } |
237 | ||
238 | source(argc, argv) | |
239 | int argc; | |
240 | char **argv; | |
241 | { | |
242 | char *last, *name; | |
243 | struct stat stb; | |
244 | char buf[BUFSIZ]; | |
5f10dd79 KM |
245 | int x, sizerr, f; |
246 | off_t i; | |
b7ff3bb3 BJ |
247 | |
248 | for (x = 0; x < argc; x++) { | |
249 | name = argv[x]; | |
250 | if (access(name, 4) < 0 || (f = open(name, 0)) < 0) { | |
251 | error("rcp: %s: %s\n", name, sys_errlist[errno]); | |
252 | continue; | |
253 | } | |
254 | if (fstat(f, &stb) < 0) | |
255 | goto notreg; | |
256 | switch (stb.st_mode&S_IFMT) { | |
257 | ||
258 | case S_IFREG: | |
259 | break; | |
260 | ||
261 | case S_IFDIR: | |
262 | if (iamrecursive) { | |
263 | (void) close(f); | |
264 | rsource(name, (int)stb.st_mode); | |
265 | continue; | |
266 | } | |
267 | /* fall into ... */ | |
268 | default: | |
269 | notreg: | |
270 | (void) close(f); | |
271 | error("rcp: %s: not a plain file\n", name); | |
272 | continue; | |
273 | } | |
274 | last = rindex(name, '/'); | |
275 | if (last == 0) | |
276 | last = name; | |
277 | else | |
278 | last++; | |
5f10dd79 | 279 | (void) sprintf(buf, "C%04o %D %s\n", |
b7ff3bb3 BJ |
280 | stb.st_mode&07777, stb.st_size, last); |
281 | (void) write(rem, buf, strlen(buf)); | |
282 | if (response() < 0) { | |
283 | (void) close(f); | |
284 | continue; | |
285 | } | |
286 | sizerr = 0; | |
287 | for (i = 0; i < stb.st_size; i += BUFSIZ) { | |
288 | int amt = BUFSIZ; | |
289 | if (i + amt > stb.st_size) | |
290 | amt = stb.st_size - i; | |
291 | if (sizerr == 0 && read(f, buf, amt) != amt) | |
292 | sizerr = 1; | |
293 | (void) write(rem, buf, amt); | |
294 | } | |
295 | (void) close(f); | |
296 | if (sizerr == 0) | |
297 | ga(); | |
298 | else | |
299 | error("rcp: %s: file changed size\n", name); | |
300 | (void) response(); | |
301 | } | |
302 | } | |
303 | ||
06fb72f5 | 304 | #include <sys/dir.h> |
b7ff3bb3 BJ |
305 | |
306 | rsource(name, mode) | |
307 | char *name; | |
308 | int mode; | |
309 | { | |
310 | DIR *d = opendir(name); | |
311 | char *last; | |
312 | struct direct *dp; | |
313 | char buf[BUFSIZ]; | |
314 | char *bufv[1]; | |
315 | ||
316 | if (d == 0) { | |
317 | error("%s: %s\n", name, sys_errlist[errno]); | |
318 | return; | |
319 | } | |
320 | last = rindex(name, '/'); | |
321 | if (last == 0) | |
322 | last = name; | |
323 | else | |
324 | last++; | |
325 | (void) sprintf(buf, "D%04o %d %s\n", mode&07777, 0, last); | |
326 | (void) write(rem, buf, strlen(buf)); | |
327 | if (response() < 0) { | |
328 | closedir(d); | |
329 | return; | |
330 | } | |
331 | while (dp = readdir(d)) { | |
332 | if (dp->d_ino == 0) | |
333 | continue; | |
334 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
335 | continue; | |
336 | if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
337 | error("%s/%s: Name too long.\n", name, dp->d_name); | |
338 | continue; | |
339 | } | |
340 | (void) sprintf(buf, "%s/%s", name, dp->d_name); | |
341 | bufv[0] = buf; | |
342 | source(1, bufv); | |
343 | } | |
344 | closedir(d); | |
345 | (void) write(rem, "E\n", 2); | |
346 | (void) response(); | |
347 | } | |
348 | ||
349 | response() | |
350 | { | |
351 | char resp, c, rbuf[BUFSIZ], *cp = rbuf; | |
352 | ||
353 | if (read(rem, &resp, 1) != 1) | |
354 | lostconn(); | |
355 | switch (resp) { | |
356 | ||
357 | case 0: | |
358 | return (0); | |
359 | ||
360 | default: | |
361 | *cp++ = resp; | |
362 | /* fall into... */ | |
363 | case 1: | |
364 | case 2: | |
365 | do { | |
366 | if (read(rem, &c, 1) != 1) | |
367 | lostconn(); | |
368 | *cp++ = c; | |
369 | } while (cp < &rbuf[BUFSIZ] && c != '\n'); | |
370 | if (iamremote == 0) | |
371 | (void) write(2, rbuf, cp - rbuf); | |
372 | errs++; | |
373 | if (resp == 1) | |
374 | return (-1); | |
375 | exit(1); | |
376 | } | |
377 | /*NOTREACHED*/ | |
378 | } | |
379 | ||
380 | lostconn() | |
381 | { | |
382 | ||
383 | if (iamremote == 0) | |
384 | fprintf(stderr, "rcp: lost connection\n"); | |
385 | exit(1); | |
386 | } | |
387 | ||
388 | sink(argc, argv) | |
389 | int argc; | |
390 | char **argv; | |
391 | { | |
392 | char *targ; | |
393 | char cmdbuf[BUFSIZ], nambuf[BUFSIZ], buf[BUFSIZ], *cp; | |
4550ab9e | 394 | int of, mode, wrerr, exists, first; |
5f10dd79 | 395 | off_t i, size; |
b7ff3bb3 BJ |
396 | char *whopp; |
397 | struct stat stb; int targisdir = 0; | |
398 | #define SCREWUP(str) { whopp = str; goto screwup; } | |
399 | int mask = umask(0); | |
400 | char *myargv[1]; | |
401 | ||
402 | umask(mask); | |
403 | if (argc > 1) { | |
404 | error("rcp: ambiguous target\n"); | |
405 | exit(1); | |
406 | } | |
407 | targ = *argv; | |
408 | if (targetshouldbedirectory) | |
409 | verifydir(targ); | |
410 | ga(); | |
411 | if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) | |
412 | targisdir = 1; | |
4550ab9e | 413 | for (first = 1; ; first = 0) { |
b7ff3bb3 BJ |
414 | cp = cmdbuf; |
415 | if (read(rem, cp, 1) <= 0) | |
416 | return; | |
417 | if (*cp++ == '\n') | |
418 | SCREWUP("unexpected '\\n'"); | |
419 | do { | |
420 | if (read(rem, cp, 1) != 1) | |
421 | SCREWUP("lost connection"); | |
422 | } while (*cp++ != '\n'); | |
423 | *cp = 0; | |
424 | if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { | |
425 | if (iamremote == 0) | |
06fb72f5 | 426 | (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); |
b7ff3bb3 BJ |
427 | if (cmdbuf[0] == '\02') |
428 | exit(1); | |
429 | errs++; | |
430 | continue; | |
431 | } | |
432 | *--cp = 0; | |
433 | cp = cmdbuf; | |
434 | if (*cp == 'E') { | |
435 | ga(); | |
436 | return; | |
437 | } | |
4550ab9e RC |
438 | if (*cp != 'C' && *cp != 'D') { |
439 | /* | |
440 | * Check for the case "rcp remote:foo\* local:bar". | |
441 | * In this case, the line "No match." can be returned | |
442 | * by the shell before the rcp command on the remote is | |
443 | * executed so the ^Aerror_message convention isn't | |
444 | * followed. | |
445 | */ | |
446 | if (first) { | |
447 | error("%s\n", cp); | |
448 | exit(1); | |
449 | } | |
b7ff3bb3 | 450 | SCREWUP("expected control record"); |
4550ab9e | 451 | } |
b7ff3bb3 BJ |
452 | cp++; |
453 | mode = 0; | |
454 | for (; cp < cmdbuf+5; cp++) { | |
455 | if (*cp < '0' || *cp > '7') | |
456 | SCREWUP("bad mode"); | |
457 | mode = (mode << 3) | (*cp - '0'); | |
458 | } | |
459 | if (*cp++ != ' ') | |
460 | SCREWUP("mode not delimited"); | |
461 | size = 0; | |
462 | while (*cp >= '0' && *cp <= '9') | |
463 | size = size * 10 + (*cp++ - '0'); | |
464 | if (*cp++ != ' ') | |
465 | SCREWUP("size not delimited"); | |
466 | if (targisdir) | |
467 | (void) sprintf(nambuf, "%s%s%s", targ, | |
468 | *targ ? "/" : "", cp); | |
469 | else | |
470 | (void) strcpy(nambuf, targ); | |
471 | exists = stat(nambuf, &stb) == 0; | |
472 | if (exists && access(nambuf, 2) < 0) | |
473 | goto bad2; | |
474 | { char *slash = rindex(nambuf, '/'), *dir; | |
475 | if (slash == 0) { | |
476 | slash = "/"; | |
477 | dir = "."; | |
478 | } else { | |
479 | *slash = 0; | |
480 | dir = nambuf; | |
04f0ebce RC |
481 | if (*dir == '\0') |
482 | dir = "/"; | |
b7ff3bb3 BJ |
483 | } |
484 | if (exists == 0 && access(dir, 2) < 0) | |
485 | goto bad; | |
486 | *slash = '/'; | |
487 | if (cmdbuf[0] == 'D') { | |
488 | if (stat(nambuf, &stb) == 0) { | |
489 | if ((stb.st_mode&S_IFMT) != S_IFDIR) { | |
490 | errno = ENOTDIR; | |
491 | goto bad; | |
492 | } | |
48755084 | 493 | } else if (makedir(nambuf, mode) < 0) |
b7ff3bb3 BJ |
494 | goto bad; |
495 | myargv[0] = nambuf; | |
496 | sink(1, myargv); | |
497 | continue; | |
498 | } | |
499 | if ((of = creat(nambuf, mode)) < 0) { | |
500 | bad: | |
501 | *slash = '/'; | |
502 | bad2: | |
503 | error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); | |
504 | continue; | |
505 | } | |
506 | } | |
507 | if (exists == 0) { | |
48755084 | 508 | (void) chown(nambuf, pwd->pw_uid, -1); |
b7ff3bb3 BJ |
509 | (void) chmod(nambuf, mode &~ mask); |
510 | } | |
511 | ga(); | |
512 | wrerr = 0; | |
513 | for (i = 0; i < size; i += BUFSIZ) { | |
514 | int amt = BUFSIZ; | |
515 | char *cp = buf; | |
516 | ||
517 | if (i + amt > size) | |
518 | amt = size - i; | |
519 | do { | |
520 | int j = read(rem, cp, amt); | |
521 | ||
522 | if (j <= 0) | |
523 | exit(1); | |
524 | amt -= j; | |
525 | cp += j; | |
526 | } while (amt > 0); | |
527 | amt = BUFSIZ; | |
528 | if (i + amt > size) | |
529 | amt = size - i; | |
530 | if (wrerr == 0 && write(of, buf, amt) != amt) | |
531 | wrerr++; | |
532 | } | |
533 | (void) close(of); | |
534 | (void) response(); | |
535 | if (wrerr) | |
536 | error("rcp: %s: %s\n", cp, sys_errlist[errno]); | |
537 | else | |
538 | ga(); | |
539 | } | |
540 | screwup: | |
541 | error("rcp: protocol screwup: %s\n", whopp); | |
542 | exit(1); | |
543 | } | |
544 | ||
545 | /*VARARGS*/ | |
546 | error(fmt, a1, a2, a3, a4, a5) | |
547 | char *fmt; | |
548 | int a1, a2, a3, a4, a5; | |
549 | { | |
550 | char buf[BUFSIZ], *cp = buf; | |
551 | ||
552 | errs++; | |
553 | *cp++ = 1; | |
554 | (void) sprintf(cp, fmt, a1, a2, a3, a4, a5); | |
555 | (void) write(rem, buf, strlen(buf)); | |
556 | if (iamremote == 0) | |
557 | (void) write(2, buf+1, strlen(buf+1)); | |
558 | } | |
559 | ||
48755084 S |
560 | makedir(name, mode) |
561 | register char *name; | |
562 | register int mode; | |
b7ff3bb3 | 563 | { |
48755084 S |
564 | register int _errno; |
565 | ||
566 | if (mkdir(name, mode) < 0 || chown(name, getuid(), -1) < 0) { | |
567 | _errno = errno; | |
568 | rmdir(name); | |
569 | errno = _errno; | |
570 | return (-1); | |
b7ff3bb3 | 571 | } |
48755084 S |
572 | |
573 | return (0); | |
b7ff3bb3 | 574 | } |