xref: /csrg-svn/bin/rcp/rcp.c (revision 66619)
121569Sdist /*
260688Sbostic  * Copyright (c) 1983, 1990, 1992, 1993
360688Sbostic  *	The Regents of the University of California.  All rights reserved.
435619Sbostic  *
542539Sbostic  * %sccs.include.redist.c%
621569Sdist  */
721569Sdist 
86440Swnj #ifndef lint
960688Sbostic static char copyright[] =
1060688Sbostic "@(#) Copyright (c) 1983, 1990, 1992, 1993\n\
1160688Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1235619Sbostic #endif /* not lint */
136440Swnj 
1421569Sdist #ifndef lint
15*66619Spendry static char sccsid[] = "@(#)rcp.c	8.2 (Berkeley) 04/02/94";
1635619Sbostic #endif /* not lint */
1721569Sdist 
186720Smckusick #include <sys/param.h>
196440Swnj #include <sys/stat.h>
2023101Slepreau #include <sys/time.h>
2146655Sbostic #include <sys/socket.h>
229199Ssam #include <netinet/in.h>
2344342Skarels #include <netinet/in_systm.h>
2444342Skarels #include <netinet/ip.h>
2554151Sbostic 
2656587Sbostic #include <ctype.h>
2746655Sbostic #include <dirent.h>
2859512Sbostic #include <err.h>
2956587Sbostic #include <errno.h>
3046655Sbostic #include <fcntl.h>
3156587Sbostic #include <netdb.h>
326440Swnj #include <pwd.h>
3356587Sbostic #include <signal.h>
3438098Sbostic #include <stdio.h>
3545647Storek #include <stdlib.h>
3646655Sbostic #include <string.h>
3756587Sbostic #include <string.h>
3856587Sbostic #include <unistd.h>
3956587Sbostic 
4037037Sbostic #include "pathnames.h"
4154151Sbostic #include "extern.h"
4212989Ssam 
4338098Sbostic #ifdef KERBEROS
4442498Sbostic #include <kerberosIV/des.h>
4541785Sbostic #include <kerberosIV/krb.h>
4654151Sbostic 
4738879Skfall char	dst_realm_buf[REALM_SZ];
4838879Skfall char	*dest_realm = NULL;
4945397Smckusick int	use_kerberos = 1;
5038879Skfall CREDENTIALS 	cred;
5138879Skfall Key_schedule	schedule;
5238879Skfall extern	char	*krb_realmofhost();
5359512Sbostic #ifdef CRYPT
5459512Sbostic int	doencrypt = 0;
5559512Sbostic #define	OPTIONS	"dfKk:prtx"
5659512Sbostic #else
5754151Sbostic #define	OPTIONS	"dfKk:prt"
5859512Sbostic #endif
5954151Sbostic #else
6038879Skfall #define	OPTIONS "dfprt"
6136624Skfall #endif
6236624Skfall 
6338098Sbostic struct passwd *pwd;
6438879Skfall u_short	port;
6538879Skfall uid_t	userid;
6638879Skfall int errs, rem;
6738879Skfall int pflag, iamremote, iamrecursive, targetshouldbedirectory;
686440Swnj 
6938879Skfall #define	CMDNEEDS	64
7038100Sbostic char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
7138098Sbostic 
7254151Sbostic #ifdef KERBEROS
7354151Sbostic int	 kerberos __P((char **, char *, char *, char *));
7454151Sbostic void	 oldw __P((const char *, ...));
7554151Sbostic #endif
7654151Sbostic int	 response __P((void));
7754151Sbostic void	 rsource __P((char *, struct stat *));
7854151Sbostic void	 sink __P((int, char *[]));
7954151Sbostic void	 source __P((int, char *[]));
8054151Sbostic void	 tolocal __P((int, char *[]));
8154151Sbostic void	 toremote __P((char *, int, char *[]));
8254151Sbostic void	 usage __P((void));
8321254Smckusick 
8454151Sbostic int
main(argc,argv)856440Swnj main(argc, argv)
866440Swnj 	int argc;
8754151Sbostic 	char *argv[];
886440Swnj {
8918126Sralph 	struct servent *sp;
9038099Sbostic 	int ch, fflag, tflag;
9154151Sbostic 	char *targ, *shell;
9218026Sralph 
9338099Sbostic 	fflag = tflag = 0;
9438879Skfall 	while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
9554151Sbostic 		switch(ch) {			/* User-visible flags. */
9654151Sbostic 		case 'K':
9754151Sbostic #ifdef KERBEROS
9854151Sbostic 			use_kerberos = 0;
9954151Sbostic #endif
10023101Slepreau 			break;
10138879Skfall #ifdef	KERBEROS
10238879Skfall 		case 'k':
10338879Skfall 			dest_realm = dst_realm_buf;
10454151Sbostic 			(void)strncpy(dst_realm_buf, optarg, REALM_SZ);
10538099Sbostic 			break;
10659512Sbostic #ifdef CRYPT
10759512Sbostic 		case 'x':
10859512Sbostic 			doencrypt = 1;
10959512Sbostic 			/* des_set_key(cred.session, schedule); */
11059512Sbostic 			break;
11138098Sbostic #endif
11259512Sbostic #endif
11354151Sbostic 		case 'p':
11454151Sbostic 			pflag = 1;
11554151Sbostic 			break;
11654151Sbostic 		case 'r':
11754151Sbostic 			iamrecursive = 1;
11854151Sbostic 			break;
11954151Sbostic 						/* Server options. */
12038879Skfall 		case 'd':
12138879Skfall 			targetshouldbedirectory = 1;
12238879Skfall 			break;
12338879Skfall 		case 'f':			/* "from" */
12438879Skfall 			iamremote = 1;
12538879Skfall 			fflag = 1;
12638879Skfall 			break;
12738879Skfall 		case 't':			/* "to" */
12838879Skfall 			iamremote = 1;
12938879Skfall 			tflag = 1;
13038879Skfall 			break;
13138098Sbostic 		case '?':
13238098Sbostic 		default:
13332127Sbostic 			usage();
13423101Slepreau 		}
13538098Sbostic 	argc -= optind;
13638098Sbostic 	argv += optind;
13738098Sbostic 
13838879Skfall #ifdef KERBEROS
13954151Sbostic 	if (use_kerberos) {
14059512Sbostic #ifdef CRYPT
14159512Sbostic 		shell = doencrypt ? "ekshell" : "kshell";
14259512Sbostic #else
14354151Sbostic 		shell = "kshell";
14459512Sbostic #endif
14554151Sbostic 		if ((sp = getservbyname(shell, "tcp")) == NULL) {
14654151Sbostic 			use_kerberos = 0;
14754151Sbostic 			oldw("can't get entry for %s/tcp service", shell);
14854151Sbostic 			sp = getservbyname(shell = "shell", "tcp");
14954151Sbostic 		}
15054151Sbostic 	} else
15145647Storek 		sp = getservbyname(shell = "shell", "tcp");
15238879Skfall #else
15345647Storek 	sp = getservbyname(shell = "shell", "tcp");
15438879Skfall #endif
15559512Sbostic 	if (sp == NULL)
15659512Sbostic 		errx(1, "%s/tcp: unknown service", shell);
15738879Skfall 	port = sp->s_port;
15838879Skfall 
15959512Sbostic 	if ((pwd = getpwuid(userid = getuid())) == NULL)
16059512Sbostic 		errx(1, "unknown user %d", (int)userid);
16138879Skfall 
16254151Sbostic 	rem = STDIN_FILENO;		/* XXX */
16354151Sbostic 
16454151Sbostic 	if (fflag) {			/* Follow "protocol", send data. */
16538099Sbostic 		(void)response();
16638099Sbostic 		(void)setuid(userid);
16738099Sbostic 		source(argc, argv);
16838099Sbostic 		exit(errs);
16938099Sbostic 	}
17038099Sbostic 
17154151Sbostic 	if (tflag) {			/* Receive data. */
17238099Sbostic 		(void)setuid(userid);
17338099Sbostic 		sink(argc, argv);
17438099Sbostic 		exit(errs);
17538099Sbostic 	}
17638099Sbostic 
17732127Sbostic 	if (argc < 2)
17832127Sbostic 		usage();
1796440Swnj 	if (argc > 2)
1806440Swnj 		targetshouldbedirectory = 1;
18138098Sbostic 
18232127Sbostic 	rem = -1;
18354151Sbostic 	/* Command to be executed on remote system using "rsh". */
18438879Skfall #ifdef	KERBEROS
18546655Sbostic 	(void)snprintf(cmd, sizeof(cmd),
18646655Sbostic 	    "rcp%s%s%s%s", iamrecursive ? " -r" : "",
18759512Sbostic #ifdef CRYPT
18859512Sbostic 	    (doencrypt && use_kerberos ? " -x" : ""),
18959512Sbostic #else
19045397Smckusick 	    "",
19159512Sbostic #endif
19238879Skfall 	    pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
19338879Skfall #else
19446655Sbostic 	(void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
19546655Sbostic 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
19646655Sbostic 	    targetshouldbedirectory ? " -d" : "");
19738879Skfall #endif
19838098Sbostic 
19938098Sbostic 	(void)signal(SIGPIPE, lostconn);
20038098Sbostic 
20154151Sbostic 	if (targ = colon(argv[argc - 1]))	/* Dest is remote host. */
20254151Sbostic 		toremote(targ, argc, argv);
20338098Sbostic 	else {
20454151Sbostic 		tolocal(argc, argv);		/* Dest is local host. */
20538098Sbostic 		if (targetshouldbedirectory)
20638098Sbostic 			verifydir(argv[argc - 1]);
20738098Sbostic 	}
20838098Sbostic 	exit(errs);
20938098Sbostic }
21038098Sbostic 
21154151Sbostic void
toremote(targ,argc,argv)21238098Sbostic toremote(targ, argc, argv)
21354151Sbostic 	char *targ, *argv[];
21438098Sbostic 	int argc;
21538098Sbostic {
21646655Sbostic 	int i, len, tos;
21738100Sbostic 	char *bp, *host, *src, *suser, *thost, *tuser;
21838098Sbostic 
21938098Sbostic 	*targ++ = 0;
22038098Sbostic 	if (*targ == 0)
22138098Sbostic 		targ = ".";
22238098Sbostic 
22359512Sbostic 	if (thost = strchr(argv[argc - 1], '@')) {
22438879Skfall 		/* user@host */
22538098Sbostic 		*thost++ = 0;
22638098Sbostic 		tuser = argv[argc - 1];
22738098Sbostic 		if (*tuser == '\0')
22824711Sbloom 			tuser = NULL;
22938098Sbostic 		else if (!okname(tuser))
23038098Sbostic 			exit(1);
23138098Sbostic 	} else {
23238098Sbostic 		thost = argv[argc - 1];
23338098Sbostic 		tuser = NULL;
23438098Sbostic 	}
23536624Skfall 
23638098Sbostic 	for (i = 0; i < argc - 1; i++) {
23738098Sbostic 		src = colon(argv[i]);
23838098Sbostic 		if (src) {			/* remote to remote */
23938098Sbostic 			*src++ = 0;
24038098Sbostic 			if (*src == 0)
24138098Sbostic 				src = ".";
24259512Sbostic 			host = strchr(argv[i], '@');
24346655Sbostic 			len = strlen(_PATH_RSH) + strlen(argv[i]) +
24446655Sbostic 			    strlen(src) + (tuser ? strlen(tuser) : 0) +
24546655Sbostic 			    strlen(thost) + strlen(targ) + CMDNEEDS + 20;
24646655Sbostic 			if (!(bp = malloc(len)))
24759512Sbostic 				err(1, NULL);
24838098Sbostic 			if (host) {
24938098Sbostic 				*host++ = 0;
25038098Sbostic 				suser = argv[i];
25138098Sbostic 				if (*suser == '\0')
25238098Sbostic 					suser = pwd->pw_name;
25338098Sbostic 				else if (!okname(suser))
25438098Sbostic 					continue;
25546655Sbostic 				(void)snprintf(bp, len,
25638098Sbostic 				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
25738098Sbostic 				    _PATH_RSH, host, suser, cmd, src,
25838098Sbostic 				    tuser ? tuser : "", tuser ? "@" : "",
25938098Sbostic 				    thost, targ);
26038098Sbostic 			} else
26146655Sbostic 				(void)snprintf(bp, len,
262*66619Spendry 				    "exec %s %s -n %s %s '%s%s%s:%s'",
26338098Sbostic 				    _PATH_RSH, argv[i], cmd, src,
26438098Sbostic 				    tuser ? tuser : "", tuser ? "@" : "",
26538098Sbostic 				    thost, targ);
26654151Sbostic 			(void)susystem(bp, userid);
26738100Sbostic 			(void)free(bp);
26838098Sbostic 		} else {			/* local to remote */
26938098Sbostic 			if (rem == -1) {
27046655Sbostic 				len = strlen(targ) + CMDNEEDS + 20;
27146655Sbostic 				if (!(bp = malloc(len)))
27259512Sbostic 					err(1, NULL);
27346655Sbostic 				(void)snprintf(bp, len, "%s -t %s", cmd, targ);
27438098Sbostic 				host = thost;
27538098Sbostic #ifdef KERBEROS
27638098Sbostic 				if (use_kerberos)
27738879Skfall 					rem = kerberos(&host, bp,
27838879Skfall 					    pwd->pw_name,
27938100Sbostic 					    tuser ? tuser : pwd->pw_name);
28038098Sbostic 				else
28138098Sbostic #endif
28224711Sbloom 					rem = rcmd(&host, port, pwd->pw_name,
28324711Sbloom 					    tuser ? tuser : pwd->pw_name,
28438100Sbostic 					    bp, 0);
28538098Sbostic 				if (rem < 0)
28638098Sbostic 					exit(1);
28744342Skarels 				tos = IPTOS_THROUGHPUT;
28844342Skarels 				if (setsockopt(rem, IPPROTO_IP, IP_TOS,
28954151Sbostic 				    &tos, sizeof(int)) < 0)
29059512Sbostic 					warn("TOS (ignored)");
29138098Sbostic 				if (response() < 0)
29238098Sbostic 					exit(1);
29338100Sbostic 				(void)free(bp);
29438098Sbostic 				(void)setuid(userid);
2956440Swnj 			}
29638098Sbostic 			source(1, argv+i);
2976440Swnj 		}
29838098Sbostic 	}
29938098Sbostic }
30038098Sbostic 
30154151Sbostic void
tolocal(argc,argv)30238098Sbostic tolocal(argc, argv)
30338098Sbostic 	int argc;
30454151Sbostic 	char *argv[];
30538098Sbostic {
30646655Sbostic 	int i, len, tos;
30738100Sbostic 	char *bp, *host, *src, *suser;
30838098Sbostic 
30938098Sbostic 	for (i = 0; i < argc - 1; i++) {
31054151Sbostic 		if (!(src = colon(argv[i]))) {		/* Local to local. */
31146655Sbostic 			len = strlen(_PATH_CP) + strlen(argv[i]) +
31246655Sbostic 			    strlen(argv[argc - 1]) + 20;
31346655Sbostic 			if (!(bp = malloc(len)))
31459512Sbostic 				err(1, NULL);
315*66619Spendry 			(void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
31638100Sbostic 			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
31738100Sbostic 			    argv[i], argv[argc - 1]);
31854570Sandrew 			if (susystem(bp, userid))
31954570Sandrew 				++errs;
32038100Sbostic 			(void)free(bp);
32138100Sbostic 			continue;
32238100Sbostic 		}
32338100Sbostic 		*src++ = 0;
32438100Sbostic 		if (*src == 0)
32538100Sbostic 			src = ".";
32659512Sbostic 		if ((host = strchr(argv[i], '@')) == NULL) {
32754151Sbostic 			host = argv[i];
32854151Sbostic 			suser = pwd->pw_name;
32954151Sbostic 		} else {
33038100Sbostic 			*host++ = 0;
33138100Sbostic 			suser = argv[i];
33238100Sbostic 			if (*suser == '\0')
33338098Sbostic 				suser = pwd->pw_name;
33438100Sbostic 			else if (!okname(suser))
33538100Sbostic 				continue;
33638100Sbostic 		}
33746655Sbostic 		len = strlen(src) + CMDNEEDS + 20;
33854151Sbostic 		if ((bp = malloc(len)) == NULL)
33959512Sbostic 			err(1, NULL);
34046655Sbostic 		(void)snprintf(bp, len, "%s -f %s", cmd, src);
34154151Sbostic 		rem =
34238098Sbostic #ifdef KERBEROS
34354151Sbostic 		    use_kerberos ?
34454151Sbostic 			kerberos(&host, bp, pwd->pw_name, suser) :
34538098Sbostic #endif
34654151Sbostic 			rcmd(&host, port, pwd->pw_name, suser, bp, 0);
34738100Sbostic 		(void)free(bp);
34854570Sandrew 		if (rem < 0) {
34954570Sandrew 			++errs;
35038100Sbostic 			continue;
35154570Sandrew 		}
35244342Skarels 		(void)seteuid(userid);
35344342Skarels 		tos = IPTOS_THROUGHPUT;
35454151Sbostic 		if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
35559512Sbostic 			warn("TOS (ignored)");
35638100Sbostic 		sink(1, argv + argc - 1);
35744342Skarels 		(void)seteuid(0);
35838100Sbostic 		(void)close(rem);
35938100Sbostic 		rem = -1;
3606440Swnj 	}
3616440Swnj }
3626440Swnj 
36354151Sbostic void
source(argc,argv)3646440Swnj source(argc, argv)
3656440Swnj 	int argc;
36654151Sbostic 	char *argv[];
3676440Swnj {
3686440Swnj 	struct stat stb;
36938098Sbostic 	static BUF buffer;
37038098Sbostic 	BUF *bp;
37138098Sbostic 	off_t i;
37254570Sandrew 	int amt, fd, haderr, indx, result;
37338098Sbostic 	char *last, *name, buf[BUFSIZ];
3746440Swnj 
37554570Sandrew 	for (indx = 0; indx < argc; ++indx) {
37654570Sandrew                 name = argv[indx];
37754151Sbostic 		if ((fd = open(name, O_RDONLY, 0)) < 0)
37854151Sbostic 			goto syserr;
37954151Sbostic 		if (fstat(fd, &stb)) {
38059512Sbostic syserr:			run_err("%s: %s", name, strerror(errno));
38154151Sbostic 			goto next;
3826440Swnj 		}
38354151Sbostic 		switch (stb.st_mode & S_IFMT) {
3846440Swnj 		case S_IFREG:
3856440Swnj 			break;
3866440Swnj 		case S_IFDIR:
3876440Swnj 			if (iamrecursive) {
38823101Slepreau 				rsource(name, &stb);
38954151Sbostic 				goto next;
3906440Swnj 			}
39138098Sbostic 			/* FALLTHROUGH */
3926440Swnj 		default:
39359512Sbostic 			run_err("%s: not a regular file", name);
39454151Sbostic 			goto next;
3956440Swnj 		}
39659512Sbostic 		if ((last = strrchr(name, '/')) == NULL)
3976440Swnj 			last = name;
3986440Swnj 		else
39954151Sbostic 			++last;
40023101Slepreau 		if (pflag) {
40123101Slepreau 			/*
40223101Slepreau 			 * Make it compatible with possible future
40323101Slepreau 			 * versions expecting microseconds.
40423101Slepreau 			 */
40556587Sbostic 			(void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
40654151Sbostic 			    stb.st_mtimespec.ts_sec, stb.st_atimespec.ts_sec);
40756587Sbostic 			(void)write(rem, buf, strlen(buf));
40854151Sbostic 			if (response() < 0)
40954151Sbostic 				goto next;
41023101Slepreau 		}
41154151Sbostic #define	MODEMASK	(S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
41256587Sbostic 		(void)snprintf(buf, sizeof(buf), "C%04o %qd %s\n",
41354151Sbostic 		    stb.st_mode & MODEMASK, stb.st_size, last);
41456587Sbostic 		(void)write(rem, buf, strlen(buf));
41554151Sbostic 		if (response() < 0)
41654151Sbostic 			goto next;
41754570Sandrew 		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
41854570Sandrew next:			(void)close(fd);
41954570Sandrew 			continue;
42054570Sandrew 		}
42154570Sandrew 
42254570Sandrew 		/* Keep writing after an error so that we stay sync'd up. */
42354570Sandrew 		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
42421254Smckusick 			amt = bp->cnt;
4256440Swnj 			if (i + amt > stb.st_size)
4266440Swnj 				amt = stb.st_size - i;
42754570Sandrew 			if (!haderr) {
42854570Sandrew 				result = read(fd, bp->buf, amt);
42954570Sandrew 				if (result != amt)
43054570Sandrew 					haderr = result >= 0 ? EIO : errno;
43154151Sbostic 			}
43254570Sandrew 			if (haderr)
43354570Sandrew 				(void)write(rem, bp->buf, amt);
43454570Sandrew 			else {
43554570Sandrew 				result = write(rem, bp->buf, amt);
43654570Sandrew 				if (result != amt)
43754570Sandrew 					haderr = result >= 0 ? EIO : errno;
43854570Sandrew 			}
4396440Swnj 		}
44054570Sandrew 		if (close(fd) && !haderr)
44154570Sandrew 			haderr = errno;
44254570Sandrew 		if (!haderr)
44338098Sbostic 			(void)write(rem, "", 1);
44454570Sandrew 		else
44559512Sbostic 			run_err("%s: %s", name, strerror(haderr));
44638098Sbostic 		(void)response();
4476440Swnj 	}
4486440Swnj }
4496440Swnj 
45054151Sbostic void
rsource(name,statp)45123101Slepreau rsource(name, statp)
4526440Swnj 	char *name;
45323101Slepreau 	struct stat *statp;
4546440Swnj {
45546655Sbostic 	DIR *dirp;
45646655Sbostic 	struct dirent *dp;
45738100Sbostic 	char *last, *vect[1], path[MAXPATHLEN];
4586440Swnj 
45946655Sbostic 	if (!(dirp = opendir(name))) {
46059512Sbostic 		run_err("%s: %s", name, strerror(errno));
4616440Swnj 		return;
4626440Swnj 	}
46359512Sbostic 	last = strrchr(name, '/');
4646440Swnj 	if (last == 0)
4656440Swnj 		last = name;
4666440Swnj 	else
4676440Swnj 		last++;
46823101Slepreau 	if (pflag) {
46956587Sbostic 		(void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
47054151Sbostic 		    statp->st_mtimespec.ts_sec, statp->st_atimespec.ts_sec);
47156587Sbostic 		(void)write(rem, path, strlen(path));
47223101Slepreau 		if (response() < 0) {
47346655Sbostic 			closedir(dirp);
47423101Slepreau 			return;
47523101Slepreau 		}
47623101Slepreau 	}
47756587Sbostic 	(void)snprintf(path, sizeof(path),
47854151Sbostic 	    "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
47956587Sbostic 	(void)write(rem, path, strlen(path));
4806440Swnj 	if (response() < 0) {
48146655Sbostic 		closedir(dirp);
4826440Swnj 		return;
4836440Swnj 	}
48446655Sbostic 	while (dp = readdir(dirp)) {
4856440Swnj 		if (dp->d_ino == 0)
4866440Swnj 			continue;
4876440Swnj 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
4886440Swnj 			continue;
48938100Sbostic 		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
49059512Sbostic 			run_err("%s/%s: name too long", name, dp->d_name);
4916440Swnj 			continue;
4926440Swnj 		}
49346655Sbostic 		(void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
49438100Sbostic 		vect[0] = path;
49538100Sbostic 		source(1, vect);
4966440Swnj 	}
49754151Sbostic 	(void)closedir(dirp);
49838098Sbostic 	(void)write(rem, "E\n", 2);
49938098Sbostic 	(void)response();
5006440Swnj }
5016440Swnj 
50246655Sbostic void
sink(argc,argv)5036440Swnj sink(argc, argv)
5046440Swnj 	int argc;
50554151Sbostic 	char *argv[];
5066440Swnj {
50754151Sbostic 	static BUF buffer;
50821254Smckusick 	struct stat stb;
50923101Slepreau 	struct timeval tv[2];
51038469Sbostic 	enum { YES, NO, DISPLAYED } wrerr;
51154151Sbostic 	BUF *bp;
51238100Sbostic 	off_t i, j;
51354570Sandrew 	int amt, count, exists, first, mask, mode, ofd, omode;
51454570Sandrew 	int setimes, size, targisdir, wrerrno;
515*66619Spendry 	char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ];
5166440Swnj 
51738100Sbostic #define	atime	tv[0]
51838100Sbostic #define	mtime	tv[1]
51938100Sbostic #define	SCREWUP(str)	{ why = str; goto screwup; }
52038100Sbostic 
52138100Sbostic 	setimes = targisdir = 0;
52238100Sbostic 	mask = umask(0);
52323112Slepreau 	if (!pflag)
52438098Sbostic 		(void)umask(mask);
52518126Sralph 	if (argc != 1) {
52659512Sbostic 		run_err("ambiguous target");
5276440Swnj 		exit(1);
5286440Swnj 	}
5296440Swnj 	targ = *argv;
5306440Swnj 	if (targetshouldbedirectory)
5316440Swnj 		verifydir(targ);
53238098Sbostic 	(void)write(rem, "", 1);
53354151Sbostic 	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
5346440Swnj 		targisdir = 1;
53538100Sbostic 	for (first = 1;; first = 0) {
53638100Sbostic 		cp = buf;
5376440Swnj 		if (read(rem, cp, 1) <= 0)
5386440Swnj 			return;
5396440Swnj 		if (*cp++ == '\n')
54038100Sbostic 			SCREWUP("unexpected <newline>");
5416440Swnj 		do {
54238100Sbostic 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
5436440Swnj 				SCREWUP("lost connection");
54438100Sbostic 			*cp++ = ch;
54538100Sbostic 		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
5466440Swnj 		*cp = 0;
54738100Sbostic 
54838100Sbostic 		if (buf[0] == '\01' || buf[0] == '\02') {
5496440Swnj 			if (iamremote == 0)
55054151Sbostic 				(void)write(STDERR_FILENO,
55154151Sbostic 				    buf + 1, strlen(buf + 1));
55238100Sbostic 			if (buf[0] == '\02')
5536440Swnj 				exit(1);
55454151Sbostic 			++errs;
5556440Swnj 			continue;
5566440Swnj 		}
55738100Sbostic 		if (buf[0] == 'E') {
55838098Sbostic 			(void)write(rem, "", 1);
5596440Swnj 			return;
5606440Swnj 		}
56123101Slepreau 
56238108Sbostic 		if (ch == '\n')
56338115Sbostic 			*--cp = 0;
56438108Sbostic 
56523101Slepreau #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
56638100Sbostic 		cp = buf;
56723101Slepreau 		if (*cp == 'T') {
56823101Slepreau 			setimes++;
56923101Slepreau 			cp++;
57023101Slepreau 			getnum(mtime.tv_sec);
57123101Slepreau 			if (*cp++ != ' ')
57223101Slepreau 				SCREWUP("mtime.sec not delimited");
57323101Slepreau 			getnum(mtime.tv_usec);
57423101Slepreau 			if (*cp++ != ' ')
57523101Slepreau 				SCREWUP("mtime.usec not delimited");
57623101Slepreau 			getnum(atime.tv_sec);
57723101Slepreau 			if (*cp++ != ' ')
57823101Slepreau 				SCREWUP("atime.sec not delimited");
57923101Slepreau 			getnum(atime.tv_usec);
58023101Slepreau 			if (*cp++ != '\0')
58123101Slepreau 				SCREWUP("atime.usec not delimited");
58238098Sbostic 			(void)write(rem, "", 1);
58323101Slepreau 			continue;
58423101Slepreau 		}
58514580Sralph 		if (*cp != 'C' && *cp != 'D') {
58614580Sralph 			/*
58714580Sralph 			 * Check for the case "rcp remote:foo\* local:bar".
58814580Sralph 			 * In this case, the line "No match." can be returned
58914580Sralph 			 * by the shell before the rcp command on the remote is
59014580Sralph 			 * executed so the ^Aerror_message convention isn't
59114580Sralph 			 * followed.
59214580Sralph 			 */
59314580Sralph 			if (first) {
59459512Sbostic 				run_err("%s", cp);
59514580Sralph 				exit(1);
59614580Sralph 			}
5976440Swnj 			SCREWUP("expected control record");
59814580Sralph 		}
5996440Swnj 		mode = 0;
60038100Sbostic 		for (++cp; cp < buf + 5; cp++) {
6016440Swnj 			if (*cp < '0' || *cp > '7')
6026440Swnj 				SCREWUP("bad mode");
6036440Swnj 			mode = (mode << 3) | (*cp - '0');
6046440Swnj 		}
6056440Swnj 		if (*cp++ != ' ')
6066440Swnj 			SCREWUP("mode not delimited");
60754151Sbostic 
60854151Sbostic 		for (size = 0; isdigit(*cp);)
6096440Swnj 			size = size * 10 + (*cp++ - '0');
6106440Swnj 		if (*cp++ != ' ')
6116440Swnj 			SCREWUP("size not delimited");
61238100Sbostic 		if (targisdir) {
61338100Sbostic 			static char *namebuf;
61438100Sbostic 			static int cursize;
61545647Storek 			size_t need;
61638100Sbostic 
61738100Sbostic 			need = strlen(targ) + strlen(cp) + 250;
61838100Sbostic 			if (need > cursize) {
61945647Storek 				if (!(namebuf = malloc(need)))
62059512Sbostic 					run_err("%s", strerror(errno));
62138100Sbostic 			}
62246655Sbostic 			(void)snprintf(namebuf, need, "%s%s%s", targ,
6236440Swnj 			    *targ ? "/" : "", cp);
62438100Sbostic 			np = namebuf;
62554570Sandrew 		} else
62638100Sbostic 			np = targ;
62738100Sbostic 		exists = stat(np, &stb) == 0;
62838100Sbostic 		if (buf[0] == 'D') {
62954570Sandrew 			int mod_flag = pflag;
63018126Sralph 			if (exists) {
63154151Sbostic 				if (!S_ISDIR(stb.st_mode)) {
6326440Swnj 					errno = ENOTDIR;
6336440Swnj 					goto bad;
6346440Swnj 				}
63523112Slepreau 				if (pflag)
63638100Sbostic 					(void)chmod(np, mode);
63754570Sandrew 			} else {
63854570Sandrew 				/* Handle copying from a read-only directory */
63954570Sandrew 				mod_flag = 1;
64054570Sandrew 				if (mkdir(np, mode | S_IRWXU) < 0)
64154570Sandrew 					goto bad;
64254570Sandrew 			}
64338100Sbostic 			vect[0] = np;
64438100Sbostic 			sink(1, vect);
64523101Slepreau 			if (setimes) {
64623101Slepreau 				setimes = 0;
64738100Sbostic 				if (utimes(np, tv) < 0)
64859512Sbostic 				    run_err("%s: set times: %s",
64938469Sbostic 					np, strerror(errno));
65023101Slepreau 			}
65154570Sandrew 			if (mod_flag)
65254570Sandrew 				(void)chmod(np, mode);
6536440Swnj 			continue;
65418126Sralph 		}
65554570Sandrew 		omode = mode;
65654570Sandrew 		mode |= S_IWRITE;
65738100Sbostic 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
65859512Sbostic bad:			run_err("%s: %s", np, strerror(errno));
6596440Swnj 			continue;
6606440Swnj 		}
66138098Sbostic 		(void)write(rem, "", 1);
66254151Sbostic 		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
66338100Sbostic 			(void)close(ofd);
66421254Smckusick 			continue;
66521254Smckusick 		}
66621254Smckusick 		cp = bp->buf;
66738469Sbostic 		wrerr = NO;
66854570Sandrew 		for (count = i = 0; i < size; i += BUFSIZ) {
66921254Smckusick 			amt = BUFSIZ;
6706440Swnj 			if (i + amt > size)
6716440Swnj 				amt = size - i;
67221254Smckusick 			count += amt;
6736440Swnj 			do {
67421254Smckusick 				j = read(rem, cp, amt);
67524711Sbloom 				if (j <= 0) {
67659512Sbostic 					run_err("%s", j ? strerror(errno) :
67738100Sbostic 					    "dropped connection");
6786440Swnj 					exit(1);
67924711Sbloom 				}
6806440Swnj 				amt -= j;
6816440Swnj 				cp += j;
6826440Swnj 			} while (amt > 0);
68321254Smckusick 			if (count == bp->cnt) {
68454570Sandrew 				/* Keep reading so we stay sync'd up. */
68554570Sandrew 				if (wrerr == NO) {
68654570Sandrew 					j = write(ofd, bp->buf, count);
68754570Sandrew 					if (j != count) {
68854570Sandrew 						wrerr = YES;
68954570Sandrew 						wrerrno = j >= 0 ? EIO : errno;
69054570Sandrew 					}
69154570Sandrew 				}
69221254Smckusick 				count = 0;
69321254Smckusick 				cp = bp->buf;
69421254Smckusick 			}
6956440Swnj 		}
69638469Sbostic 		if (count != 0 && wrerr == NO &&
69756587Sbostic 		    (j = write(ofd, bp->buf, count)) != count) {
69838469Sbostic 			wrerr = YES;
69956587Sbostic 			wrerrno = j >= 0 ? EIO : errno;
70056587Sbostic 		}
70138469Sbostic 		if (ftruncate(ofd, size)) {
70259512Sbostic 			run_err("%s: truncate: %s", np, strerror(errno));
70338469Sbostic 			wrerr = DISPLAYED;
70438469Sbostic 		}
70554570Sandrew 		if (pflag) {
70654570Sandrew 			if (exists || omode != mode)
70759512Sbostic 				if (fchmod(ofd, omode))
70859512Sbostic 					run_err("%s: set mode: %s",
70959512Sbostic 					    np, strerror(errno));
71054570Sandrew 		} else {
71154570Sandrew 			if (!exists && omode != mode)
71259512Sbostic 				if (fchmod(ofd, omode & ~mask))
71359512Sbostic 					run_err("%s: set mode: %s",
71459512Sbostic 					    np, strerror(errno));
71554570Sandrew 		}
71638100Sbostic 		(void)close(ofd);
71738098Sbostic 		(void)response();
71838469Sbostic 		if (setimes && wrerr == NO) {
71923101Slepreau 			setimes = 0;
72038469Sbostic 			if (utimes(np, tv) < 0) {
72159512Sbostic 				run_err("%s: set times: %s",
72238469Sbostic 				    np, strerror(errno));
72338469Sbostic 				wrerr = DISPLAYED;
72438469Sbostic 			}
72538469Sbostic 		}
72638469Sbostic 		switch(wrerr) {
72738469Sbostic 		case YES:
72859512Sbostic 			run_err("%s: %s", np, strerror(wrerrno));
72938469Sbostic 			break;
73038469Sbostic 		case NO:
73138098Sbostic 			(void)write(rem, "", 1);
73238469Sbostic 			break;
73338469Sbostic 		case DISPLAYED:
73438469Sbostic 			break;
73538469Sbostic 		}
7366440Swnj 	}
7376440Swnj screwup:
73859512Sbostic 	run_err("protocol error: %s", why);
7396440Swnj 	exit(1);
7406440Swnj }
7416440Swnj 
74254151Sbostic #ifdef KERBEROS
74354151Sbostic int
kerberos(host,bp,locuser,user)74454151Sbostic kerberos(host, bp, locuser, user)
74554151Sbostic 	char **host, *bp, *locuser, *user;
74621254Smckusick {
74754151Sbostic 	struct servent *sp;
74821254Smckusick 
74954151Sbostic again:
75054151Sbostic 	if (use_kerberos) {
75154151Sbostic 		rem = KSUCCESS;
75254151Sbostic 		errno = 0;
75354151Sbostic 		if (dest_realm == NULL)
75454151Sbostic 			dest_realm = krb_realmofhost(*host);
75554151Sbostic 		rem =
75659512Sbostic #ifdef CRYPT
75759512Sbostic 		    doencrypt ?
75859512Sbostic 			krcmd_mutual(host,
75959512Sbostic 			    port, user, bp, 0, dest_realm, &cred, schedule) :
76059512Sbostic #endif
76154151Sbostic 			krcmd(host, port, user, bp, 0, dest_realm);
76254151Sbostic 
76354151Sbostic 		if (rem < 0) {
76454151Sbostic 			use_kerberos = 0;
76559512Sbostic 			if ((sp = getservbyname("shell", "tcp")) == NULL)
76659512Sbostic 				errx(1, "unknown service shell/tcp");
76754151Sbostic 			if (errno == ECONNREFUSED)
76854151Sbostic 			    oldw("remote host doesn't support Kerberos");
76954151Sbostic 			else if (errno == ENOENT)
77054151Sbostic 			    oldw("can't provide Kerberos authentication data");
77154151Sbostic 			port = sp->s_port;
77254151Sbostic 			goto again;
77321254Smckusick 		}
77454151Sbostic 	} else {
77559512Sbostic #ifdef CRYPT
77659512Sbostic 		if (doencrypt)
77759512Sbostic 			errx(1,
77859512Sbostic 			   "the -x option requires Kerberos authentication");
77959512Sbostic #endif
78056587Sbostic 		rem = rcmd(host, port, locuser, user, bp, 0);
78121254Smckusick 	}
78254151Sbostic 	return (rem);
78321254Smckusick }
78454151Sbostic #endif /* KERBEROS */
78521254Smckusick 
78654151Sbostic int
response()78754151Sbostic response()
7886440Swnj {
789*66619Spendry 	char ch, *cp, resp, rbuf[BUFSIZ];
7906440Swnj 
79154151Sbostic 	if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
79254151Sbostic 		lostconn(0);
79332127Sbostic 
79454151Sbostic 	cp = rbuf;
79554151Sbostic 	switch(resp) {
79654151Sbostic 	case 0:				/* ok */
79754151Sbostic 		return (0);
79854151Sbostic 	default:
79954151Sbostic 		*cp++ = resp;
80054151Sbostic 		/* FALLTHROUGH */
80154151Sbostic 	case 1:				/* error, followed by error msg */
80254151Sbostic 	case 2:				/* fatal error, "" */
80354151Sbostic 		do {
80454151Sbostic 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
80554151Sbostic 				lostconn(0);
80654151Sbostic 			*cp++ = ch;
80754151Sbostic 		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
80854151Sbostic 
80954151Sbostic 		if (!iamremote)
81054151Sbostic 			(void)write(STDERR_FILENO, rbuf, cp - rbuf);
81154151Sbostic 		++errs;
81254151Sbostic 		if (resp == 1)
81354151Sbostic 			return (-1);
81454151Sbostic 		exit(1);
81554151Sbostic 	}
81654151Sbostic 	/* NOTREACHED */
81738100Sbostic }
81838100Sbostic 
81954151Sbostic void
usage()82032127Sbostic usage()
82132127Sbostic {
82238098Sbostic #ifdef KERBEROS
82359512Sbostic #ifdef CRYPT
82438098Sbostic 	(void)fprintf(stderr, "%s\n\t%s\n",
82559512Sbostic 	    "usage: rcp [-Kpx] [-k realm] f1 f2",
82659512Sbostic 	    "or: rcp [-Kprx] [-k realm] f1 ... fn directory");
82759512Sbostic #else
82859512Sbostic 	(void)fprintf(stderr, "%s\n\t%s\n",
82954151Sbostic 	    "usage: rcp [-Kp] [-k realm] f1 f2",
83054151Sbostic 	    "or: rcp [-Kpr] [-k realm] f1 ... fn directory");
83159512Sbostic #endif
83254151Sbostic #else
83338098Sbostic 	(void)fprintf(stderr,
83454151Sbostic 	    "usage: rcp [-p] f1 f2; or: rcp [-pr] f1 ... fn directory\n");
83536626Skfall #endif
83632127Sbostic 	exit(1);
83732127Sbostic }
83838879Skfall 
83954151Sbostic #if __STDC__
84054151Sbostic #include <stdarg.h>
84154151Sbostic #else
84254151Sbostic #include <varargs.h>
84354151Sbostic #endif
84454151Sbostic 
84538879Skfall #ifdef KERBEROS
84654151Sbostic void
84754151Sbostic #if __STDC__
oldw(const char * fmt,...)84854151Sbostic oldw(const char *fmt, ...)
84954151Sbostic #else
85054151Sbostic oldw(fmt, va_alist)
85154151Sbostic 	char *fmt;
85254151Sbostic         va_dcl
85354151Sbostic #endif
85438879Skfall {
85554151Sbostic 	va_list ap;
85654151Sbostic #if __STDC__
85754151Sbostic 	va_start(ap, fmt);
85854151Sbostic #else
85954151Sbostic 	va_start(ap);
86054151Sbostic #endif
86154151Sbostic 	(void)fprintf(stderr, "rcp: ");
86254151Sbostic 	(void)vfprintf(stderr, fmt, ap);
86354151Sbostic 	(void)fprintf(stderr, ", using standard rcp\n");
86454151Sbostic 	va_end(ap);
86538879Skfall }
86654151Sbostic #endif
86738879Skfall 
86854151Sbostic void
86954151Sbostic #if __STDC__
run_err(const char * fmt,...)87059512Sbostic run_err(const char *fmt, ...)
87154151Sbostic #else
87259512Sbostic run_err(fmt, va_alist)
87354151Sbostic 	char *fmt;
87454151Sbostic         va_dcl
87554151Sbostic #endif
87638879Skfall {
87754151Sbostic 	static FILE *fp;
87854151Sbostic 	va_list ap;
87954151Sbostic #if __STDC__
88054151Sbostic 	va_start(ap, fmt);
88154151Sbostic #else
88254151Sbostic 	va_start(ap);
88354151Sbostic #endif
88438879Skfall 
88554151Sbostic 	++errs;
88659512Sbostic 	if (fp == NULL && !(fp = fdopen(rem, "w")))
88754151Sbostic 		return;
88854151Sbostic 	(void)fprintf(fp, "%c", 0x01);
88954151Sbostic 	(void)fprintf(fp, "rcp: ");
89054151Sbostic 	(void)vfprintf(fp, fmt, ap);
89154151Sbostic 	(void)fprintf(fp, "\n");
89259512Sbostic 	(void)fflush(fp);
89359512Sbostic 
89459512Sbostic 	if (!iamremote)
89559512Sbostic 		vwarnx(fmt, ap);
89659512Sbostic 
89754151Sbostic 	va_end(ap);
89838879Skfall }
899