rcp.c revision 36149
11556Srgrimes/* 21556Srgrimes * Copyright (c) 1983, 1990, 1992, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 3. All advertising materials mentioning features or use of this software 141556Srgrimes * must display the following acknowledgement: 151556Srgrimes * This product includes software developed by the University of 161556Srgrimes * California, Berkeley and its contributors. 171556Srgrimes * 4. Neither the name of the University nor the names of its contributors 181556Srgrimes * may be used to endorse or promote products derived from this software 191556Srgrimes * without specific prior written permission. 201556Srgrimes * 211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311556Srgrimes * SUCH DAMAGE. 321556Srgrimes */ 331556Srgrimes 341556Srgrimes#ifndef lint 3520420Sstevestatic char const copyright[] = 361556Srgrimes"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\ 371556Srgrimes The Regents of the University of California. All rights reserved.\n"; 381556Srgrimes#endif /* not lint */ 391556Srgrimes 401556Srgrimes#ifndef lint 4136149Scharnier#if 0 4236149Scharnierstatic char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94"; 4336149Scharnier#endif 4436149Scharnierstatic const char rcsid[] = 4536149Scharnier "$Id$"; 461556Srgrimes#endif /* not lint */ 471556Srgrimes 481556Srgrimes#include <sys/param.h> 491556Srgrimes#include <sys/stat.h> 501556Srgrimes#include <sys/time.h> 511556Srgrimes#include <sys/socket.h> 521556Srgrimes#include <netinet/in.h> 531556Srgrimes#include <netinet/in_systm.h> 541556Srgrimes#include <netinet/ip.h> 551556Srgrimes 561556Srgrimes#include <ctype.h> 571556Srgrimes#include <dirent.h> 581556Srgrimes#include <err.h> 591556Srgrimes#include <errno.h> 601556Srgrimes#include <fcntl.h> 611556Srgrimes#include <netdb.h> 621556Srgrimes#include <pwd.h> 631556Srgrimes#include <signal.h> 641556Srgrimes#include <stdio.h> 651556Srgrimes#include <stdlib.h> 661556Srgrimes#include <string.h> 671556Srgrimes#include <string.h> 681556Srgrimes#include <unistd.h> 691556Srgrimes 701556Srgrimes#include "pathnames.h" 711556Srgrimes#include "extern.h" 721556Srgrimes 731556Srgrimes#ifdef KERBEROS 7414024Smarkm#include <des.h> 7529914Smarkm#include <krb.h> 761556Srgrimes 771556Srgrimeschar dst_realm_buf[REALM_SZ]; 781556Srgrimeschar *dest_realm = NULL; 791556Srgrimesint use_kerberos = 1; 801556SrgrimesCREDENTIALS cred; 811556SrgrimesKey_schedule schedule; 821556Srgrimesextern char *krb_realmofhost(); 831556Srgrimes#ifdef CRYPT 841556Srgrimesint doencrypt = 0; 851556Srgrimes#define OPTIONS "dfKk:prtx" 861556Srgrimes#else 871556Srgrimes#define OPTIONS "dfKk:prt" 881556Srgrimes#endif 891556Srgrimes#else 901556Srgrimes#define OPTIONS "dfprt" 911556Srgrimes#endif 921556Srgrimes 931556Srgrimesstruct passwd *pwd; 941556Srgrimesu_short port; 951556Srgrimesuid_t userid; 961556Srgrimesint errs, rem; 971556Srgrimesint pflag, iamremote, iamrecursive, targetshouldbedirectory; 981556Srgrimes 9934898Smarkmstatic int argc_copy; 10034898Smarkmstatic char **argv_copy; 10134898Smarkm 1021556Srgrimes#define CMDNEEDS 64 1031556Srgrimeschar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 1041556Srgrimes 1051556Srgrimes#ifdef KERBEROS 1061556Srgrimesint kerberos __P((char **, char *, char *, char *)); 1071556Srgrimesvoid oldw __P((const char *, ...)); 1081556Srgrimes#endif 1091556Srgrimesint response __P((void)); 1101556Srgrimesvoid rsource __P((char *, struct stat *)); 1111556Srgrimesvoid sink __P((int, char *[])); 1121556Srgrimesvoid source __P((int, char *[])); 1131556Srgrimesvoid tolocal __P((int, char *[])); 1141556Srgrimesvoid toremote __P((char *, int, char *[])); 1151556Srgrimesvoid usage __P((void)); 1161556Srgrimes 1171556Srgrimesint 1181556Srgrimesmain(argc, argv) 1191556Srgrimes int argc; 1201556Srgrimes char *argv[]; 1211556Srgrimes{ 1221556Srgrimes struct servent *sp; 1231556Srgrimes int ch, fflag, tflag; 1241556Srgrimes char *targ, *shell; 12534898Smarkm int i; 1261556Srgrimes 12734898Smarkm /* 12834898Smarkm * Prepare for execing ourselves. 12934898Smarkm */ 13034898Smarkm 13134898Smarkm argc_copy = argc + 1; 13234898Smarkm argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy)); 13334898Smarkm if (argv_copy == NULL) 13434898Smarkm err(1, "malloc"); 13534898Smarkm argv_copy[0] = argv[0]; 13634898Smarkm argv_copy[1] = "-K"; 13734898Smarkm for(i = 1; i < argc; ++i) { 13834898Smarkm argv_copy[i + 1] = strdup(argv[i]); 13934898Smarkm if (argv_copy[i + 1] == NULL) 14034898Smarkm errx(1, "strdup: out of memory"); 14134898Smarkm } 14234898Smarkm argv_copy[argc + 1] = NULL; 14334898Smarkm 1441556Srgrimes fflag = tflag = 0; 14524348Simp while ((ch = getopt(argc, argv, OPTIONS)) != -1) 1461556Srgrimes switch(ch) { /* User-visible flags. */ 1471556Srgrimes case 'K': 1481556Srgrimes#ifdef KERBEROS 1491556Srgrimes use_kerberos = 0; 1501556Srgrimes#endif 1511556Srgrimes break; 1521556Srgrimes#ifdef KERBEROS 1531556Srgrimes case 'k': 1541556Srgrimes dest_realm = dst_realm_buf; 15525612Sjoerg (void)strncpy(dst_realm_buf, optarg, REALM_SZ - 1); 15625612Sjoerg dst_realm_buf[REALM_SZ - 1] = '\0'; 1571556Srgrimes break; 1581556Srgrimes#ifdef CRYPT 1591556Srgrimes case 'x': 1601556Srgrimes doencrypt = 1; 1611556Srgrimes /* des_set_key(cred.session, schedule); */ 1621556Srgrimes break; 1631556Srgrimes#endif 1641556Srgrimes#endif 1651556Srgrimes case 'p': 1661556Srgrimes pflag = 1; 1671556Srgrimes break; 1681556Srgrimes case 'r': 1691556Srgrimes iamrecursive = 1; 1701556Srgrimes break; 1711556Srgrimes /* Server options. */ 1721556Srgrimes case 'd': 1731556Srgrimes targetshouldbedirectory = 1; 1741556Srgrimes break; 1751556Srgrimes case 'f': /* "from" */ 1761556Srgrimes iamremote = 1; 1771556Srgrimes fflag = 1; 1781556Srgrimes break; 1791556Srgrimes case 't': /* "to" */ 1801556Srgrimes iamremote = 1; 1811556Srgrimes tflag = 1; 1821556Srgrimes break; 1831556Srgrimes case '?': 1841556Srgrimes default: 1851556Srgrimes usage(); 1861556Srgrimes } 1871556Srgrimes argc -= optind; 1881556Srgrimes argv += optind; 1891556Srgrimes 1901556Srgrimes#ifdef KERBEROS 1911556Srgrimes if (use_kerberos) { 1921556Srgrimes#ifdef CRYPT 1931556Srgrimes shell = doencrypt ? "ekshell" : "kshell"; 1941556Srgrimes#else 1951556Srgrimes shell = "kshell"; 1961556Srgrimes#endif 1971556Srgrimes if ((sp = getservbyname(shell, "tcp")) == NULL) { 1981556Srgrimes use_kerberos = 0; 1991556Srgrimes oldw("can't get entry for %s/tcp service", shell); 2001556Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2011556Srgrimes } 2021556Srgrimes } else 2031556Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2041556Srgrimes#else 2051556Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2061556Srgrimes#endif 2071556Srgrimes if (sp == NULL) 2081556Srgrimes errx(1, "%s/tcp: unknown service", shell); 2091556Srgrimes port = sp->s_port; 2101556Srgrimes 2111556Srgrimes if ((pwd = getpwuid(userid = getuid())) == NULL) 2121556Srgrimes errx(1, "unknown user %d", (int)userid); 2131556Srgrimes 2141556Srgrimes rem = STDIN_FILENO; /* XXX */ 2151556Srgrimes 2161556Srgrimes if (fflag) { /* Follow "protocol", send data. */ 2171556Srgrimes (void)response(); 2181556Srgrimes (void)setuid(userid); 2191556Srgrimes source(argc, argv); 2201556Srgrimes exit(errs); 2211556Srgrimes } 2221556Srgrimes 2231556Srgrimes if (tflag) { /* Receive data. */ 2241556Srgrimes (void)setuid(userid); 2251556Srgrimes sink(argc, argv); 2261556Srgrimes exit(errs); 2271556Srgrimes } 2281556Srgrimes 2291556Srgrimes if (argc < 2) 2301556Srgrimes usage(); 2311556Srgrimes if (argc > 2) 2321556Srgrimes targetshouldbedirectory = 1; 2331556Srgrimes 2341556Srgrimes rem = -1; 2351556Srgrimes /* Command to be executed on remote system using "rsh". */ 2361556Srgrimes#ifdef KERBEROS 2371556Srgrimes (void)snprintf(cmd, sizeof(cmd), 2381556Srgrimes "rcp%s%s%s%s", iamrecursive ? " -r" : "", 2391556Srgrimes#ifdef CRYPT 2401556Srgrimes (doencrypt && use_kerberos ? " -x" : ""), 2411556Srgrimes#else 2421556Srgrimes "", 2431556Srgrimes#endif 2441556Srgrimes pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 2451556Srgrimes#else 2461556Srgrimes (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", 2471556Srgrimes iamrecursive ? " -r" : "", pflag ? " -p" : "", 2481556Srgrimes targetshouldbedirectory ? " -d" : ""); 2491556Srgrimes#endif 2501556Srgrimes 2511556Srgrimes (void)signal(SIGPIPE, lostconn); 2521556Srgrimes 2537165Sjoerg if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 2541556Srgrimes toremote(targ, argc, argv); 2551556Srgrimes else { 2561556Srgrimes tolocal(argc, argv); /* Dest is local host. */ 2571556Srgrimes if (targetshouldbedirectory) 2581556Srgrimes verifydir(argv[argc - 1]); 2591556Srgrimes } 2601556Srgrimes exit(errs); 2611556Srgrimes} 2621556Srgrimes 2631556Srgrimesvoid 2641556Srgrimestoremote(targ, argc, argv) 2651556Srgrimes char *targ, *argv[]; 2661556Srgrimes int argc; 2671556Srgrimes{ 2681556Srgrimes int i, len, tos; 2691556Srgrimes char *bp, *host, *src, *suser, *thost, *tuser; 2701556Srgrimes 2711556Srgrimes *targ++ = 0; 2721556Srgrimes if (*targ == 0) 2731556Srgrimes targ = "."; 2741556Srgrimes 2757165Sjoerg if ((thost = strchr(argv[argc - 1], '@'))) { 2761556Srgrimes /* user@host */ 2771556Srgrimes *thost++ = 0; 2781556Srgrimes tuser = argv[argc - 1]; 2791556Srgrimes if (*tuser == '\0') 2801556Srgrimes tuser = NULL; 2811556Srgrimes else if (!okname(tuser)) 2821556Srgrimes exit(1); 2831556Srgrimes } else { 2841556Srgrimes thost = argv[argc - 1]; 2851556Srgrimes tuser = NULL; 2861556Srgrimes } 2871556Srgrimes 2881556Srgrimes for (i = 0; i < argc - 1; i++) { 2891556Srgrimes src = colon(argv[i]); 2901556Srgrimes if (src) { /* remote to remote */ 2911556Srgrimes *src++ = 0; 2921556Srgrimes if (*src == 0) 2931556Srgrimes src = "."; 2941556Srgrimes host = strchr(argv[i], '@'); 2951556Srgrimes len = strlen(_PATH_RSH) + strlen(argv[i]) + 2961556Srgrimes strlen(src) + (tuser ? strlen(tuser) : 0) + 2971556Srgrimes strlen(thost) + strlen(targ) + CMDNEEDS + 20; 2981556Srgrimes if (!(bp = malloc(len))) 2991556Srgrimes err(1, NULL); 3001556Srgrimes if (host) { 3011556Srgrimes *host++ = 0; 3021556Srgrimes suser = argv[i]; 3031556Srgrimes if (*suser == '\0') 3041556Srgrimes suser = pwd->pw_name; 3051556Srgrimes else if (!okname(suser)) 3061556Srgrimes continue; 3071556Srgrimes (void)snprintf(bp, len, 3081556Srgrimes "%s %s -l %s -n %s %s '%s%s%s:%s'", 3091556Srgrimes _PATH_RSH, host, suser, cmd, src, 3101556Srgrimes tuser ? tuser : "", tuser ? "@" : "", 3111556Srgrimes thost, targ); 3121556Srgrimes } else 3131556Srgrimes (void)snprintf(bp, len, 3141556Srgrimes "exec %s %s -n %s %s '%s%s%s:%s'", 3151556Srgrimes _PATH_RSH, argv[i], cmd, src, 3161556Srgrimes tuser ? tuser : "", tuser ? "@" : "", 3171556Srgrimes thost, targ); 3181556Srgrimes (void)susystem(bp, userid); 3191556Srgrimes (void)free(bp); 3201556Srgrimes } else { /* local to remote */ 3211556Srgrimes if (rem == -1) { 3221556Srgrimes len = strlen(targ) + CMDNEEDS + 20; 3231556Srgrimes if (!(bp = malloc(len))) 3241556Srgrimes err(1, NULL); 3251556Srgrimes (void)snprintf(bp, len, "%s -t %s", cmd, targ); 3261556Srgrimes host = thost; 3271556Srgrimes#ifdef KERBEROS 3281556Srgrimes if (use_kerberos) 3291556Srgrimes rem = kerberos(&host, bp, 3301556Srgrimes pwd->pw_name, 3311556Srgrimes tuser ? tuser : pwd->pw_name); 3321556Srgrimes else 3331556Srgrimes#endif 3341556Srgrimes rem = rcmd(&host, port, pwd->pw_name, 3351556Srgrimes tuser ? tuser : pwd->pw_name, 3361556Srgrimes bp, 0); 3371556Srgrimes if (rem < 0) 3381556Srgrimes exit(1); 3391556Srgrimes tos = IPTOS_THROUGHPUT; 3401556Srgrimes if (setsockopt(rem, IPPROTO_IP, IP_TOS, 3411556Srgrimes &tos, sizeof(int)) < 0) 3421556Srgrimes warn("TOS (ignored)"); 3431556Srgrimes if (response() < 0) 3441556Srgrimes exit(1); 3451556Srgrimes (void)free(bp); 3461556Srgrimes (void)setuid(userid); 3471556Srgrimes } 3481556Srgrimes source(1, argv+i); 3491556Srgrimes } 3501556Srgrimes } 3511556Srgrimes} 3521556Srgrimes 3531556Srgrimesvoid 3541556Srgrimestolocal(argc, argv) 3551556Srgrimes int argc; 3561556Srgrimes char *argv[]; 3571556Srgrimes{ 3581556Srgrimes int i, len, tos; 3591556Srgrimes char *bp, *host, *src, *suser; 3601556Srgrimes 3611556Srgrimes for (i = 0; i < argc - 1; i++) { 3621556Srgrimes if (!(src = colon(argv[i]))) { /* Local to local. */ 3631556Srgrimes len = strlen(_PATH_CP) + strlen(argv[i]) + 3641556Srgrimes strlen(argv[argc - 1]) + 20; 3651556Srgrimes if (!(bp = malloc(len))) 3661556Srgrimes err(1, NULL); 3671556Srgrimes (void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 36831633Swosch iamrecursive ? " -PR" : "", pflag ? " -p" : "", 3691556Srgrimes argv[i], argv[argc - 1]); 3701556Srgrimes if (susystem(bp, userid)) 3711556Srgrimes ++errs; 3721556Srgrimes (void)free(bp); 3731556Srgrimes continue; 3741556Srgrimes } 3751556Srgrimes *src++ = 0; 3761556Srgrimes if (*src == 0) 3771556Srgrimes src = "."; 3781556Srgrimes if ((host = strchr(argv[i], '@')) == NULL) { 3791556Srgrimes host = argv[i]; 3801556Srgrimes suser = pwd->pw_name; 3811556Srgrimes } else { 3821556Srgrimes *host++ = 0; 3831556Srgrimes suser = argv[i]; 3841556Srgrimes if (*suser == '\0') 3851556Srgrimes suser = pwd->pw_name; 3861556Srgrimes else if (!okname(suser)) 3871556Srgrimes continue; 3881556Srgrimes } 3891556Srgrimes len = strlen(src) + CMDNEEDS + 20; 3901556Srgrimes if ((bp = malloc(len)) == NULL) 3911556Srgrimes err(1, NULL); 3921556Srgrimes (void)snprintf(bp, len, "%s -f %s", cmd, src); 3938855Srgrimes rem = 3941556Srgrimes#ifdef KERBEROS 3958855Srgrimes use_kerberos ? 3968855Srgrimes kerberos(&host, bp, pwd->pw_name, suser) : 3971556Srgrimes#endif 3981556Srgrimes rcmd(&host, port, pwd->pw_name, suser, bp, 0); 3991556Srgrimes (void)free(bp); 4001556Srgrimes if (rem < 0) { 4011556Srgrimes ++errs; 4021556Srgrimes continue; 4031556Srgrimes } 4041556Srgrimes (void)seteuid(userid); 4051556Srgrimes tos = IPTOS_THROUGHPUT; 4061556Srgrimes if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 4071556Srgrimes warn("TOS (ignored)"); 4081556Srgrimes sink(1, argv + argc - 1); 4091556Srgrimes (void)seteuid(0); 4101556Srgrimes (void)close(rem); 4111556Srgrimes rem = -1; 4121556Srgrimes } 4131556Srgrimes} 4141556Srgrimes 4151556Srgrimesvoid 4161556Srgrimessource(argc, argv) 4171556Srgrimes int argc; 4181556Srgrimes char *argv[]; 4191556Srgrimes{ 4201556Srgrimes struct stat stb; 4211556Srgrimes static BUF buffer; 4221556Srgrimes BUF *bp; 4231556Srgrimes off_t i; 4241556Srgrimes int amt, fd, haderr, indx, result; 4251556Srgrimes char *last, *name, buf[BUFSIZ]; 4261556Srgrimes 4271556Srgrimes for (indx = 0; indx < argc; ++indx) { 4281556Srgrimes name = argv[indx]; 4291556Srgrimes if ((fd = open(name, O_RDONLY, 0)) < 0) 4301556Srgrimes goto syserr; 4311556Srgrimes if (fstat(fd, &stb)) { 4321556Srgrimessyserr: run_err("%s: %s", name, strerror(errno)); 4331556Srgrimes goto next; 4341556Srgrimes } 4351556Srgrimes switch (stb.st_mode & S_IFMT) { 4361556Srgrimes case S_IFREG: 4371556Srgrimes break; 4381556Srgrimes case S_IFDIR: 4391556Srgrimes if (iamrecursive) { 4401556Srgrimes rsource(name, &stb); 4411556Srgrimes goto next; 4421556Srgrimes } 4431556Srgrimes /* FALLTHROUGH */ 4441556Srgrimes default: 4451556Srgrimes run_err("%s: not a regular file", name); 4461556Srgrimes goto next; 4471556Srgrimes } 4481556Srgrimes if ((last = strrchr(name, '/')) == NULL) 4491556Srgrimes last = name; 4501556Srgrimes else 4511556Srgrimes ++last; 4521556Srgrimes if (pflag) { 4531556Srgrimes /* 4541556Srgrimes * Make it compatible with possible future 4551556Srgrimes * versions expecting microseconds. 4561556Srgrimes */ 4571556Srgrimes (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 45818409Snate stb.st_mtimespec.tv_sec, stb.st_atimespec.tv_sec); 4591556Srgrimes (void)write(rem, buf, strlen(buf)); 4601556Srgrimes if (response() < 0) 4611556Srgrimes goto next; 4621556Srgrimes } 4631556Srgrimes#define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) 4641556Srgrimes (void)snprintf(buf, sizeof(buf), "C%04o %qd %s\n", 4651556Srgrimes stb.st_mode & MODEMASK, stb.st_size, last); 4661556Srgrimes (void)write(rem, buf, strlen(buf)); 4671556Srgrimes if (response() < 0) 4681556Srgrimes goto next; 4691556Srgrimes if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 4701556Srgrimesnext: (void)close(fd); 4711556Srgrimes continue; 4721556Srgrimes } 4731556Srgrimes 4741556Srgrimes /* Keep writing after an error so that we stay sync'd up. */ 4751556Srgrimes for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 4761556Srgrimes amt = bp->cnt; 4771556Srgrimes if (i + amt > stb.st_size) 4781556Srgrimes amt = stb.st_size - i; 4791556Srgrimes if (!haderr) { 4801556Srgrimes result = read(fd, bp->buf, amt); 4811556Srgrimes if (result != amt) 4821556Srgrimes haderr = result >= 0 ? EIO : errno; 4831556Srgrimes } 4841556Srgrimes if (haderr) 4851556Srgrimes (void)write(rem, bp->buf, amt); 4861556Srgrimes else { 4871556Srgrimes result = write(rem, bp->buf, amt); 4881556Srgrimes if (result != amt) 4891556Srgrimes haderr = result >= 0 ? EIO : errno; 4901556Srgrimes } 4911556Srgrimes } 4921556Srgrimes if (close(fd) && !haderr) 4931556Srgrimes haderr = errno; 4941556Srgrimes if (!haderr) 4951556Srgrimes (void)write(rem, "", 1); 4961556Srgrimes else 4971556Srgrimes run_err("%s: %s", name, strerror(haderr)); 4981556Srgrimes (void)response(); 4991556Srgrimes } 5001556Srgrimes} 5011556Srgrimes 5021556Srgrimesvoid 5031556Srgrimesrsource(name, statp) 5041556Srgrimes char *name; 5051556Srgrimes struct stat *statp; 5061556Srgrimes{ 5071556Srgrimes DIR *dirp; 5081556Srgrimes struct dirent *dp; 5091556Srgrimes char *last, *vect[1], path[MAXPATHLEN]; 5101556Srgrimes 5111556Srgrimes if (!(dirp = opendir(name))) { 5121556Srgrimes run_err("%s: %s", name, strerror(errno)); 5131556Srgrimes return; 5141556Srgrimes } 5151556Srgrimes last = strrchr(name, '/'); 5161556Srgrimes if (last == 0) 5171556Srgrimes last = name; 5181556Srgrimes else 5191556Srgrimes last++; 5201556Srgrimes if (pflag) { 5211556Srgrimes (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 52218409Snate statp->st_mtimespec.tv_sec, statp->st_atimespec.tv_sec); 5231556Srgrimes (void)write(rem, path, strlen(path)); 5241556Srgrimes if (response() < 0) { 5251556Srgrimes closedir(dirp); 5261556Srgrimes return; 5271556Srgrimes } 5281556Srgrimes } 5291556Srgrimes (void)snprintf(path, sizeof(path), 5301556Srgrimes "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 5311556Srgrimes (void)write(rem, path, strlen(path)); 5321556Srgrimes if (response() < 0) { 5331556Srgrimes closedir(dirp); 5341556Srgrimes return; 5351556Srgrimes } 5367165Sjoerg while ((dp = readdir(dirp))) { 5371556Srgrimes if (dp->d_ino == 0) 5381556Srgrimes continue; 5391556Srgrimes if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 5401556Srgrimes continue; 5411556Srgrimes if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 5421556Srgrimes run_err("%s/%s: name too long", name, dp->d_name); 5431556Srgrimes continue; 5441556Srgrimes } 5451556Srgrimes (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 5461556Srgrimes vect[0] = path; 5471556Srgrimes source(1, vect); 5481556Srgrimes } 5491556Srgrimes (void)closedir(dirp); 5501556Srgrimes (void)write(rem, "E\n", 2); 5511556Srgrimes (void)response(); 5521556Srgrimes} 5531556Srgrimes 5541556Srgrimesvoid 5551556Srgrimessink(argc, argv) 5561556Srgrimes int argc; 5571556Srgrimes char *argv[]; 5581556Srgrimes{ 5591556Srgrimes static BUF buffer; 5601556Srgrimes struct stat stb; 5611556Srgrimes struct timeval tv[2]; 5621556Srgrimes enum { YES, NO, DISPLAYED } wrerr; 5631556Srgrimes BUF *bp; 5641556Srgrimes off_t i, j; 5651556Srgrimes int amt, count, exists, first, mask, mode, ofd, omode; 5667165Sjoerg int setimes, size, targisdir, wrerrno = 0; 5671556Srgrimes char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 5681556Srgrimes 5691556Srgrimes#define atime tv[0] 5701556Srgrimes#define mtime tv[1] 5711556Srgrimes#define SCREWUP(str) { why = str; goto screwup; } 5721556Srgrimes 5731556Srgrimes setimes = targisdir = 0; 5741556Srgrimes mask = umask(0); 5751556Srgrimes if (!pflag) 5761556Srgrimes (void)umask(mask); 5771556Srgrimes if (argc != 1) { 5781556Srgrimes run_err("ambiguous target"); 5791556Srgrimes exit(1); 5801556Srgrimes } 5811556Srgrimes targ = *argv; 5821556Srgrimes if (targetshouldbedirectory) 5831556Srgrimes verifydir(targ); 5841556Srgrimes (void)write(rem, "", 1); 5851556Srgrimes if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 5861556Srgrimes targisdir = 1; 5871556Srgrimes for (first = 1;; first = 0) { 5881556Srgrimes cp = buf; 5891556Srgrimes if (read(rem, cp, 1) <= 0) 5901556Srgrimes return; 5911556Srgrimes if (*cp++ == '\n') 5921556Srgrimes SCREWUP("unexpected <newline>"); 5931556Srgrimes do { 5941556Srgrimes if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 5951556Srgrimes SCREWUP("lost connection"); 5961556Srgrimes *cp++ = ch; 5971556Srgrimes } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 5981556Srgrimes *cp = 0; 5991556Srgrimes 6001556Srgrimes if (buf[0] == '\01' || buf[0] == '\02') { 6011556Srgrimes if (iamremote == 0) 6021556Srgrimes (void)write(STDERR_FILENO, 6031556Srgrimes buf + 1, strlen(buf + 1)); 6041556Srgrimes if (buf[0] == '\02') 6051556Srgrimes exit(1); 6061556Srgrimes ++errs; 6071556Srgrimes continue; 6081556Srgrimes } 6091556Srgrimes if (buf[0] == 'E') { 6101556Srgrimes (void)write(rem, "", 1); 6111556Srgrimes return; 6121556Srgrimes } 6131556Srgrimes 6141556Srgrimes if (ch == '\n') 6151556Srgrimes *--cp = 0; 6161556Srgrimes 6171556Srgrimes cp = buf; 6181556Srgrimes if (*cp == 'T') { 6191556Srgrimes setimes++; 6201556Srgrimes cp++; 62113978Spst mtime.tv_sec = strtol(cp, &cp, 10); 62213978Spst if (!cp || *cp++ != ' ') 6231556Srgrimes SCREWUP("mtime.sec not delimited"); 62413978Spst mtime.tv_usec = strtol(cp, &cp, 10); 62513978Spst if (!cp || *cp++ != ' ') 6261556Srgrimes SCREWUP("mtime.usec not delimited"); 62713978Spst atime.tv_sec = strtol(cp, &cp, 10); 62813978Spst if (!cp || *cp++ != ' ') 6291556Srgrimes SCREWUP("atime.sec not delimited"); 63013978Spst atime.tv_usec = strtol(cp, &cp, 10); 63113978Spst if (!cp || *cp++ != '\0') 6321556Srgrimes SCREWUP("atime.usec not delimited"); 6331556Srgrimes (void)write(rem, "", 1); 6341556Srgrimes continue; 6351556Srgrimes } 6361556Srgrimes if (*cp != 'C' && *cp != 'D') { 6371556Srgrimes /* 6381556Srgrimes * Check for the case "rcp remote:foo\* local:bar". 6391556Srgrimes * In this case, the line "No match." can be returned 6401556Srgrimes * by the shell before the rcp command on the remote is 6411556Srgrimes * executed so the ^Aerror_message convention isn't 6421556Srgrimes * followed. 6431556Srgrimes */ 6441556Srgrimes if (first) { 6451556Srgrimes run_err("%s", cp); 6461556Srgrimes exit(1); 6471556Srgrimes } 6481556Srgrimes SCREWUP("expected control record"); 6491556Srgrimes } 6501556Srgrimes mode = 0; 6511556Srgrimes for (++cp; cp < buf + 5; cp++) { 6521556Srgrimes if (*cp < '0' || *cp > '7') 6531556Srgrimes SCREWUP("bad mode"); 6541556Srgrimes mode = (mode << 3) | (*cp - '0'); 6551556Srgrimes } 6561556Srgrimes if (*cp++ != ' ') 6571556Srgrimes SCREWUP("mode not delimited"); 6581556Srgrimes 6591556Srgrimes for (size = 0; isdigit(*cp);) 6601556Srgrimes size = size * 10 + (*cp++ - '0'); 6611556Srgrimes if (*cp++ != ' ') 6621556Srgrimes SCREWUP("size not delimited"); 6631556Srgrimes if (targisdir) { 6641556Srgrimes static char *namebuf; 6651556Srgrimes static int cursize; 6661556Srgrimes size_t need; 6671556Srgrimes 6681556Srgrimes need = strlen(targ) + strlen(cp) + 250; 6691556Srgrimes if (need > cursize) { 6701556Srgrimes if (!(namebuf = malloc(need))) 6711556Srgrimes run_err("%s", strerror(errno)); 6721556Srgrimes } 6731556Srgrimes (void)snprintf(namebuf, need, "%s%s%s", targ, 6741556Srgrimes *targ ? "/" : "", cp); 6751556Srgrimes np = namebuf; 6761556Srgrimes } else 6771556Srgrimes np = targ; 6781556Srgrimes exists = stat(np, &stb) == 0; 6791556Srgrimes if (buf[0] == 'D') { 6801556Srgrimes int mod_flag = pflag; 6811556Srgrimes if (exists) { 6821556Srgrimes if (!S_ISDIR(stb.st_mode)) { 6831556Srgrimes errno = ENOTDIR; 6841556Srgrimes goto bad; 6851556Srgrimes } 6861556Srgrimes if (pflag) 6871556Srgrimes (void)chmod(np, mode); 6881556Srgrimes } else { 6891556Srgrimes /* Handle copying from a read-only directory */ 6901556Srgrimes mod_flag = 1; 6911556Srgrimes if (mkdir(np, mode | S_IRWXU) < 0) 6921556Srgrimes goto bad; 6931556Srgrimes } 6941556Srgrimes vect[0] = np; 6951556Srgrimes sink(1, vect); 6961556Srgrimes if (setimes) { 6971556Srgrimes setimes = 0; 6981556Srgrimes if (utimes(np, tv) < 0) 6991556Srgrimes run_err("%s: set times: %s", 7001556Srgrimes np, strerror(errno)); 7011556Srgrimes } 7021556Srgrimes if (mod_flag) 7031556Srgrimes (void)chmod(np, mode); 7041556Srgrimes continue; 7051556Srgrimes } 7061556Srgrimes omode = mode; 7071556Srgrimes mode |= S_IWRITE; 7081556Srgrimes if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 7091556Srgrimesbad: run_err("%s: %s", np, strerror(errno)); 7101556Srgrimes continue; 7111556Srgrimes } 7121556Srgrimes (void)write(rem, "", 1); 7131556Srgrimes if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 7141556Srgrimes (void)close(ofd); 7151556Srgrimes continue; 7161556Srgrimes } 7171556Srgrimes cp = bp->buf; 7181556Srgrimes wrerr = NO; 7191556Srgrimes for (count = i = 0; i < size; i += BUFSIZ) { 7201556Srgrimes amt = BUFSIZ; 7211556Srgrimes if (i + amt > size) 7221556Srgrimes amt = size - i; 7231556Srgrimes count += amt; 7241556Srgrimes do { 7251556Srgrimes j = read(rem, cp, amt); 7261556Srgrimes if (j <= 0) { 7271556Srgrimes run_err("%s", j ? strerror(errno) : 7281556Srgrimes "dropped connection"); 7291556Srgrimes exit(1); 7301556Srgrimes } 7311556Srgrimes amt -= j; 7321556Srgrimes cp += j; 7331556Srgrimes } while (amt > 0); 7341556Srgrimes if (count == bp->cnt) { 7351556Srgrimes /* Keep reading so we stay sync'd up. */ 7361556Srgrimes if (wrerr == NO) { 7371556Srgrimes j = write(ofd, bp->buf, count); 7381556Srgrimes if (j != count) { 7391556Srgrimes wrerr = YES; 7408855Srgrimes wrerrno = j >= 0 ? EIO : errno; 7411556Srgrimes } 7421556Srgrimes } 7431556Srgrimes count = 0; 7441556Srgrimes cp = bp->buf; 7451556Srgrimes } 7461556Srgrimes } 7471556Srgrimes if (count != 0 && wrerr == NO && 7481556Srgrimes (j = write(ofd, bp->buf, count)) != count) { 7491556Srgrimes wrerr = YES; 7508855Srgrimes wrerrno = j >= 0 ? EIO : errno; 7511556Srgrimes } 7521556Srgrimes if (ftruncate(ofd, size)) { 7531556Srgrimes run_err("%s: truncate: %s", np, strerror(errno)); 7541556Srgrimes wrerr = DISPLAYED; 7551556Srgrimes } 7561556Srgrimes if (pflag) { 7571556Srgrimes if (exists || omode != mode) 7581556Srgrimes if (fchmod(ofd, omode)) 7591556Srgrimes run_err("%s: set mode: %s", 7601556Srgrimes np, strerror(errno)); 7611556Srgrimes } else { 7621556Srgrimes if (!exists && omode != mode) 7631556Srgrimes if (fchmod(ofd, omode & ~mask)) 7641556Srgrimes run_err("%s: set mode: %s", 7651556Srgrimes np, strerror(errno)); 7661556Srgrimes } 7671556Srgrimes (void)close(ofd); 7681556Srgrimes (void)response(); 7691556Srgrimes if (setimes && wrerr == NO) { 7701556Srgrimes setimes = 0; 7711556Srgrimes if (utimes(np, tv) < 0) { 7721556Srgrimes run_err("%s: set times: %s", 7731556Srgrimes np, strerror(errno)); 7741556Srgrimes wrerr = DISPLAYED; 7751556Srgrimes } 7761556Srgrimes } 7771556Srgrimes switch(wrerr) { 7781556Srgrimes case YES: 7791556Srgrimes run_err("%s: %s", np, strerror(wrerrno)); 7801556Srgrimes break; 7811556Srgrimes case NO: 7821556Srgrimes (void)write(rem, "", 1); 7831556Srgrimes break; 7841556Srgrimes case DISPLAYED: 7851556Srgrimes break; 7861556Srgrimes } 7871556Srgrimes } 7881556Srgrimesscrewup: 7891556Srgrimes run_err("protocol error: %s", why); 7901556Srgrimes exit(1); 7911556Srgrimes} 7921556Srgrimes 7931556Srgrimes#ifdef KERBEROS 7941556Srgrimesint 7951556Srgrimeskerberos(host, bp, locuser, user) 7961556Srgrimes char **host, *bp, *locuser, *user; 7971556Srgrimes{ 7981556Srgrimes if (use_kerberos) { 79934898Smarkm setuid(getuid()); 8001556Srgrimes rem = KSUCCESS; 8011556Srgrimes errno = 0; 8021556Srgrimes if (dest_realm == NULL) 8031556Srgrimes dest_realm = krb_realmofhost(*host); 8048855Srgrimes rem = 8051556Srgrimes#ifdef CRYPT 8068855Srgrimes doencrypt ? 8071556Srgrimes krcmd_mutual(host, 8081556Srgrimes port, user, bp, 0, dest_realm, &cred, schedule) : 8091556Srgrimes#endif 8101556Srgrimes krcmd(host, port, user, bp, 0, dest_realm); 8111556Srgrimes 8121556Srgrimes if (rem < 0) { 8131556Srgrimes if (errno == ECONNREFUSED) 8141556Srgrimes oldw("remote host doesn't support Kerberos"); 8151556Srgrimes else if (errno == ENOENT) 8161556Srgrimes oldw("can't provide Kerberos authentication data"); 81734898Smarkm execv(_PATH_RCP, argv_copy); 8181556Srgrimes } 8191556Srgrimes } else { 8201556Srgrimes#ifdef CRYPT 8211556Srgrimes if (doencrypt) 8221556Srgrimes errx(1, 8231556Srgrimes "the -x option requires Kerberos authentication"); 8241556Srgrimes#endif 8251556Srgrimes rem = rcmd(host, port, locuser, user, bp, 0); 8261556Srgrimes } 8271556Srgrimes return (rem); 8281556Srgrimes} 8291556Srgrimes#endif /* KERBEROS */ 8301556Srgrimes 8311556Srgrimesint 8321556Srgrimesresponse() 8331556Srgrimes{ 8341556Srgrimes char ch, *cp, resp, rbuf[BUFSIZ]; 8351556Srgrimes 8361556Srgrimes if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) 8371556Srgrimes lostconn(0); 8381556Srgrimes 8391556Srgrimes cp = rbuf; 8401556Srgrimes switch(resp) { 8411556Srgrimes case 0: /* ok */ 8421556Srgrimes return (0); 8431556Srgrimes default: 8441556Srgrimes *cp++ = resp; 8451556Srgrimes /* FALLTHROUGH */ 8461556Srgrimes case 1: /* error, followed by error msg */ 8471556Srgrimes case 2: /* fatal error, "" */ 8481556Srgrimes do { 8491556Srgrimes if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 8501556Srgrimes lostconn(0); 8511556Srgrimes *cp++ = ch; 8521556Srgrimes } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 8531556Srgrimes 8541556Srgrimes if (!iamremote) 8551556Srgrimes (void)write(STDERR_FILENO, rbuf, cp - rbuf); 8561556Srgrimes ++errs; 8571556Srgrimes if (resp == 1) 8581556Srgrimes return (-1); 8591556Srgrimes exit(1); 8601556Srgrimes } 8611556Srgrimes /* NOTREACHED */ 8621556Srgrimes} 8631556Srgrimes 8641556Srgrimesvoid 8651556Srgrimesusage() 8661556Srgrimes{ 8671556Srgrimes#ifdef KERBEROS 8681556Srgrimes#ifdef CRYPT 86926466Scharnier (void)fprintf(stderr, "%s\n%s\n", 8701556Srgrimes "usage: rcp [-Kpx] [-k realm] f1 f2", 87126466Scharnier " rcp [-Kprx] [-k realm] f1 ... fn directory"); 8721556Srgrimes#else 87326466Scharnier (void)fprintf(stderr, "%s\n%s\n", 8741556Srgrimes "usage: rcp [-Kp] [-k realm] f1 f2", 87526466Scharnier " rcp [-Kpr] [-k realm] f1 ... fn directory"); 8761556Srgrimes#endif 8771556Srgrimes#else 87826466Scharnier (void)fprintf(stderr, "%s\n%s\n", 87926466Scharnier "usage: rcp [-p] f1 f2", 88026466Scharnier " rcp [-pr] f1 ... fn directory"); 8811556Srgrimes#endif 8821556Srgrimes exit(1); 8831556Srgrimes} 8841556Srgrimes 8851556Srgrimes#if __STDC__ 8861556Srgrimes#include <stdarg.h> 8871556Srgrimes#else 8881556Srgrimes#include <varargs.h> 8891556Srgrimes#endif 8901556Srgrimes 8911556Srgrimes#ifdef KERBEROS 8921556Srgrimesvoid 8931556Srgrimes#if __STDC__ 8941556Srgrimesoldw(const char *fmt, ...) 8951556Srgrimes#else 8961556Srgrimesoldw(fmt, va_alist) 8971556Srgrimes char *fmt; 8981556Srgrimes va_dcl 8991556Srgrimes#endif 9001556Srgrimes{ 9011556Srgrimes va_list ap; 9021556Srgrimes#if __STDC__ 9031556Srgrimes va_start(ap, fmt); 9041556Srgrimes#else 9051556Srgrimes va_start(ap); 9061556Srgrimes#endif 9071556Srgrimes (void)fprintf(stderr, "rcp: "); 9081556Srgrimes (void)vfprintf(stderr, fmt, ap); 9091556Srgrimes (void)fprintf(stderr, ", using standard rcp\n"); 9101556Srgrimes va_end(ap); 9111556Srgrimes} 9121556Srgrimes#endif 9131556Srgrimes 9141556Srgrimesvoid 9151556Srgrimes#if __STDC__ 9161556Srgrimesrun_err(const char *fmt, ...) 9171556Srgrimes#else 9181556Srgrimesrun_err(fmt, va_alist) 9191556Srgrimes char *fmt; 9201556Srgrimes va_dcl 9211556Srgrimes#endif 9221556Srgrimes{ 9231556Srgrimes static FILE *fp; 9241556Srgrimes va_list ap; 9251556Srgrimes#if __STDC__ 9261556Srgrimes va_start(ap, fmt); 9271556Srgrimes#else 9281556Srgrimes va_start(ap); 9291556Srgrimes#endif 9301556Srgrimes 9311556Srgrimes ++errs; 9321556Srgrimes if (fp == NULL && !(fp = fdopen(rem, "w"))) 9331556Srgrimes return; 9341556Srgrimes (void)fprintf(fp, "%c", 0x01); 9351556Srgrimes (void)fprintf(fp, "rcp: "); 9361556Srgrimes (void)vfprintf(fp, fmt, ap); 9371556Srgrimes (void)fprintf(fp, "\n"); 9381556Srgrimes (void)fflush(fp); 9391556Srgrimes 9401556Srgrimes if (!iamremote) 9411556Srgrimes vwarnx(fmt, ap); 9421556Srgrimes 9431556Srgrimes va_end(ap); 9441556Srgrimes} 945