16440Swnj #ifndef lint 2*18126Sralph static char sccsid[] = "@(#)rcp.c 4.13 85/02/27"; 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> 1818026Sralph #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*18126Sralph int userid; 34*18126Sralph int port; 356440Swnj 366440Swnj /*VARARGS*/ 376440Swnj int error(); 386440Swnj 396440Swnj #define ga() (void) write(rem, "", 1) 406440Swnj 416440Swnj main(argc, argv) 426440Swnj int argc; 436440Swnj char **argv; 446440Swnj { 456440Swnj char *targ, *host, *src; 466440Swnj char *suser, *tuser; 476440Swnj int i; 486440Swnj char buf[BUFSIZ], cmd[16]; 49*18126Sralph struct servent *sp; 5018026Sralph 5118026Sralph sp = getservbyname("shell", "tcp"); 5218026Sralph if (sp == NULL) { 5318026Sralph fprintf(stderr, "rcp: shell/tcp: unknown service\n"); 5418026Sralph exit(1); 5518026Sralph } 56*18126Sralph port = sp->s_port; 57*18126Sralph pwd = getpwuid(userid = getuid()); 586440Swnj if (pwd == 0) { 596440Swnj fprintf(stderr, "who are you?\n"); 606440Swnj exit(1); 616440Swnj } 626440Swnj argc--, argv++; 636440Swnj if (argc > 0 && !strcmp(*argv, "-r")) { 646440Swnj iamrecursive++; 656440Swnj argc--, argv++; 666440Swnj } 676440Swnj if (argc > 0 && !strcmp(*argv, "-d")) { 686440Swnj targetshouldbedirectory = 1; 696440Swnj argc--, argv++; 706440Swnj } 716440Swnj if (argc > 0 && !strcmp(*argv, "-f")) { 726440Swnj argc--, argv++; iamremote = 1; 736440Swnj (void) response(); 74*18126Sralph (void) setuid(userid); 756440Swnj source(argc, argv); 766440Swnj exit(errs); 776440Swnj } 786440Swnj if (argc > 0 && !strcmp(*argv, "-t")) { 796440Swnj argc--, argv++; iamremote = 1; 80*18126Sralph (void) setuid(userid); 816440Swnj sink(argc, argv); 826440Swnj exit(errs); 836440Swnj } 846440Swnj rem = -1; 856440Swnj if (argc > 2) 866440Swnj targetshouldbedirectory = 1; 876440Swnj (void) sprintf(cmd, "rcp%s%s", 886440Swnj iamrecursive ? " -r" : "", targetshouldbedirectory ? " -d" : ""); 8912989Ssam signal(SIGPIPE, lostconn); 906440Swnj targ = colon(argv[argc - 1]); 916440Swnj if (targ) { 926440Swnj *targ++ = 0; 9313542Ssam if (*targ == 0) 9413542Ssam targ = "."; 956440Swnj tuser = rindex(argv[argc - 1], '.'); 966440Swnj if (tuser) { 976440Swnj *tuser++ = 0; 986440Swnj if (!okname(tuser)) 996440Swnj exit(1); 1006440Swnj } else 1016440Swnj tuser = pwd->pw_name; 1026440Swnj for (i = 0; i < argc - 1; i++) { 1036440Swnj src = colon(argv[i]); 1046440Swnj if (src) { 1056440Swnj *src++ = 0; 10613542Ssam if (*src == 0) 10713738Ssam src = "."; 1086440Swnj suser = rindex(argv[i], '.'); 1096440Swnj if (suser) { 1106440Swnj *suser++ = 0; 1116440Swnj if (!okname(suser)) 1126440Swnj continue; 11315898Sralph (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s.%s:%s'", 11415898Sralph argv[i], suser, cmd, src, 11515898Sralph argv[argc - 1], tuser, targ); 1166440Swnj } else 11715898Sralph (void) sprintf(buf, "rsh %s -n %s %s '%s.%s:%s'", 11815898Sralph argv[i], cmd, src, 11915898Sralph argv[argc - 1], tuser, targ); 1206440Swnj (void) susystem(buf); 1216440Swnj } else { 1226440Swnj if (rem == -1) { 1236440Swnj (void) sprintf(buf, "%s -t %s", 1246440Swnj cmd, targ); 1256440Swnj host = argv[argc - 1]; 126*18126Sralph rem = rcmd(&host, port, pwd->pw_name, tuser, 1276440Swnj buf, 0); 1286440Swnj if (rem < 0) 1296440Swnj exit(1); 1306440Swnj if (response() < 0) 1316440Swnj exit(1); 132*18126Sralph (void) setuid(userid); 1336440Swnj } 1346440Swnj source(1, argv+i); 1356440Swnj } 1366440Swnj } 1376440Swnj } else { 1386440Swnj if (targetshouldbedirectory) 1396440Swnj verifydir(argv[argc - 1]); 1406440Swnj for (i = 0; i < argc - 1; i++) { 1416440Swnj src = colon(argv[i]); 1426440Swnj if (src == 0) { 1436440Swnj (void) sprintf(buf, "/bin/cp%s %s %s", 1446440Swnj iamrecursive ? " -r" : "", 1456440Swnj argv[i], argv[argc - 1]); 1466440Swnj (void) susystem(buf); 1476440Swnj } else { 1486440Swnj *src++ = 0; 14913542Ssam if (*src == 0) 15013542Ssam src = "."; 1516440Swnj suser = rindex(argv[i], '.'); 1526440Swnj if (suser) { 1536440Swnj *suser++ = 0; 1546440Swnj if (!okname(suser)) 1556440Swnj continue; 1566440Swnj } else 1576440Swnj suser = pwd->pw_name; 1586440Swnj (void) sprintf(buf, "%s -f %s", cmd, src); 1596440Swnj host = argv[i]; 160*18126Sralph rem = rcmd(&host, port, pwd->pw_name, suser, 1616440Swnj buf, 0); 1626440Swnj if (rem < 0) 1636440Swnj exit(1); 164*18126Sralph (void) setreuid(0, userid); 1656440Swnj sink(1, argv+argc-1); 166*18126Sralph (void) setreuid(userid, 0); 1676440Swnj (void) close(rem); 1686440Swnj rem = -1; 1696440Swnj } 1706440Swnj } 1716440Swnj } 1726440Swnj exit(errs); 1736440Swnj } 1746440Swnj 1756440Swnj verifydir(cp) 1766440Swnj char *cp; 1776440Swnj { 1786440Swnj struct stat stb; 1796440Swnj 180*18126Sralph if (stat(cp, &stb) >= 0) { 181*18126Sralph if ((stb.st_mode & S_IFMT) == S_IFDIR) 182*18126Sralph return; 183*18126Sralph errno = ENOTDIR; 184*18126Sralph } 1856440Swnj error("rcp: %s: %s.\n", cp, sys_errlist[errno]); 1866440Swnj exit(1); 1876440Swnj } 1886440Swnj 1896440Swnj char * 1906440Swnj colon(cp) 1916440Swnj char *cp; 1926440Swnj { 1936440Swnj 1946440Swnj while (*cp) { 1956440Swnj if (*cp == ':') 1966440Swnj return (cp); 1976440Swnj if (*cp == '/') 1986440Swnj return (0); 1996440Swnj cp++; 2006440Swnj } 2016440Swnj return (0); 2026440Swnj } 2036440Swnj 2046440Swnj okname(cp0) 2056440Swnj char *cp0; 2066440Swnj { 2076440Swnj register char *cp = cp0; 2086440Swnj register int c; 2096440Swnj 2106440Swnj do { 2116440Swnj c = *cp; 2126440Swnj if (c & 0200) 2136440Swnj goto bad; 2146440Swnj if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 2156440Swnj goto bad; 2166440Swnj cp++; 2176440Swnj } while (*cp); 2186440Swnj return (1); 2196440Swnj bad: 2206440Swnj fprintf(stderr, "rcp: invalid user name %s\n", cp0); 2216440Swnj return (0); 2226440Swnj } 2236440Swnj 22417999Sserge susystem(s) 22517999Sserge char *s; 2266440Swnj { 22717999Sserge int status, pid, w; 22817999Sserge register int (*istat)(), (*qstat)(); 2296440Swnj 23017999Sserge if ((pid = vfork()) == 0) { 231*18126Sralph setuid(userid); 23217999Sserge execl("/bin/sh", "sh", "-c", s, (char *)0); 23317999Sserge _exit(127); 23417999Sserge } 23517999Sserge istat = signal(SIGINT, SIG_IGN); 23617999Sserge qstat = signal(SIGQUIT, SIG_IGN); 23717999Sserge while ((w = wait(&status)) != pid && w != -1) 23817999Sserge ; 23917999Sserge if (w == -1) 24017999Sserge status = -1; 24117999Sserge signal(SIGINT, istat); 24217999Sserge signal(SIGQUIT, qstat); 24317999Sserge return (status); 2446440Swnj } 2456440Swnj 2466440Swnj source(argc, argv) 2476440Swnj int argc; 2486440Swnj char **argv; 2496440Swnj { 2506440Swnj char *last, *name; 2516440Swnj struct stat stb; 2526440Swnj char buf[BUFSIZ]; 2536623Smckusick int x, sizerr, f; 2546623Smckusick off_t i; 2556440Swnj 2566440Swnj for (x = 0; x < argc; x++) { 2576440Swnj name = argv[x]; 258*18126Sralph if ((f = open(name, 0)) < 0) { 2596440Swnj error("rcp: %s: %s\n", name, sys_errlist[errno]); 2606440Swnj continue; 2616440Swnj } 2626440Swnj if (fstat(f, &stb) < 0) 2636440Swnj goto notreg; 2646440Swnj switch (stb.st_mode&S_IFMT) { 2656440Swnj 2666440Swnj case S_IFREG: 2676440Swnj break; 2686440Swnj 2696440Swnj case S_IFDIR: 2706440Swnj if (iamrecursive) { 2716440Swnj (void) close(f); 2726440Swnj rsource(name, (int)stb.st_mode); 2736440Swnj continue; 2746440Swnj } 2756440Swnj /* fall into ... */ 2766440Swnj default: 2776440Swnj notreg: 2786440Swnj (void) close(f); 2796440Swnj error("rcp: %s: not a plain file\n", name); 2806440Swnj continue; 2816440Swnj } 2826440Swnj last = rindex(name, '/'); 2836440Swnj if (last == 0) 2846440Swnj last = name; 2856440Swnj else 2866440Swnj last++; 2876623Smckusick (void) sprintf(buf, "C%04o %D %s\n", 2886440Swnj stb.st_mode&07777, stb.st_size, last); 2896440Swnj (void) write(rem, buf, strlen(buf)); 2906440Swnj if (response() < 0) { 2916440Swnj (void) close(f); 2926440Swnj continue; 2936440Swnj } 2946440Swnj sizerr = 0; 2956440Swnj for (i = 0; i < stb.st_size; i += BUFSIZ) { 2966440Swnj int amt = BUFSIZ; 2976440Swnj if (i + amt > stb.st_size) 2986440Swnj amt = stb.st_size - i; 2996440Swnj if (sizerr == 0 && read(f, buf, amt) != amt) 3006440Swnj sizerr = 1; 3016440Swnj (void) write(rem, buf, amt); 3026440Swnj } 3036440Swnj (void) close(f); 3046440Swnj if (sizerr == 0) 3056440Swnj ga(); 3066440Swnj else 3076440Swnj error("rcp: %s: file changed size\n", name); 3086440Swnj (void) response(); 3096440Swnj } 3106440Swnj } 3116440Swnj 31213542Ssam #include <sys/dir.h> 3136440Swnj 3146440Swnj rsource(name, mode) 3156440Swnj char *name; 3166440Swnj int mode; 3176440Swnj { 3186440Swnj DIR *d = opendir(name); 3196440Swnj char *last; 3206440Swnj struct direct *dp; 3216440Swnj char buf[BUFSIZ]; 3226440Swnj char *bufv[1]; 3236440Swnj 3246440Swnj if (d == 0) { 325*18126Sralph error("rcp: %s: %s\n", name, sys_errlist[errno]); 3266440Swnj return; 3276440Swnj } 3286440Swnj last = rindex(name, '/'); 3296440Swnj if (last == 0) 3306440Swnj last = name; 3316440Swnj else 3326440Swnj last++; 3336440Swnj (void) sprintf(buf, "D%04o %d %s\n", mode&07777, 0, last); 3346440Swnj (void) write(rem, buf, strlen(buf)); 3356440Swnj if (response() < 0) { 3366440Swnj closedir(d); 3376440Swnj return; 3386440Swnj } 3396440Swnj while (dp = readdir(d)) { 3406440Swnj if (dp->d_ino == 0) 3416440Swnj continue; 3426440Swnj if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 3436440Swnj continue; 3446440Swnj if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 3456440Swnj error("%s/%s: Name too long.\n", name, dp->d_name); 3466440Swnj continue; 3476440Swnj } 3486440Swnj (void) sprintf(buf, "%s/%s", name, dp->d_name); 3496440Swnj bufv[0] = buf; 3506440Swnj source(1, bufv); 3516440Swnj } 3526440Swnj closedir(d); 3536440Swnj (void) write(rem, "E\n", 2); 3546440Swnj (void) response(); 3556440Swnj } 3566440Swnj 3576440Swnj response() 3586440Swnj { 3596440Swnj char resp, c, rbuf[BUFSIZ], *cp = rbuf; 3606440Swnj 3616440Swnj if (read(rem, &resp, 1) != 1) 3626440Swnj lostconn(); 3636440Swnj switch (resp) { 3646440Swnj 3656440Swnj case 0: 3666440Swnj return (0); 3676440Swnj 3686440Swnj default: 3696440Swnj *cp++ = resp; 3706440Swnj /* fall into... */ 3716440Swnj case 1: 3726440Swnj case 2: 3736440Swnj do { 3746440Swnj if (read(rem, &c, 1) != 1) 3756440Swnj lostconn(); 3766440Swnj *cp++ = c; 3776440Swnj } while (cp < &rbuf[BUFSIZ] && c != '\n'); 3786440Swnj if (iamremote == 0) 3796440Swnj (void) write(2, rbuf, cp - rbuf); 3806440Swnj errs++; 3816440Swnj if (resp == 1) 3826440Swnj return (-1); 3836440Swnj exit(1); 3846440Swnj } 3856440Swnj /*NOTREACHED*/ 3866440Swnj } 3876440Swnj 3886440Swnj lostconn() 3896440Swnj { 3906440Swnj 3916440Swnj if (iamremote == 0) 3926440Swnj fprintf(stderr, "rcp: lost connection\n"); 3936440Swnj exit(1); 3946440Swnj } 3956440Swnj 3966440Swnj sink(argc, argv) 3976440Swnj int argc; 3986440Swnj char **argv; 3996440Swnj { 4006440Swnj char *targ; 4016440Swnj char cmdbuf[BUFSIZ], nambuf[BUFSIZ], buf[BUFSIZ], *cp; 40214580Sralph int of, mode, wrerr, exists, first; 4036623Smckusick off_t i, size; 4046440Swnj char *whopp; 4056440Swnj struct stat stb; int targisdir = 0; 4066440Swnj #define SCREWUP(str) { whopp = str; goto screwup; } 4076440Swnj int mask = umask(0); 4086440Swnj char *myargv[1]; 4096440Swnj 4106440Swnj umask(mask); 411*18126Sralph if (argc != 1) { 4126440Swnj error("rcp: ambiguous target\n"); 4136440Swnj exit(1); 4146440Swnj } 4156440Swnj targ = *argv; 4166440Swnj if (targetshouldbedirectory) 4176440Swnj verifydir(targ); 4186440Swnj ga(); 4196440Swnj if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) 4206440Swnj targisdir = 1; 42114580Sralph for (first = 1; ; first = 0) { 4226440Swnj cp = cmdbuf; 4236440Swnj if (read(rem, cp, 1) <= 0) 4246440Swnj return; 4256440Swnj if (*cp++ == '\n') 4266440Swnj SCREWUP("unexpected '\\n'"); 4276440Swnj do { 4286440Swnj if (read(rem, cp, 1) != 1) 4296440Swnj SCREWUP("lost connection"); 4306440Swnj } while (*cp++ != '\n'); 4316440Swnj *cp = 0; 4326440Swnj if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { 4336440Swnj if (iamremote == 0) 43413542Ssam (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); 4356440Swnj if (cmdbuf[0] == '\02') 4366440Swnj exit(1); 4376440Swnj errs++; 4386440Swnj continue; 4396440Swnj } 4406440Swnj *--cp = 0; 4416440Swnj cp = cmdbuf; 4426440Swnj if (*cp == 'E') { 4436440Swnj ga(); 4446440Swnj return; 4456440Swnj } 44614580Sralph if (*cp != 'C' && *cp != 'D') { 44714580Sralph /* 44814580Sralph * Check for the case "rcp remote:foo\* local:bar". 44914580Sralph * In this case, the line "No match." can be returned 45014580Sralph * by the shell before the rcp command on the remote is 45114580Sralph * executed so the ^Aerror_message convention isn't 45214580Sralph * followed. 45314580Sralph */ 45414580Sralph if (first) { 45514580Sralph error("%s\n", cp); 45614580Sralph exit(1); 45714580Sralph } 4586440Swnj SCREWUP("expected control record"); 45914580Sralph } 4606440Swnj cp++; 4616440Swnj mode = 0; 4626440Swnj for (; cp < cmdbuf+5; cp++) { 4636440Swnj if (*cp < '0' || *cp > '7') 4646440Swnj SCREWUP("bad mode"); 4656440Swnj mode = (mode << 3) | (*cp - '0'); 4666440Swnj } 4676440Swnj if (*cp++ != ' ') 4686440Swnj SCREWUP("mode not delimited"); 4696440Swnj size = 0; 4706440Swnj while (*cp >= '0' && *cp <= '9') 4716440Swnj size = size * 10 + (*cp++ - '0'); 4726440Swnj if (*cp++ != ' ') 4736440Swnj SCREWUP("size not delimited"); 4746440Swnj if (targisdir) 4756440Swnj (void) sprintf(nambuf, "%s%s%s", targ, 4766440Swnj *targ ? "/" : "", cp); 4776440Swnj else 4786440Swnj (void) strcpy(nambuf, targ); 4796440Swnj exists = stat(nambuf, &stb) == 0; 480*18126Sralph if (cmdbuf[0] == 'D') { 481*18126Sralph if (exists) { 4826440Swnj if ((stb.st_mode&S_IFMT) != S_IFDIR) { 4836440Swnj errno = ENOTDIR; 4846440Swnj goto bad; 4856440Swnj } 486*18126Sralph } else if (mkdir(nambuf, mode) < 0) 4876440Swnj goto bad; 4886440Swnj myargv[0] = nambuf; 4896440Swnj sink(1, myargv); 4906440Swnj continue; 491*18126Sralph } 492*18126Sralph if ((of = creat(nambuf, mode)) < 0) { 4936440Swnj bad: 4946440Swnj error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); 4956440Swnj continue; 4966440Swnj } 497*18126Sralph if (exists) 498*18126Sralph (void) fchmod(of, mode &~ mask); 4996440Swnj ga(); 5006440Swnj wrerr = 0; 5016440Swnj for (i = 0; i < size; i += BUFSIZ) { 5026440Swnj int amt = BUFSIZ; 5036440Swnj char *cp = buf; 5046440Swnj 5056440Swnj if (i + amt > size) 5066440Swnj amt = size - i; 5076440Swnj do { 5086440Swnj int j = read(rem, cp, amt); 5096440Swnj 5106440Swnj if (j <= 0) 5116440Swnj exit(1); 5126440Swnj amt -= j; 5136440Swnj cp += j; 5146440Swnj } while (amt > 0); 5156440Swnj amt = BUFSIZ; 5166440Swnj if (i + amt > size) 5176440Swnj amt = size - i; 5186440Swnj if (wrerr == 0 && write(of, buf, amt) != amt) 5196440Swnj wrerr++; 5206440Swnj } 5216440Swnj (void) close(of); 5226440Swnj (void) response(); 5236440Swnj if (wrerr) 5246440Swnj error("rcp: %s: %s\n", cp, sys_errlist[errno]); 5256440Swnj else 5266440Swnj ga(); 5276440Swnj } 5286440Swnj screwup: 5296440Swnj error("rcp: protocol screwup: %s\n", whopp); 5306440Swnj exit(1); 5316440Swnj } 5326440Swnj 5336440Swnj /*VARARGS*/ 5346440Swnj error(fmt, a1, a2, a3, a4, a5) 5356440Swnj char *fmt; 5366440Swnj int a1, a2, a3, a4, a5; 5376440Swnj { 5386440Swnj char buf[BUFSIZ], *cp = buf; 5396440Swnj 5406440Swnj errs++; 5416440Swnj *cp++ = 1; 5426440Swnj (void) sprintf(cp, fmt, a1, a2, a3, a4, a5); 5436440Swnj (void) write(rem, buf, strlen(buf)); 5446440Swnj if (iamremote == 0) 5456440Swnj (void) write(2, buf+1, strlen(buf+1)); 5466440Swnj } 547