Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1983, 1990 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
35 | char copyright[] = | |
36 | "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\ | |
37 | All rights reserved.\n"; | |
38 | #endif /* not lint */ | |
39 | ||
40 | #ifndef lint | |
41 | static char sccsid[] = "@(#)rcp.c 5.32 (Berkeley) 2/25/91"; | |
42 | #endif /* not lint */ | |
43 | ||
44 | /* | |
45 | * rcp | |
46 | */ | |
47 | #include <sys/param.h> | |
48 | #include <sys/stat.h> | |
49 | #include <sys/time.h> | |
50 | #include <sys/ioctl.h> | |
51 | #include <sys/socket.h> | |
52 | #include <sys/wait.h> | |
53 | #include <netinet/in.h> | |
54 | #include <netinet/in_systm.h> | |
55 | #include <netinet/ip.h> | |
56 | #include <dirent.h> | |
57 | #include <fcntl.h> | |
58 | #include <signal.h> | |
59 | #include <pwd.h> | |
60 | #include <netdb.h> | |
61 | #include <errno.h> | |
62 | #include <unistd.h> | |
63 | #include <stdio.h> | |
64 | #include <stdlib.h> | |
65 | #include <string.h> | |
66 | #include <ctype.h> | |
67 | #include "pathnames.h" | |
68 | ||
69 | #ifdef KERBEROS | |
70 | #include <kerberosIV/des.h> | |
71 | #include <kerberosIV/krb.h> | |
72 | char dst_realm_buf[REALM_SZ]; | |
73 | char *dest_realm = NULL; | |
74 | int use_kerberos = 1; | |
75 | CREDENTIALS cred; | |
76 | Key_schedule schedule; | |
77 | extern char *krb_realmofhost(); | |
78 | #ifdef CRYPT | |
79 | int doencrypt = 0; | |
80 | #define OPTIONS "dfk:prtx" | |
81 | #else | |
82 | #define OPTIONS "dfk:prt" | |
83 | #endif | |
84 | #else | |
85 | #define OPTIONS "dfprt" | |
86 | #endif | |
87 | ||
88 | struct passwd *pwd; | |
89 | u_short port; | |
90 | uid_t userid; | |
91 | int errs, rem; | |
92 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; | |
93 | ||
94 | #define CMDNEEDS 64 | |
95 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | |
96 | ||
97 | typedef struct _buf { | |
98 | int cnt; | |
99 | char *buf; | |
100 | } BUF; | |
101 | ||
102 | void lostconn(); | |
103 | ||
104 | main(argc, argv) | |
105 | int argc; | |
106 | char **argv; | |
107 | { | |
108 | extern int optind; | |
109 | extern char *optarg; | |
110 | struct servent *sp; | |
111 | int ch, fflag, tflag; | |
112 | char *targ, *shell, *colon(); | |
113 | ||
114 | fflag = tflag = 0; | |
115 | while ((ch = getopt(argc, argv, OPTIONS)) != EOF) | |
116 | switch(ch) { | |
117 | /* user-visible flags */ | |
118 | case 'p': /* preserve access/mod times */ | |
119 | ++pflag; | |
120 | break; | |
121 | case 'r': | |
122 | ++iamrecursive; | |
123 | break; | |
124 | #ifdef KERBEROS | |
125 | case 'k': | |
126 | strncpy(dst_realm_buf, optarg, REALM_SZ); | |
127 | dest_realm = dst_realm_buf; | |
128 | break; | |
129 | #ifdef CRYPT | |
130 | case 'x': | |
131 | doencrypt = 1; | |
132 | /* des_set_key(cred.session, schedule); */ | |
133 | break; | |
134 | #endif | |
135 | #endif | |
136 | /* rshd-invoked options (server) */ | |
137 | case 'd': | |
138 | targetshouldbedirectory = 1; | |
139 | break; | |
140 | case 'f': /* "from" */ | |
141 | iamremote = 1; | |
142 | fflag = 1; | |
143 | break; | |
144 | case 't': /* "to" */ | |
145 | iamremote = 1; | |
146 | tflag = 1; | |
147 | break; | |
148 | ||
149 | case '?': | |
150 | default: | |
151 | usage(); | |
152 | } | |
153 | argc -= optind; | |
154 | argv += optind; | |
155 | ||
156 | #ifdef KERBEROS | |
157 | #ifdef CRYPT | |
158 | shell = doencrypt ? "ekshell" : "kshell"; | |
159 | #else | |
160 | shell = "kshell"; | |
161 | #endif | |
162 | sp = getservbyname(shell, "tcp"); | |
163 | if (sp == NULL) { | |
164 | char msgbuf[64]; | |
165 | use_kerberos = 0; | |
166 | (void)snprintf(msgbuf, sizeof(msgbuf), | |
167 | "can't get entry for %s/tcp service", shell); | |
168 | old_warning(msgbuf); | |
169 | sp = getservbyname(shell = "shell", "tcp"); | |
170 | } | |
171 | #else | |
172 | sp = getservbyname(shell = "shell", "tcp"); | |
173 | #endif | |
174 | if (sp == NULL) { | |
175 | (void)fprintf(stderr, "rcp: %s/tcp: unknown service\n", shell); | |
176 | exit(1); | |
177 | } | |
178 | port = sp->s_port; | |
179 | ||
180 | if (!(pwd = getpwuid(userid = getuid()))) { | |
181 | (void)fprintf(stderr, "rcp: unknown user %d.\n", (int)userid); | |
182 | exit(1); | |
183 | } | |
184 | ||
185 | if (fflag) { | |
186 | /* follow "protocol", send data */ | |
187 | (void)response(); | |
188 | (void)setuid(userid); | |
189 | source(argc, argv); | |
190 | exit(errs); | |
191 | } | |
192 | ||
193 | if (tflag) { | |
194 | /* receive data */ | |
195 | (void)setuid(userid); | |
196 | sink(argc, argv); | |
197 | exit(errs); | |
198 | } | |
199 | ||
200 | if (argc < 2) | |
201 | usage(); | |
202 | if (argc > 2) | |
203 | targetshouldbedirectory = 1; | |
204 | ||
205 | rem = -1; | |
206 | /* command to be executed on remote system using "rsh" */ | |
207 | #ifdef KERBEROS | |
208 | (void)snprintf(cmd, sizeof(cmd), | |
209 | "rcp%s%s%s%s", iamrecursive ? " -r" : "", | |
210 | #ifdef CRYPT | |
211 | ((doencrypt && use_kerberos) ? " -x" : ""), | |
212 | #else | |
213 | "", | |
214 | #endif | |
215 | pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); | |
216 | #else | |
217 | (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", | |
218 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
219 | targetshouldbedirectory ? " -d" : ""); | |
220 | #endif | |
221 | ||
222 | (void)signal(SIGPIPE, lostconn); | |
223 | ||
224 | if (targ = colon(argv[argc - 1])) | |
225 | toremote(targ, argc, argv); /* destination is remote host */ | |
226 | else { | |
227 | tolocal(argc, argv); /* destination is local host */ | |
228 | if (targetshouldbedirectory) | |
229 | verifydir(argv[argc - 1]); | |
230 | } | |
231 | exit(errs); | |
232 | } | |
233 | ||
234 | toremote(targ, argc, argv) | |
235 | char *targ; | |
236 | int argc; | |
237 | char **argv; | |
238 | { | |
239 | int i, len, tos; | |
240 | char *bp, *host, *src, *suser, *thost, *tuser; | |
241 | char *colon(); | |
242 | ||
243 | *targ++ = 0; | |
244 | if (*targ == 0) | |
245 | targ = "."; | |
246 | ||
247 | if (thost = index(argv[argc - 1], '@')) { | |
248 | /* user@host */ | |
249 | *thost++ = 0; | |
250 | tuser = argv[argc - 1]; | |
251 | if (*tuser == '\0') | |
252 | tuser = NULL; | |
253 | else if (!okname(tuser)) | |
254 | exit(1); | |
255 | } else { | |
256 | thost = argv[argc - 1]; | |
257 | tuser = NULL; | |
258 | } | |
259 | ||
260 | for (i = 0; i < argc - 1; i++) { | |
261 | src = colon(argv[i]); | |
262 | if (src) { /* remote to remote */ | |
263 | *src++ = 0; | |
264 | if (*src == 0) | |
265 | src = "."; | |
266 | host = index(argv[i], '@'); | |
267 | len = strlen(_PATH_RSH) + strlen(argv[i]) + | |
268 | strlen(src) + (tuser ? strlen(tuser) : 0) + | |
269 | strlen(thost) + strlen(targ) + CMDNEEDS + 20; | |
270 | if (!(bp = malloc(len))) | |
271 | nospace(); | |
272 | if (host) { | |
273 | *host++ = 0; | |
274 | suser = argv[i]; | |
275 | if (*suser == '\0') | |
276 | suser = pwd->pw_name; | |
277 | else if (!okname(suser)) | |
278 | continue; | |
279 | (void)snprintf(bp, len, | |
280 | "%s %s -l %s -n %s %s '%s%s%s:%s'", | |
281 | _PATH_RSH, host, suser, cmd, src, | |
282 | tuser ? tuser : "", tuser ? "@" : "", | |
283 | thost, targ); | |
284 | } else | |
285 | (void)snprintf(bp, len, | |
286 | "%s %s -n %s %s '%s%s%s:%s'", | |
287 | _PATH_RSH, argv[i], cmd, src, | |
288 | tuser ? tuser : "", tuser ? "@" : "", | |
289 | thost, targ); | |
290 | (void)susystem(bp); | |
291 | (void)free(bp); | |
292 | } else { /* local to remote */ | |
293 | if (rem == -1) { | |
294 | len = strlen(targ) + CMDNEEDS + 20; | |
295 | if (!(bp = malloc(len))) | |
296 | nospace(); | |
297 | (void)snprintf(bp, len, "%s -t %s", cmd, targ); | |
298 | host = thost; | |
299 | #ifdef KERBEROS | |
300 | if (use_kerberos) | |
301 | rem = kerberos(&host, bp, | |
302 | pwd->pw_name, | |
303 | tuser ? tuser : pwd->pw_name); | |
304 | else | |
305 | #endif | |
306 | rem = rcmd(&host, port, pwd->pw_name, | |
307 | tuser ? tuser : pwd->pw_name, | |
308 | bp, 0); | |
309 | if (rem < 0) | |
310 | exit(1); | |
311 | tos = IPTOS_THROUGHPUT; | |
312 | if (setsockopt(rem, IPPROTO_IP, IP_TOS, | |
313 | (char *)&tos, sizeof(int)) < 0) | |
314 | perror("rcp: setsockopt TOS (ignored)"); | |
315 | if (response() < 0) | |
316 | exit(1); | |
317 | (void)free(bp); | |
318 | (void)setuid(userid); | |
319 | } | |
320 | source(1, argv+i); | |
321 | } | |
322 | } | |
323 | } | |
324 | ||
325 | tolocal(argc, argv) | |
326 | int argc; | |
327 | char **argv; | |
328 | { | |
329 | int i, len, tos; | |
330 | char *bp, *host, *src, *suser; | |
331 | char *colon(); | |
332 | ||
333 | for (i = 0; i < argc - 1; i++) { | |
334 | if (!(src = colon(argv[i]))) { /* local to local */ | |
335 | len = strlen(_PATH_CP) + strlen(argv[i]) + | |
336 | strlen(argv[argc - 1]) + 20; | |
337 | if (!(bp = malloc(len))) | |
338 | nospace(); | |
339 | (void)snprintf(bp, len, "%s%s%s %s %s", _PATH_CP, | |
340 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
341 | argv[i], argv[argc - 1]); | |
342 | (void)susystem(bp); | |
343 | (void)free(bp); | |
344 | continue; | |
345 | } | |
346 | *src++ = 0; | |
347 | if (*src == 0) | |
348 | src = "."; | |
349 | host = index(argv[i], '@'); | |
350 | if (host) { | |
351 | *host++ = 0; | |
352 | suser = argv[i]; | |
353 | if (*suser == '\0') | |
354 | suser = pwd->pw_name; | |
355 | else if (!okname(suser)) | |
356 | continue; | |
357 | } else { | |
358 | host = argv[i]; | |
359 | suser = pwd->pw_name; | |
360 | } | |
361 | len = strlen(src) + CMDNEEDS + 20; | |
362 | if (!(bp = malloc(len))) | |
363 | nospace(); | |
364 | (void)snprintf(bp, len, "%s -f %s", cmd, src); | |
365 | #ifdef KERBEROS | |
366 | if (use_kerberos) | |
367 | rem = kerberos(&host, bp, pwd->pw_name, suser); | |
368 | else | |
369 | #endif | |
370 | rem = rcmd(&host, port, pwd->pw_name, suser, bp, 0); | |
371 | (void)free(bp); | |
372 | if (rem < 0) | |
373 | continue; | |
374 | (void)seteuid(userid); | |
375 | tos = IPTOS_THROUGHPUT; | |
376 | if (setsockopt(rem, IPPROTO_IP, IP_TOS, | |
377 | (char *)&tos, sizeof(int)) < 0) | |
378 | perror("rcp: setsockopt TOS (ignored)"); | |
379 | sink(1, argv + argc - 1); | |
380 | (void)seteuid(0); | |
381 | (void)close(rem); | |
382 | rem = -1; | |
383 | } | |
384 | } | |
385 | ||
386 | verifydir(cp) | |
387 | char *cp; | |
388 | { | |
389 | struct stat stb; | |
390 | ||
391 | if (stat(cp, &stb) >= 0) { | |
392 | if ((stb.st_mode & S_IFMT) == S_IFDIR) | |
393 | return; | |
394 | errno = ENOTDIR; | |
395 | } | |
396 | error("rcp: %s: %s.\n", cp, strerror(errno)); | |
397 | exit(1); | |
398 | } | |
399 | ||
400 | char * | |
401 | colon(cp) | |
402 | register char *cp; | |
403 | { | |
404 | for (; *cp; ++cp) { | |
405 | if (*cp == ':') | |
406 | return(cp); | |
407 | if (*cp == '/') | |
408 | return(0); | |
409 | } | |
410 | return(0); | |
411 | } | |
412 | ||
413 | okname(cp0) | |
414 | char *cp0; | |
415 | { | |
416 | register char *cp = cp0; | |
417 | register int c; | |
418 | ||
419 | do { | |
420 | c = *cp; | |
421 | if (c & 0200) | |
422 | goto bad; | |
423 | if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') | |
424 | goto bad; | |
425 | } while (*++cp); | |
426 | return(1); | |
427 | bad: | |
428 | (void)fprintf(stderr, "rcp: invalid user name %s\n", cp0); | |
429 | return(0); | |
430 | } | |
431 | ||
432 | susystem(s) | |
433 | char *s; | |
434 | { | |
435 | int status, pid, w; | |
436 | register sig_t istat, qstat; | |
437 | ||
438 | if ((pid = vfork()) == 0) { | |
439 | (void)setuid(userid); | |
440 | execl(_PATH_BSHELL, "sh", "-c", s, (char *)0); | |
441 | _exit(127); | |
442 | } | |
443 | istat = signal(SIGINT, SIG_IGN); | |
444 | qstat = signal(SIGQUIT, SIG_IGN); | |
445 | while ((w = wait(&status)) != pid && w != -1) | |
446 | ; | |
447 | if (w == -1) | |
448 | status = -1; | |
449 | (void)signal(SIGINT, istat); | |
450 | (void)signal(SIGQUIT, qstat); | |
451 | return(status); | |
452 | } | |
453 | ||
454 | source(argc, argv) | |
455 | int argc; | |
456 | char **argv; | |
457 | { | |
458 | struct stat stb; | |
459 | static BUF buffer; | |
460 | BUF *bp; | |
461 | off_t i; | |
462 | int x, readerr, f, amt; | |
463 | char *last, *name, buf[BUFSIZ]; | |
464 | BUF *allocbuf(); | |
465 | ||
466 | for (x = 0; x < argc; x++) { | |
467 | name = argv[x]; | |
468 | if ((f = open(name, O_RDONLY, 0)) < 0) { | |
469 | error("rcp: %s: %s\n", name, strerror(errno)); | |
470 | continue; | |
471 | } | |
472 | if (fstat(f, &stb) < 0) | |
473 | goto notreg; | |
474 | switch (stb.st_mode&S_IFMT) { | |
475 | ||
476 | case S_IFREG: | |
477 | break; | |
478 | ||
479 | case S_IFDIR: | |
480 | if (iamrecursive) { | |
481 | (void)close(f); | |
482 | rsource(name, &stb); | |
483 | continue; | |
484 | } | |
485 | /* FALLTHROUGH */ | |
486 | default: | |
487 | notreg: (void)close(f); | |
488 | error("rcp: %s: not a plain file\n", name); | |
489 | continue; | |
490 | } | |
491 | last = rindex(name, '/'); | |
492 | if (last == 0) | |
493 | last = name; | |
494 | else | |
495 | last++; | |
496 | if (pflag) { | |
497 | /* | |
498 | * Make it compatible with possible future | |
499 | * versions expecting microseconds. | |
500 | */ | |
501 | (void)snprintf(buf, sizeof(buf), | |
502 | "T%ld 0 %ld 0\n", stb.st_mtime, stb.st_atime); | |
503 | (void)write(rem, buf, (int)strlen(buf)); | |
504 | if (response() < 0) { | |
505 | (void)close(f); | |
506 | continue; | |
507 | } | |
508 | } | |
509 | (void)snprintf(buf, sizeof(buf), | |
510 | "C%04o %ld %s\n", stb.st_mode&07777, stb.st_size, last); | |
511 | (void)write(rem, buf, (int)strlen(buf)); | |
512 | if (response() < 0) { | |
513 | (void)close(f); | |
514 | continue; | |
515 | } | |
516 | if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) { | |
517 | (void)close(f); | |
518 | continue; | |
519 | } | |
520 | readerr = 0; | |
521 | for (i = 0; i < stb.st_size; i += bp->cnt) { | |
522 | amt = bp->cnt; | |
523 | if (i + amt > stb.st_size) | |
524 | amt = stb.st_size - i; | |
525 | if (readerr == 0 && read(f, bp->buf, amt) != amt) | |
526 | readerr = errno; | |
527 | (void)write(rem, bp->buf, amt); | |
528 | } | |
529 | (void)close(f); | |
530 | if (readerr == 0) | |
531 | (void)write(rem, "", 1); | |
532 | else | |
533 | error("rcp: %s: %s\n", name, strerror(readerr)); | |
534 | (void)response(); | |
535 | } | |
536 | } | |
537 | ||
538 | rsource(name, statp) | |
539 | char *name; | |
540 | struct stat *statp; | |
541 | { | |
542 | DIR *dirp; | |
543 | struct dirent *dp; | |
544 | char *last, *vect[1], path[MAXPATHLEN]; | |
545 | ||
546 | if (!(dirp = opendir(name))) { | |
547 | error("rcp: %s: %s\n", name, strerror(errno)); | |
548 | return; | |
549 | } | |
550 | last = rindex(name, '/'); | |
551 | if (last == 0) | |
552 | last = name; | |
553 | else | |
554 | last++; | |
555 | if (pflag) { | |
556 | (void)snprintf(path, sizeof(path), | |
557 | "T%ld 0 %ld 0\n", statp->st_mtime, statp->st_atime); | |
558 | (void)write(rem, path, (int)strlen(path)); | |
559 | if (response() < 0) { | |
560 | closedir(dirp); | |
561 | return; | |
562 | } | |
563 | } | |
564 | (void)snprintf(path, sizeof(path), | |
565 | "D%04o %d %s\n", statp->st_mode&07777, 0, last); | |
566 | (void)write(rem, path, (int)strlen(path)); | |
567 | if (response() < 0) { | |
568 | closedir(dirp); | |
569 | return; | |
570 | } | |
571 | while (dp = readdir(dirp)) { | |
572 | if (dp->d_ino == 0) | |
573 | continue; | |
574 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
575 | continue; | |
576 | if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { | |
577 | error("%s/%s: name too long.\n", name, dp->d_name); | |
578 | continue; | |
579 | } | |
580 | (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); | |
581 | vect[0] = path; | |
582 | source(1, vect); | |
583 | } | |
584 | closedir(dirp); | |
585 | (void)write(rem, "E\n", 2); | |
586 | (void)response(); | |
587 | } | |
588 | ||
589 | response() | |
590 | { | |
591 | register char *cp; | |
592 | char ch, resp, rbuf[BUFSIZ]; | |
593 | ||
594 | if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) | |
595 | lostconn(); | |
596 | ||
597 | cp = rbuf; | |
598 | switch(resp) { | |
599 | case 0: /* ok */ | |
600 | return(0); | |
601 | default: | |
602 | *cp++ = resp; | |
603 | /* FALLTHROUGH */ | |
604 | case 1: /* error, followed by err msg */ | |
605 | case 2: /* fatal error, "" */ | |
606 | do { | |
607 | if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) | |
608 | lostconn(); | |
609 | *cp++ = ch; | |
610 | } while (cp < &rbuf[BUFSIZ] && ch != '\n'); | |
611 | ||
612 | if (!iamremote) | |
613 | (void)write(2, rbuf, cp - rbuf); | |
614 | ++errs; | |
615 | if (resp == 1) | |
616 | return(-1); | |
617 | exit(1); | |
618 | } | |
619 | /*NOTREACHED*/ | |
620 | } | |
621 | ||
622 | void | |
623 | lostconn() | |
624 | { | |
625 | if (!iamremote) | |
626 | (void)fprintf(stderr, "rcp: lost connection\n"); | |
627 | exit(1); | |
628 | } | |
629 | ||
630 | sink(argc, argv) | |
631 | int argc; | |
632 | char **argv; | |
633 | { | |
634 | register char *cp; | |
635 | static BUF buffer; | |
636 | struct stat stb; | |
637 | struct timeval tv[2]; | |
638 | enum { YES, NO, DISPLAYED } wrerr; | |
639 | BUF *bp, *allocbuf(); | |
640 | off_t i, j; | |
641 | char ch, *targ, *why; | |
642 | int amt, count, exists, first, mask, mode; | |
643 | int ofd, setimes, size, targisdir; | |
644 | char *np, *vect[1], buf[BUFSIZ]; | |
645 | ||
646 | #define atime tv[0] | |
647 | #define mtime tv[1] | |
648 | #define SCREWUP(str) { why = str; goto screwup; } | |
649 | ||
650 | setimes = targisdir = 0; | |
651 | mask = umask(0); | |
652 | if (!pflag) | |
653 | (void)umask(mask); | |
654 | if (argc != 1) { | |
655 | error("rcp: ambiguous target\n"); | |
656 | exit(1); | |
657 | } | |
658 | targ = *argv; | |
659 | if (targetshouldbedirectory) | |
660 | verifydir(targ); | |
661 | (void)write(rem, "", 1); | |
662 | if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) | |
663 | targisdir = 1; | |
664 | for (first = 1;; first = 0) { | |
665 | cp = buf; | |
666 | if (read(rem, cp, 1) <= 0) | |
667 | return; | |
668 | if (*cp++ == '\n') | |
669 | SCREWUP("unexpected <newline>"); | |
670 | do { | |
671 | if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) | |
672 | SCREWUP("lost connection"); | |
673 | *cp++ = ch; | |
674 | } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); | |
675 | *cp = 0; | |
676 | ||
677 | if (buf[0] == '\01' || buf[0] == '\02') { | |
678 | if (iamremote == 0) | |
679 | (void)write(2, buf + 1, (int)strlen(buf + 1)); | |
680 | if (buf[0] == '\02') | |
681 | exit(1); | |
682 | errs++; | |
683 | continue; | |
684 | } | |
685 | if (buf[0] == 'E') { | |
686 | (void)write(rem, "", 1); | |
687 | return; | |
688 | } | |
689 | ||
690 | if (ch == '\n') | |
691 | *--cp = 0; | |
692 | ||
693 | #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); | |
694 | cp = buf; | |
695 | if (*cp == 'T') { | |
696 | setimes++; | |
697 | cp++; | |
698 | getnum(mtime.tv_sec); | |
699 | if (*cp++ != ' ') | |
700 | SCREWUP("mtime.sec not delimited"); | |
701 | getnum(mtime.tv_usec); | |
702 | if (*cp++ != ' ') | |
703 | SCREWUP("mtime.usec not delimited"); | |
704 | getnum(atime.tv_sec); | |
705 | if (*cp++ != ' ') | |
706 | SCREWUP("atime.sec not delimited"); | |
707 | getnum(atime.tv_usec); | |
708 | if (*cp++ != '\0') | |
709 | SCREWUP("atime.usec not delimited"); | |
710 | (void)write(rem, "", 1); | |
711 | continue; | |
712 | } | |
713 | if (*cp != 'C' && *cp != 'D') { | |
714 | /* | |
715 | * Check for the case "rcp remote:foo\* local:bar". | |
716 | * In this case, the line "No match." can be returned | |
717 | * by the shell before the rcp command on the remote is | |
718 | * executed so the ^Aerror_message convention isn't | |
719 | * followed. | |
720 | */ | |
721 | if (first) { | |
722 | error("%s\n", cp); | |
723 | exit(1); | |
724 | } | |
725 | SCREWUP("expected control record"); | |
726 | } | |
727 | mode = 0; | |
728 | for (++cp; cp < buf + 5; cp++) { | |
729 | if (*cp < '0' || *cp > '7') | |
730 | SCREWUP("bad mode"); | |
731 | mode = (mode << 3) | (*cp - '0'); | |
732 | } | |
733 | if (*cp++ != ' ') | |
734 | SCREWUP("mode not delimited"); | |
735 | size = 0; | |
736 | while (isdigit(*cp)) | |
737 | size = size * 10 + (*cp++ - '0'); | |
738 | if (*cp++ != ' ') | |
739 | SCREWUP("size not delimited"); | |
740 | if (targisdir) { | |
741 | static char *namebuf; | |
742 | static int cursize; | |
743 | size_t need; | |
744 | ||
745 | need = strlen(targ) + strlen(cp) + 250; | |
746 | if (need > cursize) { | |
747 | if (!(namebuf = malloc(need))) | |
748 | error("out of memory\n"); | |
749 | } | |
750 | (void)snprintf(namebuf, need, "%s%s%s", targ, | |
751 | *targ ? "/" : "", cp); | |
752 | np = namebuf; | |
753 | } | |
754 | else | |
755 | np = targ; | |
756 | exists = stat(np, &stb) == 0; | |
757 | if (buf[0] == 'D') { | |
758 | if (exists) { | |
759 | if ((stb.st_mode&S_IFMT) != S_IFDIR) { | |
760 | errno = ENOTDIR; | |
761 | goto bad; | |
762 | } | |
763 | if (pflag) | |
764 | (void)chmod(np, mode); | |
765 | } else if (mkdir(np, mode) < 0) | |
766 | goto bad; | |
767 | vect[0] = np; | |
768 | sink(1, vect); | |
769 | if (setimes) { | |
770 | setimes = 0; | |
771 | if (utimes(np, tv) < 0) | |
772 | error("rcp: can't set times on %s: %s\n", | |
773 | np, strerror(errno)); | |
774 | } | |
775 | continue; | |
776 | } | |
777 | if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { | |
778 | bad: error("rcp: %s: %s\n", np, strerror(errno)); | |
779 | continue; | |
780 | } | |
781 | if (exists && pflag) | |
782 | (void)fchmod(ofd, mode); | |
783 | (void)write(rem, "", 1); | |
784 | if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == 0) { | |
785 | (void)close(ofd); | |
786 | continue; | |
787 | } | |
788 | cp = bp->buf; | |
789 | count = 0; | |
790 | wrerr = NO; | |
791 | for (i = 0; i < size; i += BUFSIZ) { | |
792 | amt = BUFSIZ; | |
793 | if (i + amt > size) | |
794 | amt = size - i; | |
795 | count += amt; | |
796 | do { | |
797 | j = read(rem, cp, amt); | |
798 | if (j <= 0) { | |
799 | error("rcp: %s\n", | |
800 | j ? strerror(errno) : | |
801 | "dropped connection"); | |
802 | exit(1); | |
803 | } | |
804 | amt -= j; | |
805 | cp += j; | |
806 | } while (amt > 0); | |
807 | if (count == bp->cnt) { | |
808 | if (wrerr == NO && | |
809 | write(ofd, bp->buf, count) != count) | |
810 | wrerr = YES; | |
811 | count = 0; | |
812 | cp = bp->buf; | |
813 | } | |
814 | } | |
815 | if (count != 0 && wrerr == NO && | |
816 | write(ofd, bp->buf, count) != count) | |
817 | wrerr = YES; | |
818 | if (ftruncate(ofd, size)) { | |
819 | error("rcp: can't truncate %s: %s\n", np, | |
820 | strerror(errno)); | |
821 | wrerr = DISPLAYED; | |
822 | } | |
823 | (void)close(ofd); | |
824 | (void)response(); | |
825 | if (setimes && wrerr == NO) { | |
826 | setimes = 0; | |
827 | if (utimes(np, tv) < 0) { | |
828 | error("rcp: can't set times on %s: %s\n", | |
829 | np, strerror(errno)); | |
830 | wrerr = DISPLAYED; | |
831 | } | |
832 | } | |
833 | switch(wrerr) { | |
834 | case YES: | |
835 | error("rcp: %s: %s\n", np, strerror(errno)); | |
836 | break; | |
837 | case NO: | |
838 | (void)write(rem, "", 1); | |
839 | break; | |
840 | case DISPLAYED: | |
841 | break; | |
842 | } | |
843 | } | |
844 | screwup: | |
845 | error("rcp: protocol screwup: %s\n", why); | |
846 | exit(1); | |
847 | } | |
848 | ||
849 | BUF * | |
850 | allocbuf(bp, fd, blksize) | |
851 | BUF *bp; | |
852 | int fd, blksize; | |
853 | { | |
854 | struct stat stb; | |
855 | size_t size; | |
856 | ||
857 | if (fstat(fd, &stb) < 0) { | |
858 | error("rcp: fstat: %s\n", strerror(errno)); | |
859 | return(0); | |
860 | } | |
861 | size = roundup(stb.st_blksize, blksize); | |
862 | if (size == 0) | |
863 | size = blksize; | |
864 | if (bp->cnt < size) { | |
865 | if (bp->buf != 0) | |
866 | free(bp->buf); | |
867 | bp->buf = malloc(size); | |
868 | if (!bp->buf) { | |
869 | error("rcp: malloc: out of memory\n"); | |
870 | return(0); | |
871 | } | |
872 | } | |
873 | bp->cnt = size; | |
874 | return(bp); | |
875 | } | |
876 | ||
877 | /* VARARGS1 */ | |
878 | error(fmt, a1, a2, a3) | |
879 | char *fmt; | |
880 | int a1, a2, a3; | |
881 | { | |
882 | static FILE *fp; | |
883 | ||
884 | ++errs; | |
885 | if (!fp && !(fp = fdopen(rem, "w"))) | |
886 | return; | |
887 | (void)fprintf(fp, "%c", 0x01); | |
888 | (void)fprintf(fp, fmt, a1, a2, a3); | |
889 | (void)fflush(fp); | |
890 | if (!iamremote) | |
891 | (void)fprintf(stderr, fmt, a1, a2, a3); | |
892 | } | |
893 | ||
894 | nospace() | |
895 | { | |
896 | (void)fprintf(stderr, "rcp: out of memory.\n"); | |
897 | exit(1); | |
898 | } | |
899 | ||
900 | ||
901 | usage() | |
902 | { | |
903 | #ifdef KERBEROS | |
904 | #ifdef CRYPT | |
905 | (void)fprintf(stderr, "%s\n\t%s\n", | |
906 | "usage: rcp [-k realm] [-px] f1 f2", | |
907 | "or: rcp [-k realm] [-rpx] f1 ... fn directory"); | |
908 | #else | |
909 | (void)fprintf(stderr, "%s\n\t%s\n", | |
910 | "usage: rcp [-k realm] [-p] f1 f2", | |
911 | "or: rcp [-k realm] [-rp] f1 ... fn directory"); | |
912 | #endif | |
913 | #else | |
914 | (void)fprintf(stderr, | |
915 | "usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn directory\n"); | |
916 | #endif | |
917 | exit(1); | |
918 | } | |
919 | ||
920 | #ifdef KERBEROS | |
921 | old_warning(str) | |
922 | char *str; | |
923 | { | |
924 | (void)fprintf(stderr, "rcp: warning: %s, using standard rcp\n", str); | |
925 | } | |
926 | ||
927 | int | |
928 | kerberos(host, bp, locuser, user) | |
929 | ||
930 | char **host, *bp, *locuser, *user; | |
931 | { | |
932 | struct servent *sp; | |
933 | ||
934 | again: | |
935 | if (use_kerberos) { | |
936 | rem = KSUCCESS; | |
937 | errno = 0; | |
938 | if (dest_realm == NULL) | |
939 | dest_realm = krb_realmofhost(*host); | |
940 | ||
941 | #ifdef CRYPT | |
942 | if (doencrypt) | |
943 | rem = krcmd_mutual( | |
944 | host, port, | |
945 | user, bp, 0, | |
946 | dest_realm, | |
947 | &cred, schedule); | |
948 | else | |
949 | #endif | |
950 | rem = krcmd( | |
951 | host, port, | |
952 | user, bp, 0, dest_realm); | |
953 | ||
954 | if (rem < 0) { | |
955 | use_kerberos = 0; | |
956 | sp = getservbyname("shell", "tcp"); | |
957 | if (sp == NULL) { | |
958 | (void)fprintf(stderr, | |
959 | "rcp: unknown service shell/tcp\n"); | |
960 | exit(1); | |
961 | } | |
962 | if (errno == ECONNREFUSED) | |
963 | old_warning( | |
964 | "remote host doesn't support Kerberos"); | |
965 | ||
966 | if (errno == ENOENT) | |
967 | old_warning( | |
968 | "Can't provide Kerberos auth data"); | |
969 | port = sp->s_port; | |
970 | goto again; | |
971 | } | |
972 | } else { | |
973 | #ifdef CRYPT | |
974 | if (doencrypt) { | |
975 | fprintf(stderr, | |
976 | "The -x option requires Kerberos authentication\n"); | |
977 | exit(1); | |
978 | } | |
979 | #endif | |
980 | rem = rcmd(host, sp->s_port, locuser, user, bp, 0); | |
981 | } | |
982 | return(rem); | |
983 | } | |
984 | #endif /* KERBEROS */ |