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