rcp.c revision 178825
11541Srgrimes/* 21541Srgrimes * Copyright (c) 1983, 1990, 1992, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 71541Srgrimes * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 91541Srgrimes * notice, this list of conditions and the following disclaimer. 101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111541Srgrimes * notice, this list of conditions and the following disclaimer in the 121541Srgrimes * documentation and/or other materials provided with the distribution. 131541Srgrimes * 3. All advertising materials mentioning features or use of this software 141541Srgrimes * must display the following acknowledgement: 151541Srgrimes * This product includes software developed by the University of 161541Srgrimes * California, Berkeley and its contributors. 171541Srgrimes * 4. Neither the name of the University nor the names of its contributors 181541Srgrimes * may be used to endorse or promote products derived from this software 191541Srgrimes * without specific prior written permission. 201541Srgrimes * 211541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311541Srgrimes * SUCH DAMAGE. 321541Srgrimes */ 331541Srgrimes 341541Srgrimes#include "rcp_locl.h" 351541Srgrimes#include <getarg.h> 361541Srgrimes 371541Srgrimes#define RSH_PROGRAM "rsh" 381541Srgrimes 391541Srgrimesstruct passwd *pwd; 401541Srgrimesuid_t userid; 411541Srgrimesint errs, remin, remout; 421541Srgrimesint pflag, iamremote, iamrecursive, targetshouldbedirectory; 431541Srgrimesint doencrypt, noencrypt; 441541Srgrimesint usebroken, usekrb4, usekrb5, forwardtkt; 451541Srgrimeschar *port; 461541Srgrimesint eflag = 0; 471541Srgrimes 481541Srgrimes#define CMDNEEDS 64 491541Srgrimeschar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 501541Srgrimes 511541Srgrimesint response (void); 521541Srgrimesvoid rsource (char *, struct stat *); 531541Srgrimesvoid sink (int, char *[]); 541541Srgrimesvoid source (int, char *[]); 551541Srgrimesvoid tolocal (int, char *[]); 561541Srgrimesvoid toremote (char *, int, char *[]); 571541Srgrimes 581541Srgrimesint do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 591541Srgrimes 601541Srgrimesstatic int fflag, tflag; 611541Srgrimes 621541Srgrimesstatic int version_flag, help_flag; 631541Srgrimes 641541Srgrimesstruct getargs args[] = { 651541Srgrimes { NULL, '4', arg_flag, &usekrb4, "use Kerberos 4 authentication" }, 661541Srgrimes { NULL, '5', arg_flag, &usekrb5, "use Kerberos 5 authentication" }, 671541Srgrimes { NULL, 'F', arg_flag, &forwardtkt, "forward credentials" }, 681541Srgrimes { NULL, 'K', arg_flag, &usebroken, "use BSD authentication" }, 691541Srgrimes { NULL, 'P', arg_string, &port, "non-default port", "port" }, 701541Srgrimes { NULL, 'p', arg_flag, &pflag, "preserve file permissions" }, 711541Srgrimes { NULL, 'r', arg_flag, &iamrecursive, "recursive mode" }, 721541Srgrimes { NULL, 'x', arg_flag, &doencrypt, "use encryption" }, 731541Srgrimes { NULL, 'z', arg_flag, &noencrypt, "don't encrypt" }, 741541Srgrimes { NULL, 'd', arg_flag, &targetshouldbedirectory }, 751541Srgrimes { NULL, 'e', arg_flag, &eflag, "passed to rsh" }, 761541Srgrimes { NULL, 'f', arg_flag, &fflag }, 771541Srgrimes { NULL, 't', arg_flag, &tflag }, 781541Srgrimes { "version", 0, arg_flag, &version_flag }, 791541Srgrimes { "help", 0, arg_flag, &help_flag } 801541Srgrimes}; 811541Srgrimes 821541Srgrimesstatic void 831541Srgrimesusage (int ret) 841541Srgrimes{ 851541Srgrimes arg_printusage (args, 861541Srgrimes sizeof(args) / sizeof(args[0]), 871541Srgrimes NULL, 881541Srgrimes "file1 file2|file... directory"); 891541Srgrimes exit (ret); 901541Srgrimes} 911541Srgrimes 921541Srgrimesint 931541Srgrimesmain(int argc, char **argv) 941541Srgrimes{ 951541Srgrimes char *targ; 961541Srgrimes int optind = 0; 971541Srgrimes 981541Srgrimes setprogname(argv[0]); 991541Srgrimes if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 1001541Srgrimes &optind)) 1011541Srgrimes usage (1); 1021541Srgrimes if(help_flag) 1031541Srgrimes usage(0); 1041541Srgrimes if (version_flag) { 1051541Srgrimes print_version (NULL); 1061541Srgrimes return 0; 1071541Srgrimes } 1081541Srgrimes 1091541Srgrimes iamremote = (fflag || tflag); 1101541Srgrimes 1111541Srgrimes argc -= optind; 1121541Srgrimes argv += optind; 1131541Srgrimes 1141549Srgrimes if ((pwd = getpwuid(userid = getuid())) == NULL) 1151541Srgrimes errx(1, "unknown user %d", (int)userid); 1161541Srgrimes 1171541Srgrimes remin = STDIN_FILENO; /* XXX */ 1181541Srgrimes remout = STDOUT_FILENO; 1191541Srgrimes 1201541Srgrimes if (fflag) { /* Follow "protocol", send data. */ 1211541Srgrimes response(); 1221541Srgrimes if (setuid(userid) < 0) 1231541Srgrimes errx(1, "setuid failed"); 1241541Srgrimes source(argc, argv); 1251541Srgrimes exit(errs); 1261541Srgrimes } 1271541Srgrimes 1281541Srgrimes if (tflag) { /* Receive data. */ 1291541Srgrimes if (setuid(userid) < 0) 1301541Srgrimes errx(1, "setuid failed"); 1311541Srgrimes sink(argc, argv); 1321549Srgrimes exit(errs); 1331541Srgrimes } 1341541Srgrimes 1351541Srgrimes if (argc < 2) 1361541Srgrimes usage(1); 1371541Srgrimes if (argc > 2) 1381541Srgrimes targetshouldbedirectory = 1; 1391541Srgrimes 1401541Srgrimes remin = remout = -1; 1411541Srgrimes /* Command to be executed on remote system using "rsh". */ 1421541Srgrimes snprintf(cmd, sizeof(cmd), 1431541Srgrimes "rcp%s%s%s", iamrecursive ? " -r" : "", 1441541Srgrimes pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 1451541Srgrimes 1461541Srgrimes signal(SIGPIPE, lostconn); 1471541Srgrimes 1481541Srgrimes if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 1491541Srgrimes toremote(targ, argc, argv); 1501541Srgrimes else { 1511541Srgrimes tolocal(argc, argv); /* Dest is local host. */ 1521541Srgrimes if (targetshouldbedirectory) 1531541Srgrimes verifydir(argv[argc - 1]); 1541541Srgrimes } 1551541Srgrimes exit(errs); 1561541Srgrimes} 1571541Srgrimes 1581541Srgrimesvoid 1591541Srgrimestoremote(char *targ, int argc, char **argv) 1601541Srgrimes{ 1611541Srgrimes int i; 1621541Srgrimes char *bp, *host, *src, *suser, *thost, *tuser; 1631541Srgrimes 1641541Srgrimes *targ++ = 0; 1651541Srgrimes if (*targ == 0) 1661541Srgrimes targ = "."; 1671541Srgrimes 1681541Srgrimes if ((thost = strchr(argv[argc - 1], '@'))) { 1691541Srgrimes /* user@host */ 1701541Srgrimes *thost++ = 0; 1711541Srgrimes tuser = argv[argc - 1]; 1721541Srgrimes if (*tuser == '\0') 1731541Srgrimes tuser = NULL; 1741541Srgrimes else if (!okname(tuser)) 1751541Srgrimes exit(1); 1761541Srgrimes } else { 1771541Srgrimes thost = argv[argc - 1]; 1781541Srgrimes tuser = NULL; 1791541Srgrimes } 1801541Srgrimes 1811541Srgrimes for (i = 0; i < argc - 1; i++) { 1821541Srgrimes src = colon(argv[i]); 1831541Srgrimes if (src) { /* remote to remote */ 1841541Srgrimes int ret; 1851541Srgrimes *src++ = 0; 1861541Srgrimes if (*src == 0) 1871541Srgrimes src = "."; 1881541Srgrimes host = strchr(argv[i], '@'); 1891541Srgrimes if (host) { 1901541Srgrimes *host++ = '\0'; 1911541Srgrimes suser = argv[i]; 1921541Srgrimes if (*suser == '\0') 1931541Srgrimes suser = pwd->pw_name; 1941541Srgrimes else if (!okname(suser)) 1951541Srgrimes continue; 1961541Srgrimes ret = asprintf(&bp, 1971541Srgrimes "%s%s %s -l %s -n %s %s '%s%s%s:%s'", 1981541Srgrimes _PATH_RSH, eflag ? " -e" : "", 1991541Srgrimes host, suser, cmd, src, 2001541Srgrimes tuser ? tuser : "", tuser ? "@" : "", 2011541Srgrimes thost, targ); 2021541Srgrimes } else { 2031541Srgrimes ret = asprintf(&bp, 2041541Srgrimes "exec %s%s %s -n %s %s '%s%s%s:%s'", 2051541Srgrimes _PATH_RSH, eflag ? " -e" : "", 2061541Srgrimes argv[i], cmd, src, 2071541Srgrimes tuser ? tuser : "", tuser ? "@" : "", 2081541Srgrimes thost, targ); 2091541Srgrimes } 2101541Srgrimes if (ret == -1) 2111541Srgrimes err (1, "malloc"); 2121541Srgrimes susystem(bp, userid); 2131541Srgrimes free(bp); 2141541Srgrimes } else { /* local to remote */ 2151541Srgrimes if (remin == -1) { 2161541Srgrimes if (asprintf(&bp, "%s -t %s", cmd, targ) == -1) 2171541Srgrimes err (1, "malloc"); 2181541Srgrimes host = thost; 2191541Srgrimes 2201541Srgrimes if (do_cmd(host, tuser, bp, &remin, &remout) < 0) 2211541Srgrimes exit(1); 2221541Srgrimes 2231549Srgrimes if (response() < 0) 2241541Srgrimes exit(1); 2251541Srgrimes free(bp); 2261541Srgrimes if (setuid(userid) < 0) 2271541Srgrimes errx(1, "setuid failed"); 2281541Srgrimes } 2291541Srgrimes source(1, argv+i); 2301541Srgrimes } 2311541Srgrimes } 2321541Srgrimes} 2331541Srgrimes 2341541Srgrimesvoid 2351541Srgrimestolocal(int argc, char **argv) 2361541Srgrimes{ 2371541Srgrimes int i; 2381541Srgrimes char *bp, *host, *src, *suser; 2391541Srgrimes 2401541Srgrimes for (i = 0; i < argc - 1; i++) { 2411541Srgrimes int ret; 2421541Srgrimes 2431541Srgrimes if (!(src = colon(argv[i]))) { /* Local to local. */ 2441541Srgrimes ret = asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP, 2451541Srgrimes iamrecursive ? " -PR" : "", pflag ? " -p" : "", 2461541Srgrimes argv[i], argv[argc - 1]); 2471541Srgrimes if (ret == -1) 2481541Srgrimes err (1, "malloc"); 2491541Srgrimes if (susystem(bp, userid)) 2501541Srgrimes ++errs; 2511541Srgrimes free(bp); 2521541Srgrimes continue; 2531541Srgrimes } 2541541Srgrimes *src++ = 0; 2551541Srgrimes if (*src == 0) 2561541Srgrimes src = "."; 2571541Srgrimes if ((host = strchr(argv[i], '@')) == NULL) { 2581541Srgrimes host = argv[i]; 2591541Srgrimes suser = pwd->pw_name; 2601541Srgrimes } else { 2611541Srgrimes *host++ = 0; 2621541Srgrimes suser = argv[i]; 2631549Srgrimes if (*suser == '\0') 2641541Srgrimes suser = pwd->pw_name; 2651541Srgrimes else if (!okname(suser)) 2661541Srgrimes continue; 2671541Srgrimes } 2681541Srgrimes ret = asprintf(&bp, "%s -f %s", cmd, src); 2691541Srgrimes if (ret == -1) 2701541Srgrimes err (1, "malloc"); 2711541Srgrimes if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 2721541Srgrimes free(bp); 2731541Srgrimes ++errs; 2741541Srgrimes continue; 2751541Srgrimes } 2761541Srgrimes free(bp); 2771541Srgrimes sink(1, argv + argc - 1); 2781541Srgrimes if (seteuid(0) < 0) 2791541Srgrimes exit(1); 2801541Srgrimes close(remin); 2811541Srgrimes remin = remout = -1; 2821541Srgrimes } 2831541Srgrimes} 2841541Srgrimes 2851541Srgrimesvoid 2861541Srgrimessource(int argc, char **argv) 2871541Srgrimes{ 2881541Srgrimes struct stat stb; 2891541Srgrimes static BUF buffer; 2901541Srgrimes BUF *bp; 2911541Srgrimes off_t i; 2921541Srgrimes int amt, fd, haderr, indx, result; 2931549Srgrimes char *last, *name, buf[BUFSIZ]; 2941541Srgrimes 2951541Srgrimes for (indx = 0; indx < argc; ++indx) { 2961541Srgrimes name = argv[indx]; 2971541Srgrimes if ((fd = open(name, O_RDONLY, 0)) < 0) 2981541Srgrimes goto syserr; 2991541Srgrimes if (fstat(fd, &stb)) { 3001541Srgrimessyserr: run_err("%s: %s", name, strerror(errno)); 3011541Srgrimes goto next; 3021541Srgrimes } 3031541Srgrimes switch (stb.st_mode & S_IFMT) { 3041541Srgrimes case S_IFREG: 3051541Srgrimes break; 3061541Srgrimes case S_IFDIR: 3071541Srgrimes if (iamrecursive) { 3081541Srgrimes rsource(name, &stb); 3091541Srgrimes goto next; 3101541Srgrimes } 3111541Srgrimes /* FALLTHROUGH */ 3121541Srgrimes default: 3131541Srgrimes run_err("%s: not a regular file", name); 3141541Srgrimes goto next; 3151541Srgrimes } 3161549Srgrimes if ((last = strrchr(name, '/')) == NULL) 3171541Srgrimes last = name; 3181541Srgrimes else 3191541Srgrimes ++last; 3201541Srgrimes if (pflag) { 3211541Srgrimes /* 3221541Srgrimes * Make it compatible with possible future 3231541Srgrimes * versions expecting microseconds. 3241541Srgrimes */ 3251541Srgrimes snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 3261541Srgrimes (long)stb.st_mtime, 3271541Srgrimes (long)stb.st_atime); 3281541Srgrimes write(remout, buf, strlen(buf)); 3291541Srgrimes if (response() < 0) 3301541Srgrimes goto next; 3311541Srgrimes } 3321541Srgrimes#undef MODEMASK 3331541Srgrimes#define MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 3341541Srgrimes snprintf(buf, sizeof(buf), "C%04o %lu %s\n", 3351541Srgrimes stb.st_mode & MODEMASK, 3361541Srgrimes (unsigned long)stb.st_size, 3371541Srgrimes last); 3381549Srgrimes write(remout, buf, strlen(buf)); 3391541Srgrimes if (response() < 0) 3401541Srgrimes goto next; 3411541Srgrimes if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 3421541Srgrimesnext: close(fd); 3431541Srgrimes continue; 3441541Srgrimes } 3451541Srgrimes 3461541Srgrimes /* Keep writing after an error so that we stay sync'd up. */ 3471541Srgrimes for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 3481541Srgrimes amt = bp->cnt; 3491541Srgrimes if (i + amt > stb.st_size) 3501541Srgrimes amt = stb.st_size - i; 3511541Srgrimes if (!haderr) { 3521541Srgrimes result = read(fd, bp->buf, amt); 3531541Srgrimes if (result != amt) 3541541Srgrimes haderr = result >= 0 ? EIO : errno; 3551541Srgrimes } 3561541Srgrimes if (haderr) 3571541Srgrimes write(remout, bp->buf, amt); 3581541Srgrimes else { 3591549Srgrimes result = write(remout, bp->buf, amt); 3601541Srgrimes if (result != amt) 3611541Srgrimes haderr = result >= 0 ? EIO : errno; 3621541Srgrimes } 3631541Srgrimes } 3641541Srgrimes if (close(fd) && !haderr) 3651541Srgrimes haderr = errno; 3661541Srgrimes if (!haderr) 3671541Srgrimes write(remout, "", 1); 3681541Srgrimes else 3691541Srgrimes run_err("%s: %s", name, strerror(haderr)); 3701549Srgrimes response(); 3711541Srgrimes } 3721541Srgrimes} 3731541Srgrimes 3741541Srgrimesvoid 3751541Srgrimesrsource(char *name, struct stat *statp) 3761541Srgrimes{ 3771541Srgrimes DIR *dirp; 3781541Srgrimes struct dirent *dp; 3791541Srgrimes char *last, *vect[1], path[MAXPATHLEN]; 3801541Srgrimes 3811541Srgrimes if (!(dirp = opendir(name))) { 3821541Srgrimes run_err("%s: %s", name, strerror(errno)); 3831541Srgrimes return; 3841549Srgrimes } 3851541Srgrimes last = strrchr(name, '/'); 3861541Srgrimes if (last == 0) 3871541Srgrimes last = name; 3881541Srgrimes else 3891541Srgrimes last++; 3901541Srgrimes if (pflag) { 3911541Srgrimes snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 3921541Srgrimes (long)statp->st_mtime, 3931541Srgrimes (long)statp->st_atime); 3941541Srgrimes write(remout, path, strlen(path)); 3951541Srgrimes if (response() < 0) { 3961541Srgrimes closedir(dirp); 3971541Srgrimes return; 3981541Srgrimes } 3991541Srgrimes } 4001541Srgrimes snprintf(path, sizeof(path), 4011541Srgrimes "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 4021541Srgrimes write(remout, path, strlen(path)); 4031541Srgrimes if (response() < 0) { 4041541Srgrimes closedir(dirp); 4051541Srgrimes return; 4061541Srgrimes } 4071541Srgrimes while ((dp = readdir(dirp))) { 4081541Srgrimes if (dp->d_ino == 0) 4091541Srgrimes continue; 4101541Srgrimes if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 4111541Srgrimes continue; 4121541Srgrimes if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 4131541Srgrimes run_err("%s/%s: name too long", name, dp->d_name); 4141541Srgrimes continue; 4151541Srgrimes } 4161541Srgrimes snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 4171541Srgrimes vect[0] = path; 4181541Srgrimes source(1, vect); 4191541Srgrimes } 4201549Srgrimes closedir(dirp); 4211541Srgrimes write(remout, "E\n", 2); 4221541Srgrimes response(); 4231541Srgrimes} 4241541Srgrimes 4251541Srgrimesvoid 4261541Srgrimessink(int argc, char **argv) 4271541Srgrimes{ 4281541Srgrimes static BUF buffer; 4291541Srgrimes struct stat stb; 4301549Srgrimes struct timeval tv[2]; 4311541Srgrimes enum { YES, NO, DISPLAYED } wrerr; 4321541Srgrimes BUF *bp; 4331541Srgrimes off_t i, j, size; 4341541Srgrimes int amt, count, exists, first, mask, mode, ofd, omode; 4351541Srgrimes int setimes, targisdir, wrerrno = 0; 4361549Srgrimes char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 4371541Srgrimes 4381541Srgrimes#define atime tv[0] 4391541Srgrimes#define mtime tv[1] 4401541Srgrimes#define SCREWUP(str) { why = str; goto screwup; } 4411541Srgrimes 4421541Srgrimes setimes = targisdir = 0; 4431541Srgrimes mask = umask(0); 4441549Srgrimes if (!pflag) 4451541Srgrimes umask(mask); 4461541Srgrimes if (argc != 1) { 4471541Srgrimes run_err("ambiguous target"); 4481541Srgrimes exit(1); 4491541Srgrimes } 4501549Srgrimes targ = *argv; 4511541Srgrimes if (targetshouldbedirectory) 4521541Srgrimes verifydir(targ); 4531541Srgrimes write(remout, "", 1); 4541541Srgrimes if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 4551541Srgrimes targisdir = 1; 4561541Srgrimes for (first = 1;; first = 0) { 4571541Srgrimes cp = buf; 4581541Srgrimes if (read(remin, cp, 1) <= 0) 4591541Srgrimes return; 4601541Srgrimes if (*cp++ == '\n') 4611541Srgrimes SCREWUP("unexpected <newline>"); 4621541Srgrimes do { 4631541Srgrimes if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 4641541Srgrimes SCREWUP("lost connection"); 4651541Srgrimes *cp++ = ch; 4661541Srgrimes } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 4671541Srgrimes *cp = 0; 4681541Srgrimes 4691541Srgrimes if (buf[0] == '\01' || buf[0] == '\02') { 4701541Srgrimes if (iamremote == 0) 4711541Srgrimes write(STDERR_FILENO, 4721541Srgrimes buf + 1, strlen(buf + 1)); 4731541Srgrimes if (buf[0] == '\02') 4741541Srgrimes exit(1); 4751541Srgrimes ++errs; 4761541Srgrimes continue; 4771541Srgrimes } 4781549Srgrimes if (buf[0] == 'E') { 4791541Srgrimes write(remout, "", 1); 4801541Srgrimes return; 4811541Srgrimes } 4821541Srgrimes 4831541Srgrimes if (ch == '\n') 4841541Srgrimes *--cp = 0; 4851541Srgrimes 4861541Srgrimes cp = buf; 4871541Srgrimes if (*cp == 'T') { 4881541Srgrimes setimes++; 4891549Srgrimes cp++; 4901541Srgrimes mtime.tv_sec = strtol(cp, &cp, 10); 4911541Srgrimes if (!cp || *cp++ != ' ') 4921541Srgrimes SCREWUP("mtime.sec not delimited"); 4931541Srgrimes mtime.tv_usec = strtol(cp, &cp, 10); 4941541Srgrimes if (!cp || *cp++ != ' ') 4951541Srgrimes SCREWUP("mtime.usec not delimited"); 4961541Srgrimes atime.tv_sec = strtol(cp, &cp, 10); 4971541Srgrimes if (!cp || *cp++ != ' ') 4981541Srgrimes SCREWUP("atime.sec not delimited"); 4991541Srgrimes atime.tv_usec = strtol(cp, &cp, 10); 5001541Srgrimes if (!cp || *cp++ != '\0') 5011541Srgrimes SCREWUP("atime.usec not delimited"); 5021541Srgrimes write(remout, "", 1); 5031541Srgrimes continue; 5041541Srgrimes } 5051541Srgrimes if (*cp != 'C' && *cp != 'D') { 5061549Srgrimes /* 5071541Srgrimes * Check for the case "rcp remote:foo\* local:bar". 5081541Srgrimes * In this case, the line "No match." can be returned 5091541Srgrimes * by the shell before the rcp command on the remote is 5101541Srgrimes * executed so the ^Aerror_message convention isn't 5111541Srgrimes * followed. 5121541Srgrimes */ 513 if (first) { 514 run_err("%s", cp); 515 exit(1); 516 } 517 SCREWUP("expected control record"); 518 } 519 mode = 0; 520 for (++cp; cp < buf + 5; cp++) { 521 if (*cp < '0' || *cp > '7') 522 SCREWUP("bad mode"); 523 mode = (mode << 3) | (*cp - '0'); 524 } 525 if (*cp++ != ' ') 526 SCREWUP("mode not delimited"); 527 528 for (size = 0; isdigit((unsigned char)*cp);) 529 size = size * 10 + (*cp++ - '0'); 530 if (*cp++ != ' ') 531 SCREWUP("size not delimited"); 532 if (targisdir) { 533 static char *namebuf; 534 static int cursize; 535 size_t need; 536 537 need = strlen(targ) + strlen(cp) + 250; 538 if (need > cursize) { 539 if (!(namebuf = malloc(need))) 540 run_err("%s", strerror(errno)); 541 } 542 snprintf(namebuf, need, "%s%s%s", targ, 543 *targ ? "/" : "", cp); 544 np = namebuf; 545 } else 546 np = targ; 547 exists = stat(np, &stb) == 0; 548 if (buf[0] == 'D') { 549 int mod_flag = pflag; 550 if (exists) { 551 if (!S_ISDIR(stb.st_mode)) { 552 errno = ENOTDIR; 553 goto bad; 554 } 555 if (pflag) 556 chmod(np, mode); 557 } else { 558 /* Handle copying from a read-only directory */ 559 mod_flag = 1; 560 if (mkdir(np, mode | S_IRWXU) < 0) 561 goto bad; 562 } 563 vect[0] = np; 564 sink(1, vect); 565 if (setimes) { 566 setimes = 0; 567 if (utimes(np, tv) < 0) 568 run_err("%s: set times: %s", 569 np, strerror(errno)); 570 } 571 if (mod_flag) 572 chmod(np, mode); 573 continue; 574 } 575 omode = mode; 576 mode |= S_IWRITE; 577 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 578bad: run_err("%s: %s", np, strerror(errno)); 579 continue; 580 } 581 write(remout, "", 1); 582 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 583 close(ofd); 584 continue; 585 } 586 cp = bp->buf; 587 wrerr = NO; 588 for (count = i = 0; i < size; i += BUFSIZ) { 589 amt = BUFSIZ; 590 if (i + amt > size) 591 amt = size - i; 592 count += amt; 593 if((j = net_read(remin, cp, amt)) != amt) { 594 run_err("%s", j ? strerror(errno) : 595 "dropped connection"); 596 exit(1); 597 } 598 amt -= j; 599 cp += j; 600 if (count == bp->cnt) { 601 /* Keep reading so we stay sync'd up. */ 602 if (wrerr == NO) { 603 j = write(ofd, bp->buf, count); 604 if (j != count) { 605 wrerr = YES; 606 wrerrno = j >= 0 ? EIO : errno; 607 } 608 } 609 count = 0; 610 cp = bp->buf; 611 } 612 } 613 if (count != 0 && wrerr == NO && 614 (j = write(ofd, bp->buf, count)) != count) { 615 wrerr = YES; 616 wrerrno = j >= 0 ? EIO : errno; 617 } 618 if (ftruncate(ofd, size)) { 619 run_err("%s: truncate: %s", np, strerror(errno)); 620 wrerr = DISPLAYED; 621 } 622 if (pflag) { 623 if (exists || omode != mode) 624 if (fchmod(ofd, omode)) 625 run_err("%s: set mode: %s", 626 np, strerror(errno)); 627 } else { 628 if (!exists && omode != mode) 629 if (fchmod(ofd, omode & ~mask)) 630 run_err("%s: set mode: %s", 631 np, strerror(errno)); 632 } 633 close(ofd); 634 response(); 635 if (setimes && wrerr == NO) { 636 setimes = 0; 637 if (utimes(np, tv) < 0) { 638 run_err("%s: set times: %s", 639 np, strerror(errno)); 640 wrerr = DISPLAYED; 641 } 642 } 643 switch(wrerr) { 644 case YES: 645 run_err("%s: %s", np, strerror(wrerrno)); 646 break; 647 case NO: 648 write(remout, "", 1); 649 break; 650 case DISPLAYED: 651 break; 652 } 653 } 654screwup: 655 run_err("protocol error: %s", why); 656 exit(1); 657} 658 659int 660response(void) 661{ 662 char ch, *cp, resp, rbuf[BUFSIZ]; 663 664 if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 665 lostconn(0); 666 667 cp = rbuf; 668 switch(resp) { 669 case 0: /* ok */ 670 return (0); 671 default: 672 *cp++ = resp; 673 /* FALLTHROUGH */ 674 case 1: /* error, followed by error msg */ 675 case 2: /* fatal error, "" */ 676 do { 677 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 678 lostconn(0); 679 *cp++ = ch; 680 } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 681 682 if (!iamremote) 683 write(STDERR_FILENO, rbuf, cp - rbuf); 684 ++errs; 685 if (resp == 1) 686 return (-1); 687 exit(1); 688 } 689 /* NOTREACHED */ 690} 691 692#include <stdarg.h> 693 694void 695run_err(const char *fmt, ...) 696{ 697 static FILE *fp; 698 va_list ap; 699 700 ++errs; 701 if (fp == NULL && !(fp = fdopen(remout, "w"))) 702 return; 703 va_start(ap, fmt); 704 fprintf(fp, "%c", 0x01); 705 fprintf(fp, "rcp: "); 706 vfprintf(fp, fmt, ap); 707 fprintf(fp, "\n"); 708 fflush(fp); 709 va_end(ap); 710 711 if (!iamremote) { 712 va_start(ap, fmt); 713 vwarnx(fmt, ap); 714 va_end(ap); 715 } 716} 717 718/* 719 * This function executes the given command as the specified user on the 720 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 721 * assigns the input and output file descriptors on success. 722 * 723 * If it cannot create necessary pipes it exits with error message. 724 */ 725 726int 727do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 728{ 729 int pin[2], pout[2], reserved[2]; 730 731 /* 732 * Reserve two descriptors so that the real pipes won't get 733 * descriptors 0 and 1 because that will screw up dup2 below. 734 */ 735 pipe(reserved); 736 737 /* Create a socket pair for communicating with rsh. */ 738 if (pipe(pin) < 0) { 739 perror("pipe"); 740 exit(255); 741 } 742 if (pipe(pout) < 0) { 743 perror("pipe"); 744 exit(255); 745 } 746 747 /* Free the reserved descriptors. */ 748 close(reserved[0]); 749 close(reserved[1]); 750 751 /* For a child to execute the command on the remote host using rsh. */ 752 if (fork() == 0) { 753 char *args[100]; 754 unsigned int i; 755 756 /* Child. */ 757 close(pin[1]); 758 close(pout[0]); 759 dup2(pin[0], 0); 760 dup2(pout[1], 1); 761 close(pin[0]); 762 close(pout[1]); 763 764 i = 0; 765 args[i++] = RSH_PROGRAM; 766 if (usekrb4) 767 args[i++] = "-4"; 768 if (usekrb5) 769 args[i++] = "-5"; 770 if (usebroken) 771 args[i++] = "-K"; 772 if (doencrypt) 773 args[i++] = "-x"; 774 if (forwardtkt) 775 args[i++] = "-F"; 776 if (noencrypt) 777 args[i++] = "-z"; 778 if (port != NULL) { 779 args[i++] = "-p"; 780 args[i++] = port; 781 } 782 if (eflag) 783 args[i++] = "-e"; 784 if (remuser != NULL) { 785 args[i++] = "-l"; 786 args[i++] = remuser; 787 } 788 args[i++] = host; 789 args[i++] = cmd; 790 args[i++] = NULL; 791 792 execvp(RSH_PROGRAM, args); 793 perror(RSH_PROGRAM); 794 exit(1); 795 } 796 /* Parent. Close the other side, and return the local side. */ 797 close(pin[0]); 798 *fdout = pin[1]; 799 close(pout[1]); 800 *fdin = pout[0]; 801 return 0; 802} 803