rcp.c revision 114583
11556Srgrimes/* 21556Srgrimes * Copyright (c) 1983, 1990, 1992, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 496196Sdes * Copyright (c) 2002 Networks Associates Technology, Inc. 596196Sdes * All rights reserved. 61556Srgrimes * 796196Sdes * Portions of this software were developed for the FreeBSD Project by 896196Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 996196Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1096196Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1196196Sdes * 121556Srgrimes * Redistribution and use in source and binary forms, with or without 131556Srgrimes * modification, are permitted provided that the following conditions 141556Srgrimes * are met: 151556Srgrimes * 1. Redistributions of source code must retain the above copyright 161556Srgrimes * notice, this list of conditions and the following disclaimer. 171556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181556Srgrimes * notice, this list of conditions and the following disclaimer in the 191556Srgrimes * documentation and/or other materials provided with the distribution. 201556Srgrimes * 3. All advertising materials mentioning features or use of this software 211556Srgrimes * must display the following acknowledgement: 221556Srgrimes * This product includes software developed by the University of 231556Srgrimes * California, Berkeley and its contributors. 241556Srgrimes * 4. Neither the name of the University nor the names of its contributors 251556Srgrimes * may be used to endorse or promote products derived from this software 261556Srgrimes * without specific prior written permission. 271556Srgrimes * 281556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 291556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 301556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 311556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 321556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 331556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 341556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 351556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 361556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 371556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 381556Srgrimes * SUCH DAMAGE. 391556Srgrimes */ 401556Srgrimes 41114433Sobrien#if 0 421556Srgrimes#ifndef lint 4320420Sstevestatic char const copyright[] = 441556Srgrimes"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\ 451556Srgrimes The Regents of the University of California. All rights reserved.\n"; 461556Srgrimes#endif /* not lint */ 471556Srgrimes 481556Srgrimes#ifndef lint 4936149Scharnierstatic char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94"; 50104559Scharnier#endif /* not lint */ 5136149Scharnier#endif 5299110Sobrien#include <sys/cdefs.h> 5399110Sobrien__FBSDID("$FreeBSD: head/bin/rcp/rcp.c 114583 2003-05-03 16:39:34Z markm $"); 541556Srgrimes 551556Srgrimes#include <sys/param.h> 561556Srgrimes#include <sys/stat.h> 571556Srgrimes#include <sys/time.h> 581556Srgrimes#include <sys/socket.h> 591556Srgrimes#include <netinet/in.h> 601556Srgrimes#include <netinet/in_systm.h> 611556Srgrimes#include <netinet/ip.h> 621556Srgrimes 631556Srgrimes#include <ctype.h> 641556Srgrimes#include <dirent.h> 651556Srgrimes#include <err.h> 661556Srgrimes#include <errno.h> 671556Srgrimes#include <fcntl.h> 6877462Simp#include <libutil.h> 6977462Simp#include <limits.h> 701556Srgrimes#include <netdb.h> 7196196Sdes#include <paths.h> 721556Srgrimes#include <pwd.h> 731556Srgrimes#include <signal.h> 74104549Stjr#include <stdint.h> 751556Srgrimes#include <stdio.h> 761556Srgrimes#include <stdlib.h> 771556Srgrimes#include <string.h> 781556Srgrimes#include <string.h> 791556Srgrimes#include <unistd.h> 801556Srgrimes 811556Srgrimes#include "extern.h" 821556Srgrimes 83101591Sume#define OPTIONS "46dfprt" 841556Srgrimes 851556Srgrimesstruct passwd *pwd; 861556Srgrimesu_short port; 871556Srgrimesuid_t userid; 881556Srgrimesint errs, rem; 891556Srgrimesint pflag, iamremote, iamrecursive, targetshouldbedirectory; 90101591Sumeint family = PF_UNSPEC; 911556Srgrimes 9234898Smarkmstatic int argc_copy; 93114583Smarkmstatic const char **argv_copy; 9434898Smarkm 95114583Smarkmstatic char period[] = "."; 96114583Smarkm 971556Srgrimes#define CMDNEEDS 64 981556Srgrimeschar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 991556Srgrimes 10090110Simpint response(void); 10190110Simpvoid rsource(char *, struct stat *); 10290110Simpvoid sink(int, char *[]); 10390110Simpvoid source(int, char *[]); 10490110Simpvoid tolocal(int, char *[]); 10590110Simpvoid toremote(char *, int, char *[]); 10690110Simpvoid usage(void); 1071556Srgrimes 1081556Srgrimesint 10990110Simpmain(int argc, char *argv[]) 1101556Srgrimes{ 1111556Srgrimes struct servent *sp; 11248560Sbde int ch, fflag, i, tflag; 113114583Smarkm char *targ; 1141556Srgrimes 11534898Smarkm /* 11634898Smarkm * Prepare for execing ourselves. 11734898Smarkm */ 11834898Smarkm argc_copy = argc + 1; 11934898Smarkm argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy)); 12034898Smarkm if (argv_copy == NULL) 12134898Smarkm err(1, "malloc"); 12234898Smarkm argv_copy[0] = argv[0]; 12334898Smarkm argv_copy[1] = "-K"; 12448560Sbde for (i = 1; i < argc; ++i) { 12534898Smarkm argv_copy[i + 1] = strdup(argv[i]); 12634898Smarkm if (argv_copy[i + 1] == NULL) 12734898Smarkm errx(1, "strdup: out of memory"); 12834898Smarkm } 12934898Smarkm argv_copy[argc + 1] = NULL; 13034898Smarkm 1311556Srgrimes fflag = tflag = 0; 13224348Simp while ((ch = getopt(argc, argv, OPTIONS)) != -1) 1331556Srgrimes switch(ch) { /* User-visible flags. */ 134101591Sume case '4': 135101591Sume family = PF_INET; 136101591Sume break; 137101591Sume 138101591Sume case '6': 139101591Sume family = PF_INET6; 140101591Sume break; 141101591Sume 1421556Srgrimes case 'p': 1431556Srgrimes pflag = 1; 1441556Srgrimes break; 1451556Srgrimes case 'r': 1461556Srgrimes iamrecursive = 1; 1471556Srgrimes break; 1481556Srgrimes /* Server options. */ 1491556Srgrimes case 'd': 1501556Srgrimes targetshouldbedirectory = 1; 1511556Srgrimes break; 1521556Srgrimes case 'f': /* "from" */ 1531556Srgrimes iamremote = 1; 1541556Srgrimes fflag = 1; 1551556Srgrimes break; 1561556Srgrimes case 't': /* "to" */ 1571556Srgrimes iamremote = 1; 1581556Srgrimes tflag = 1; 1591556Srgrimes break; 1601556Srgrimes case '?': 1611556Srgrimes default: 1621556Srgrimes usage(); 1631556Srgrimes } 1641556Srgrimes argc -= optind; 1651556Srgrimes argv += optind; 1661556Srgrimes 167114583Smarkm sp = getservbyname("shell", "tcp"); 1681556Srgrimes if (sp == NULL) 169114583Smarkm errx(1, "shell/tcp: unknown service"); 1701556Srgrimes port = sp->s_port; 1711556Srgrimes 1721556Srgrimes if ((pwd = getpwuid(userid = getuid())) == NULL) 1731556Srgrimes errx(1, "unknown user %d", (int)userid); 1741556Srgrimes 1751556Srgrimes rem = STDIN_FILENO; /* XXX */ 1761556Srgrimes 1771556Srgrimes if (fflag) { /* Follow "protocol", send data. */ 1781556Srgrimes (void)response(); 1791556Srgrimes (void)setuid(userid); 1801556Srgrimes source(argc, argv); 1811556Srgrimes exit(errs); 1821556Srgrimes } 1831556Srgrimes 1841556Srgrimes if (tflag) { /* Receive data. */ 1851556Srgrimes (void)setuid(userid); 1861556Srgrimes sink(argc, argv); 1871556Srgrimes exit(errs); 1881556Srgrimes } 1891556Srgrimes 1901556Srgrimes if (argc < 2) 1911556Srgrimes usage(); 1921556Srgrimes if (argc > 2) 1931556Srgrimes targetshouldbedirectory = 1; 1941556Srgrimes 1951556Srgrimes rem = -1; 1961556Srgrimes /* Command to be executed on remote system using "rsh". */ 1971556Srgrimes (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", 1981556Srgrimes iamrecursive ? " -r" : "", pflag ? " -p" : "", 1991556Srgrimes targetshouldbedirectory ? " -d" : ""); 2001556Srgrimes 2011556Srgrimes (void)signal(SIGPIPE, lostconn); 2021556Srgrimes 2037165Sjoerg if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 2041556Srgrimes toremote(targ, argc, argv); 2051556Srgrimes else { 2061556Srgrimes tolocal(argc, argv); /* Dest is local host. */ 2071556Srgrimes if (targetshouldbedirectory) 2081556Srgrimes verifydir(argv[argc - 1]); 2091556Srgrimes } 2101556Srgrimes exit(errs); 2111556Srgrimes} 2121556Srgrimes 2131556Srgrimesvoid 21490110Simptoremote(char *targ, int argc, char *argv[]) 2151556Srgrimes{ 2161556Srgrimes int i, len, tos; 2171556Srgrimes char *bp, *host, *src, *suser, *thost, *tuser; 2181556Srgrimes 2191556Srgrimes *targ++ = 0; 2201556Srgrimes if (*targ == 0) 221114583Smarkm targ = period; 2221556Srgrimes 2237165Sjoerg if ((thost = strchr(argv[argc - 1], '@'))) { 2241556Srgrimes /* user@host */ 2251556Srgrimes *thost++ = 0; 2261556Srgrimes tuser = argv[argc - 1]; 2271556Srgrimes if (*tuser == '\0') 2281556Srgrimes tuser = NULL; 2291556Srgrimes else if (!okname(tuser)) 2301556Srgrimes exit(1); 2311556Srgrimes } else { 2321556Srgrimes thost = argv[argc - 1]; 2331556Srgrimes tuser = NULL; 2341556Srgrimes } 2351556Srgrimes 2361556Srgrimes for (i = 0; i < argc - 1; i++) { 2371556Srgrimes src = colon(argv[i]); 2381556Srgrimes if (src) { /* remote to remote */ 2391556Srgrimes *src++ = 0; 2401556Srgrimes if (*src == 0) 241114583Smarkm src = period; 2421556Srgrimes host = strchr(argv[i], '@'); 2431556Srgrimes len = strlen(_PATH_RSH) + strlen(argv[i]) + 2441556Srgrimes strlen(src) + (tuser ? strlen(tuser) : 0) + 2451556Srgrimes strlen(thost) + strlen(targ) + CMDNEEDS + 20; 2461556Srgrimes if (!(bp = malloc(len))) 24799744Sdillon err(1, "malloc"); 2481556Srgrimes if (host) { 2491556Srgrimes *host++ = 0; 2501556Srgrimes suser = argv[i]; 2511556Srgrimes if (*suser == '\0') 2521556Srgrimes suser = pwd->pw_name; 25377491Spirzyk else if (!okname(suser)) { 25477491Spirzyk ++errs; 2551556Srgrimes continue; 25677491Spirzyk } 2571556Srgrimes (void)snprintf(bp, len, 2581556Srgrimes "%s %s -l %s -n %s %s '%s%s%s:%s'", 2591556Srgrimes _PATH_RSH, host, suser, cmd, src, 2601556Srgrimes tuser ? tuser : "", tuser ? "@" : "", 2611556Srgrimes thost, targ); 2621556Srgrimes } else 2631556Srgrimes (void)snprintf(bp, len, 2641556Srgrimes "exec %s %s -n %s %s '%s%s%s:%s'", 2651556Srgrimes _PATH_RSH, argv[i], cmd, src, 2661556Srgrimes tuser ? tuser : "", tuser ? "@" : "", 2671556Srgrimes thost, targ); 2681556Srgrimes (void)susystem(bp, userid); 2691556Srgrimes (void)free(bp); 2701556Srgrimes } else { /* local to remote */ 2711556Srgrimes if (rem == -1) { 2721556Srgrimes len = strlen(targ) + CMDNEEDS + 20; 2731556Srgrimes if (!(bp = malloc(len))) 27499744Sdillon err(1, "malloc"); 2751556Srgrimes (void)snprintf(bp, len, "%s -t %s", cmd, targ); 2761556Srgrimes host = thost; 277105269Smarkm rem = rcmd_af(&host, port, 278105269Smarkm pwd->pw_name, 279105269Smarkm tuser ? tuser : pwd->pw_name, 280105269Smarkm bp, 0, family); 2811556Srgrimes if (rem < 0) 2821556Srgrimes exit(1); 283101591Sume if (family == PF_INET) { 284101591Sume tos = IPTOS_THROUGHPUT; 285101591Sume if (setsockopt(rem, IPPROTO_IP, IP_TOS, 286101591Sume &tos, sizeof(int)) < 0) 287101591Sume warn("TOS (ignored)"); 288101591Sume } 2891556Srgrimes if (response() < 0) 2901556Srgrimes exit(1); 2911556Srgrimes (void)free(bp); 2921556Srgrimes (void)setuid(userid); 2931556Srgrimes } 2941556Srgrimes source(1, argv+i); 2951556Srgrimes } 2961556Srgrimes } 2971556Srgrimes} 2981556Srgrimes 2991556Srgrimesvoid 30090110Simptolocal(int argc, char *argv[]) 3011556Srgrimes{ 3021556Srgrimes int i, len, tos; 3031556Srgrimes char *bp, *host, *src, *suser; 3041556Srgrimes 3051556Srgrimes for (i = 0; i < argc - 1; i++) { 3061556Srgrimes if (!(src = colon(argv[i]))) { /* Local to local. */ 3071556Srgrimes len = strlen(_PATH_CP) + strlen(argv[i]) + 3081556Srgrimes strlen(argv[argc - 1]) + 20; 3091556Srgrimes if (!(bp = malloc(len))) 31099744Sdillon err(1, "malloc"); 3111556Srgrimes (void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 31231633Swosch iamrecursive ? " -PR" : "", pflag ? " -p" : "", 3131556Srgrimes argv[i], argv[argc - 1]); 3141556Srgrimes if (susystem(bp, userid)) 3151556Srgrimes ++errs; 3161556Srgrimes (void)free(bp); 3171556Srgrimes continue; 3181556Srgrimes } 3191556Srgrimes *src++ = 0; 3201556Srgrimes if (*src == 0) 321114583Smarkm src = period; 3221556Srgrimes if ((host = strchr(argv[i], '@')) == NULL) { 3231556Srgrimes host = argv[i]; 3241556Srgrimes suser = pwd->pw_name; 3251556Srgrimes } else { 3261556Srgrimes *host++ = 0; 3271556Srgrimes suser = argv[i]; 3281556Srgrimes if (*suser == '\0') 3291556Srgrimes suser = pwd->pw_name; 33077491Spirzyk else if (!okname(suser)) { 33177491Spirzyk ++errs; 3321556Srgrimes continue; 33377491Spirzyk } 3341556Srgrimes } 3351556Srgrimes len = strlen(src) + CMDNEEDS + 20; 3361556Srgrimes if ((bp = malloc(len)) == NULL) 33799744Sdillon err(1, "malloc"); 3381556Srgrimes (void)snprintf(bp, len, "%s -f %s", cmd, src); 339105269Smarkm rem = rcmd_af(&host, port, pwd->pw_name, suser, bp, 0, 340101591Sume family); 3411556Srgrimes (void)free(bp); 3421556Srgrimes if (rem < 0) { 3431556Srgrimes ++errs; 3441556Srgrimes continue; 3451556Srgrimes } 3461556Srgrimes (void)seteuid(userid); 347101591Sume if (family == PF_INET) { 348101591Sume tos = IPTOS_THROUGHPUT; 349101591Sume if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, 350101591Sume sizeof(int)) < 0) 351101591Sume warn("TOS (ignored)"); 352101591Sume } 3531556Srgrimes sink(1, argv + argc - 1); 3541556Srgrimes (void)seteuid(0); 3551556Srgrimes (void)close(rem); 3561556Srgrimes rem = -1; 3571556Srgrimes } 3581556Srgrimes} 3591556Srgrimes 3601556Srgrimesvoid 36190110Simpsource(int argc, char *argv[]) 3621556Srgrimes{ 3631556Srgrimes struct stat stb; 3641556Srgrimes static BUF buffer; 3651556Srgrimes BUF *bp; 3661556Srgrimes off_t i; 3671556Srgrimes int amt, fd, haderr, indx, result; 3681556Srgrimes char *last, *name, buf[BUFSIZ]; 3691556Srgrimes 3701556Srgrimes for (indx = 0; indx < argc; ++indx) { 37196196Sdes name = argv[indx]; 3721556Srgrimes if ((fd = open(name, O_RDONLY, 0)) < 0) 3731556Srgrimes goto syserr; 3741556Srgrimes if (fstat(fd, &stb)) { 3751556Srgrimessyserr: run_err("%s: %s", name, strerror(errno)); 3761556Srgrimes goto next; 3771556Srgrimes } 3781556Srgrimes switch (stb.st_mode & S_IFMT) { 3791556Srgrimes case S_IFREG: 3801556Srgrimes break; 3811556Srgrimes case S_IFDIR: 3821556Srgrimes if (iamrecursive) { 3831556Srgrimes rsource(name, &stb); 3841556Srgrimes goto next; 3851556Srgrimes } 3861556Srgrimes /* FALLTHROUGH */ 3871556Srgrimes default: 3881556Srgrimes run_err("%s: not a regular file", name); 3891556Srgrimes goto next; 3901556Srgrimes } 3911556Srgrimes if ((last = strrchr(name, '/')) == NULL) 3921556Srgrimes last = name; 3931556Srgrimes else 3941556Srgrimes ++last; 3951556Srgrimes if (pflag) { 3961556Srgrimes /* 3971556Srgrimes * Make it compatible with possible future 3981556Srgrimes * versions expecting microseconds. 3991556Srgrimes */ 4001556Srgrimes (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 40138018Sbde (long)stb.st_mtimespec.tv_sec, 40238018Sbde (long)stb.st_atimespec.tv_sec); 4031556Srgrimes (void)write(rem, buf, strlen(buf)); 4041556Srgrimes if (response() < 0) 4051556Srgrimes goto next; 4061556Srgrimes } 4071556Srgrimes#define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) 408104549Stjr (void)snprintf(buf, sizeof(buf), "C%04o %jd %s\n", 409104549Stjr stb.st_mode & MODEMASK, (intmax_t)stb.st_size, last); 4101556Srgrimes (void)write(rem, buf, strlen(buf)); 4111556Srgrimes if (response() < 0) 4121556Srgrimes goto next; 4131556Srgrimes if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 4141556Srgrimesnext: (void)close(fd); 4151556Srgrimes continue; 4161556Srgrimes } 4171556Srgrimes 4181556Srgrimes /* Keep writing after an error so that we stay sync'd up. */ 4191556Srgrimes for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 4201556Srgrimes amt = bp->cnt; 4211556Srgrimes if (i + amt > stb.st_size) 4221556Srgrimes amt = stb.st_size - i; 4231556Srgrimes if (!haderr) { 4241556Srgrimes result = read(fd, bp->buf, amt); 4251556Srgrimes if (result != amt) 4261556Srgrimes haderr = result >= 0 ? EIO : errno; 4271556Srgrimes } 4281556Srgrimes if (haderr) 4291556Srgrimes (void)write(rem, bp->buf, amt); 4301556Srgrimes else { 4311556Srgrimes result = write(rem, bp->buf, amt); 4321556Srgrimes if (result != amt) 4331556Srgrimes haderr = result >= 0 ? EIO : errno; 4341556Srgrimes } 4351556Srgrimes } 4361556Srgrimes if (close(fd) && !haderr) 4371556Srgrimes haderr = errno; 4381556Srgrimes if (!haderr) 4391556Srgrimes (void)write(rem, "", 1); 4401556Srgrimes else 4411556Srgrimes run_err("%s: %s", name, strerror(haderr)); 4421556Srgrimes (void)response(); 4431556Srgrimes } 4441556Srgrimes} 4451556Srgrimes 4461556Srgrimesvoid 44790110Simprsource(char *name, struct stat *statp) 4481556Srgrimes{ 4491556Srgrimes DIR *dirp; 4501556Srgrimes struct dirent *dp; 45177462Simp char *last, *vect[1], path[PATH_MAX]; 4521556Srgrimes 4531556Srgrimes if (!(dirp = opendir(name))) { 4541556Srgrimes run_err("%s: %s", name, strerror(errno)); 4551556Srgrimes return; 4561556Srgrimes } 4571556Srgrimes last = strrchr(name, '/'); 4581556Srgrimes if (last == 0) 4591556Srgrimes last = name; 4601556Srgrimes else 4611556Srgrimes last++; 4621556Srgrimes if (pflag) { 4631556Srgrimes (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 46438018Sbde (long)statp->st_mtimespec.tv_sec, 46538018Sbde (long)statp->st_atimespec.tv_sec); 4661556Srgrimes (void)write(rem, path, strlen(path)); 4671556Srgrimes if (response() < 0) { 4681556Srgrimes closedir(dirp); 4691556Srgrimes return; 4701556Srgrimes } 4711556Srgrimes } 4721556Srgrimes (void)snprintf(path, sizeof(path), 4731556Srgrimes "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 4741556Srgrimes (void)write(rem, path, strlen(path)); 4751556Srgrimes if (response() < 0) { 4761556Srgrimes closedir(dirp); 4771556Srgrimes return; 4781556Srgrimes } 4797165Sjoerg while ((dp = readdir(dirp))) { 4801556Srgrimes if (dp->d_ino == 0) 4811556Srgrimes continue; 4821556Srgrimes if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 4831556Srgrimes continue; 48477462Simp if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path)) { 4851556Srgrimes run_err("%s/%s: name too long", name, dp->d_name); 4861556Srgrimes continue; 4871556Srgrimes } 4881556Srgrimes (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 4891556Srgrimes vect[0] = path; 4901556Srgrimes source(1, vect); 4911556Srgrimes } 4921556Srgrimes (void)closedir(dirp); 4931556Srgrimes (void)write(rem, "E\n", 2); 4941556Srgrimes (void)response(); 4951556Srgrimes} 4961556Srgrimes 4971556Srgrimesvoid 49890110Simpsink(int argc, char *argv[]) 4991556Srgrimes{ 5001556Srgrimes static BUF buffer; 5011556Srgrimes struct stat stb; 5021556Srgrimes struct timeval tv[2]; 5031556Srgrimes enum { YES, NO, DISPLAYED } wrerr; 5041556Srgrimes BUF *bp; 50546057Sdt off_t i, j, size; 506114509Sobrien int amt, exists, first, mask, mode, ofd, omode; 507114509Sobrien size_t count; 50846057Sdt int setimes, targisdir, wrerrno = 0; 509114583Smarkm char ch, *cp, *np, *targ, *vect[1], buf[BUFSIZ], path[PATH_MAX]; 510114583Smarkm const char *why; 5111556Srgrimes 5121556Srgrimes#define atime tv[0] 5131556Srgrimes#define mtime tv[1] 5141556Srgrimes#define SCREWUP(str) { why = str; goto screwup; } 5151556Srgrimes 5161556Srgrimes setimes = targisdir = 0; 5171556Srgrimes mask = umask(0); 5181556Srgrimes if (!pflag) 5191556Srgrimes (void)umask(mask); 5201556Srgrimes if (argc != 1) { 5211556Srgrimes run_err("ambiguous target"); 5221556Srgrimes exit(1); 5231556Srgrimes } 5241556Srgrimes targ = *argv; 5251556Srgrimes if (targetshouldbedirectory) 5261556Srgrimes verifydir(targ); 5271556Srgrimes (void)write(rem, "", 1); 5281556Srgrimes if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 5291556Srgrimes targisdir = 1; 5301556Srgrimes for (first = 1;; first = 0) { 5311556Srgrimes cp = buf; 5321556Srgrimes if (read(rem, cp, 1) <= 0) 5331556Srgrimes return; 5341556Srgrimes if (*cp++ == '\n') 5351556Srgrimes SCREWUP("unexpected <newline>"); 5361556Srgrimes do { 5371556Srgrimes if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 5381556Srgrimes SCREWUP("lost connection"); 5391556Srgrimes *cp++ = ch; 5401556Srgrimes } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 5411556Srgrimes *cp = 0; 5421556Srgrimes 5431556Srgrimes if (buf[0] == '\01' || buf[0] == '\02') { 5441556Srgrimes if (iamremote == 0) 5451556Srgrimes (void)write(STDERR_FILENO, 5461556Srgrimes buf + 1, strlen(buf + 1)); 5471556Srgrimes if (buf[0] == '\02') 5481556Srgrimes exit(1); 5491556Srgrimes ++errs; 5501556Srgrimes continue; 5511556Srgrimes } 5521556Srgrimes if (buf[0] == 'E') { 5531556Srgrimes (void)write(rem, "", 1); 5541556Srgrimes return; 5551556Srgrimes } 5561556Srgrimes 5571556Srgrimes if (ch == '\n') 5581556Srgrimes *--cp = 0; 5591556Srgrimes 5601556Srgrimes cp = buf; 5611556Srgrimes if (*cp == 'T') { 5621556Srgrimes setimes++; 5631556Srgrimes cp++; 56413978Spst mtime.tv_sec = strtol(cp, &cp, 10); 56513978Spst if (!cp || *cp++ != ' ') 5661556Srgrimes SCREWUP("mtime.sec not delimited"); 56713978Spst mtime.tv_usec = strtol(cp, &cp, 10); 56813978Spst if (!cp || *cp++ != ' ') 5691556Srgrimes SCREWUP("mtime.usec not delimited"); 57013978Spst atime.tv_sec = strtol(cp, &cp, 10); 57113978Spst if (!cp || *cp++ != ' ') 5721556Srgrimes SCREWUP("atime.sec not delimited"); 57313978Spst atime.tv_usec = strtol(cp, &cp, 10); 57413978Spst if (!cp || *cp++ != '\0') 5751556Srgrimes SCREWUP("atime.usec not delimited"); 5761556Srgrimes (void)write(rem, "", 1); 5771556Srgrimes continue; 5781556Srgrimes } 5791556Srgrimes if (*cp != 'C' && *cp != 'D') { 5801556Srgrimes /* 5811556Srgrimes * Check for the case "rcp remote:foo\* local:bar". 5821556Srgrimes * In this case, the line "No match." can be returned 5831556Srgrimes * by the shell before the rcp command on the remote is 5841556Srgrimes * executed so the ^Aerror_message convention isn't 5851556Srgrimes * followed. 5861556Srgrimes */ 5871556Srgrimes if (first) { 5881556Srgrimes run_err("%s", cp); 5891556Srgrimes exit(1); 5901556Srgrimes } 5911556Srgrimes SCREWUP("expected control record"); 5921556Srgrimes } 5931556Srgrimes mode = 0; 5941556Srgrimes for (++cp; cp < buf + 5; cp++) { 5951556Srgrimes if (*cp < '0' || *cp > '7') 5961556Srgrimes SCREWUP("bad mode"); 5971556Srgrimes mode = (mode << 3) | (*cp - '0'); 5981556Srgrimes } 5991556Srgrimes if (*cp++ != ' ') 6001556Srgrimes SCREWUP("mode not delimited"); 6011556Srgrimes 6021556Srgrimes for (size = 0; isdigit(*cp);) 6031556Srgrimes size = size * 10 + (*cp++ - '0'); 6041556Srgrimes if (*cp++ != ' ') 6051556Srgrimes SCREWUP("size not delimited"); 6061556Srgrimes if (targisdir) { 607104559Scharnier if (strlen(targ) + (*targ ? 1 : 0) + strlen(cp) 608104559Scharnier >= sizeof(path)) { 609104559Scharnier run_err("%s%s%s: name too long", targ, 610104559Scharnier *targ ? "/" : "", cp); 611104559Scharnier exit(1); 6121556Srgrimes } 613104559Scharnier (void)snprintf(path, sizeof(path), "%s%s%s", targ, 6141556Srgrimes *targ ? "/" : "", cp); 615104559Scharnier np = path; 6161556Srgrimes } else 6171556Srgrimes np = targ; 6181556Srgrimes exists = stat(np, &stb) == 0; 6191556Srgrimes if (buf[0] == 'D') { 6201556Srgrimes int mod_flag = pflag; 6211556Srgrimes if (exists) { 6221556Srgrimes if (!S_ISDIR(stb.st_mode)) { 6231556Srgrimes errno = ENOTDIR; 6241556Srgrimes goto bad; 6251556Srgrimes } 6261556Srgrimes if (pflag) 6271556Srgrimes (void)chmod(np, mode); 6281556Srgrimes } else { 6291556Srgrimes /* Handle copying from a read-only directory */ 6301556Srgrimes mod_flag = 1; 6311556Srgrimes if (mkdir(np, mode | S_IRWXU) < 0) 6321556Srgrimes goto bad; 6331556Srgrimes } 6341556Srgrimes vect[0] = np; 6351556Srgrimes sink(1, vect); 6361556Srgrimes if (setimes) { 6371556Srgrimes setimes = 0; 6381556Srgrimes if (utimes(np, tv) < 0) 6391556Srgrimes run_err("%s: set times: %s", 6401556Srgrimes np, strerror(errno)); 6411556Srgrimes } 6421556Srgrimes if (mod_flag) 6431556Srgrimes (void)chmod(np, mode); 6441556Srgrimes continue; 6451556Srgrimes } 6461556Srgrimes omode = mode; 6471556Srgrimes mode |= S_IWRITE; 6481556Srgrimes if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 6491556Srgrimesbad: run_err("%s: %s", np, strerror(errno)); 6501556Srgrimes continue; 6511556Srgrimes } 6521556Srgrimes (void)write(rem, "", 1); 6531556Srgrimes if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 6541556Srgrimes (void)close(ofd); 6551556Srgrimes continue; 6561556Srgrimes } 6571556Srgrimes cp = bp->buf; 6581556Srgrimes wrerr = NO; 6591556Srgrimes for (count = i = 0; i < size; i += BUFSIZ) { 6601556Srgrimes amt = BUFSIZ; 6611556Srgrimes if (i + amt > size) 6621556Srgrimes amt = size - i; 6631556Srgrimes count += amt; 6641556Srgrimes do { 6651556Srgrimes j = read(rem, cp, amt); 6661556Srgrimes if (j <= 0) { 6671556Srgrimes run_err("%s", j ? strerror(errno) : 6681556Srgrimes "dropped connection"); 6691556Srgrimes exit(1); 6701556Srgrimes } 6711556Srgrimes amt -= j; 6721556Srgrimes cp += j; 6731556Srgrimes } while (amt > 0); 6741556Srgrimes if (count == bp->cnt) { 6751556Srgrimes /* Keep reading so we stay sync'd up. */ 6761556Srgrimes if (wrerr == NO) { 6771556Srgrimes j = write(ofd, bp->buf, count); 678114583Smarkm if (j != (off_t)count) { 6791556Srgrimes wrerr = YES; 6808855Srgrimes wrerrno = j >= 0 ? EIO : errno; 6811556Srgrimes } 6821556Srgrimes } 6831556Srgrimes count = 0; 6841556Srgrimes cp = bp->buf; 6851556Srgrimes } 6861556Srgrimes } 6871556Srgrimes if (count != 0 && wrerr == NO && 688114583Smarkm (j = write(ofd, bp->buf, count)) != (off_t)count) { 6891556Srgrimes wrerr = YES; 6908855Srgrimes wrerrno = j >= 0 ? EIO : errno; 6911556Srgrimes } 6921556Srgrimes if (ftruncate(ofd, size)) { 6931556Srgrimes run_err("%s: truncate: %s", np, strerror(errno)); 6941556Srgrimes wrerr = DISPLAYED; 6951556Srgrimes } 6961556Srgrimes if (pflag) { 6971556Srgrimes if (exists || omode != mode) 6981556Srgrimes if (fchmod(ofd, omode)) 6991556Srgrimes run_err("%s: set mode: %s", 7001556Srgrimes np, strerror(errno)); 7011556Srgrimes } else { 7021556Srgrimes if (!exists && omode != mode) 7031556Srgrimes if (fchmod(ofd, omode & ~mask)) 7041556Srgrimes run_err("%s: set mode: %s", 7051556Srgrimes np, strerror(errno)); 7061556Srgrimes } 7071556Srgrimes (void)close(ofd); 7081556Srgrimes (void)response(); 7091556Srgrimes if (setimes && wrerr == NO) { 7101556Srgrimes setimes = 0; 7111556Srgrimes if (utimes(np, tv) < 0) { 7121556Srgrimes run_err("%s: set times: %s", 7131556Srgrimes np, strerror(errno)); 7141556Srgrimes wrerr = DISPLAYED; 7151556Srgrimes } 7161556Srgrimes } 7171556Srgrimes switch(wrerr) { 7181556Srgrimes case YES: 7191556Srgrimes run_err("%s: %s", np, strerror(wrerrno)); 7201556Srgrimes break; 7211556Srgrimes case NO: 7221556Srgrimes (void)write(rem, "", 1); 7231556Srgrimes break; 7241556Srgrimes case DISPLAYED: 7251556Srgrimes break; 7261556Srgrimes } 7271556Srgrimes } 7281556Srgrimesscrewup: 7291556Srgrimes run_err("protocol error: %s", why); 7301556Srgrimes exit(1); 7311556Srgrimes} 7321556Srgrimes 7331556Srgrimesint 73490110Simpresponse(void) 7351556Srgrimes{ 7361556Srgrimes char ch, *cp, resp, rbuf[BUFSIZ]; 7371556Srgrimes 7381556Srgrimes if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) 7391556Srgrimes lostconn(0); 7401556Srgrimes 7411556Srgrimes cp = rbuf; 7421556Srgrimes switch(resp) { 7431556Srgrimes case 0: /* ok */ 7441556Srgrimes return (0); 7451556Srgrimes default: 7461556Srgrimes *cp++ = resp; 7471556Srgrimes /* FALLTHROUGH */ 7481556Srgrimes case 1: /* error, followed by error msg */ 7491556Srgrimes case 2: /* fatal error, "" */ 7501556Srgrimes do { 7511556Srgrimes if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 7521556Srgrimes lostconn(0); 7531556Srgrimes *cp++ = ch; 7541556Srgrimes } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 7551556Srgrimes 7561556Srgrimes if (!iamremote) 7571556Srgrimes (void)write(STDERR_FILENO, rbuf, cp - rbuf); 7581556Srgrimes ++errs; 7591556Srgrimes if (resp == 1) 7601556Srgrimes return (-1); 7611556Srgrimes exit(1); 7621556Srgrimes } 7631556Srgrimes /* NOTREACHED */ 7641556Srgrimes} 7651556Srgrimes 7661556Srgrimesvoid 76790110Simpusage(void) 7681556Srgrimes{ 76926466Scharnier (void)fprintf(stderr, "%s\n%s\n", 770101591Sume "usage: rcp [-46p] f1 f2", 771101591Sume " rcp [-46pr] f1 ... fn directory"); 7721556Srgrimes exit(1); 7731556Srgrimes} 7741556Srgrimes 7751556Srgrimes#include <stdarg.h> 7761556Srgrimes 7771556Srgrimesvoid 7781556Srgrimesrun_err(const char *fmt, ...) 7791556Srgrimes{ 7801556Srgrimes static FILE *fp; 7811556Srgrimes va_list ap; 7821556Srgrimes 7831556Srgrimes ++errs; 7841556Srgrimes if (fp == NULL && !(fp = fdopen(rem, "w"))) 7851556Srgrimes return; 7861556Srgrimes (void)fprintf(fp, "%c", 0x01); 7871556Srgrimes (void)fprintf(fp, "rcp: "); 788104563Stjr va_start(ap, fmt); 7891556Srgrimes (void)vfprintf(fp, fmt, ap); 790104563Stjr va_end(ap); 7911556Srgrimes (void)fprintf(fp, "\n"); 7921556Srgrimes (void)fflush(fp); 7931556Srgrimes 794104563Stjr if (!iamremote) { 795104563Stjr va_start(ap, fmt); 7961556Srgrimes vwarnx(fmt, ap); 797104563Stjr va_end(ap); 798104563Stjr } 7991556Srgrimes 8001556Srgrimes va_end(ap); 8011556Srgrimes} 802