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