1 /* 2 * Copyright (c) 1983 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 char copyright[] = 9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10 All rights reserved.\n"; 11 #endif not lint 12 13 #ifndef lint 14 static char sccsid[] = "@(#)rcp.c 5.4 (Berkeley) 09/12/85"; 15 #endif not lint 16 17 /* 18 * rcp 19 */ 20 #include <sys/param.h> 21 #include <sys/stat.h> 22 #include <sys/time.h> 23 #include <sys/ioctl.h> 24 25 #include <netinet/in.h> 26 27 #include <stdio.h> 28 #include <signal.h> 29 #include <pwd.h> 30 #include <ctype.h> 31 #include <netdb.h> 32 #include <errno.h> 33 34 int rem; 35 char *colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf(); 36 int errs; 37 int lostconn(); 38 int errno; 39 char *sys_errlist[]; 40 int iamremote, targetshouldbedirectory; 41 int iamrecursive; 42 int pflag; 43 struct passwd *pwd; 44 struct passwd *getpwuid(); 45 int userid; 46 int port; 47 48 struct buffer { 49 int cnt; 50 char *buf; 51 } *allocbuf(); 52 53 /*VARARGS*/ 54 int error(); 55 56 #define ga() (void) write(rem, "", 1) 57 58 main(argc, argv) 59 int argc; 60 char **argv; 61 { 62 char *targ, *host, *src; 63 char *suser, *tuser, *thost; 64 int i; 65 char buf[BUFSIZ], cmd[16]; 66 struct servent *sp; 67 68 sp = getservbyname("shell", "tcp"); 69 if (sp == NULL) { 70 fprintf(stderr, "rcp: shell/tcp: unknown service\n"); 71 exit(1); 72 } 73 port = sp->s_port; 74 pwd = getpwuid(userid = getuid()); 75 if (pwd == 0) { 76 fprintf(stderr, "who are you?\n"); 77 exit(1); 78 } 79 80 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) { 81 (*argv)++; 82 while (**argv) switch (*(*argv)++) { 83 84 case 'r': 85 iamrecursive++; 86 break; 87 88 case 'p': /* preserve mtimes and atimes */ 89 pflag++; 90 break; 91 92 /* The rest of these are not for users. */ 93 case 'd': 94 targetshouldbedirectory = 1; 95 break; 96 97 case 'f': /* "from" */ 98 iamremote = 1; 99 (void) response(); 100 (void) setuid(userid); 101 source(--argc, ++argv); 102 exit(errs); 103 104 case 't': /* "to" */ 105 iamremote = 1; 106 (void) setuid(userid); 107 sink(--argc, ++argv); 108 exit(errs); 109 110 default: 111 fprintf(stderr, 112 "Usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n"); 113 exit(1); 114 } 115 } 116 rem = -1; 117 if (argc > 2) 118 targetshouldbedirectory = 1; 119 (void) sprintf(cmd, "rcp%s%s%s", 120 iamrecursive ? " -r" : "", pflag ? " -p" : "", 121 targetshouldbedirectory ? " -d" : ""); 122 (void) signal(SIGPIPE, lostconn); 123 targ = colon(argv[argc - 1]); 124 if (targ) { /* ... to remote */ 125 *targ++ = 0; 126 if (*targ == 0) 127 targ = "."; 128 thost = index(argv[argc - 1], '@'); 129 if (thost) { 130 *thost++ = 0; 131 tuser = argv[argc - 1]; 132 if (*tuser == '\0') 133 tuser = NULL; 134 else if (!okname(tuser)) 135 exit(1); 136 } else { 137 thost = argv[argc - 1]; 138 tuser = NULL; 139 } 140 for (i = 0; i < argc - 1; i++) { 141 src = colon(argv[i]); 142 if (src) { /* remote to remote */ 143 *src++ = 0; 144 if (*src == 0) 145 src = "."; 146 host = index(argv[i], '@'); 147 if (host) { 148 *host++ = 0; 149 suser = argv[i]; 150 if (*suser == '\0') 151 suser = pwd->pw_name; 152 else if (!okname(suser)) 153 continue; 154 (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s%s%s:%s'", 155 host, suser, cmd, src, tuser, 156 tuser ? "@" : "", 157 thost, targ); 158 } else 159 (void) sprintf(buf, "rsh %s -n %s %s '%s%s%s:%s'", 160 argv[i], cmd, src, tuser, 161 tuser ? "@" : "", 162 thost, targ); 163 (void) susystem(buf); 164 } else { /* local to remote */ 165 if (rem == -1) { 166 (void) sprintf(buf, "%s -t %s", 167 cmd, targ); 168 host = thost; 169 rem = rcmd(&host, port, pwd->pw_name, 170 tuser ? tuser : pwd->pw_name, 171 buf, 0); 172 if (rem < 0) 173 exit(1); 174 if (response() < 0) 175 exit(1); 176 (void) setuid(userid); 177 } 178 source(1, argv+i); 179 } 180 } 181 } else { /* ... to local */ 182 if (targetshouldbedirectory) 183 verifydir(argv[argc - 1]); 184 for (i = 0; i < argc - 1; i++) { 185 src = colon(argv[i]); 186 if (src == 0) { /* local to local */ 187 (void) sprintf(buf, "/bin/cp%s%s %s %s", 188 iamrecursive ? " -r" : "", 189 pflag ? " -p" : "", 190 argv[i], argv[argc - 1]); 191 (void) susystem(buf); 192 } else { /* remote to local */ 193 *src++ = 0; 194 if (*src == 0) 195 src = "."; 196 host = index(argv[i], '@'); 197 if (host) { 198 *host++ = 0; 199 suser = argv[i]; 200 if (*suser == '\0') 201 suser = pwd->pw_name; 202 else if (!okname(suser)) 203 continue; 204 } else { 205 host = argv[i]; 206 suser = pwd->pw_name; 207 } 208 (void) sprintf(buf, "%s -f %s", cmd, src); 209 rem = rcmd(&host, port, pwd->pw_name, suser, 210 buf, 0); 211 if (rem < 0) 212 continue; 213 (void) setreuid(0, userid); 214 sink(1, argv+argc-1); 215 (void) setreuid(userid, 0); 216 (void) close(rem); 217 rem = -1; 218 } 219 } 220 } 221 exit(errs); 222 } 223 224 verifydir(cp) 225 char *cp; 226 { 227 struct stat stb; 228 229 if (stat(cp, &stb) >= 0) { 230 if ((stb.st_mode & S_IFMT) == S_IFDIR) 231 return; 232 errno = ENOTDIR; 233 } 234 error("rcp: %s: %s.\n", cp, sys_errlist[errno]); 235 exit(1); 236 } 237 238 char * 239 colon(cp) 240 char *cp; 241 { 242 243 while (*cp) { 244 if (*cp == ':') 245 return (cp); 246 if (*cp == '/') 247 return (0); 248 cp++; 249 } 250 return (0); 251 } 252 253 okname(cp0) 254 char *cp0; 255 { 256 register char *cp = cp0; 257 register int c; 258 259 do { 260 c = *cp; 261 if (c & 0200) 262 goto bad; 263 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 264 goto bad; 265 cp++; 266 } while (*cp); 267 return (1); 268 bad: 269 fprintf(stderr, "rcp: invalid user name %s\n", cp0); 270 return (0); 271 } 272 273 susystem(s) 274 char *s; 275 { 276 int status, pid, w; 277 register int (*istat)(), (*qstat)(); 278 279 if ((pid = vfork()) == 0) { 280 (void) setuid(userid); 281 execl("/bin/sh", "sh", "-c", s, (char *)0); 282 _exit(127); 283 } 284 istat = signal(SIGINT, SIG_IGN); 285 qstat = signal(SIGQUIT, SIG_IGN); 286 while ((w = wait(&status)) != pid && w != -1) 287 ; 288 if (w == -1) 289 status = -1; 290 (void) signal(SIGINT, istat); 291 (void) signal(SIGQUIT, qstat); 292 return (status); 293 } 294 295 source(argc, argv) 296 int argc; 297 char **argv; 298 { 299 char *last, *name; 300 struct stat stb; 301 static struct buffer buffer; 302 struct buffer *bp; 303 int x, sizerr, f, amt; 304 off_t i; 305 char buf[BUFSIZ]; 306 307 for (x = 0; x < argc; x++) { 308 name = argv[x]; 309 if ((f = open(name, 0)) < 0) { 310 error("rcp: %s: %s\n", name, sys_errlist[errno]); 311 continue; 312 } 313 if (fstat(f, &stb) < 0) 314 goto notreg; 315 switch (stb.st_mode&S_IFMT) { 316 317 case S_IFREG: 318 break; 319 320 case S_IFDIR: 321 if (iamrecursive) { 322 (void) close(f); 323 rsource(name, &stb); 324 continue; 325 } 326 /* fall into ... */ 327 default: 328 notreg: 329 (void) close(f); 330 error("rcp: %s: not a plain file\n", name); 331 continue; 332 } 333 last = rindex(name, '/'); 334 if (last == 0) 335 last = name; 336 else 337 last++; 338 if (pflag) { 339 /* 340 * Make it compatible with possible future 341 * versions expecting microseconds. 342 */ 343 (void) sprintf(buf, "T%ld 0 %ld 0\n", 344 stb.st_mtime, stb.st_atime); 345 (void) write(rem, buf, strlen(buf)); 346 if (response() < 0) { 347 (void) close(f); 348 continue; 349 } 350 } 351 (void) sprintf(buf, "C%04o %ld %s\n", 352 stb.st_mode&07777, stb.st_size, last); 353 (void) write(rem, buf, strlen(buf)); 354 if (response() < 0) { 355 (void) close(f); 356 continue; 357 } 358 if ((bp = allocbuf(&buffer, f, BUFSIZ)) < 0) { 359 (void) close(f); 360 continue; 361 } 362 sizerr = 0; 363 for (i = 0; i < stb.st_size; i += bp->cnt) { 364 amt = bp->cnt; 365 if (i + amt > stb.st_size) 366 amt = stb.st_size - i; 367 if (sizerr == 0 && read(f, bp->buf, amt) != amt) 368 sizerr = 1; 369 (void) write(rem, bp->buf, amt); 370 } 371 (void) close(f); 372 if (sizerr == 0) 373 ga(); 374 else 375 error("rcp: %s: file changed size\n", name); 376 (void) response(); 377 } 378 } 379 380 #include <sys/dir.h> 381 382 rsource(name, statp) 383 char *name; 384 struct stat *statp; 385 { 386 DIR *d = opendir(name); 387 char *last; 388 struct direct *dp; 389 char buf[BUFSIZ]; 390 char *bufv[1]; 391 392 if (d == 0) { 393 error("rcp: %s: %s\n", name, sys_errlist[errno]); 394 return; 395 } 396 last = rindex(name, '/'); 397 if (last == 0) 398 last = name; 399 else 400 last++; 401 if (pflag) { 402 (void) sprintf(buf, "T%ld 0 %ld 0\n", 403 statp->st_mtime, statp->st_atime); 404 (void) write(rem, buf, strlen(buf)); 405 if (response() < 0) { 406 closedir(d); 407 return; 408 } 409 } 410 (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last); 411 (void) write(rem, buf, strlen(buf)); 412 if (response() < 0) { 413 closedir(d); 414 return; 415 } 416 while (dp = readdir(d)) { 417 if (dp->d_ino == 0) 418 continue; 419 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 420 continue; 421 if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 422 error("%s/%s: Name too long.\n", name, dp->d_name); 423 continue; 424 } 425 (void) sprintf(buf, "%s/%s", name, dp->d_name); 426 bufv[0] = buf; 427 source(1, bufv); 428 } 429 closedir(d); 430 (void) write(rem, "E\n", 2); 431 (void) response(); 432 } 433 434 response() 435 { 436 char resp, c, rbuf[BUFSIZ], *cp = rbuf; 437 438 if (read(rem, &resp, 1) != 1) 439 lostconn(); 440 switch (resp) { 441 442 case 0: /* ok */ 443 return (0); 444 445 default: 446 *cp++ = resp; 447 /* fall into... */ 448 case 1: /* error, followed by err msg */ 449 case 2: /* fatal error, "" */ 450 do { 451 if (read(rem, &c, 1) != 1) 452 lostconn(); 453 *cp++ = c; 454 } while (cp < &rbuf[BUFSIZ] && c != '\n'); 455 if (iamremote == 0) 456 (void) write(2, rbuf, cp - rbuf); 457 errs++; 458 if (resp == 1) 459 return (-1); 460 exit(1); 461 } 462 /*NOTREACHED*/ 463 } 464 465 lostconn() 466 { 467 468 if (iamremote == 0) 469 fprintf(stderr, "rcp: lost connection\n"); 470 exit(1); 471 } 472 473 sink(argc, argv) 474 int argc; 475 char **argv; 476 { 477 off_t i, j; 478 char *targ, *whopp, *cp; 479 int of, mode, wrerr, exists, first, count, amt, size; 480 struct buffer *bp; 481 static struct buffer buffer; 482 struct stat stb; 483 int targisdir = 0; 484 int mask = umask(0); 485 char *myargv[1]; 486 char cmdbuf[BUFSIZ], nambuf[BUFSIZ]; 487 int setimes = 0; 488 struct timeval tv[2]; 489 #define atime tv[0] 490 #define mtime tv[1] 491 #define SCREWUP(str) { whopp = str; goto screwup; } 492 493 if (!pflag) 494 (void) umask(mask); 495 if (argc != 1) { 496 error("rcp: ambiguous target\n"); 497 exit(1); 498 } 499 targ = *argv; 500 if (targetshouldbedirectory) 501 verifydir(targ); 502 ga(); 503 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) 504 targisdir = 1; 505 for (first = 1; ; first = 0) { 506 cp = cmdbuf; 507 if (read(rem, cp, 1) <= 0) 508 return; 509 if (*cp++ == '\n') 510 SCREWUP("unexpected '\\n'"); 511 do { 512 if (read(rem, cp, 1) != 1) 513 SCREWUP("lost connection"); 514 } while (*cp++ != '\n'); 515 *cp = 0; 516 if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { 517 if (iamremote == 0) 518 (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); 519 if (cmdbuf[0] == '\02') 520 exit(1); 521 errs++; 522 continue; 523 } 524 *--cp = 0; 525 cp = cmdbuf; 526 if (*cp == 'E') { 527 ga(); 528 return; 529 } 530 531 #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); 532 if (*cp == 'T') { 533 setimes++; 534 cp++; 535 getnum(mtime.tv_sec); 536 if (*cp++ != ' ') 537 SCREWUP("mtime.sec not delimited"); 538 getnum(mtime.tv_usec); 539 if (*cp++ != ' ') 540 SCREWUP("mtime.usec not delimited"); 541 getnum(atime.tv_sec); 542 if (*cp++ != ' ') 543 SCREWUP("atime.sec not delimited"); 544 getnum(atime.tv_usec); 545 if (*cp++ != '\0') 546 SCREWUP("atime.usec not delimited"); 547 ga(); 548 continue; 549 } 550 if (*cp != 'C' && *cp != 'D') { 551 /* 552 * Check for the case "rcp remote:foo\* local:bar". 553 * In this case, the line "No match." can be returned 554 * by the shell before the rcp command on the remote is 555 * executed so the ^Aerror_message convention isn't 556 * followed. 557 */ 558 if (first) { 559 error("%s\n", cp); 560 exit(1); 561 } 562 SCREWUP("expected control record"); 563 } 564 cp++; 565 mode = 0; 566 for (; cp < cmdbuf+5; cp++) { 567 if (*cp < '0' || *cp > '7') 568 SCREWUP("bad mode"); 569 mode = (mode << 3) | (*cp - '0'); 570 } 571 if (*cp++ != ' ') 572 SCREWUP("mode not delimited"); 573 size = 0; 574 while (isdigit(*cp)) 575 size = size * 10 + (*cp++ - '0'); 576 if (*cp++ != ' ') 577 SCREWUP("size not delimited"); 578 if (targisdir) 579 (void) sprintf(nambuf, "%s%s%s", targ, 580 *targ ? "/" : "", cp); 581 else 582 (void) strcpy(nambuf, targ); 583 exists = stat(nambuf, &stb) == 0; 584 if (cmdbuf[0] == 'D') { 585 if (exists) { 586 if ((stb.st_mode&S_IFMT) != S_IFDIR) { 587 errno = ENOTDIR; 588 goto bad; 589 } 590 if (pflag) 591 (void) chmod(nambuf, mode); 592 } else if (mkdir(nambuf, mode) < 0) 593 goto bad; 594 myargv[0] = nambuf; 595 sink(1, myargv); 596 if (setimes) { 597 setimes = 0; 598 if (utimes(nambuf, tv) < 0) 599 error("rcp: can't set times on %s: %s\n", 600 nambuf, sys_errlist[errno]); 601 } 602 continue; 603 } 604 if ((of = creat(nambuf, mode)) < 0) { 605 bad: 606 error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); 607 continue; 608 } 609 if (exists && pflag) 610 (void) fchmod(of, mode); 611 ga(); 612 if ((bp = allocbuf(&buffer, of, BUFSIZ)) < 0) { 613 (void) close(of); 614 continue; 615 } 616 cp = bp->buf; 617 count = 0; 618 wrerr = 0; 619 for (i = 0; i < size; i += BUFSIZ) { 620 amt = BUFSIZ; 621 if (i + amt > size) 622 amt = size - i; 623 count += amt; 624 do { 625 j = read(rem, cp, amt); 626 if (j <= 0) { 627 if (j == 0) 628 error("rcp: dropped connection"); 629 else 630 error("rcp: %s\n", 631 sys_errlist[errno]); 632 exit(1); 633 } 634 amt -= j; 635 cp += j; 636 } while (amt > 0); 637 if (count == bp->cnt) { 638 if (wrerr == 0 && 639 write(of, bp->buf, count) != count) 640 wrerr++; 641 count = 0; 642 cp = bp->buf; 643 } 644 } 645 if (count != 0 && wrerr == 0 && 646 write(of, bp->buf, count) != count) 647 wrerr++; 648 (void) close(of); 649 (void) response(); 650 if (setimes) { 651 setimes = 0; 652 if (utimes(nambuf, tv) < 0) 653 error("rcp: can't set times on %s: %s\n", 654 nambuf, sys_errlist[errno]); 655 } 656 if (wrerr) 657 error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); 658 else 659 ga(); 660 } 661 screwup: 662 error("rcp: protocol screwup: %s\n", whopp); 663 exit(1); 664 } 665 666 struct buffer * 667 allocbuf(bp, fd, blksize) 668 struct buffer *bp; 669 int fd, blksize; 670 { 671 struct stat stb; 672 int size; 673 674 if (fstat(fd, &stb) < 0) { 675 error("rcp: fstat: %s\n", sys_errlist[errno]); 676 return ((struct buffer *)-1); 677 } 678 size = roundup(stb.st_blksize, blksize); 679 if (size == 0) 680 size = blksize; 681 if (bp->cnt < size) { 682 if (bp->buf != 0) 683 free(bp->buf); 684 bp->buf = (char *)malloc((unsigned) size); 685 if (bp->buf == 0) { 686 error("rcp: malloc: out of memory\n"); 687 return ((struct buffer *)-1); 688 } 689 } 690 bp->cnt = size; 691 return (bp); 692 } 693 694 /*VARARGS1*/ 695 error(fmt, a1, a2, a3, a4, a5) 696 char *fmt; 697 int a1, a2, a3, a4, a5; 698 { 699 char buf[BUFSIZ], *cp = buf; 700 701 errs++; 702 *cp++ = 1; 703 (void) sprintf(cp, fmt, a1, a2, a3, a4, a5); 704 (void) write(rem, buf, strlen(buf)); 705 if (iamremote == 0) 706 (void) write(2, buf+1, strlen(buf+1)); 707 } 708