16440Swnj #ifndef lint 2*18026Sralph static char sccsid[] = "@(#)rcp.c 4.12 85/02/20"; 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> 18*18026Sralph #include <netdb.h> 196440Swnj #include <errno.h> 2012989Ssam 216440Swnj int rem; 226440Swnj char *colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf(); 236440Swnj int errs; 246440Swnj int lostconn(); 256440Swnj int iamremote; 266440Swnj 276440Swnj int errno; 286440Swnj char *sys_errlist[]; 296440Swnj int iamremote, targetshouldbedirectory; 306440Swnj int iamrecursive; 316440Swnj struct passwd *pwd; 326440Swnj struct passwd *getpwuid(); 33*18026Sralph struct servent *sp; 346440Swnj 356440Swnj /*VARARGS*/ 366440Swnj int error(); 376440Swnj 386440Swnj #define ga() (void) write(rem, "", 1) 396440Swnj 406440Swnj main(argc, argv) 416440Swnj int argc; 426440Swnj char **argv; 436440Swnj { 446440Swnj char *targ, *host, *src; 456440Swnj char *suser, *tuser; 466440Swnj int i; 476440Swnj char buf[BUFSIZ], cmd[16]; 48*18026Sralph 49*18026Sralph sp = getservbyname("shell", "tcp"); 50*18026Sralph if (sp == NULL) { 51*18026Sralph fprintf(stderr, "rcp: shell/tcp: unknown service\n"); 52*18026Sralph exit(1); 53*18026Sralph } 546440Swnj setpwent(); 556440Swnj pwd = getpwuid(getuid()); 566440Swnj endpwent(); 576440Swnj if (pwd == 0) { 586440Swnj fprintf(stderr, "who are you?\n"); 596440Swnj exit(1); 606440Swnj } 616440Swnj argc--, argv++; 626440Swnj if (argc > 0 && !strcmp(*argv, "-r")) { 636440Swnj iamrecursive++; 646440Swnj argc--, argv++; 656440Swnj } 666440Swnj if (argc > 0 && !strcmp(*argv, "-d")) { 676440Swnj targetshouldbedirectory = 1; 686440Swnj argc--, argv++; 696440Swnj } 706440Swnj if (argc > 0 && !strcmp(*argv, "-f")) { 716440Swnj argc--, argv++; iamremote = 1; 726440Swnj (void) response(); 736440Swnj (void) setuid(getuid()); 746440Swnj source(argc, argv); 756440Swnj exit(errs); 766440Swnj } 776440Swnj if (argc > 0 && !strcmp(*argv, "-t")) { 786440Swnj argc--, argv++; iamremote = 1; 796440Swnj (void) setuid(getuid()); 806440Swnj sink(argc, argv); 816440Swnj exit(errs); 826440Swnj } 836440Swnj rem = -1; 846440Swnj if (argc > 2) 856440Swnj targetshouldbedirectory = 1; 866440Swnj (void) sprintf(cmd, "rcp%s%s", 876440Swnj iamrecursive ? " -r" : "", targetshouldbedirectory ? " -d" : ""); 8812989Ssam signal(SIGPIPE, lostconn); 896440Swnj targ = colon(argv[argc - 1]); 906440Swnj if (targ) { 916440Swnj *targ++ = 0; 9213542Ssam if (*targ == 0) 9313542Ssam targ = "."; 946440Swnj tuser = rindex(argv[argc - 1], '.'); 956440Swnj if (tuser) { 966440Swnj *tuser++ = 0; 976440Swnj if (!okname(tuser)) 986440Swnj exit(1); 996440Swnj } else 1006440Swnj tuser = pwd->pw_name; 1016440Swnj for (i = 0; i < argc - 1; i++) { 1026440Swnj src = colon(argv[i]); 1036440Swnj if (src) { 1046440Swnj *src++ = 0; 10513542Ssam if (*src == 0) 10613738Ssam src = "."; 1076440Swnj suser = rindex(argv[i], '.'); 1086440Swnj if (suser) { 1096440Swnj *suser++ = 0; 1106440Swnj if (!okname(suser)) 1116440Swnj continue; 11215898Sralph (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s.%s:%s'", 11315898Sralph argv[i], suser, cmd, src, 11415898Sralph argv[argc - 1], tuser, targ); 1156440Swnj } else 11615898Sralph (void) sprintf(buf, "rsh %s -n %s %s '%s.%s:%s'", 11715898Sralph argv[i], cmd, src, 11815898Sralph argv[argc - 1], tuser, targ); 1196440Swnj (void) susystem(buf); 1206440Swnj } else { 1216440Swnj if (rem == -1) { 1226440Swnj (void) sprintf(buf, "%s -t %s", 1236440Swnj cmd, targ); 1246440Swnj host = argv[argc - 1]; 125*18026Sralph rem = rcmd(&host, sp->s_port, 1266440Swnj pwd->pw_name, tuser, 1276440Swnj buf, 0); 1286440Swnj if (rem < 0) 1296440Swnj exit(1); 1306440Swnj if (response() < 0) 1316440Swnj exit(1); 1326440Swnj } 1336440Swnj source(1, argv+i); 1346440Swnj } 1356440Swnj } 1366440Swnj } else { 1376440Swnj if (targetshouldbedirectory) 1386440Swnj verifydir(argv[argc - 1]); 1396440Swnj for (i = 0; i < argc - 1; i++) { 1406440Swnj src = colon(argv[i]); 1416440Swnj if (src == 0) { 1426440Swnj (void) sprintf(buf, "/bin/cp%s %s %s", 1436440Swnj iamrecursive ? " -r" : "", 1446440Swnj argv[i], argv[argc - 1]); 1456440Swnj (void) susystem(buf); 1466440Swnj } else { 1476440Swnj *src++ = 0; 14813542Ssam if (*src == 0) 14913542Ssam src = "."; 1506440Swnj suser = rindex(argv[i], '.'); 1516440Swnj if (suser) { 1526440Swnj *suser++ = 0; 1536440Swnj if (!okname(suser)) 1546440Swnj continue; 1556440Swnj } else 1566440Swnj suser = pwd->pw_name; 1576440Swnj (void) sprintf(buf, "%s -f %s", cmd, src); 1586440Swnj host = argv[i]; 159*18026Sralph rem = rcmd(&host, sp->s_port, 1606440Swnj pwd->pw_name, suser, 1616440Swnj buf, 0); 1626440Swnj if (rem < 0) 1636440Swnj exit(1); 1646440Swnj sink(1, argv+argc-1); 1656440Swnj (void) close(rem); 1666440Swnj rem = -1; 1676440Swnj } 1686440Swnj } 1696440Swnj } 1706440Swnj exit(errs); 1716440Swnj } 1726440Swnj 1736440Swnj verifydir(cp) 1746440Swnj char *cp; 1756440Swnj { 1766440Swnj struct stat stb; 1776440Swnj 1786440Swnj if (stat(cp, &stb) < 0) 1796440Swnj goto bad; 1806440Swnj if ((stb.st_mode & S_IFMT) == S_IFDIR) 1816440Swnj return; 1826440Swnj errno = ENOTDIR; 1836440Swnj bad: 1846440Swnj error("rcp: %s: %s.\n", cp, sys_errlist[errno]); 1856440Swnj exit(1); 1866440Swnj } 1876440Swnj 1886440Swnj char * 1896440Swnj colon(cp) 1906440Swnj char *cp; 1916440Swnj { 1926440Swnj 1936440Swnj while (*cp) { 1946440Swnj if (*cp == ':') 1956440Swnj return (cp); 1966440Swnj if (*cp == '/') 1976440Swnj return (0); 1986440Swnj cp++; 1996440Swnj } 2006440Swnj return (0); 2016440Swnj } 2026440Swnj 2036440Swnj okname(cp0) 2046440Swnj char *cp0; 2056440Swnj { 2066440Swnj register char *cp = cp0; 2076440Swnj register int c; 2086440Swnj 2096440Swnj do { 2106440Swnj c = *cp; 2116440Swnj if (c & 0200) 2126440Swnj goto bad; 2136440Swnj if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 2146440Swnj goto bad; 2156440Swnj cp++; 2166440Swnj } while (*cp); 2176440Swnj return (1); 2186440Swnj bad: 2196440Swnj fprintf(stderr, "rcp: invalid user name %s\n", cp0); 2206440Swnj return (0); 2216440Swnj } 2226440Swnj 22317999Sserge susystem(s) 22417999Sserge char *s; 2256440Swnj { 22617999Sserge int status, pid, w; 22717999Sserge register int (*istat)(), (*qstat)(); 2286440Swnj 22917999Sserge if ((pid = vfork()) == 0) { 23017999Sserge setuid(getuid()); 23117999Sserge execl("/bin/sh", "sh", "-c", s, (char *)0); 23217999Sserge _exit(127); 23317999Sserge } 23417999Sserge istat = signal(SIGINT, SIG_IGN); 23517999Sserge qstat = signal(SIGQUIT, SIG_IGN); 23617999Sserge while ((w = wait(&status)) != pid && w != -1) 23717999Sserge ; 23817999Sserge if (w == -1) 23917999Sserge status = -1; 24017999Sserge signal(SIGINT, istat); 24117999Sserge signal(SIGQUIT, qstat); 24217999Sserge return (status); 2436440Swnj } 2446440Swnj 2456440Swnj source(argc, argv) 2466440Swnj int argc; 2476440Swnj char **argv; 2486440Swnj { 2496440Swnj char *last, *name; 2506440Swnj struct stat stb; 2516440Swnj char buf[BUFSIZ]; 2526623Smckusick int x, sizerr, f; 2536623Smckusick off_t i; 2546440Swnj 2556440Swnj for (x = 0; x < argc; x++) { 2566440Swnj name = argv[x]; 2576440Swnj if (access(name, 4) < 0 || (f = open(name, 0)) < 0) { 2586440Swnj error("rcp: %s: %s\n", name, sys_errlist[errno]); 2596440Swnj continue; 2606440Swnj } 2616440Swnj if (fstat(f, &stb) < 0) 2626440Swnj goto notreg; 2636440Swnj switch (stb.st_mode&S_IFMT) { 2646440Swnj 2656440Swnj case S_IFREG: 2666440Swnj break; 2676440Swnj 2686440Swnj case S_IFDIR: 2696440Swnj if (iamrecursive) { 2706440Swnj (void) close(f); 2716440Swnj rsource(name, (int)stb.st_mode); 2726440Swnj continue; 2736440Swnj } 2746440Swnj /* fall into ... */ 2756440Swnj default: 2766440Swnj notreg: 2776440Swnj (void) close(f); 2786440Swnj error("rcp: %s: not a plain file\n", name); 2796440Swnj continue; 2806440Swnj } 2816440Swnj last = rindex(name, '/'); 2826440Swnj if (last == 0) 2836440Swnj last = name; 2846440Swnj else 2856440Swnj last++; 2866623Smckusick (void) sprintf(buf, "C%04o %D %s\n", 2876440Swnj stb.st_mode&07777, stb.st_size, last); 2886440Swnj (void) write(rem, buf, strlen(buf)); 2896440Swnj if (response() < 0) { 2906440Swnj (void) close(f); 2916440Swnj continue; 2926440Swnj } 2936440Swnj sizerr = 0; 2946440Swnj for (i = 0; i < stb.st_size; i += BUFSIZ) { 2956440Swnj int amt = BUFSIZ; 2966440Swnj if (i + amt > stb.st_size) 2976440Swnj amt = stb.st_size - i; 2986440Swnj if (sizerr == 0 && read(f, buf, amt) != amt) 2996440Swnj sizerr = 1; 3006440Swnj (void) write(rem, buf, amt); 3016440Swnj } 3026440Swnj (void) close(f); 3036440Swnj if (sizerr == 0) 3046440Swnj ga(); 3056440Swnj else 3066440Swnj error("rcp: %s: file changed size\n", name); 3076440Swnj (void) response(); 3086440Swnj } 3096440Swnj } 3106440Swnj 31113542Ssam #include <sys/dir.h> 3126440Swnj 3136440Swnj rsource(name, mode) 3146440Swnj char *name; 3156440Swnj int mode; 3166440Swnj { 3176440Swnj DIR *d = opendir(name); 3186440Swnj char *last; 3196440Swnj struct direct *dp; 3206440Swnj char buf[BUFSIZ]; 3216440Swnj char *bufv[1]; 3226440Swnj 3236440Swnj if (d == 0) { 3246440Swnj error("%s: %s\n", name, sys_errlist[errno]); 3256440Swnj return; 3266440Swnj } 3276440Swnj last = rindex(name, '/'); 3286440Swnj if (last == 0) 3296440Swnj last = name; 3306440Swnj else 3316440Swnj last++; 3326440Swnj (void) sprintf(buf, "D%04o %d %s\n", mode&07777, 0, last); 3336440Swnj (void) write(rem, buf, strlen(buf)); 3346440Swnj if (response() < 0) { 3356440Swnj closedir(d); 3366440Swnj return; 3376440Swnj } 3386440Swnj while (dp = readdir(d)) { 3396440Swnj if (dp->d_ino == 0) 3406440Swnj continue; 3416440Swnj if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 3426440Swnj continue; 3436440Swnj if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 3446440Swnj error("%s/%s: Name too long.\n", name, dp->d_name); 3456440Swnj continue; 3466440Swnj } 3476440Swnj (void) sprintf(buf, "%s/%s", name, dp->d_name); 3486440Swnj bufv[0] = buf; 3496440Swnj source(1, bufv); 3506440Swnj } 3516440Swnj closedir(d); 3526440Swnj (void) write(rem, "E\n", 2); 3536440Swnj (void) response(); 3546440Swnj } 3556440Swnj 3566440Swnj response() 3576440Swnj { 3586440Swnj char resp, c, rbuf[BUFSIZ], *cp = rbuf; 3596440Swnj 3606440Swnj if (read(rem, &resp, 1) != 1) 3616440Swnj lostconn(); 3626440Swnj switch (resp) { 3636440Swnj 3646440Swnj case 0: 3656440Swnj return (0); 3666440Swnj 3676440Swnj default: 3686440Swnj *cp++ = resp; 3696440Swnj /* fall into... */ 3706440Swnj case 1: 3716440Swnj case 2: 3726440Swnj do { 3736440Swnj if (read(rem, &c, 1) != 1) 3746440Swnj lostconn(); 3756440Swnj *cp++ = c; 3766440Swnj } while (cp < &rbuf[BUFSIZ] && c != '\n'); 3776440Swnj if (iamremote == 0) 3786440Swnj (void) write(2, rbuf, cp - rbuf); 3796440Swnj errs++; 3806440Swnj if (resp == 1) 3816440Swnj return (-1); 3826440Swnj exit(1); 3836440Swnj } 3846440Swnj /*NOTREACHED*/ 3856440Swnj } 3866440Swnj 3876440Swnj lostconn() 3886440Swnj { 3896440Swnj 3906440Swnj if (iamremote == 0) 3916440Swnj fprintf(stderr, "rcp: lost connection\n"); 3926440Swnj exit(1); 3936440Swnj } 3946440Swnj 3956440Swnj sink(argc, argv) 3966440Swnj int argc; 3976440Swnj char **argv; 3986440Swnj { 3996440Swnj char *targ; 4006440Swnj char cmdbuf[BUFSIZ], nambuf[BUFSIZ], buf[BUFSIZ], *cp; 40114580Sralph int of, mode, wrerr, exists, first; 4026623Smckusick off_t i, size; 4036440Swnj char *whopp; 4046440Swnj struct stat stb; int targisdir = 0; 4056440Swnj #define SCREWUP(str) { whopp = str; goto screwup; } 4066440Swnj int mask = umask(0); 4076440Swnj char *myargv[1]; 4086440Swnj 4096440Swnj umask(mask); 4106440Swnj if (argc > 1) { 4116440Swnj error("rcp: ambiguous target\n"); 4126440Swnj exit(1); 4136440Swnj } 4146440Swnj targ = *argv; 4156440Swnj if (targetshouldbedirectory) 4166440Swnj verifydir(targ); 4176440Swnj ga(); 4186440Swnj if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) 4196440Swnj targisdir = 1; 42014580Sralph for (first = 1; ; first = 0) { 4216440Swnj cp = cmdbuf; 4226440Swnj if (read(rem, cp, 1) <= 0) 4236440Swnj return; 4246440Swnj if (*cp++ == '\n') 4256440Swnj SCREWUP("unexpected '\\n'"); 4266440Swnj do { 4276440Swnj if (read(rem, cp, 1) != 1) 4286440Swnj SCREWUP("lost connection"); 4296440Swnj } while (*cp++ != '\n'); 4306440Swnj *cp = 0; 4316440Swnj if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { 4326440Swnj if (iamremote == 0) 43313542Ssam (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); 4346440Swnj if (cmdbuf[0] == '\02') 4356440Swnj exit(1); 4366440Swnj errs++; 4376440Swnj continue; 4386440Swnj } 4396440Swnj *--cp = 0; 4406440Swnj cp = cmdbuf; 4416440Swnj if (*cp == 'E') { 4426440Swnj ga(); 4436440Swnj return; 4446440Swnj } 44514580Sralph if (*cp != 'C' && *cp != 'D') { 44614580Sralph /* 44714580Sralph * Check for the case "rcp remote:foo\* local:bar". 44814580Sralph * In this case, the line "No match." can be returned 44914580Sralph * by the shell before the rcp command on the remote is 45014580Sralph * executed so the ^Aerror_message convention isn't 45114580Sralph * followed. 45214580Sralph */ 45314580Sralph if (first) { 45414580Sralph error("%s\n", cp); 45514580Sralph exit(1); 45614580Sralph } 4576440Swnj SCREWUP("expected control record"); 45814580Sralph } 4596440Swnj cp++; 4606440Swnj mode = 0; 4616440Swnj for (; cp < cmdbuf+5; cp++) { 4626440Swnj if (*cp < '0' || *cp > '7') 4636440Swnj SCREWUP("bad mode"); 4646440Swnj mode = (mode << 3) | (*cp - '0'); 4656440Swnj } 4666440Swnj if (*cp++ != ' ') 4676440Swnj SCREWUP("mode not delimited"); 4686440Swnj size = 0; 4696440Swnj while (*cp >= '0' && *cp <= '9') 4706440Swnj size = size * 10 + (*cp++ - '0'); 4716440Swnj if (*cp++ != ' ') 4726440Swnj SCREWUP("size not delimited"); 4736440Swnj if (targisdir) 4746440Swnj (void) sprintf(nambuf, "%s%s%s", targ, 4756440Swnj *targ ? "/" : "", cp); 4766440Swnj else 4776440Swnj (void) strcpy(nambuf, targ); 4786440Swnj exists = stat(nambuf, &stb) == 0; 4796440Swnj if (exists && access(nambuf, 2) < 0) 4806440Swnj goto bad2; 4816440Swnj { char *slash = rindex(nambuf, '/'), *dir; 4826440Swnj if (slash == 0) { 4836440Swnj slash = "/"; 4846440Swnj dir = "."; 4856440Swnj } else { 4866440Swnj *slash = 0; 4876440Swnj dir = nambuf; 48816039Sralph if (*dir == '\0') 48916039Sralph dir = "/"; 4906440Swnj } 4916440Swnj if (exists == 0 && access(dir, 2) < 0) 4926440Swnj goto bad; 4936440Swnj *slash = '/'; 4946440Swnj if (cmdbuf[0] == 'D') { 4956440Swnj if (stat(nambuf, &stb) == 0) { 4966440Swnj if ((stb.st_mode&S_IFMT) != S_IFDIR) { 4976440Swnj errno = ENOTDIR; 4986440Swnj goto bad; 4996440Swnj } 50017999Sserge } else if (makedir(nambuf, mode) < 0) 5016440Swnj goto bad; 5026440Swnj myargv[0] = nambuf; 5036440Swnj sink(1, myargv); 5046440Swnj continue; 5056440Swnj } 5066440Swnj if ((of = creat(nambuf, mode)) < 0) { 5076440Swnj bad: 5086440Swnj *slash = '/'; 5096440Swnj bad2: 5106440Swnj error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); 5116440Swnj continue; 5126440Swnj } 5136440Swnj } 5146440Swnj if (exists == 0) { 51517999Sserge (void) chown(nambuf, pwd->pw_uid, -1); 5166440Swnj (void) chmod(nambuf, mode &~ mask); 5176440Swnj } 5186440Swnj ga(); 5196440Swnj wrerr = 0; 5206440Swnj for (i = 0; i < size; i += BUFSIZ) { 5216440Swnj int amt = BUFSIZ; 5226440Swnj char *cp = buf; 5236440Swnj 5246440Swnj if (i + amt > size) 5256440Swnj amt = size - i; 5266440Swnj do { 5276440Swnj int j = read(rem, cp, amt); 5286440Swnj 5296440Swnj if (j <= 0) 5306440Swnj exit(1); 5316440Swnj amt -= j; 5326440Swnj cp += j; 5336440Swnj } while (amt > 0); 5346440Swnj amt = BUFSIZ; 5356440Swnj if (i + amt > size) 5366440Swnj amt = size - i; 5376440Swnj if (wrerr == 0 && write(of, buf, amt) != amt) 5386440Swnj wrerr++; 5396440Swnj } 5406440Swnj (void) close(of); 5416440Swnj (void) response(); 5426440Swnj if (wrerr) 5436440Swnj error("rcp: %s: %s\n", cp, sys_errlist[errno]); 5446440Swnj else 5456440Swnj ga(); 5466440Swnj } 5476440Swnj screwup: 5486440Swnj error("rcp: protocol screwup: %s\n", whopp); 5496440Swnj exit(1); 5506440Swnj } 5516440Swnj 5526440Swnj /*VARARGS*/ 5536440Swnj error(fmt, a1, a2, a3, a4, a5) 5546440Swnj char *fmt; 5556440Swnj int a1, a2, a3, a4, a5; 5566440Swnj { 5576440Swnj char buf[BUFSIZ], *cp = buf; 5586440Swnj 5596440Swnj errs++; 5606440Swnj *cp++ = 1; 5616440Swnj (void) sprintf(cp, fmt, a1, a2, a3, a4, a5); 5626440Swnj (void) write(rem, buf, strlen(buf)); 5636440Swnj if (iamremote == 0) 5646440Swnj (void) write(2, buf+1, strlen(buf+1)); 5656440Swnj } 5666440Swnj 56717999Sserge makedir(name, mode) 56817999Sserge register char *name; 56917999Sserge register int mode; 5706440Swnj { 57117999Sserge register int _errno; 5726440Swnj 57317999Sserge if (mkdir(name, mode) < 0 || chown(name, getuid(), -1) < 0) { 57417999Sserge _errno = errno; 57517999Sserge rmdir(name); 57617999Sserge errno = _errno; 57717999Sserge return (-1); 5786440Swnj } 57917999Sserge 58017999Sserge return (0); 5816440Swnj } 582