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