rcp.c revision 48560
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1990, 1992, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * Redistribution and use in source and binary forms, with or without 61553Srgrimes * modification, are permitted provided that the following conditions 71553Srgrimes * are met: 81553Srgrimes * 1. Redistributions of source code must retain the above copyright 91553Srgrimes * notice, this list of conditions and the following disclaimer. 101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111553Srgrimes * notice, this list of conditions and the following disclaimer in the 121553Srgrimes * documentation and/or other materials provided with the distribution. 131553Srgrimes * 3. All advertising materials mentioning features or use of this software 141553Srgrimes * must display the following acknowledgement: 151553Srgrimes * This product includes software developed by the University of 161553Srgrimes * California, Berkeley and its contributors. 171553Srgrimes * 4. Neither the name of the University nor the names of its contributors 181553Srgrimes * may be used to endorse or promote products derived from this software 191553Srgrimes * without specific prior written permission. 201553Srgrimes * 211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311553Srgrimes * SUCH DAMAGE. 321553Srgrimes */ 331553Srgrimes 341553Srgrimes#ifndef lint 3530027Scharnierstatic char const copyright[] = 361553Srgrimes"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\ 3730027Scharnier The Regents of the University of California. All rights reserved.\n"; 3830027Scharnier#endif /* not lint */ 3950479Speter 401553Srgrimes#ifndef lint 411553Srgrimes#if 0 421553Srgrimesstatic char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94"; 431553Srgrimes#endif 4430027Scharnierstatic const char rcsid[] = 4530027Scharnier "$Id: rcp.c,v 1.22 1999/04/25 10:36:00 dt Exp $"; 4630027Scharnier#endif /* not lint */ 471553Srgrimes 481553Srgrimes#include <sys/param.h> 491553Srgrimes#include <sys/stat.h> 5044303Swollman#include <sys/time.h> 5130027Scharnier#include <sys/socket.h> 5244303Swollman#include <netinet/in.h> 5344303Swollman#include <netinet/in_systm.h> 5444303Swollman#include <netinet/ip.h> 5544303Swollman 5644303Swollman#include <ctype.h> 5744303Swollman#include <dirent.h> 5844303Swollman#include <err.h> 591553Srgrimes#include <errno.h> 6030027Scharnier#include <fcntl.h> 6130027Scharnier#include <netdb.h> 621553Srgrimes#include <pwd.h> 6342561Sjkoshy#include <signal.h> 641553Srgrimes#include <stdio.h> 651553Srgrimes#include <stdlib.h> 661553Srgrimes#include <string.h> 671553Srgrimes#include <string.h> 681553Srgrimes#include <unistd.h> 691553Srgrimes#include <libutil.h> 702860Srgrimes 712860Srgrimes#include "pathnames.h" 722860Srgrimes#include "extern.h" 7336670Speter 741553Srgrimes#ifdef KERBEROS 7530027Scharnier#include <des.h> 761553Srgrimes#include <krb.h> 771553Srgrimes 781553Srgrimeschar dst_realm_buf[REALM_SZ]; 791553Srgrimeschar *dest_realm = NULL; 8066584Sphkint use_kerberos = 1; 811553SrgrimesCREDENTIALS cred; 8299800SalfredKey_schedule schedule; 8399800Salfredextern char *krb_realmofhost(); 8499800Salfred#ifdef CRYPT 8599800Salfredint doencrypt = 0; 861553Srgrimes#define OPTIONS "dfKk:prtx" 871553Srgrimes#else 881553Srgrimes#define OPTIONS "dfKk:prt" 891553Srgrimes#endif 901553Srgrimes#else 911553Srgrimes#define OPTIONS "dfprt" 921553Srgrimes#endif 931553Srgrimes 942860Srgrimesstruct passwd *pwd; 951553Srgrimesu_short port; 961553Srgrimesuid_t userid; 971553Srgrimesint errs, rem; 981553Srgrimesint pflag, iamremote, iamrecursive, targetshouldbedirectory; 991553Srgrimes 1001553Srgrimesstatic int argc_copy; 1011553Srgrimesstatic char **argv_copy; 1021553Srgrimes 1031553Srgrimes#define CMDNEEDS 64 1041553Srgrimeschar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 10530027Scharnier 1062860Srgrimes#ifdef KERBEROS 1072860Srgrimesint kerberos __P((char **, char *, char *, char *)); 1082860Srgrimesvoid oldw __P((const char *, ...)); 10960418Swollman#endif 11060418Swollmanint response __P((void)); 11160418Swollmanvoid rsource __P((char *, struct stat *)); 11260418Swollmanvoid sink __P((int, char *[])); 1131553Srgrimesvoid source __P((int, char *[])); 1141553Srgrimesvoid tolocal __P((int, char *[])); 1152860Srgrimesvoid toremote __P((char *, int, char *[])); 1162860Srgrimesvoid usage __P((void)); 1172860Srgrimes 1182860Srgrimesint 11954375Sjoemain(argc, argv) 1202860Srgrimes int argc; 1211553Srgrimes char *argv[]; 1221553Srgrimes{ 1232860Srgrimes struct servent *sp; 1242860Srgrimes int ch, fflag, i, tflag; 1252860Srgrimes char *targ, *shell; 1262860Srgrimes#ifdef KERBEROS 1272860Srgrimes char *k; 1281553Srgrimes#endif 1291553Srgrimes 1301553Srgrimes /* 1311553Srgrimes * Prepare for execing ourselves. 13230027Scharnier */ 1331553Srgrimes argc_copy = argc + 1; 1341553Srgrimes argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy)); 1351553Srgrimes if (argv_copy == NULL) 1362860Srgrimes err(1, "malloc"); 1371553Srgrimes argv_copy[0] = argv[0]; 1388857Srgrimes argv_copy[1] = "-K"; 1391553Srgrimes for (i = 1; i < argc; ++i) { 1402860Srgrimes argv_copy[i + 1] = strdup(argv[i]); 1411553Srgrimes if (argv_copy[i + 1] == NULL) 1421553Srgrimes errx(1, "strdup: out of memory"); 14330027Scharnier } 1441553Srgrimes argv_copy[argc + 1] = NULL; 1451553Srgrimes 1461553Srgrimes fflag = tflag = 0; 1472860Srgrimes while ((ch = getopt(argc, argv, OPTIONS)) != -1) 1482860Srgrimes switch(ch) { /* User-visible flags. */ 1491553Srgrimes case 'K': 1501553Srgrimes#ifdef KERBEROS 1511553Srgrimes use_kerberos = 0; 1521553Srgrimes#endif 1531553Srgrimes break; 1542860Srgrimes#ifdef KERBEROS 15561749Sjoe case 'k': 15642561Sjkoshy dest_realm = dst_realm_buf; 1571553Srgrimes (void)strncpy(dst_realm_buf, optarg, REALM_SZ - 1); 15842561Sjkoshy dst_realm_buf[REALM_SZ - 1] = '\0'; 15942561Sjkoshy break; 16042561Sjkoshy#ifdef CRYPT 16142787Sjkoshy case 'x': 16242561Sjkoshy doencrypt = 1; 1632860Srgrimes /* des_set_key(cred.session, schedule); */ 16442561Sjkoshy break; 1651553Srgrimes#endif 16642561Sjkoshy#endif 16742561Sjkoshy case 'p': 16842561Sjkoshy pflag = 1; 1691553Srgrimes break; 1702860Srgrimes case 'r': 1712860Srgrimes iamrecursive = 1; 1721553Srgrimes break; 1732860Srgrimes /* Server options. */ 1741553Srgrimes case 'd': 1752860Srgrimes targetshouldbedirectory = 1; 1762860Srgrimes break; 1772860Srgrimes case 'f': /* "from" */ 1782860Srgrimes iamremote = 1; 1792860Srgrimes fflag = 1; 1802860Srgrimes break; 1812860Srgrimes case 't': /* "to" */ 18230027Scharnier iamremote = 1; 18330027Scharnier tflag = 1; 18430027Scharnier break; 1852860Srgrimes case '?': 1862860Srgrimes default: 1872860Srgrimes usage(); 1882860Srgrimes } 1892860Srgrimes argc -= optind; 1902860Srgrimes argv += optind; 1912860Srgrimes 1922860Srgrimes#ifdef KERBEROS 1932860Srgrimes k = auth_getval("auth_list"); 1942860Srgrimes if (k && !strstr(k, "kerberos")) 19530027Scharnier use_kerberos = 0; 19630027Scharnier if (use_kerberos) { 19730027Scharnier#ifdef CRYPT 1982860Srgrimes shell = doencrypt ? "ekshell" : "kshell"; 1992860Srgrimes#else 2002860Srgrimes shell = "kshell"; 2012860Srgrimes#endif 2022860Srgrimes if ((sp = getservbyname(shell, "tcp")) == NULL) { 2031553Srgrimes use_kerberos = 0; 2042860Srgrimes oldw("can't get entry for %s/tcp service", shell); 2051553Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2062860Srgrimes } 2071553Srgrimes } else 2082860Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2091553Srgrimes#else 2102860Srgrimes sp = getservbyname(shell = "shell", "tcp"); 21118404Snate#endif 21218404Snate if (sp == NULL) 2131553Srgrimes errx(1, "%s/tcp: unknown service", shell); 2141553Srgrimes port = sp->s_port; 2151553Srgrimes 21630027Scharnier if ((pwd = getpwuid(userid = getuid())) == NULL) 2171553Srgrimes errx(1, "unknown user %d", (int)userid); 2182860Srgrimes 2191553Srgrimes rem = STDIN_FILENO; /* XXX */ 22044303Swollman 2216286Swollman if (fflag) { /* Follow "protocol", send data. */ 22244303Swollman (void)response(); 2236286Swollman (void)setuid(userid); 22444303Swollman source(argc, argv); 22544303Swollman exit(errs); 22630027Scharnier } 2276286Swollman 22844303Swollman if (tflag) { /* Receive data. */ 2296286Swollman (void)setuid(userid); 2306286Swollman sink(argc, argv); 23144303Swollman exit(errs); 23244303Swollman } 23344303Swollman 23444303Swollman if (argc < 2) 23544303Swollman usage(); 23644303Swollman if (argc > 2) 23744303Swollman targetshouldbedirectory = 1; 23844303Swollman 23944303Swollman rem = -1; 24044303Swollman /* Command to be executed on remote system using "rsh". */ 24144303Swollman#ifdef KERBEROS 24244303Swollman (void)snprintf(cmd, sizeof(cmd), 24344303Swollman "rcp%s%s%s%s", iamrecursive ? " -r" : "", 24444303Swollman#ifdef CRYPT 24544303Swollman (doencrypt && use_kerberos ? " -x" : ""), 24644303Swollman#else 24744303Swollman "", 24844303Swollman#endif 24944303Swollman pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 25044303Swollman#else 25144303Swollman (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", 25244303Swollman iamrecursive ? " -r" : "", pflag ? " -p" : "", 25344303Swollman targetshouldbedirectory ? " -d" : ""); 25444303Swollman#endif 25544303Swollman 2561553Srgrimes (void)signal(SIGPIPE, lostconn); 2571553Srgrimes 2582860Srgrimes if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 25961749Sjoe toremote(targ, argc, argv); 26061749Sjoe else { 26161749Sjoe tolocal(argc, argv); /* Dest is local host. */ 26261749Sjoe if (targetshouldbedirectory) 26361749Sjoe verifydir(argv[argc - 1]); 2641553Srgrimes } 2651553Srgrimes exit(errs); 2661553Srgrimes} 2671553Srgrimes 2681553Srgrimesvoid 2691553Srgrimestoremote(targ, argc, argv) 27054375Sjoe char *targ, *argv[]; 27154375Sjoe int argc; 2721553Srgrimes{ 2731553Srgrimes int i, len, tos; 27454375Sjoe char *bp, *host, *src, *suser, *thost, *tuser; 2751553Srgrimes 2761553Srgrimes *targ++ = 0; 2771553Srgrimes if (*targ == 0) 2781553Srgrimes targ = "."; 2791553Srgrimes 28054375Sjoe if ((thost = strchr(argv[argc - 1], '@'))) { 2811553Srgrimes /* user@host */ 2821553Srgrimes *thost++ = 0; 2831553Srgrimes tuser = argv[argc - 1]; 2841553Srgrimes if (*tuser == '\0') 2851553Srgrimes tuser = NULL; 28654375Sjoe else if (!okname(tuser)) 2871553Srgrimes exit(1); 2881553Srgrimes } else { 2892860Srgrimes thost = argv[argc - 1]; 2902860Srgrimes tuser = NULL; 2912860Srgrimes } 29266584Sphk 29354375Sjoe for (i = 0; i < argc - 1; i++) { 29454375Sjoe src = colon(argv[i]); 29561749Sjoe if (src) { /* remote to remote */ 2962877Srgrimes *src++ = 0; 2971553Srgrimes if (*src == 0) 2981553Srgrimes src = "."; 2991553Srgrimes host = strchr(argv[i], '@'); 30030027Scharnier len = strlen(_PATH_RSH) + strlen(argv[i]) + 3011553Srgrimes strlen(src) + (tuser ? strlen(tuser) : 0) + 3021553Srgrimes strlen(thost) + strlen(targ) + CMDNEEDS + 20; 3031553Srgrimes if (!(bp = malloc(len))) 3041553Srgrimes err(1, NULL); 3051553Srgrimes if (host) { 3061553Srgrimes *host++ = 0; 30754375Sjoe suser = argv[i]; 3081553Srgrimes if (*suser == '\0') 30954375Sjoe suser = pwd->pw_name; 3101553Srgrimes else if (!okname(suser)) 3112860Srgrimes continue; 3122860Srgrimes (void)snprintf(bp, len, 3132860Srgrimes "%s %s -l %s -n %s %s '%s%s%s:%s'", 3142860Srgrimes _PATH_RSH, host, suser, cmd, src, 3152860Srgrimes tuser ? tuser : "", tuser ? "@" : "", 3162860Srgrimes thost, targ); 3172860Srgrimes } else 3182860Srgrimes (void)snprintf(bp, len, 3192860Srgrimes "exec %s %s -n %s %s '%s%s%s:%s'", 3202860Srgrimes _PATH_RSH, argv[i], cmd, src, 3212860Srgrimes tuser ? tuser : "", tuser ? "@" : "", 3222860Srgrimes thost, targ); 3232860Srgrimes (void)susystem(bp, userid); 3242860Srgrimes (void)free(bp); 3252860Srgrimes } else { /* local to remote */ 3262860Srgrimes if (rem == -1) { 32754375Sjoe len = strlen(targ) + CMDNEEDS + 20; 32854375Sjoe if (!(bp = malloc(len))) 32954375Sjoe err(1, NULL); 33054375Sjoe (void)snprintf(bp, len, "%s -t %s", cmd, targ); 33154375Sjoe host = thost; 33254375Sjoe#ifdef KERBEROS 33354375Sjoe if (use_kerberos) 33454375Sjoe rem = kerberos(&host, bp, 33554375Sjoe pwd->pw_name, 33654375Sjoe tuser ? tuser : pwd->pw_name); 33754375Sjoe else 33854375Sjoe#endif 33966584Sphk rem = rcmd(&host, port, pwd->pw_name, 34054375Sjoe tuser ? tuser : pwd->pw_name, 3411553Srgrimes bp, 0); 3421553Srgrimes if (rem < 0) 3432860Srgrimes exit(1); 3442860Srgrimes tos = IPTOS_THROUGHPUT; 3452877Srgrimes if (setsockopt(rem, IPPROTO_IP, IP_TOS, 3462877Srgrimes &tos, sizeof(int)) < 0) 3472860Srgrimes warn("TOS (ignored)"); 3482860Srgrimes if (response() < 0) 3492860Srgrimes exit(1); 35066584Sphk (void)free(bp); 35166584Sphk (void)setuid(userid); 35266584Sphk } 3532877Srgrimes source(1, argv+i); 3542860Srgrimes } 3552860Srgrimes } 3561553Srgrimes} 3572860Srgrimes 35851705Sbillfvoid 3592860Srgrimestolocal(argc, argv) 3602860Srgrimes int argc; 3612860Srgrimes char *argv[]; 36230027Scharnier{ 36330027Scharnier int i, len, tos; 36430027Scharnier char *bp, *host, *src, *suser; 36551705Sbillf 3662860Srgrimes for (i = 0; i < argc - 1; i++) { 36738020Sbde if (!(src = colon(argv[i]))) { /* Local to local. */ 36851705Sbillf len = strlen(_PATH_CP) + strlen(argv[i]) + 3692860Srgrimes strlen(argv[argc - 1]) + 20; 3702860Srgrimes if (!(bp = malloc(len))) 3712860Srgrimes err(1, NULL); 37230027Scharnier (void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 37330027Scharnier iamrecursive ? " -PR" : "", pflag ? " -p" : "", 37430027Scharnier argv[i], argv[argc - 1]); 37551705Sbillf if (susystem(bp, userid)) 3762860Srgrimes ++errs; 37738020Sbde (void)free(bp); 3782860Srgrimes continue; 3792860Srgrimes } 3802860Srgrimes *src++ = 0; 3812860Srgrimes if (*src == 0) 38266584Sphk src = "."; 38361749Sjoe if ((host = strchr(argv[i], '@')) == NULL) { 38461749Sjoe host = argv[i]; 38561749Sjoe suser = pwd->pw_name; 38661749Sjoe } else { 3872860Srgrimes *host++ = 0; 3882860Srgrimes suser = argv[i]; 3892860Srgrimes if (*suser == '\0') 3902860Srgrimes suser = pwd->pw_name; 39154375Sjoe else if (!okname(suser)) 3922860Srgrimes continue; 3931553Srgrimes } 3941553Srgrimes len = strlen(src) + CMDNEEDS + 20; 3951553Srgrimes if ((bp = malloc(len)) == NULL) 3961553Srgrimes err(1, NULL); 3971553Srgrimes (void)snprintf(bp, len, "%s -f %s", cmd, src); 3981553Srgrimes rem = 3991553Srgrimes#ifdef KERBEROS 4001553Srgrimes use_kerberos ? 4011553Srgrimes kerberos(&host, bp, pwd->pw_name, suser) : 4021553Srgrimes#endif 4031553Srgrimes rcmd(&host, port, pwd->pw_name, suser, bp, 0); 4041553Srgrimes (void)free(bp); 4051553Srgrimes if (rem < 0) { 4061553Srgrimes ++errs; 4071553Srgrimes continue; 4081553Srgrimes } 4091553Srgrimes (void)seteuid(userid); 4101553Srgrimes tos = IPTOS_THROUGHPUT; 4112860Srgrimes if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 4121553Srgrimes warn("TOS (ignored)"); 4131553Srgrimes sink(1, argv + argc - 1); 4141553Srgrimes (void)seteuid(0); 4151553Srgrimes (void)close(rem); 4161553Srgrimes rem = -1; 4171553Srgrimes } 4181553Srgrimes} 4191553Srgrimes 4202860Srgrimesvoid 4212860Srgrimessource(argc, argv) 4221553Srgrimes int argc; 4231553Srgrimes char *argv[]; 4241553Srgrimes{ 425 struct stat stb; 426 static BUF buffer; 427 BUF *bp; 428 off_t i; 429 int amt, fd, haderr, indx, result; 430 char *last, *name, buf[BUFSIZ]; 431 432 for (indx = 0; indx < argc; ++indx) { 433 name = argv[indx]; 434 if ((fd = open(name, O_RDONLY, 0)) < 0) 435 goto syserr; 436 if (fstat(fd, &stb)) { 437syserr: run_err("%s: %s", name, strerror(errno)); 438 goto next; 439 } 440 switch (stb.st_mode & S_IFMT) { 441 case S_IFREG: 442 break; 443 case S_IFDIR: 444 if (iamrecursive) { 445 rsource(name, &stb); 446 goto next; 447 } 448 /* FALLTHROUGH */ 449 default: 450 run_err("%s: not a regular file", name); 451 goto next; 452 } 453 if ((last = strrchr(name, '/')) == NULL) 454 last = name; 455 else 456 ++last; 457 if (pflag) { 458 /* 459 * Make it compatible with possible future 460 * versions expecting microseconds. 461 */ 462 (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 463 (long)stb.st_mtimespec.tv_sec, 464 (long)stb.st_atimespec.tv_sec); 465 (void)write(rem, buf, strlen(buf)); 466 if (response() < 0) 467 goto next; 468 } 469#define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) 470 (void)snprintf(buf, sizeof(buf), "C%04o %qd %s\n", 471 stb.st_mode & MODEMASK, stb.st_size, last); 472 (void)write(rem, buf, strlen(buf)); 473 if (response() < 0) 474 goto next; 475 if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 476next: (void)close(fd); 477 continue; 478 } 479 480 /* Keep writing after an error so that we stay sync'd up. */ 481 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 482 amt = bp->cnt; 483 if (i + amt > stb.st_size) 484 amt = stb.st_size - i; 485 if (!haderr) { 486 result = read(fd, bp->buf, amt); 487 if (result != amt) 488 haderr = result >= 0 ? EIO : errno; 489 } 490 if (haderr) 491 (void)write(rem, bp->buf, amt); 492 else { 493 result = write(rem, bp->buf, amt); 494 if (result != amt) 495 haderr = result >= 0 ? EIO : errno; 496 } 497 } 498 if (close(fd) && !haderr) 499 haderr = errno; 500 if (!haderr) 501 (void)write(rem, "", 1); 502 else 503 run_err("%s: %s", name, strerror(haderr)); 504 (void)response(); 505 } 506} 507 508void 509rsource(name, statp) 510 char *name; 511 struct stat *statp; 512{ 513 DIR *dirp; 514 struct dirent *dp; 515 char *last, *vect[1], path[MAXPATHLEN]; 516 517 if (!(dirp = opendir(name))) { 518 run_err("%s: %s", name, strerror(errno)); 519 return; 520 } 521 last = strrchr(name, '/'); 522 if (last == 0) 523 last = name; 524 else 525 last++; 526 if (pflag) { 527 (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 528 (long)statp->st_mtimespec.tv_sec, 529 (long)statp->st_atimespec.tv_sec); 530 (void)write(rem, path, strlen(path)); 531 if (response() < 0) { 532 closedir(dirp); 533 return; 534 } 535 } 536 (void)snprintf(path, sizeof(path), 537 "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 538 (void)write(rem, path, strlen(path)); 539 if (response() < 0) { 540 closedir(dirp); 541 return; 542 } 543 while ((dp = readdir(dirp))) { 544 if (dp->d_ino == 0) 545 continue; 546 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 547 continue; 548 if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 549 run_err("%s/%s: name too long", name, dp->d_name); 550 continue; 551 } 552 (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 553 vect[0] = path; 554 source(1, vect); 555 } 556 (void)closedir(dirp); 557 (void)write(rem, "E\n", 2); 558 (void)response(); 559} 560 561void 562sink(argc, argv) 563 int argc; 564 char *argv[]; 565{ 566 static BUF buffer; 567 struct stat stb; 568 struct timeval tv[2]; 569 enum { YES, NO, DISPLAYED } wrerr; 570 BUF *bp; 571 off_t i, j, size; 572 int amt, count, exists, first, mask, mode, ofd, omode; 573 int setimes, targisdir, wrerrno = 0; 574 char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 575 576#define atime tv[0] 577#define mtime tv[1] 578#define SCREWUP(str) { why = str; goto screwup; } 579 580 setimes = targisdir = 0; 581 mask = umask(0); 582 if (!pflag) 583 (void)umask(mask); 584 if (argc != 1) { 585 run_err("ambiguous target"); 586 exit(1); 587 } 588 targ = *argv; 589 if (targetshouldbedirectory) 590 verifydir(targ); 591 (void)write(rem, "", 1); 592 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 593 targisdir = 1; 594 for (first = 1;; first = 0) { 595 cp = buf; 596 if (read(rem, cp, 1) <= 0) 597 return; 598 if (*cp++ == '\n') 599 SCREWUP("unexpected <newline>"); 600 do { 601 if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 602 SCREWUP("lost connection"); 603 *cp++ = ch; 604 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 605 *cp = 0; 606 607 if (buf[0] == '\01' || buf[0] == '\02') { 608 if (iamremote == 0) 609 (void)write(STDERR_FILENO, 610 buf + 1, strlen(buf + 1)); 611 if (buf[0] == '\02') 612 exit(1); 613 ++errs; 614 continue; 615 } 616 if (buf[0] == 'E') { 617 (void)write(rem, "", 1); 618 return; 619 } 620 621 if (ch == '\n') 622 *--cp = 0; 623 624 cp = buf; 625 if (*cp == 'T') { 626 setimes++; 627 cp++; 628 mtime.tv_sec = strtol(cp, &cp, 10); 629 if (!cp || *cp++ != ' ') 630 SCREWUP("mtime.sec not delimited"); 631 mtime.tv_usec = strtol(cp, &cp, 10); 632 if (!cp || *cp++ != ' ') 633 SCREWUP("mtime.usec not delimited"); 634 atime.tv_sec = strtol(cp, &cp, 10); 635 if (!cp || *cp++ != ' ') 636 SCREWUP("atime.sec not delimited"); 637 atime.tv_usec = strtol(cp, &cp, 10); 638 if (!cp || *cp++ != '\0') 639 SCREWUP("atime.usec not delimited"); 640 (void)write(rem, "", 1); 641 continue; 642 } 643 if (*cp != 'C' && *cp != 'D') { 644 /* 645 * Check for the case "rcp remote:foo\* local:bar". 646 * In this case, the line "No match." can be returned 647 * by the shell before the rcp command on the remote is 648 * executed so the ^Aerror_message convention isn't 649 * followed. 650 */ 651 if (first) { 652 run_err("%s", cp); 653 exit(1); 654 } 655 SCREWUP("expected control record"); 656 } 657 mode = 0; 658 for (++cp; cp < buf + 5; cp++) { 659 if (*cp < '0' || *cp > '7') 660 SCREWUP("bad mode"); 661 mode = (mode << 3) | (*cp - '0'); 662 } 663 if (*cp++ != ' ') 664 SCREWUP("mode not delimited"); 665 666 for (size = 0; isdigit(*cp);) 667 size = size * 10 + (*cp++ - '0'); 668 if (*cp++ != ' ') 669 SCREWUP("size not delimited"); 670 if (targisdir) { 671 static char *namebuf; 672 static int cursize; 673 size_t need; 674 675 need = strlen(targ) + strlen(cp) + 250; 676 if (need > cursize) { 677 if (!(namebuf = malloc(need))) 678 run_err("%s", strerror(errno)); 679 } 680 (void)snprintf(namebuf, need, "%s%s%s", targ, 681 *targ ? "/" : "", cp); 682 np = namebuf; 683 } else 684 np = targ; 685 exists = stat(np, &stb) == 0; 686 if (buf[0] == 'D') { 687 int mod_flag = pflag; 688 if (exists) { 689 if (!S_ISDIR(stb.st_mode)) { 690 errno = ENOTDIR; 691 goto bad; 692 } 693 if (pflag) 694 (void)chmod(np, mode); 695 } else { 696 /* Handle copying from a read-only directory */ 697 mod_flag = 1; 698 if (mkdir(np, mode | S_IRWXU) < 0) 699 goto bad; 700 } 701 vect[0] = np; 702 sink(1, vect); 703 if (setimes) { 704 setimes = 0; 705 if (utimes(np, tv) < 0) 706 run_err("%s: set times: %s", 707 np, strerror(errno)); 708 } 709 if (mod_flag) 710 (void)chmod(np, mode); 711 continue; 712 } 713 omode = mode; 714 mode |= S_IWRITE; 715 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 716bad: run_err("%s: %s", np, strerror(errno)); 717 continue; 718 } 719 (void)write(rem, "", 1); 720 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 721 (void)close(ofd); 722 continue; 723 } 724 cp = bp->buf; 725 wrerr = NO; 726 for (count = i = 0; i < size; i += BUFSIZ) { 727 amt = BUFSIZ; 728 if (i + amt > size) 729 amt = size - i; 730 count += amt; 731 do { 732 j = read(rem, cp, amt); 733 if (j <= 0) { 734 run_err("%s", j ? strerror(errno) : 735 "dropped connection"); 736 exit(1); 737 } 738 amt -= j; 739 cp += j; 740 } while (amt > 0); 741 if (count == bp->cnt) { 742 /* Keep reading so we stay sync'd up. */ 743 if (wrerr == NO) { 744 j = write(ofd, bp->buf, count); 745 if (j != count) { 746 wrerr = YES; 747 wrerrno = j >= 0 ? EIO : errno; 748 } 749 } 750 count = 0; 751 cp = bp->buf; 752 } 753 } 754 if (count != 0 && wrerr == NO && 755 (j = write(ofd, bp->buf, count)) != count) { 756 wrerr = YES; 757 wrerrno = j >= 0 ? EIO : errno; 758 } 759 if (ftruncate(ofd, size)) { 760 run_err("%s: truncate: %s", np, strerror(errno)); 761 wrerr = DISPLAYED; 762 } 763 if (pflag) { 764 if (exists || omode != mode) 765 if (fchmod(ofd, omode)) 766 run_err("%s: set mode: %s", 767 np, strerror(errno)); 768 } else { 769 if (!exists && omode != mode) 770 if (fchmod(ofd, omode & ~mask)) 771 run_err("%s: set mode: %s", 772 np, strerror(errno)); 773 } 774 (void)close(ofd); 775 (void)response(); 776 if (setimes && wrerr == NO) { 777 setimes = 0; 778 if (utimes(np, tv) < 0) { 779 run_err("%s: set times: %s", 780 np, strerror(errno)); 781 wrerr = DISPLAYED; 782 } 783 } 784 switch(wrerr) { 785 case YES: 786 run_err("%s: %s", np, strerror(wrerrno)); 787 break; 788 case NO: 789 (void)write(rem, "", 1); 790 break; 791 case DISPLAYED: 792 break; 793 } 794 } 795screwup: 796 run_err("protocol error: %s", why); 797 exit(1); 798} 799 800#ifdef KERBEROS 801int 802kerberos(host, bp, locuser, user) 803 char **host, *bp, *locuser, *user; 804{ 805 if (use_kerberos) { 806 setuid(getuid()); 807 rem = KSUCCESS; 808 errno = 0; 809 if (dest_realm == NULL) 810 dest_realm = krb_realmofhost(*host); 811 rem = 812#ifdef CRYPT 813 doencrypt ? 814 krcmd_mutual(host, 815 port, user, bp, 0, dest_realm, &cred, schedule) : 816#endif 817 krcmd(host, port, user, bp, 0, dest_realm); 818 819 if (rem < 0) { 820 if (errno == ECONNREFUSED) 821 oldw("remote host doesn't support Kerberos"); 822 else if (errno == ENOENT) 823 oldw("can't provide Kerberos authentication data"); 824 execv(_PATH_RCP, argv_copy); 825 err(1, "execv: %s", _PATH_RCP); 826 } 827 } else { 828#ifdef CRYPT 829 if (doencrypt) 830 errx(1, 831 "the -x option requires Kerberos authentication"); 832#endif 833 rem = rcmd(host, port, locuser, user, bp, 0); 834 } 835 return (rem); 836} 837#endif /* KERBEROS */ 838 839int 840response() 841{ 842 char ch, *cp, resp, rbuf[BUFSIZ]; 843 844 if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) 845 lostconn(0); 846 847 cp = rbuf; 848 switch(resp) { 849 case 0: /* ok */ 850 return (0); 851 default: 852 *cp++ = resp; 853 /* FALLTHROUGH */ 854 case 1: /* error, followed by error msg */ 855 case 2: /* fatal error, "" */ 856 do { 857 if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 858 lostconn(0); 859 *cp++ = ch; 860 } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 861 862 if (!iamremote) 863 (void)write(STDERR_FILENO, rbuf, cp - rbuf); 864 ++errs; 865 if (resp == 1) 866 return (-1); 867 exit(1); 868 } 869 /* NOTREACHED */ 870} 871 872void 873usage() 874{ 875#ifdef KERBEROS 876#ifdef CRYPT 877 (void)fprintf(stderr, "%s\n%s\n", 878 "usage: rcp [-Kpx] [-k realm] f1 f2", 879 " rcp [-Kprx] [-k realm] f1 ... fn directory"); 880#else 881 (void)fprintf(stderr, "%s\n%s\n", 882 "usage: rcp [-Kp] [-k realm] f1 f2", 883 " rcp [-Kpr] [-k realm] f1 ... fn directory"); 884#endif 885#else 886 (void)fprintf(stderr, "%s\n%s\n", 887 "usage: rcp [-p] f1 f2", 888 " rcp [-pr] f1 ... fn directory"); 889#endif 890 exit(1); 891} 892 893#if __STDC__ 894#include <stdarg.h> 895#else 896#include <varargs.h> 897#endif 898 899#ifdef KERBEROS 900void 901#if __STDC__ 902oldw(const char *fmt, ...) 903#else 904oldw(fmt, va_alist) 905 char *fmt; 906 va_dcl 907#endif 908{ 909 va_list ap; 910#if __STDC__ 911 va_start(ap, fmt); 912#else 913 va_start(ap); 914#endif 915 (void)fprintf(stderr, "rcp: "); 916 (void)vfprintf(stderr, fmt, ap); 917 (void)fprintf(stderr, ", using standard rcp\n"); 918 va_end(ap); 919} 920#endif 921 922void 923#if __STDC__ 924run_err(const char *fmt, ...) 925#else 926run_err(fmt, va_alist) 927 char *fmt; 928 va_dcl 929#endif 930{ 931 static FILE *fp; 932 va_list ap; 933#if __STDC__ 934 va_start(ap, fmt); 935#else 936 va_start(ap); 937#endif 938 939 ++errs; 940 if (fp == NULL && !(fp = fdopen(rem, "w"))) 941 return; 942 (void)fprintf(fp, "%c", 0x01); 943 (void)fprintf(fp, "rcp: "); 944 (void)vfprintf(fp, fmt, ap); 945 (void)fprintf(fp, "\n"); 946 (void)fflush(fp); 947 948 if (!iamremote) 949 vwarnx(fmt, ap); 950 951 va_end(ap); 952} 953