rcp.c revision 90926
1283424Sdchagin/* 2283424Sdchagin * Copyright (c) 1983, 1990, 1992, 1993 3283424Sdchagin * The Regents of the University of California. All rights reserved. 4283424Sdchagin * 5283424Sdchagin * Redistribution and use in source and binary forms, with or without 6283424Sdchagin * modification, are permitted provided that the following conditions 7283424Sdchagin * are met: 8283424Sdchagin * 1. Redistributions of source code must retain the above copyright 9283424Sdchagin * notice, this list of conditions and the following disclaimer. 10283424Sdchagin * 2. Redistributions in binary form must reproduce the above copyright 11283424Sdchagin * notice, this list of conditions and the following disclaimer in the 12283424Sdchagin * documentation and/or other materials provided with the distribution. 13283424Sdchagin * 3. All advertising materials mentioning features or use of this software 14283424Sdchagin * must display the following acknowledgement: 15283424Sdchagin * This product includes software developed by the University of 16283424Sdchagin * California, Berkeley and its contributors. 17283424Sdchagin * 4. Neither the name of the University nor the names of its contributors 18283424Sdchagin * may be used to endorse or promote products derived from this software 19283424Sdchagin * without specific prior written permission. 20283424Sdchagin * 21283424Sdchagin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22283424Sdchagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23283424Sdchagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24283424Sdchagin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25283424Sdchagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26283424Sdchagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27283424Sdchagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28283424Sdchagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29283424Sdchagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30283424Sdchagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31283424Sdchagin * SUCH DAMAGE. 32283424Sdchagin */ 33283424Sdchagin 34283424Sdchagin#include "rcp_locl.h" 35283424Sdchagin#include <getarg.h> 36283424Sdchagin 37283424Sdchagin#define RSH_PROGRAM "rsh" 38283424Sdchagin 39283424Sdchaginstruct passwd *pwd; 40283424Sdchaginuid_t userid; 41283424Sdchaginint errs, remin, remout; 42283424Sdchaginint pflag, iamremote, iamrecursive, targetshouldbedirectory; 43283424Sdchaginint doencrypt, noencrypt; 44283424Sdchaginint usebroken, usekrb5, forwardtkt; 45283424Sdchaginchar *port; 46283424Sdchagin 47283424Sdchagin#define CMDNEEDS 64 48283424Sdchaginchar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 49283424Sdchagin 50283424Sdchaginint response (void); 51283424Sdchaginvoid rsource (char *, struct stat *); 52283424Sdchaginvoid sink (int, char *[]); 53283424Sdchaginvoid source (int, char *[]); 54283424Sdchaginvoid tolocal (int, char *[]); 55283424Sdchaginvoid toremote (char *, int, char *[]); 56283424Sdchagin 57283424Sdchaginint do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 58283424Sdchagin 59283424Sdchaginstatic int fflag, tflag; 60283424Sdchagin 61283424Sdchaginstatic int version_flag, help_flag; 62283424Sdchagin 63283424Sdchaginstruct getargs args[] = { 64283424Sdchagin { NULL, '5', arg_flag, &usekrb5, "use Kerberos 5 authentication" }, 65283424Sdchagin { NULL, 'F', arg_flag, &forwardtkt, "forward credentials" }, 66283424Sdchagin { NULL, 'K', arg_flag, &usebroken, "use BSD authentication" }, 67283424Sdchagin { NULL, 'P', arg_string, &port, "non-default port", "port" }, 68283424Sdchagin { NULL, 'p', arg_flag, &pflag, "preserve file permissions" }, 69283424Sdchagin { NULL, 'r', arg_flag, &iamrecursive, "recursive mode" }, 70283424Sdchagin { NULL, 'x', arg_flag, &doencrypt, "use encryption" }, 71283424Sdchagin { NULL, 'z', arg_flag, &noencrypt, "don't encrypt" }, 72283424Sdchagin { NULL, 'd', arg_flag, &targetshouldbedirectory }, 73283424Sdchagin { NULL, 'f', arg_flag, &fflag }, 74283424Sdchagin { NULL, 't', arg_flag, &tflag }, 75283424Sdchagin { "version", 0, arg_flag, &version_flag }, 76283424Sdchagin { "help", 0, arg_flag, &help_flag } 77283424Sdchagin}; 78283424Sdchagin 79283424Sdchaginstatic void 80283424Sdchaginusage (int ret) 81283424Sdchagin{ 82283424Sdchagin arg_printusage (args, 83283424Sdchagin sizeof(args) / sizeof(args[0]), 84283424Sdchagin NULL, 85283424Sdchagin "file1 file2|file... directory"); 86283424Sdchagin exit (ret); 87283424Sdchagin} 88283424Sdchagin 89283424Sdchaginint 90283424Sdchaginmain(int argc, char **argv) 91283424Sdchagin{ 92283424Sdchagin char *targ; 93283424Sdchagin int optind = 0; 94283424Sdchagin 95283424Sdchagin setprogname(argv[0]); 96283424Sdchagin if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 97283424Sdchagin &optind)) 98283424Sdchagin usage (1); 99283424Sdchagin if(help_flag) 100283424Sdchagin usage(0); 101283424Sdchagin if (version_flag) { 102283424Sdchagin print_version (NULL); 103283424Sdchagin return 0; 104283424Sdchagin } 105283424Sdchagin 106283424Sdchagin iamremote = (fflag || tflag); 107283424Sdchagin 108283424Sdchagin argc -= optind; 109283424Sdchagin argv += optind; 110283424Sdchagin 111283424Sdchagin if ((pwd = getpwuid(userid = getuid())) == NULL) 112283424Sdchagin errx(1, "unknown user %d", (int)userid); 113283424Sdchagin 114283424Sdchagin remin = STDIN_FILENO; /* XXX */ 115283424Sdchagin remout = STDOUT_FILENO; 116283424Sdchagin 117283424Sdchagin if (fflag) { /* Follow "protocol", send data. */ 118283424Sdchagin response(); 119283424Sdchagin setuid(userid); 120283424Sdchagin source(argc, argv); 121283424Sdchagin exit(errs); 122283424Sdchagin } 123283424Sdchagin 124283424Sdchagin if (tflag) { /* Receive data. */ 125283424Sdchagin setuid(userid); 126283424Sdchagin sink(argc, argv); 127283424Sdchagin exit(errs); 128283424Sdchagin } 129283424Sdchagin 130283424Sdchagin if (argc < 2) 131283424Sdchagin usage(1); 132283424Sdchagin if (argc > 2) 133283424Sdchagin targetshouldbedirectory = 1; 134283424Sdchagin 135283424Sdchagin remin = remout = -1; 136283424Sdchagin /* Command to be executed on remote system using "rsh". */ 137283424Sdchagin snprintf(cmd, sizeof(cmd), 138283424Sdchagin "rcp%s%s%s", iamrecursive ? " -r" : "", 139283424Sdchagin pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 140283424Sdchagin 141283424Sdchagin signal(SIGPIPE, lostconn); 142283424Sdchagin 143283424Sdchagin if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 144283424Sdchagin toremote(targ, argc, argv); 145283424Sdchagin else { 146283424Sdchagin tolocal(argc, argv); /* Dest is local host. */ 147283424Sdchagin if (targetshouldbedirectory) 148283424Sdchagin verifydir(argv[argc - 1]); 149283424Sdchagin } 150283424Sdchagin exit(errs); 151283424Sdchagin} 152283424Sdchagin 153283424Sdchaginvoid 154283424Sdchagintoremote(char *targ, int argc, char **argv) 155283424Sdchagin{ 156283424Sdchagin int i; 157283424Sdchagin char *bp, *host, *src, *suser, *thost, *tuser; 158283424Sdchagin 159283424Sdchagin *targ++ = 0; 160283424Sdchagin if (*targ == 0) 161283424Sdchagin targ = "."; 162283424Sdchagin 163283424Sdchagin if ((thost = strchr(argv[argc - 1], '@'))) { 164283424Sdchagin /* user@host */ 165283424Sdchagin *thost++ = 0; 166283424Sdchagin tuser = argv[argc - 1]; 167283424Sdchagin if (*tuser == '\0') 168283424Sdchagin tuser = NULL; 169283424Sdchagin else if (!okname(tuser)) 170283424Sdchagin exit(1); 171283424Sdchagin } else { 172283424Sdchagin thost = argv[argc - 1]; 173283424Sdchagin tuser = NULL; 174283424Sdchagin } 175283424Sdchagin 176283424Sdchagin for (i = 0; i < argc - 1; i++) { 177283424Sdchagin src = colon(argv[i]); 178283424Sdchagin if (src) { /* remote to remote */ 179283424Sdchagin *src++ = 0; 180283424Sdchagin if (*src == 0) 181283424Sdchagin src = "."; 182283424Sdchagin host = strchr(argv[i], '@'); 183283424Sdchagin if (host) { 184283424Sdchagin *host++ = '\0'; 185283424Sdchagin suser = argv[i]; 186283424Sdchagin if (*suser == '\0') 187283424Sdchagin suser = pwd->pw_name; 188283424Sdchagin else if (!okname(suser)) 189283424Sdchagin continue; 190283424Sdchagin asprintf(&bp, 191283424Sdchagin "%s %s -l %s -n %s %s '%s%s%s:%s'", 192283424Sdchagin _PATH_RSH, host, suser, cmd, src, 193283424Sdchagin tuser ? tuser : "", tuser ? "@" : "", 194283424Sdchagin thost, targ); 195283424Sdchagin } else { 196283424Sdchagin asprintf(&bp, 197283424Sdchagin "exec %s %s -n %s %s '%s%s%s:%s'", 198283424Sdchagin _PATH_RSH, argv[i], cmd, src, 199283424Sdchagin tuser ? tuser : "", tuser ? "@" : "", 200283424Sdchagin thost, targ); 201283424Sdchagin } 202283424Sdchagin if (bp == NULL) 203283424Sdchagin err (1, "malloc"); 204283424Sdchagin susystem(bp, userid); 205283424Sdchagin free(bp); 206283424Sdchagin } else { /* local to remote */ 207283424Sdchagin if (remin == -1) { 208283424Sdchagin asprintf(&bp, "%s -t %s", cmd, targ); 209283424Sdchagin if (bp == NULL) 210283424Sdchagin err (1, "malloc"); 211283424Sdchagin host = thost; 212283424Sdchagin 213283424Sdchagin if (do_cmd(host, tuser, bp, &remin, &remout) < 0) 214283424Sdchagin exit(1); 215283424Sdchagin 216283424Sdchagin if (response() < 0) 217283424Sdchagin exit(1); 218283424Sdchagin free(bp); 219283424Sdchagin setuid(userid); 220283424Sdchagin } 221283424Sdchagin source(1, argv+i); 222283424Sdchagin } 223283424Sdchagin } 224283424Sdchagin} 225283424Sdchagin 226283424Sdchaginvoid 227283424Sdchagintolocal(int argc, char **argv) 228283424Sdchagin{ 229283424Sdchagin int i; 230283424Sdchagin char *bp, *host, *src, *suser; 231283424Sdchagin 232283424Sdchagin for (i = 0; i < argc - 1; i++) { 233283424Sdchagin if (!(src = colon(argv[i]))) { /* Local to local. */ 234283424Sdchagin asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP, 235283424Sdchagin iamrecursive ? " -PR" : "", pflag ? " -p" : "", 236283424Sdchagin argv[i], argv[argc - 1]); 237283424Sdchagin if (bp == NULL) 238283424Sdchagin err (1, "malloc"); 239283424Sdchagin if (susystem(bp, userid)) 240283424Sdchagin ++errs; 241283424Sdchagin free(bp); 242283424Sdchagin continue; 243283424Sdchagin } 244283424Sdchagin *src++ = 0; 245283424Sdchagin if (*src == 0) 246283424Sdchagin src = "."; 247283424Sdchagin if ((host = strchr(argv[i], '@')) == NULL) { 248283424Sdchagin host = argv[i]; 249283424Sdchagin suser = pwd->pw_name; 250283424Sdchagin } else { 251283424Sdchagin *host++ = 0; 252283424Sdchagin suser = argv[i]; 253283424Sdchagin if (*suser == '\0') 254283424Sdchagin suser = pwd->pw_name; 255283424Sdchagin else if (!okname(suser)) 256283424Sdchagin continue; 257283424Sdchagin } 258283424Sdchagin asprintf(&bp, "%s -f %s", cmd, src); 259283424Sdchagin if (bp == NULL) 260283424Sdchagin err (1, "malloc"); 261283424Sdchagin if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 262283424Sdchagin free(bp); 263283424Sdchagin ++errs; 264283424Sdchagin continue; 265283424Sdchagin } 266283424Sdchagin free(bp); 267283424Sdchagin sink(1, argv + argc - 1); 268283424Sdchagin seteuid(0); 269283424Sdchagin close(remin); 270283424Sdchagin remin = remout = -1; 271283424Sdchagin } 272283424Sdchagin} 273283424Sdchagin 274283424Sdchaginvoid 275283424Sdchaginsource(int argc, char **argv) 276283424Sdchagin{ 277283424Sdchagin struct stat stb; 278283424Sdchagin static BUF buffer; 279283424Sdchagin BUF *bp; 280283424Sdchagin off_t i; 281283424Sdchagin int amt, fd, haderr, indx, result; 282283424Sdchagin char *last, *name, buf[BUFSIZ]; 283283424Sdchagin 284283424Sdchagin for (indx = 0; indx < argc; ++indx) { 285283424Sdchagin name = argv[indx]; 286283424Sdchagin if ((fd = open(name, O_RDONLY, 0)) < 0) 287283424Sdchagin goto syserr; 288283424Sdchagin if (fstat(fd, &stb)) { 289283424Sdchaginsyserr: run_err("%s: %s", name, strerror(errno)); 290283424Sdchagin goto next; 291283424Sdchagin } 292283424Sdchagin switch (stb.st_mode & S_IFMT) { 293283424Sdchagin case S_IFREG: 294283424Sdchagin break; 295283424Sdchagin case S_IFDIR: 296283424Sdchagin if (iamrecursive) { 297283424Sdchagin rsource(name, &stb); 298283424Sdchagin goto next; 299283424Sdchagin } 300283424Sdchagin /* FALLTHROUGH */ 301283424Sdchagin default: 302283424Sdchagin run_err("%s: not a regular file", name); 303283424Sdchagin goto next; 304283424Sdchagin } 305283424Sdchagin if ((last = strrchr(name, '/')) == NULL) 306283424Sdchagin last = name; 307283424Sdchagin else 308283424Sdchagin ++last; 309283424Sdchagin if (pflag) { 310283424Sdchagin /* 311283424Sdchagin * Make it compatible with possible future 312283424Sdchagin * versions expecting microseconds. 313283424Sdchagin */ 314283424Sdchagin snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 315283424Sdchagin (long)stb.st_mtime, 316283424Sdchagin (long)stb.st_atime); 317283424Sdchagin write(remout, buf, strlen(buf)); 318283424Sdchagin if (response() < 0) 319283424Sdchagin goto next; 320283424Sdchagin } 321283424Sdchagin#define MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 322283424Sdchagin snprintf(buf, sizeof(buf), "C%04o %lu %s\n", 323283424Sdchagin stb.st_mode & MODEMASK, 324283424Sdchagin (unsigned long)stb.st_size, 325283424Sdchagin last); 326283424Sdchagin write(remout, buf, strlen(buf)); 327283424Sdchagin if (response() < 0) 328283424Sdchagin goto next; 329283424Sdchagin if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 330283424Sdchaginnext: close(fd); 331283424Sdchagin continue; 332283424Sdchagin } 333283424Sdchagin 334283424Sdchagin /* Keep writing after an error so that we stay sync'd up. */ 335283424Sdchagin for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 336283424Sdchagin amt = bp->cnt; 337283424Sdchagin if (i + amt > stb.st_size) 338283424Sdchagin amt = stb.st_size - i; 339283424Sdchagin if (!haderr) { 340283424Sdchagin result = read(fd, bp->buf, amt); 341283424Sdchagin if (result != amt) 342283424Sdchagin haderr = result >= 0 ? EIO : errno; 343283424Sdchagin } 344283424Sdchagin if (haderr) 345283424Sdchagin write(remout, bp->buf, amt); 346283424Sdchagin else { 347283424Sdchagin result = write(remout, bp->buf, amt); 348283424Sdchagin if (result != amt) 349283424Sdchagin haderr = result >= 0 ? EIO : errno; 350283424Sdchagin } 351283424Sdchagin } 352283424Sdchagin if (close(fd) && !haderr) 353283424Sdchagin haderr = errno; 354283424Sdchagin if (!haderr) 355283424Sdchagin write(remout, "", 1); 356283424Sdchagin else 357283424Sdchagin run_err("%s: %s", name, strerror(haderr)); 358283424Sdchagin response(); 359283424Sdchagin } 360283424Sdchagin} 361283424Sdchagin 362283424Sdchaginvoid 363283424Sdchaginrsource(char *name, struct stat *statp) 364283424Sdchagin{ 365283424Sdchagin DIR *dirp; 366283424Sdchagin struct dirent *dp; 367283424Sdchagin char *last, *vect[1], path[MAXPATHLEN]; 368283424Sdchagin 369283424Sdchagin if (!(dirp = opendir(name))) { 370283424Sdchagin run_err("%s: %s", name, strerror(errno)); 371283424Sdchagin return; 372283424Sdchagin } 373283424Sdchagin last = strrchr(name, '/'); 374283424Sdchagin if (last == 0) 375283424Sdchagin last = name; 376283424Sdchagin else 377283424Sdchagin last++; 378283424Sdchagin if (pflag) { 379283424Sdchagin snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 380283424Sdchagin (long)statp->st_mtime, 381283424Sdchagin (long)statp->st_atime); 382283424Sdchagin write(remout, path, strlen(path)); 383283424Sdchagin if (response() < 0) { 384283424Sdchagin closedir(dirp); 385283424Sdchagin return; 386283424Sdchagin } 387283424Sdchagin } 388283424Sdchagin snprintf(path, sizeof(path), 389283424Sdchagin "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 390283424Sdchagin write(remout, path, strlen(path)); 391283424Sdchagin if (response() < 0) { 392283424Sdchagin closedir(dirp); 393283424Sdchagin return; 394283424Sdchagin } 395283424Sdchagin while ((dp = readdir(dirp))) { 396283424Sdchagin if (dp->d_ino == 0) 397283424Sdchagin continue; 398283424Sdchagin if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 399283424Sdchagin continue; 400283424Sdchagin if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 401283424Sdchagin run_err("%s/%s: name too long", name, dp->d_name); 402283424Sdchagin continue; 403283424Sdchagin } 404283424Sdchagin snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 405283424Sdchagin vect[0] = path; 406283424Sdchagin source(1, vect); 407283424Sdchagin } 408283424Sdchagin closedir(dirp); 409283424Sdchagin write(remout, "E\n", 2); 410283424Sdchagin response(); 411283424Sdchagin} 412283424Sdchagin 413283424Sdchaginvoid 414283424Sdchaginsink(int argc, char **argv) 415283424Sdchagin{ 416283424Sdchagin static BUF buffer; 417283424Sdchagin struct stat stb; 418283424Sdchagin struct timeval tv[2]; 419283424Sdchagin enum { YES, NO, DISPLAYED } wrerr; 420283424Sdchagin BUF *bp; 421283424Sdchagin off_t i, j, size; 422283424Sdchagin int amt, count, exists, first, mask, mode, ofd, omode; 423283424Sdchagin int setimes, targisdir, wrerrno = 0; 424283424Sdchagin char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 425283424Sdchagin 426283424Sdchagin#define atime tv[0] 427283424Sdchagin#define mtime tv[1] 428283424Sdchagin#define SCREWUP(str) { why = str; goto screwup; } 429283424Sdchagin 430283424Sdchagin setimes = targisdir = 0; 431283424Sdchagin mask = umask(0); 432283424Sdchagin if (!pflag) 433 umask(mask); 434 if (argc != 1) { 435 run_err("ambiguous target"); 436 exit(1); 437 } 438 targ = *argv; 439 if (targetshouldbedirectory) 440 verifydir(targ); 441 write(remout, "", 1); 442 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 443 targisdir = 1; 444 for (first = 1;; first = 0) { 445 cp = buf; 446 if (read(remin, cp, 1) <= 0) 447 return; 448 if (*cp++ == '\n') 449 SCREWUP("unexpected <newline>"); 450 do { 451 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 452 SCREWUP("lost connection"); 453 *cp++ = ch; 454 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 455 *cp = 0; 456 457 if (buf[0] == '\01' || buf[0] == '\02') { 458 if (iamremote == 0) 459 write(STDERR_FILENO, 460 buf + 1, strlen(buf + 1)); 461 if (buf[0] == '\02') 462 exit(1); 463 ++errs; 464 continue; 465 } 466 if (buf[0] == 'E') { 467 write(remout, "", 1); 468 return; 469 } 470 471 if (ch == '\n') 472 *--cp = 0; 473 474 cp = buf; 475 if (*cp == 'T') { 476 setimes++; 477 cp++; 478 mtime.tv_sec = strtol(cp, &cp, 10); 479 if (!cp || *cp++ != ' ') 480 SCREWUP("mtime.sec not delimited"); 481 mtime.tv_usec = strtol(cp, &cp, 10); 482 if (!cp || *cp++ != ' ') 483 SCREWUP("mtime.usec not delimited"); 484 atime.tv_sec = strtol(cp, &cp, 10); 485 if (!cp || *cp++ != ' ') 486 SCREWUP("atime.sec not delimited"); 487 atime.tv_usec = strtol(cp, &cp, 10); 488 if (!cp || *cp++ != '\0') 489 SCREWUP("atime.usec not delimited"); 490 write(remout, "", 1); 491 continue; 492 } 493 if (*cp != 'C' && *cp != 'D') { 494 /* 495 * Check for the case "rcp remote:foo\* local:bar". 496 * In this case, the line "No match." can be returned 497 * by the shell before the rcp command on the remote is 498 * executed so the ^Aerror_message convention isn't 499 * followed. 500 */ 501 if (first) { 502 run_err("%s", cp); 503 exit(1); 504 } 505 SCREWUP("expected control record"); 506 } 507 mode = 0; 508 for (++cp; cp < buf + 5; cp++) { 509 if (*cp < '0' || *cp > '7') 510 SCREWUP("bad mode"); 511 mode = (mode << 3) | (*cp - '0'); 512 } 513 if (*cp++ != ' ') 514 SCREWUP("mode not delimited"); 515 516 for (size = 0; isdigit((unsigned char)*cp);) 517 size = size * 10 + (*cp++ - '0'); 518 if (*cp++ != ' ') 519 SCREWUP("size not delimited"); 520 if (targisdir) { 521 static char *namebuf; 522 static int cursize; 523 size_t need; 524 525 need = strlen(targ) + strlen(cp) + 250; 526 if (need > cursize) { 527 if (!(namebuf = malloc(need))) 528 run_err("%s", strerror(errno)); 529 } 530 snprintf(namebuf, need, "%s%s%s", targ, 531 *targ ? "/" : "", cp); 532 np = namebuf; 533 } else 534 np = targ; 535 exists = stat(np, &stb) == 0; 536 if (buf[0] == 'D') { 537 int mod_flag = pflag; 538 if (exists) { 539 if (!S_ISDIR(stb.st_mode)) { 540 errno = ENOTDIR; 541 goto bad; 542 } 543 if (pflag) 544 chmod(np, mode); 545 } else { 546 /* Handle copying from a read-only directory */ 547 mod_flag = 1; 548 if (mkdir(np, mode | S_IRWXU) < 0) 549 goto bad; 550 } 551 vect[0] = np; 552 sink(1, vect); 553 if (setimes) { 554 setimes = 0; 555 if (utimes(np, tv) < 0) 556 run_err("%s: set times: %s", 557 np, strerror(errno)); 558 } 559 if (mod_flag) 560 chmod(np, mode); 561 continue; 562 } 563 omode = mode; 564 mode |= S_IWRITE; 565 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 566bad: run_err("%s: %s", np, strerror(errno)); 567 continue; 568 } 569 write(remout, "", 1); 570 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 571 close(ofd); 572 continue; 573 } 574 cp = bp->buf; 575 wrerr = NO; 576 for (count = i = 0; i < size; i += BUFSIZ) { 577 amt = BUFSIZ; 578 if (i + amt > size) 579 amt = size - i; 580 count += amt; 581 if((j = net_read(remin, cp, amt)) != amt) { 582 run_err("%s", j ? strerror(errno) : 583 "dropped connection"); 584 exit(1); 585 } 586 amt -= j; 587 cp += j; 588 if (count == bp->cnt) { 589 /* Keep reading so we stay sync'd up. */ 590 if (wrerr == NO) { 591 j = write(ofd, bp->buf, count); 592 if (j != count) { 593 wrerr = YES; 594 wrerrno = j >= 0 ? EIO : errno; 595 } 596 } 597 count = 0; 598 cp = bp->buf; 599 } 600 } 601 if (count != 0 && wrerr == NO && 602 (j = write(ofd, bp->buf, count)) != count) { 603 wrerr = YES; 604 wrerrno = j >= 0 ? EIO : errno; 605 } 606 if (ftruncate(ofd, size)) { 607 run_err("%s: truncate: %s", np, strerror(errno)); 608 wrerr = DISPLAYED; 609 } 610 if (pflag) { 611 if (exists || omode != mode) 612 if (fchmod(ofd, omode)) 613 run_err("%s: set mode: %s", 614 np, strerror(errno)); 615 } else { 616 if (!exists && omode != mode) 617 if (fchmod(ofd, omode & ~mask)) 618 run_err("%s: set mode: %s", 619 np, strerror(errno)); 620 } 621 close(ofd); 622 response(); 623 if (setimes && wrerr == NO) { 624 setimes = 0; 625 if (utimes(np, tv) < 0) { 626 run_err("%s: set times: %s", 627 np, strerror(errno)); 628 wrerr = DISPLAYED; 629 } 630 } 631 switch(wrerr) { 632 case YES: 633 run_err("%s: %s", np, strerror(wrerrno)); 634 break; 635 case NO: 636 write(remout, "", 1); 637 break; 638 case DISPLAYED: 639 break; 640 } 641 } 642screwup: 643 run_err("protocol error: %s", why); 644 exit(1); 645} 646 647int 648response(void) 649{ 650 char ch, *cp, resp, rbuf[BUFSIZ]; 651 652 if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 653 lostconn(0); 654 655 cp = rbuf; 656 switch(resp) { 657 case 0: /* ok */ 658 return (0); 659 default: 660 *cp++ = resp; 661 /* FALLTHROUGH */ 662 case 1: /* error, followed by error msg */ 663 case 2: /* fatal error, "" */ 664 do { 665 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 666 lostconn(0); 667 *cp++ = ch; 668 } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 669 670 if (!iamremote) 671 write(STDERR_FILENO, rbuf, cp - rbuf); 672 ++errs; 673 if (resp == 1) 674 return (-1); 675 exit(1); 676 } 677 /* NOTREACHED */ 678} 679 680#include <stdarg.h> 681 682void 683run_err(const char *fmt, ...) 684{ 685 static FILE *fp; 686 va_list ap; 687 688 ++errs; 689 if (fp == NULL && !(fp = fdopen(remout, "w"))) 690 return; 691 va_start(ap, fmt); 692 fprintf(fp, "%c", 0x01); 693 fprintf(fp, "rcp: "); 694 vfprintf(fp, fmt, ap); 695 fprintf(fp, "\n"); 696 fflush(fp); 697 va_end(ap); 698 699 if (!iamremote) { 700 va_start(ap, fmt); 701 vwarnx(fmt, ap); 702 va_end(ap); 703 } 704} 705 706/* 707 * This function executes the given command as the specified user on the 708 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 709 * assigns the input and output file descriptors on success. 710 * 711 * If it cannot create necessary pipes it exits with error message. 712 */ 713 714int 715do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 716{ 717 int pin[2], pout[2], reserved[2]; 718 719 /* 720 * Reserve two descriptors so that the real pipes won't get 721 * descriptors 0 and 1 because that will screw up dup2 below. 722 */ 723 pipe(reserved); 724 725 /* Create a socket pair for communicating with rsh. */ 726 if (pipe(pin) < 0) { 727 perror("pipe"); 728 exit(255); 729 } 730 if (pipe(pout) < 0) { 731 perror("pipe"); 732 exit(255); 733 } 734 735 /* Free the reserved descriptors. */ 736 close(reserved[0]); 737 close(reserved[1]); 738 739 /* For a child to execute the command on the remote host using rsh. */ 740 if (fork() == 0) { 741 char *args[100]; 742 unsigned int i; 743 744 /* Child. */ 745 close(pin[1]); 746 close(pout[0]); 747 dup2(pin[0], 0); 748 dup2(pout[1], 1); 749 close(pin[0]); 750 close(pout[1]); 751 752 i = 0; 753 args[i++] = RSH_PROGRAM; 754 if (usekrb5) 755 args[i++] = "-5"; 756 if (usebroken) 757 args[i++] = "-K"; 758 if (doencrypt) 759 args[i++] = "-x"; 760 if (forwardtkt) 761 args[i++] = "-F"; 762 if (noencrypt) 763 args[i++] = "-z"; 764 if (port != NULL) { 765 args[i++] = "-p"; 766 args[i++] = port; 767 } 768 if (remuser != NULL) { 769 args[i++] = "-l"; 770 args[i++] = remuser; 771 } 772 args[i++] = host; 773 args[i++] = cmd; 774 args[i++] = NULL; 775 776 execvp(RSH_PROGRAM, args); 777 perror(RSH_PROGRAM); 778 exit(1); 779 } 780 /* Parent. Close the other side, and return the local side. */ 781 close(pin[0]); 782 *fdout = pin[1]; 783 close(pout[1]); 784 *fdin = pout[0]; 785 return 0; 786} 787