16440Swnj #ifndef lint 2*13738Ssam static char sccsid[] = "@(#)rcp.c 4.7 83/07/03"; 36440Swnj #endif 46440Swnj 512989Ssam /* 612989Ssam * rcp 712989Ssam */ 86720Smckusick #include <sys/param.h> 96440Swnj #include <sys/stat.h> 106440Swnj #include <sys/ioctl.h> 119199Ssam 129199Ssam #include <netinet/in.h> 139199Ssam 149199Ssam #include <stdio.h> 159199Ssam #include <signal.h> 166440Swnj #include <pwd.h> 176440Swnj #include <ctype.h> 186440Swnj #include <errno.h> 1912989Ssam 206440Swnj int rem; 216440Swnj char *colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf(); 226440Swnj int errs; 236440Swnj int lostconn(); 246440Swnj int iamremote; 256440Swnj 266440Swnj int errno; 276440Swnj char *sys_errlist[]; 286440Swnj int iamremote, targetshouldbedirectory; 296440Swnj int iamrecursive; 306440Swnj struct passwd *pwd; 316440Swnj struct passwd *getpwuid(); 326440Swnj 336440Swnj /*VARARGS*/ 346440Swnj int error(); 356440Swnj 366440Swnj #define ga() (void) write(rem, "", 1) 376440Swnj 386440Swnj main(argc, argv) 396440Swnj int argc; 406440Swnj char **argv; 416440Swnj { 426440Swnj char *targ, *host, *src; 436440Swnj char *suser, *tuser; 446440Swnj int i; 456440Swnj char buf[BUFSIZ], cmd[16]; 466440Swnj 476440Swnj setpwent(); 486440Swnj pwd = getpwuid(getuid()); 496440Swnj endpwent(); 506440Swnj if (pwd == 0) { 516440Swnj fprintf(stderr, "who are you?\n"); 526440Swnj exit(1); 536440Swnj } 546440Swnj argc--, argv++; 556440Swnj if (argc > 0 && !strcmp(*argv, "-r")) { 566440Swnj iamrecursive++; 576440Swnj argc--, argv++; 586440Swnj } 596440Swnj if (argc > 0 && !strcmp(*argv, "-d")) { 606440Swnj targetshouldbedirectory = 1; 616440Swnj argc--, argv++; 626440Swnj } 636440Swnj if (argc > 0 && !strcmp(*argv, "-f")) { 646440Swnj argc--, argv++; iamremote = 1; 656440Swnj (void) response(); 666440Swnj (void) setuid(getuid()); 676440Swnj source(argc, argv); 686440Swnj exit(errs); 696440Swnj } 706440Swnj if (argc > 0 && !strcmp(*argv, "-t")) { 716440Swnj argc--, argv++; iamremote = 1; 726440Swnj (void) setuid(getuid()); 736440Swnj sink(argc, argv); 746440Swnj exit(errs); 756440Swnj } 766440Swnj rem = -1; 776440Swnj if (argc > 2) 786440Swnj targetshouldbedirectory = 1; 796440Swnj (void) sprintf(cmd, "rcp%s%s", 806440Swnj iamrecursive ? " -r" : "", targetshouldbedirectory ? " -d" : ""); 8112989Ssam signal(SIGPIPE, lostconn); 826440Swnj targ = colon(argv[argc - 1]); 836440Swnj if (targ) { 846440Swnj *targ++ = 0; 8513542Ssam if (*targ == 0) 8613542Ssam targ = "."; 876440Swnj tuser = rindex(argv[argc - 1], '.'); 886440Swnj if (tuser) { 896440Swnj *tuser++ = 0; 906440Swnj if (!okname(tuser)) 916440Swnj exit(1); 926440Swnj } else 936440Swnj tuser = pwd->pw_name; 946440Swnj for (i = 0; i < argc - 1; i++) { 956440Swnj src = colon(argv[i]); 966440Swnj if (src) { 976440Swnj *src++ = 0; 9813542Ssam if (*src == 0) 99*13738Ssam src = "."; 1006440Swnj suser = rindex(argv[i], '.'); 1016440Swnj if (suser) { 1026440Swnj *suser++ = 0; 1036440Swnj if (!okname(suser)) 1046440Swnj continue; 1056623Smckusick (void) sprintf(buf, "rsh %s -L %s %s %s '%s:%s' </dev/null", 1066440Swnj argv[i], suser, cmd, 1076440Swnj src, argv[argc - 1], targ); 1086440Swnj } else 1096440Swnj (void) sprintf(buf, "rsh %s %s %s '%s:%s' </dev/null", 1106440Swnj argv[i], cmd, 1116440Swnj src, argv[argc - 1], targ); 1126440Swnj (void) susystem(buf); 1136440Swnj } else { 1146440Swnj if (rem == -1) { 1156440Swnj (void) sprintf(buf, "%s -t %s", 1166440Swnj cmd, targ); 1176440Swnj host = argv[argc - 1]; 1186440Swnj rem = rcmd(&host, IPPORT_CMDSERVER, 1196440Swnj pwd->pw_name, tuser, 1206440Swnj buf, 0); 1216440Swnj if (rem < 0) 1226440Swnj exit(1); 1236440Swnj if (response() < 0) 1246440Swnj exit(1); 1256440Swnj } 1266440Swnj source(1, argv+i); 1276440Swnj } 1286440Swnj } 1296440Swnj } else { 1306440Swnj if (targetshouldbedirectory) 1316440Swnj verifydir(argv[argc - 1]); 1326440Swnj for (i = 0; i < argc - 1; i++) { 1336440Swnj src = colon(argv[i]); 1346440Swnj if (src == 0) { 1356440Swnj (void) sprintf(buf, "/bin/cp%s %s %s", 1366440Swnj iamrecursive ? " -r" : "", 1376440Swnj argv[i], argv[argc - 1]); 1386440Swnj (void) susystem(buf); 1396440Swnj } else { 1406440Swnj *src++ = 0; 14113542Ssam if (*src == 0) 14213542Ssam src = "."; 1436440Swnj suser = rindex(argv[i], '.'); 1446440Swnj if (suser) { 1456440Swnj *suser++ = 0; 1466440Swnj if (!okname(suser)) 1476440Swnj continue; 1486440Swnj } else 1496440Swnj suser = pwd->pw_name; 1506440Swnj (void) sprintf(buf, "%s -f %s", cmd, src); 1516440Swnj host = argv[i]; 1526440Swnj rem = rcmd(&host, IPPORT_CMDSERVER, 1536440Swnj pwd->pw_name, suser, 1546440Swnj buf, 0); 1556440Swnj if (rem < 0) 1566440Swnj exit(1); 1576440Swnj sink(1, argv+argc-1); 1586440Swnj (void) close(rem); 1596440Swnj rem = -1; 1606440Swnj } 1616440Swnj } 1626440Swnj } 1636440Swnj exit(errs); 1646440Swnj } 1656440Swnj 1666440Swnj verifydir(cp) 1676440Swnj char *cp; 1686440Swnj { 1696440Swnj struct stat stb; 1706440Swnj 1716440Swnj if (stat(cp, &stb) < 0) 1726440Swnj goto bad; 1736440Swnj if ((stb.st_mode & S_IFMT) == S_IFDIR) 1746440Swnj return; 1756440Swnj errno = ENOTDIR; 1766440Swnj bad: 1776440Swnj error("rcp: %s: %s.\n", cp, sys_errlist[errno]); 1786440Swnj exit(1); 1796440Swnj } 1806440Swnj 1816440Swnj char * 1826440Swnj colon(cp) 1836440Swnj char *cp; 1846440Swnj { 1856440Swnj 1866440Swnj while (*cp) { 1876440Swnj if (*cp == ':') 1886440Swnj return (cp); 1896440Swnj if (*cp == '/') 1906440Swnj return (0); 1916440Swnj cp++; 1926440Swnj } 1936440Swnj return (0); 1946440Swnj } 1956440Swnj 1966440Swnj okname(cp0) 1976440Swnj char *cp0; 1986440Swnj { 1996440Swnj register char *cp = cp0; 2006440Swnj register int c; 2016440Swnj 2026440Swnj do { 2036440Swnj c = *cp; 2046440Swnj if (c & 0200) 2056440Swnj goto bad; 2066440Swnj if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 2076440Swnj goto bad; 2086440Swnj cp++; 2096440Swnj } while (*cp); 2106440Swnj return (1); 2116440Swnj bad: 2126440Swnj fprintf(stderr, "rcp: invalid user name %s\n", cp0); 2136440Swnj return (0); 2146440Swnj } 2156440Swnj 2166440Swnj susystem(buf) 2176440Swnj char *buf; 2186440Swnj { 2196440Swnj 2206440Swnj if (fork() == 0) { 2216440Swnj (void) setuid(getuid()); 2226440Swnj (void) system(buf); 2236440Swnj _exit(0); 2246440Swnj } else 2256440Swnj (void) wait((int *)0); 2266440Swnj } 2276440Swnj 2286440Swnj source(argc, argv) 2296440Swnj int argc; 2306440Swnj char **argv; 2316440Swnj { 2326440Swnj char *last, *name; 2336440Swnj struct stat stb; 2346440Swnj char buf[BUFSIZ]; 2356623Smckusick int x, sizerr, f; 2366623Smckusick off_t i; 2376440Swnj 2386440Swnj for (x = 0; x < argc; x++) { 2396440Swnj name = argv[x]; 2406440Swnj if (access(name, 4) < 0 || (f = open(name, 0)) < 0) { 2416440Swnj error("rcp: %s: %s\n", name, sys_errlist[errno]); 2426440Swnj continue; 2436440Swnj } 2446440Swnj if (fstat(f, &stb) < 0) 2456440Swnj goto notreg; 2466440Swnj switch (stb.st_mode&S_IFMT) { 2476440Swnj 2486440Swnj case S_IFREG: 2496440Swnj break; 2506440Swnj 2516440Swnj case S_IFDIR: 2526440Swnj if (iamrecursive) { 2536440Swnj (void) close(f); 2546440Swnj rsource(name, (int)stb.st_mode); 2556440Swnj continue; 2566440Swnj } 2576440Swnj /* fall into ... */ 2586440Swnj default: 2596440Swnj notreg: 2606440Swnj (void) close(f); 2616440Swnj error("rcp: %s: not a plain file\n", name); 2626440Swnj continue; 2636440Swnj } 2646440Swnj last = rindex(name, '/'); 2656440Swnj if (last == 0) 2666440Swnj last = name; 2676440Swnj else 2686440Swnj last++; 2696623Smckusick (void) sprintf(buf, "C%04o %D %s\n", 2706440Swnj stb.st_mode&07777, stb.st_size, last); 2716440Swnj (void) write(rem, buf, strlen(buf)); 2726440Swnj if (response() < 0) { 2736440Swnj (void) close(f); 2746440Swnj continue; 2756440Swnj } 2766440Swnj sizerr = 0; 2776440Swnj for (i = 0; i < stb.st_size; i += BUFSIZ) { 2786440Swnj int amt = BUFSIZ; 2796440Swnj if (i + amt > stb.st_size) 2806440Swnj amt = stb.st_size - i; 2816440Swnj if (sizerr == 0 && read(f, buf, amt) != amt) 2826440Swnj sizerr = 1; 2836440Swnj (void) write(rem, buf, amt); 2846440Swnj } 2856440Swnj (void) close(f); 2866440Swnj if (sizerr == 0) 2876440Swnj ga(); 2886440Swnj else 2896440Swnj error("rcp: %s: file changed size\n", name); 2906440Swnj (void) response(); 2916440Swnj } 2926440Swnj } 2936440Swnj 29413542Ssam #include <sys/dir.h> 2956440Swnj 2966440Swnj rsource(name, mode) 2976440Swnj char *name; 2986440Swnj int mode; 2996440Swnj { 3006440Swnj DIR *d = opendir(name); 3016440Swnj char *last; 3026440Swnj struct direct *dp; 3036440Swnj char buf[BUFSIZ]; 3046440Swnj char *bufv[1]; 3056440Swnj 3066440Swnj if (d == 0) { 3076440Swnj error("%s: %s\n", name, sys_errlist[errno]); 3086440Swnj return; 3096440Swnj } 3106440Swnj last = rindex(name, '/'); 3116440Swnj if (last == 0) 3126440Swnj last = name; 3136440Swnj else 3146440Swnj last++; 3156440Swnj (void) sprintf(buf, "D%04o %d %s\n", mode&07777, 0, last); 3166440Swnj (void) write(rem, buf, strlen(buf)); 3176440Swnj if (response() < 0) { 3186440Swnj closedir(d); 3196440Swnj return; 3206440Swnj } 3216440Swnj while (dp = readdir(d)) { 3226440Swnj if (dp->d_ino == 0) 3236440Swnj continue; 3246440Swnj if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 3256440Swnj continue; 3266440Swnj if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 3276440Swnj error("%s/%s: Name too long.\n", name, dp->d_name); 3286440Swnj continue; 3296440Swnj } 3306440Swnj (void) sprintf(buf, "%s/%s", name, dp->d_name); 3316440Swnj bufv[0] = buf; 3326440Swnj source(1, bufv); 3336440Swnj } 3346440Swnj closedir(d); 3356440Swnj (void) write(rem, "E\n", 2); 3366440Swnj (void) response(); 3376440Swnj } 3386440Swnj 3396440Swnj response() 3406440Swnj { 3416440Swnj char resp, c, rbuf[BUFSIZ], *cp = rbuf; 3426440Swnj 3436440Swnj if (read(rem, &resp, 1) != 1) 3446440Swnj lostconn(); 3456440Swnj switch (resp) { 3466440Swnj 3476440Swnj case 0: 3486440Swnj return (0); 3496440Swnj 3506440Swnj default: 3516440Swnj *cp++ = resp; 3526440Swnj /* fall into... */ 3536440Swnj case 1: 3546440Swnj case 2: 3556440Swnj do { 3566440Swnj if (read(rem, &c, 1) != 1) 3576440Swnj lostconn(); 3586440Swnj *cp++ = c; 3596440Swnj } while (cp < &rbuf[BUFSIZ] && c != '\n'); 3606440Swnj if (iamremote == 0) 3616440Swnj (void) write(2, rbuf, cp - rbuf); 3626440Swnj errs++; 3636440Swnj if (resp == 1) 3646440Swnj return (-1); 3656440Swnj exit(1); 3666440Swnj } 3676440Swnj /*NOTREACHED*/ 3686440Swnj } 3696440Swnj 3706440Swnj lostconn() 3716440Swnj { 3726440Swnj 3736440Swnj if (iamremote == 0) 3746440Swnj fprintf(stderr, "rcp: lost connection\n"); 3756440Swnj exit(1); 3766440Swnj } 3776440Swnj 3786440Swnj sink(argc, argv) 3796440Swnj int argc; 3806440Swnj char **argv; 3816440Swnj { 3826440Swnj char *targ; 3836440Swnj char cmdbuf[BUFSIZ], nambuf[BUFSIZ], buf[BUFSIZ], *cp; 3846623Smckusick int of, mode, wrerr, exists; 3856623Smckusick off_t i, size; 3866440Swnj char *whopp; 3876440Swnj struct stat stb; int targisdir = 0; 3886440Swnj #define SCREWUP(str) { whopp = str; goto screwup; } 3896440Swnj int mask = umask(0); 3906440Swnj char *myargv[1]; 3916440Swnj 3926440Swnj umask(mask); 3936440Swnj if (argc > 1) { 3946440Swnj error("rcp: ambiguous target\n"); 3956440Swnj exit(1); 3966440Swnj } 3976440Swnj targ = *argv; 3986440Swnj if (targetshouldbedirectory) 3996440Swnj verifydir(targ); 4006440Swnj ga(); 4016440Swnj if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) 4026440Swnj targisdir = 1; 4036440Swnj for (;;) { 4046440Swnj cp = cmdbuf; 4056440Swnj if (read(rem, cp, 1) <= 0) 4066440Swnj return; 4076440Swnj if (*cp++ == '\n') 4086440Swnj SCREWUP("unexpected '\\n'"); 4096440Swnj do { 4106440Swnj if (read(rem, cp, 1) != 1) 4116440Swnj SCREWUP("lost connection"); 4126440Swnj } while (*cp++ != '\n'); 4136440Swnj *cp = 0; 4146440Swnj if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { 4156440Swnj if (iamremote == 0) 41613542Ssam (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); 4176440Swnj if (cmdbuf[0] == '\02') 4186440Swnj exit(1); 4196440Swnj errs++; 4206440Swnj continue; 4216440Swnj } 4226440Swnj *--cp = 0; 4236440Swnj cp = cmdbuf; 4246440Swnj if (*cp == 'E') { 4256440Swnj ga(); 4266440Swnj return; 4276440Swnj } 4286440Swnj if (*cp != 'C' && *cp != 'D') 4296440Swnj SCREWUP("expected control record"); 4306440Swnj cp++; 4316440Swnj mode = 0; 4326440Swnj for (; cp < cmdbuf+5; cp++) { 4336440Swnj if (*cp < '0' || *cp > '7') 4346440Swnj SCREWUP("bad mode"); 4356440Swnj mode = (mode << 3) | (*cp - '0'); 4366440Swnj } 4376440Swnj if (*cp++ != ' ') 4386440Swnj SCREWUP("mode not delimited"); 4396440Swnj size = 0; 4406440Swnj while (*cp >= '0' && *cp <= '9') 4416440Swnj size = size * 10 + (*cp++ - '0'); 4426440Swnj if (*cp++ != ' ') 4436440Swnj SCREWUP("size not delimited"); 4446440Swnj if (targisdir) 4456440Swnj (void) sprintf(nambuf, "%s%s%s", targ, 4466440Swnj *targ ? "/" : "", cp); 4476440Swnj else 4486440Swnj (void) strcpy(nambuf, targ); 4496440Swnj exists = stat(nambuf, &stb) == 0; 4506440Swnj if (exists && access(nambuf, 2) < 0) 4516440Swnj goto bad2; 4526440Swnj { char *slash = rindex(nambuf, '/'), *dir; 4536440Swnj if (slash == 0) { 4546440Swnj slash = "/"; 4556440Swnj dir = "."; 4566440Swnj } else { 4576440Swnj *slash = 0; 4586440Swnj dir = nambuf; 4596440Swnj } 4606440Swnj if (exists == 0 && access(dir, 2) < 0) 4616440Swnj goto bad; 4626440Swnj *slash = '/'; 4636440Swnj if (cmdbuf[0] == 'D') { 4646440Swnj if (stat(nambuf, &stb) == 0) { 4656440Swnj if ((stb.st_mode&S_IFMT) != S_IFDIR) { 4666440Swnj errno = ENOTDIR; 4676440Swnj goto bad; 4686440Swnj } 4696440Swnj } else if (mkdir(nambuf, mode) < 0) 4706440Swnj goto bad; 4716440Swnj myargv[0] = nambuf; 4726440Swnj sink(1, myargv); 4736440Swnj continue; 4746440Swnj } 4756440Swnj if ((of = creat(nambuf, mode)) < 0) { 4766440Swnj bad: 4776440Swnj *slash = '/'; 4786440Swnj bad2: 4796440Swnj error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); 4806440Swnj continue; 4816440Swnj } 4826440Swnj } 4836440Swnj if (exists == 0) { 4846440Swnj (void) stat(nambuf, &stb); 4856440Swnj (void) chown(nambuf, pwd->pw_uid, stb.st_gid); 4866440Swnj (void) chmod(nambuf, mode &~ mask); 4876440Swnj } 4886440Swnj ga(); 4896440Swnj wrerr = 0; 4906440Swnj for (i = 0; i < size; i += BUFSIZ) { 4916440Swnj int amt = BUFSIZ; 4926440Swnj char *cp = buf; 4936440Swnj 4946440Swnj if (i + amt > size) 4956440Swnj amt = size - i; 4966440Swnj do { 4976440Swnj int j = read(rem, cp, amt); 4986440Swnj 4996440Swnj if (j <= 0) 5006440Swnj exit(1); 5016440Swnj amt -= j; 5026440Swnj cp += j; 5036440Swnj } while (amt > 0); 5046440Swnj amt = BUFSIZ; 5056440Swnj if (i + amt > size) 5066440Swnj amt = size - i; 5076440Swnj if (wrerr == 0 && write(of, buf, amt) != amt) 5086440Swnj wrerr++; 5096440Swnj } 5106440Swnj (void) close(of); 5116440Swnj (void) response(); 5126440Swnj if (wrerr) 5136440Swnj error("rcp: %s: %s\n", cp, sys_errlist[errno]); 5146440Swnj else 5156440Swnj ga(); 5166440Swnj } 5176440Swnj screwup: 5186440Swnj error("rcp: protocol screwup: %s\n", whopp); 5196440Swnj exit(1); 5206440Swnj } 5216440Swnj 5226440Swnj /*VARARGS*/ 5236440Swnj error(fmt, a1, a2, a3, a4, a5) 5246440Swnj char *fmt; 5256440Swnj int a1, a2, a3, a4, a5; 5266440Swnj { 5276440Swnj char buf[BUFSIZ], *cp = buf; 5286440Swnj 5296440Swnj errs++; 5306440Swnj *cp++ = 1; 5316440Swnj (void) sprintf(cp, fmt, a1, a2, a3, a4, a5); 5326440Swnj (void) write(rem, buf, strlen(buf)); 5336440Swnj if (iamremote == 0) 5346440Swnj (void) write(2, buf+1, strlen(buf+1)); 5356440Swnj } 5366440Swnj 5376440Swnj mkdir(name, mode) 5386440Swnj char *name; 5396440Swnj int mode; 5406440Swnj { 5416440Swnj char *argv[4]; 5426440Swnj int pid, rc; 5436440Swnj 5446440Swnj argv[0] = "mkdir"; 5456440Swnj argv[1] = name; 5466440Swnj argv[2] = 0; 5476440Swnj pid = fork(); 5486440Swnj if (pid < 0) { 5496440Swnj perror("cp"); 5506440Swnj return (1); 5516440Swnj } 5526440Swnj if (pid) { 5536440Swnj while (wait(&rc) != pid) 5546440Swnj continue; 5556440Swnj if (rc == 0) 5566440Swnj if (chmod(name, mode) < 0) { 5576440Swnj perror(name); 5586440Swnj rc = 1; 5596440Swnj } 5606440Swnj return (rc); 5616440Swnj } 5626440Swnj (void) setuid(getuid()); 5636440Swnj execv("/bin/mkdir", argv); 5646440Swnj execv("/usr/bin/mkdir", argv); 5656440Swnj perror("mkdir"); 5666440Swnj _exit(1); 5676440Swnj /*NOTREACHED*/ 5686440Swnj } 5696623Smckusick 570