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