xref: /csrg-svn/bin/rcp/rcp.c (revision 54151)
121569Sdist /*
244342Skarels  * Copyright (c) 1983, 1990 The Regents of the University of California.
335619Sbostic  * All rights reserved.
435619Sbostic  *
542539Sbostic  * %sccs.include.redist.c%
621569Sdist  */
721569Sdist 
86440Swnj #ifndef lint
921569Sdist char copyright[] =
1044342Skarels "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
1121569Sdist  All rights reserved.\n";
1235619Sbostic #endif /* not lint */
136440Swnj 
1421569Sdist #ifndef lint
15*54151Sbostic static char sccsid[] = "@(#)rcp.c	5.33 (Berkeley) 06/20/92";
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>
25*54151Sbostic 
26*54151Sbostic #include <signal.h>
27*54151Sbostic #include <netdb.h>
2846655Sbostic #include <dirent.h>
2946655Sbostic #include <fcntl.h>
306440Swnj #include <pwd.h>
316440Swnj #include <errno.h>
3246655Sbostic #include <unistd.h>
3338098Sbostic #include <stdio.h>
3445647Storek #include <stdlib.h>
3546655Sbostic #include <string.h>
3638098Sbostic #include <ctype.h>
3737037Sbostic #include "pathnames.h"
38*54151Sbostic #include "extern.h"
3912989Ssam 
4038098Sbostic #ifdef KERBEROS
4142498Sbostic #include <kerberosIV/des.h>
4241785Sbostic #include <kerberosIV/krb.h>
43*54151Sbostic 
4438879Skfall char	dst_realm_buf[REALM_SZ];
4538879Skfall char	*dest_realm = NULL;
4645397Smckusick int	use_kerberos = 1;
4738879Skfall CREDENTIALS 	cred;
4838879Skfall Key_schedule	schedule;
4938879Skfall extern	char	*krb_realmofhost();
50*54151Sbostic #ifdef CRYPT
51*54151Sbostic int	doencrypt = 0;
52*54151Sbostic #define	OPTIONS	"dfKk:prtx"
5345397Smckusick #else
54*54151Sbostic #define	OPTIONS	"dfKk:prt"
55*54151Sbostic #endif
56*54151Sbostic #else
5738879Skfall #define	OPTIONS "dfprt"
5836624Skfall #endif
5936624Skfall 
6038098Sbostic struct passwd *pwd;
6138879Skfall u_short	port;
6238879Skfall uid_t	userid;
6338879Skfall int errs, rem;
6438879Skfall int pflag, iamremote, iamrecursive, targetshouldbedirectory;
656440Swnj 
6638879Skfall #define	CMDNEEDS	64
6738100Sbostic char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
6838098Sbostic 
69*54151Sbostic #ifdef KERBEROS
70*54151Sbostic int	 kerberos __P((char **, char *, char *, char *));
71*54151Sbostic void	 oldw __P((const char *, ...));
72*54151Sbostic #endif
73*54151Sbostic int	 response __P((void));
74*54151Sbostic void	 rsource __P((char *, struct stat *));
75*54151Sbostic void	 sink __P((int, char *[]));
76*54151Sbostic void	 source __P((int, char *[]));
77*54151Sbostic void	 tolocal __P((int, char *[]));
78*54151Sbostic void	 toremote __P((char *, int, char *[]));
79*54151Sbostic void	 usage __P((void));
8021254Smckusick 
81*54151Sbostic int
826440Swnj main(argc, argv)
836440Swnj 	int argc;
84*54151Sbostic 	char *argv[];
856440Swnj {
8618126Sralph 	struct servent *sp;
8738099Sbostic 	int ch, fflag, tflag;
88*54151Sbostic 	char *targ, *shell;
8918026Sralph 
9038099Sbostic 	fflag = tflag = 0;
9138879Skfall 	while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
92*54151Sbostic 		switch(ch) {			/* User-visible flags. */
93*54151Sbostic 		case 'K':
94*54151Sbostic #ifdef KERBEROS
95*54151Sbostic 			use_kerberos = 0;
96*54151Sbostic #endif
9723101Slepreau 			break;
9838879Skfall #ifdef	KERBEROS
9938879Skfall 		case 'k':
10038879Skfall 			dest_realm = dst_realm_buf;
101*54151Sbostic 			(void)strncpy(dst_realm_buf, optarg, REALM_SZ);
10238099Sbostic 			break;
103*54151Sbostic #ifdef CRYPT
104*54151Sbostic 		case 'x':
105*54151Sbostic 			doencrypt = 1;
106*54151Sbostic 			/* des_set_key(cred.session, schedule); */
107*54151Sbostic 			break;
10838098Sbostic #endif
109*54151Sbostic #endif
110*54151Sbostic 		case 'p':
111*54151Sbostic 			pflag = 1;
112*54151Sbostic 			break;
113*54151Sbostic 		case 'r':
114*54151Sbostic 			iamrecursive = 1;
115*54151Sbostic 			break;
116*54151Sbostic 						/* Server options. */
11738879Skfall 		case 'd':
11838879Skfall 			targetshouldbedirectory = 1;
11938879Skfall 			break;
12038879Skfall 		case 'f':			/* "from" */
12138879Skfall 			iamremote = 1;
12238879Skfall 			fflag = 1;
12338879Skfall 			break;
12438879Skfall 		case 't':			/* "to" */
12538879Skfall 			iamremote = 1;
12638879Skfall 			tflag = 1;
12738879Skfall 			break;
12838879Skfall 
12938098Sbostic 		case '?':
13038098Sbostic 		default:
13132127Sbostic 			usage();
13223101Slepreau 		}
13338098Sbostic 	argc -= optind;
13438098Sbostic 	argv += optind;
13538098Sbostic 
13638879Skfall #ifdef KERBEROS
137*54151Sbostic 	if (use_kerberos) {
138*54151Sbostic #ifdef CRYPT
139*54151Sbostic 		shell = doencrypt ? "ekshell" : "kshell";
140*54151Sbostic #else
141*54151Sbostic 		shell = "kshell";
142*54151Sbostic #endif
143*54151Sbostic 		if ((sp = getservbyname(shell, "tcp")) == NULL) {
144*54151Sbostic 			use_kerberos = 0;
145*54151Sbostic 			oldw("can't get entry for %s/tcp service", shell);
146*54151Sbostic 			sp = getservbyname(shell = "shell", "tcp");
147*54151Sbostic 		}
148*54151Sbostic 	} else
14945647Storek 		sp = getservbyname(shell = "shell", "tcp");
15038879Skfall #else
15145647Storek 	sp = getservbyname(shell = "shell", "tcp");
15238879Skfall #endif
15338879Skfall 	if (sp == NULL) {
15445647Storek 		(void)fprintf(stderr, "rcp: %s/tcp: unknown service\n", shell);
15538879Skfall 		exit(1);
15638879Skfall 	}
15738879Skfall 	port = sp->s_port;
15838879Skfall 
159*54151Sbostic 	if ((pwd = getpwuid(userid = getuid())) == NULL) {
16045647Storek 		(void)fprintf(stderr, "rcp: unknown user %d.\n", (int)userid);
16138879Skfall 		exit(1);
16238879Skfall 	}
16338879Skfall 
164*54151Sbostic 	rem = STDIN_FILENO;		/* XXX */
165*54151Sbostic 
166*54151Sbostic 	if (fflag) {			/* Follow "protocol", send data. */
16738099Sbostic 		(void)response();
16838099Sbostic 		(void)setuid(userid);
16938099Sbostic 		source(argc, argv);
17038099Sbostic 		exit(errs);
17138099Sbostic 	}
17238099Sbostic 
173*54151Sbostic 	if (tflag) {			/* Receive data. */
17438099Sbostic 		(void)setuid(userid);
17538099Sbostic 		sink(argc, argv);
17638099Sbostic 		exit(errs);
17738099Sbostic 	}
17838099Sbostic 
17932127Sbostic 	if (argc < 2)
18032127Sbostic 		usage();
1816440Swnj 	if (argc > 2)
1826440Swnj 		targetshouldbedirectory = 1;
18338098Sbostic 
18432127Sbostic 	rem = -1;
185*54151Sbostic 	/* Command to be executed on remote system using "rsh". */
18638879Skfall #ifdef	KERBEROS
18746655Sbostic 	(void)snprintf(cmd, sizeof(cmd),
18846655Sbostic 	    "rcp%s%s%s%s", iamrecursive ? " -r" : "",
189*54151Sbostic #ifdef CRYPT
190*54151Sbostic 	    (doencrypt && use_kerberos ? " -x" : ""),
191*54151Sbostic #else
19245397Smckusick 	    "",
193*54151Sbostic #endif
19438879Skfall 	    pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
19538879Skfall #else
19646655Sbostic 	(void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
19746655Sbostic 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
19846655Sbostic 	    targetshouldbedirectory ? " -d" : "");
19938879Skfall #endif
20038098Sbostic 
20138098Sbostic 	(void)signal(SIGPIPE, lostconn);
20238098Sbostic 
203*54151Sbostic 	if (targ = colon(argv[argc - 1]))	/* Dest is remote host. */
204*54151Sbostic 		toremote(targ, argc, argv);
20538098Sbostic 	else {
206*54151Sbostic 		tolocal(argc, argv);		/* Dest is local host. */
20738098Sbostic 		if (targetshouldbedirectory)
20838098Sbostic 			verifydir(argv[argc - 1]);
20938098Sbostic 	}
21038098Sbostic 	exit(errs);
21138098Sbostic }
21238098Sbostic 
213*54151Sbostic void
21438098Sbostic toremote(targ, argc, argv)
215*54151Sbostic 	char *targ, *argv[];
21638098Sbostic 	int argc;
21738098Sbostic {
21846655Sbostic 	int i, len, tos;
21938100Sbostic 	char *bp, *host, *src, *suser, *thost, *tuser;
22038098Sbostic 
22138098Sbostic 	*targ++ = 0;
22238098Sbostic 	if (*targ == 0)
22338098Sbostic 		targ = ".";
22438098Sbostic 
22538100Sbostic 	if (thost = index(argv[argc - 1], '@')) {
22638879Skfall 		/* user@host */
22738098Sbostic 		*thost++ = 0;
22838098Sbostic 		tuser = argv[argc - 1];
22938098Sbostic 		if (*tuser == '\0')
23024711Sbloom 			tuser = NULL;
23138098Sbostic 		else if (!okname(tuser))
23238098Sbostic 			exit(1);
23338098Sbostic 	} else {
23438098Sbostic 		thost = argv[argc - 1];
23538098Sbostic 		tuser = NULL;
23638098Sbostic 	}
23736624Skfall 
23838098Sbostic 	for (i = 0; i < argc - 1; i++) {
23938098Sbostic 		src = colon(argv[i]);
24038098Sbostic 		if (src) {			/* remote to remote */
24138098Sbostic 			*src++ = 0;
24238098Sbostic 			if (*src == 0)
24338098Sbostic 				src = ".";
24438098Sbostic 			host = index(argv[i], '@');
24546655Sbostic 			len = strlen(_PATH_RSH) + strlen(argv[i]) +
24646655Sbostic 			    strlen(src) + (tuser ? strlen(tuser) : 0) +
24746655Sbostic 			    strlen(thost) + strlen(targ) + CMDNEEDS + 20;
24846655Sbostic 			if (!(bp = malloc(len)))
24946655Sbostic 				nospace();
25038098Sbostic 			if (host) {
25138098Sbostic 				*host++ = 0;
25238098Sbostic 				suser = argv[i];
25338098Sbostic 				if (*suser == '\0')
25438098Sbostic 					suser = pwd->pw_name;
25538098Sbostic 				else if (!okname(suser))
25638098Sbostic 					continue;
25746655Sbostic 				(void)snprintf(bp, len,
25838098Sbostic 				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
25938098Sbostic 				    _PATH_RSH, host, suser, cmd, src,
26038098Sbostic 				    tuser ? tuser : "", tuser ? "@" : "",
26138098Sbostic 				    thost, targ);
26238098Sbostic 			} else
26346655Sbostic 				(void)snprintf(bp, len,
26446655Sbostic 				    "%s %s -n %s %s '%s%s%s:%s'",
26538098Sbostic 				    _PATH_RSH, argv[i], cmd, src,
26638098Sbostic 				    tuser ? tuser : "", tuser ? "@" : "",
26738098Sbostic 				    thost, targ);
268*54151Sbostic 			(void)susystem(bp, userid);
26938100Sbostic 			(void)free(bp);
27038098Sbostic 		} else {			/* local to remote */
27138098Sbostic 			if (rem == -1) {
27246655Sbostic 				len = strlen(targ) + CMDNEEDS + 20;
27346655Sbostic 				if (!(bp = malloc(len)))
27438100Sbostic 					nospace();
27546655Sbostic 				(void)snprintf(bp, len, "%s -t %s", cmd, targ);
27638098Sbostic 				host = thost;
27738098Sbostic #ifdef KERBEROS
27838098Sbostic 				if (use_kerberos)
27938879Skfall 					rem = kerberos(&host, bp,
28038879Skfall 					    pwd->pw_name,
28138100Sbostic 					    tuser ? tuser : pwd->pw_name);
28238098Sbostic 				else
28338098Sbostic #endif
28424711Sbloom 					rem = rcmd(&host, port, pwd->pw_name,
28524711Sbloom 					    tuser ? tuser : pwd->pw_name,
28638100Sbostic 					    bp, 0);
28738098Sbostic 				if (rem < 0)
28838098Sbostic 					exit(1);
28944342Skarels 				tos = IPTOS_THROUGHPUT;
29044342Skarels 				if (setsockopt(rem, IPPROTO_IP, IP_TOS,
291*54151Sbostic 				    &tos, sizeof(int)) < 0)
292*54151Sbostic 					(void)fprintf(stderr,
293*54151Sbostic 					    "rcp: TOS (ignored): %s\n",
294*54151Sbostic 					    strerror(errno));
29538098Sbostic 				if (response() < 0)
29638098Sbostic 					exit(1);
29738100Sbostic 				(void)free(bp);
29838098Sbostic 				(void)setuid(userid);
2996440Swnj 			}
30038098Sbostic 			source(1, argv+i);
3016440Swnj 		}
30238098Sbostic 	}
30338098Sbostic }
30438098Sbostic 
305*54151Sbostic void
30638098Sbostic tolocal(argc, argv)
30738098Sbostic 	int argc;
308*54151Sbostic 	char *argv[];
30938098Sbostic {
31046655Sbostic 	int i, len, tos;
31138100Sbostic 	char *bp, *host, *src, *suser;
31238098Sbostic 
31338098Sbostic 	for (i = 0; i < argc - 1; i++) {
314*54151Sbostic 		if (!(src = colon(argv[i]))) {		/* Local to local. */
31546655Sbostic 			len = strlen(_PATH_CP) + strlen(argv[i]) +
31646655Sbostic 			    strlen(argv[argc - 1]) + 20;
31746655Sbostic 			if (!(bp = malloc(len)))
31838100Sbostic 				nospace();
31946655Sbostic 			(void)snprintf(bp, len, "%s%s%s %s %s", _PATH_CP,
32038100Sbostic 			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
32138100Sbostic 			    argv[i], argv[argc - 1]);
322*54151Sbostic 			(void)susystem(bp, userid);
32338100Sbostic 			(void)free(bp);
32438100Sbostic 			continue;
32538100Sbostic 		}
32638100Sbostic 		*src++ = 0;
32738100Sbostic 		if (*src == 0)
32838100Sbostic 			src = ".";
329*54151Sbostic 		if ((host = index(argv[i], '@')) == NULL) {
330*54151Sbostic 			host = argv[i];
331*54151Sbostic 			suser = pwd->pw_name;
332*54151Sbostic 		} else {
33338100Sbostic 			*host++ = 0;
33438100Sbostic 			suser = argv[i];
33538100Sbostic 			if (*suser == '\0')
33638098Sbostic 				suser = pwd->pw_name;
33738100Sbostic 			else if (!okname(suser))
33838100Sbostic 				continue;
33938100Sbostic 		}
34046655Sbostic 		len = strlen(src) + CMDNEEDS + 20;
341*54151Sbostic 		if ((bp = malloc(len)) == NULL)
34238100Sbostic 			nospace();
34346655Sbostic 		(void)snprintf(bp, len, "%s -f %s", cmd, src);
344*54151Sbostic 		rem =
34538098Sbostic #ifdef KERBEROS
346*54151Sbostic 		    use_kerberos ?
347*54151Sbostic 			kerberos(&host, bp, pwd->pw_name, suser) :
34838098Sbostic #endif
349*54151Sbostic 			rcmd(&host, port, pwd->pw_name, suser, bp, 0);
35038100Sbostic 		(void)free(bp);
35138100Sbostic 		if (rem < 0)
35238100Sbostic 			continue;
35344342Skarels 		(void)seteuid(userid);
35444342Skarels 		tos = IPTOS_THROUGHPUT;
355*54151Sbostic 		if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
356*54151Sbostic 			(void)fprintf(stderr, "rcp: TOS (ignored): %s\n",
357*54151Sbostic 			    strerror(errno));
35838100Sbostic 		sink(1, argv + argc - 1);
35944342Skarels 		(void)seteuid(0);
36038100Sbostic 		(void)close(rem);
36138100Sbostic 		rem = -1;
3626440Swnj 	}
3636440Swnj }
3646440Swnj 
365*54151Sbostic void
3666440Swnj source(argc, argv)
3676440Swnj 	int argc;
368*54151Sbostic 	char *argv[];
3696440Swnj {
3706440Swnj 	struct stat stb;
37138098Sbostic 	static BUF buffer;
37238098Sbostic 	BUF *bp;
37338098Sbostic 	off_t i;
374*54151Sbostic 	int fd, readerr, amt;
37538098Sbostic 	char *last, *name, buf[BUFSIZ];
3766440Swnj 
377*54151Sbostic 	while (name = *argv++) {
378*54151Sbostic 		if ((fd = open(name, O_RDONLY, 0)) < 0)
379*54151Sbostic 			goto syserr;
380*54151Sbostic 		if (fstat(fd, &stb)) {
381*54151Sbostic syserr:			err("%s: %s", name, strerror(errno));
382*54151Sbostic 			goto next;
3836440Swnj 		}
384*54151Sbostic 		switch (stb.st_mode & S_IFMT) {
3856440Swnj 		case S_IFREG:
3866440Swnj 			break;
3876440Swnj 		case S_IFDIR:
3886440Swnj 			if (iamrecursive) {
38923101Slepreau 				rsource(name, &stb);
390*54151Sbostic 				goto next;
3916440Swnj 			}
39238098Sbostic 			/* FALLTHROUGH */
3936440Swnj 		default:
394*54151Sbostic 			err("%s: not a regular file", name);
395*54151Sbostic 			goto next;
3966440Swnj 		}
397*54151Sbostic 		if ((last = rindex(name, '/')) == NULL)
3986440Swnj 			last = name;
3996440Swnj 		else
400*54151Sbostic 			++last;
40123101Slepreau 		if (pflag) {
40223101Slepreau 			/*
40323101Slepreau 			 * Make it compatible with possible future
40423101Slepreau 			 * versions expecting microseconds.
40523101Slepreau 			 */
406*54151Sbostic 			amt = snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
407*54151Sbostic 			    stb.st_mtimespec.ts_sec, stb.st_atimespec.ts_sec);
408*54151Sbostic 			(void)write(rem, buf, amt);
409*54151Sbostic 			if (response() < 0)
410*54151Sbostic 				goto next;
41123101Slepreau 		}
412*54151Sbostic #define	MODEMASK	(S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
413*54151Sbostic 		amt = snprintf(buf, sizeof(buf), "C%04o %qd %s\n",
414*54151Sbostic 		    stb.st_mode & MODEMASK, stb.st_size, last);
415*54151Sbostic 		(void)write(rem, buf, amt);
416*54151Sbostic 		if (response() < 0)
417*54151Sbostic 			goto next;
418*54151Sbostic 		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL)
419*54151Sbostic 			goto next;
42032268Sbostic 		readerr = 0;
42121254Smckusick 		for (i = 0; i < stb.st_size; i += bp->cnt) {
42221254Smckusick 			amt = bp->cnt;
4236440Swnj 			if (i + amt > stb.st_size)
4246440Swnj 				amt = stb.st_size - i;
425*54151Sbostic 			if (read(fd, bp->buf, amt) != amt) {
426*54151Sbostic 				readerr = 1;
427*54151Sbostic 				err("%s: %s", name, strerror(errno));
428*54151Sbostic 				break;
429*54151Sbostic 			}
43038098Sbostic 			(void)write(rem, bp->buf, amt);
4316440Swnj 		}
432*54151Sbostic 		if (!readerr)
43338098Sbostic 			(void)write(rem, "", 1);
43438098Sbostic 		(void)response();
435*54151Sbostic 
436*54151Sbostic next:		(void)close(fd);
4376440Swnj 	}
4386440Swnj }
4396440Swnj 
440*54151Sbostic void
44123101Slepreau rsource(name, statp)
4426440Swnj 	char *name;
44323101Slepreau 	struct stat *statp;
4446440Swnj {
44546655Sbostic 	DIR *dirp;
44646655Sbostic 	struct dirent *dp;
447*54151Sbostic 	int amt;
44838100Sbostic 	char *last, *vect[1], path[MAXPATHLEN];
4496440Swnj 
45046655Sbostic 	if (!(dirp = opendir(name))) {
451*54151Sbostic 		err("%s: %s", name, strerror(errno));
4526440Swnj 		return;
4536440Swnj 	}
4546440Swnj 	last = rindex(name, '/');
4556440Swnj 	if (last == 0)
4566440Swnj 		last = name;
4576440Swnj 	else
4586440Swnj 		last++;
45923101Slepreau 	if (pflag) {
460*54151Sbostic 		amt = snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
461*54151Sbostic 		    statp->st_mtimespec.ts_sec, statp->st_atimespec.ts_sec);
462*54151Sbostic 		(void)write(rem, path, amt);
46323101Slepreau 		if (response() < 0) {
46446655Sbostic 			closedir(dirp);
46523101Slepreau 			return;
46623101Slepreau 		}
46723101Slepreau 	}
468*54151Sbostic 	amt = snprintf(path, sizeof(path),
469*54151Sbostic 	    "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
470*54151Sbostic 	(void)write(rem, path, amt);
4716440Swnj 	if (response() < 0) {
47246655Sbostic 		closedir(dirp);
4736440Swnj 		return;
4746440Swnj 	}
47546655Sbostic 	while (dp = readdir(dirp)) {
4766440Swnj 		if (dp->d_ino == 0)
4776440Swnj 			continue;
4786440Swnj 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
4796440Swnj 			continue;
48038100Sbostic 		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
481*54151Sbostic 			err("%s/%s: name too long", name, dp->d_name);
4826440Swnj 			continue;
4836440Swnj 		}
48446655Sbostic 		(void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
48538100Sbostic 		vect[0] = path;
48638100Sbostic 		source(1, vect);
4876440Swnj 	}
488*54151Sbostic 	(void)closedir(dirp);
48938098Sbostic 	(void)write(rem, "E\n", 2);
49038098Sbostic 	(void)response();
4916440Swnj }
4926440Swnj 
49346655Sbostic void
4946440Swnj sink(argc, argv)
4956440Swnj 	int argc;
496*54151Sbostic 	char *argv[];
4976440Swnj {
498*54151Sbostic 	static BUF buffer;
49938100Sbostic 	register char *cp;
50021254Smckusick 	struct stat stb;
50123101Slepreau 	struct timeval tv[2];
50238469Sbostic 	enum { YES, NO, DISPLAYED } wrerr;
503*54151Sbostic 	BUF *bp;
50438100Sbostic 	off_t i, j;
50538100Sbostic 	char ch, *targ, *why;
50638100Sbostic 	int amt, count, exists, first, mask, mode;
50738469Sbostic 	int ofd, setimes, size, targisdir;
50845647Storek 	char *np, *vect[1], buf[BUFSIZ];
5096440Swnj 
51038100Sbostic #define	atime	tv[0]
51138100Sbostic #define	mtime	tv[1]
51238100Sbostic #define	SCREWUP(str)	{ why = str; goto screwup; }
51338100Sbostic 
51438100Sbostic 	setimes = targisdir = 0;
51538100Sbostic 	mask = umask(0);
51623112Slepreau 	if (!pflag)
51738098Sbostic 		(void)umask(mask);
51818126Sralph 	if (argc != 1) {
519*54151Sbostic 		err("ambiguous target");
5206440Swnj 		exit(1);
5216440Swnj 	}
5226440Swnj 	targ = *argv;
5236440Swnj 	if (targetshouldbedirectory)
5246440Swnj 		verifydir(targ);
52538098Sbostic 	(void)write(rem, "", 1);
526*54151Sbostic 	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
5276440Swnj 		targisdir = 1;
52838100Sbostic 	for (first = 1;; first = 0) {
52938100Sbostic 		cp = buf;
5306440Swnj 		if (read(rem, cp, 1) <= 0)
5316440Swnj 			return;
5326440Swnj 		if (*cp++ == '\n')
53338100Sbostic 			SCREWUP("unexpected <newline>");
5346440Swnj 		do {
53538100Sbostic 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
5366440Swnj 				SCREWUP("lost connection");
53738100Sbostic 			*cp++ = ch;
53838100Sbostic 		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
5396440Swnj 		*cp = 0;
54038100Sbostic 
54138100Sbostic 		if (buf[0] == '\01' || buf[0] == '\02') {
5426440Swnj 			if (iamremote == 0)
543*54151Sbostic 				(void)write(STDERR_FILENO,
544*54151Sbostic 				    buf + 1, strlen(buf + 1));
54538100Sbostic 			if (buf[0] == '\02')
5466440Swnj 				exit(1);
547*54151Sbostic 			++errs;
5486440Swnj 			continue;
5496440Swnj 		}
55038100Sbostic 		if (buf[0] == 'E') {
55138098Sbostic 			(void)write(rem, "", 1);
5526440Swnj 			return;
5536440Swnj 		}
55423101Slepreau 
55538108Sbostic 		if (ch == '\n')
55638115Sbostic 			*--cp = 0;
55738108Sbostic 
55823101Slepreau #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
55938100Sbostic 		cp = buf;
56023101Slepreau 		if (*cp == 'T') {
56123101Slepreau 			setimes++;
56223101Slepreau 			cp++;
56323101Slepreau 			getnum(mtime.tv_sec);
56423101Slepreau 			if (*cp++ != ' ')
56523101Slepreau 				SCREWUP("mtime.sec not delimited");
56623101Slepreau 			getnum(mtime.tv_usec);
56723101Slepreau 			if (*cp++ != ' ')
56823101Slepreau 				SCREWUP("mtime.usec not delimited");
56923101Slepreau 			getnum(atime.tv_sec);
57023101Slepreau 			if (*cp++ != ' ')
57123101Slepreau 				SCREWUP("atime.sec not delimited");
57223101Slepreau 			getnum(atime.tv_usec);
57323101Slepreau 			if (*cp++ != '\0')
57423101Slepreau 				SCREWUP("atime.usec not delimited");
57538098Sbostic 			(void)write(rem, "", 1);
57623101Slepreau 			continue;
57723101Slepreau 		}
57814580Sralph 		if (*cp != 'C' && *cp != 'D') {
57914580Sralph 			/*
58014580Sralph 			 * Check for the case "rcp remote:foo\* local:bar".
58114580Sralph 			 * In this case, the line "No match." can be returned
58214580Sralph 			 * by the shell before the rcp command on the remote is
58314580Sralph 			 * executed so the ^Aerror_message convention isn't
58414580Sralph 			 * followed.
58514580Sralph 			 */
58614580Sralph 			if (first) {
587*54151Sbostic 				err("%s", cp);
58814580Sralph 				exit(1);
58914580Sralph 			}
5906440Swnj 			SCREWUP("expected control record");
59114580Sralph 		}
5926440Swnj 		mode = 0;
59338100Sbostic 		for (++cp; cp < buf + 5; cp++) {
5946440Swnj 			if (*cp < '0' || *cp > '7')
5956440Swnj 				SCREWUP("bad mode");
5966440Swnj 			mode = (mode << 3) | (*cp - '0');
5976440Swnj 		}
5986440Swnj 		if (*cp++ != ' ')
5996440Swnj 			SCREWUP("mode not delimited");
600*54151Sbostic 
601*54151Sbostic 		for (size = 0; isdigit(*cp);)
6026440Swnj 			size = size * 10 + (*cp++ - '0');
6036440Swnj 		if (*cp++ != ' ')
6046440Swnj 			SCREWUP("size not delimited");
60538100Sbostic 		if (targisdir) {
60638100Sbostic 			static char *namebuf;
60738100Sbostic 			static int cursize;
60845647Storek 			size_t need;
60938100Sbostic 
61038100Sbostic 			need = strlen(targ) + strlen(cp) + 250;
61138100Sbostic 			if (need > cursize) {
61245647Storek 				if (!(namebuf = malloc(need)))
613*54151Sbostic 					err("%s", strerror(errno));
61438100Sbostic 			}
61546655Sbostic 			(void)snprintf(namebuf, need, "%s%s%s", targ,
6166440Swnj 			    *targ ? "/" : "", cp);
61738100Sbostic 			np = namebuf;
61838100Sbostic 		}
6196440Swnj 		else
62038100Sbostic 			np = targ;
62138100Sbostic 		exists = stat(np, &stb) == 0;
62238100Sbostic 		if (buf[0] == 'D') {
62318126Sralph 			if (exists) {
624*54151Sbostic 				if (!S_ISDIR(stb.st_mode)) {
6256440Swnj 					errno = ENOTDIR;
6266440Swnj 					goto bad;
6276440Swnj 				}
62823112Slepreau 				if (pflag)
62938100Sbostic 					(void)chmod(np, mode);
63038100Sbostic 			} else if (mkdir(np, mode) < 0)
6316440Swnj 				goto bad;
63238100Sbostic 			vect[0] = np;
63338100Sbostic 			sink(1, vect);
63423101Slepreau 			if (setimes) {
63523101Slepreau 				setimes = 0;
63638100Sbostic 				if (utimes(np, tv) < 0)
637*54151Sbostic 				    err("can't set times on %s: %s",
63838469Sbostic 					np, strerror(errno));
63923101Slepreau 			}
6406440Swnj 			continue;
64118126Sralph 		}
64238100Sbostic 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
643*54151Sbostic bad:			err("%s: %s", np, strerror(errno));
6446440Swnj 			continue;
6456440Swnj 		}
64623112Slepreau 		if (exists && pflag)
64738100Sbostic 			(void)fchmod(ofd, mode);
64838098Sbostic 		(void)write(rem, "", 1);
649*54151Sbostic 		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
65038100Sbostic 			(void)close(ofd);
65121254Smckusick 			continue;
65221254Smckusick 		}
65321254Smckusick 		cp = bp->buf;
65421254Smckusick 		count = 0;
65538469Sbostic 		wrerr = NO;
6566440Swnj 		for (i = 0; i < size; i += BUFSIZ) {
65721254Smckusick 			amt = BUFSIZ;
6586440Swnj 			if (i + amt > size)
6596440Swnj 				amt = size - i;
66021254Smckusick 			count += amt;
6616440Swnj 			do {
66221254Smckusick 				j = read(rem, cp, amt);
66324711Sbloom 				if (j <= 0) {
664*54151Sbostic 					err("%s", j ? strerror(errno) :
66538100Sbostic 					    "dropped connection");
6666440Swnj 					exit(1);
66724711Sbloom 				}
6686440Swnj 				amt -= j;
6696440Swnj 				cp += j;
6706440Swnj 			} while (amt > 0);
67121254Smckusick 			if (count == bp->cnt) {
67238469Sbostic 				if (wrerr == NO &&
67338100Sbostic 				    write(ofd, bp->buf, count) != count)
67438469Sbostic 					wrerr = YES;
67521254Smckusick 				count = 0;
67621254Smckusick 				cp = bp->buf;
67721254Smckusick 			}
6786440Swnj 		}
67938469Sbostic 		if (count != 0 && wrerr == NO &&
68038100Sbostic 		    write(ofd, bp->buf, count) != count)
68138469Sbostic 			wrerr = YES;
68238469Sbostic 		if (ftruncate(ofd, size)) {
683*54151Sbostic 			err("can't truncate %s: %s", np,
68438469Sbostic 			    strerror(errno));
68538469Sbostic 			wrerr = DISPLAYED;
68638469Sbostic 		}
68738100Sbostic 		(void)close(ofd);
68838098Sbostic 		(void)response();
68938469Sbostic 		if (setimes && wrerr == NO) {
69023101Slepreau 			setimes = 0;
69138469Sbostic 			if (utimes(np, tv) < 0) {
692*54151Sbostic 				err("can't set times on %s: %s",
69338469Sbostic 				    np, strerror(errno));
69438469Sbostic 				wrerr = DISPLAYED;
69538469Sbostic 			}
69638469Sbostic 		}
69738469Sbostic 		switch(wrerr) {
69838469Sbostic 		case YES:
699*54151Sbostic 			err("%s: %s", np, strerror(errno));
70038469Sbostic 			break;
70138469Sbostic 		case NO:
70238098Sbostic 			(void)write(rem, "", 1);
70338469Sbostic 			break;
70438469Sbostic 		case DISPLAYED:
70538469Sbostic 			break;
70638469Sbostic 		}
7076440Swnj 	}
7086440Swnj screwup:
709*54151Sbostic 	err("protocol screwup: %s", why);
7106440Swnj 	exit(1);
7116440Swnj }
7126440Swnj 
713*54151Sbostic #ifdef KERBEROS
714*54151Sbostic int
715*54151Sbostic kerberos(host, bp, locuser, user)
716*54151Sbostic 	char **host, *bp, *locuser, *user;
71721254Smckusick {
718*54151Sbostic 	struct servent *sp;
71921254Smckusick 
720*54151Sbostic again:
721*54151Sbostic 	if (use_kerberos) {
722*54151Sbostic 		rem = KSUCCESS;
723*54151Sbostic 		errno = 0;
724*54151Sbostic 		if (dest_realm == NULL)
725*54151Sbostic 			dest_realm = krb_realmofhost(*host);
726*54151Sbostic 		rem =
727*54151Sbostic #ifdef CRYPT
728*54151Sbostic 		    doencrypt ?
729*54151Sbostic 			krcmd_mutual(host,
730*54151Sbostic 			    port, user, bp, 0, dest_realm, &cred, schedule) :
731*54151Sbostic #endif
732*54151Sbostic 			krcmd(host, port, user, bp, 0, dest_realm);
733*54151Sbostic 
734*54151Sbostic 		if (rem < 0) {
735*54151Sbostic 			use_kerberos = 0;
736*54151Sbostic 			if ((sp = getservbyname("shell", "tcp")) == NULL) {
737*54151Sbostic 				(void)fprintf(stderr,
738*54151Sbostic 			    	    "rcp: unknown service shell/tcp\n");
739*54151Sbostic 				exit(1);
740*54151Sbostic 			}
741*54151Sbostic 			if (errno == ECONNREFUSED)
742*54151Sbostic 			    oldw("remote host doesn't support Kerberos");
743*54151Sbostic 			else if (errno == ENOENT)
744*54151Sbostic 			    oldw("can't provide Kerberos authentication data");
745*54151Sbostic 			port = sp->s_port;
746*54151Sbostic 			goto again;
74721254Smckusick 		}
748*54151Sbostic 	} else {
749*54151Sbostic #ifdef CRYPT
750*54151Sbostic 		if (doencrypt) {
751*54151Sbostic 			(void)fprintf(stderr,
752*54151Sbostic 			    "the -x option requires Kerberos authentication\n");
753*54151Sbostic 			exit(1);
754*54151Sbostic 		}
755*54151Sbostic #endif
756*54151Sbostic 		rem = rcmd(host, sp->s_port, locuser, user, bp, 0);
75721254Smckusick 	}
758*54151Sbostic 	return (rem);
75921254Smckusick }
760*54151Sbostic #endif /* KERBEROS */
76121254Smckusick 
762*54151Sbostic int
763*54151Sbostic response()
7646440Swnj {
765*54151Sbostic 	register char *cp;
766*54151Sbostic 	char ch, resp, rbuf[BUFSIZ];
7676440Swnj 
768*54151Sbostic 	if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
769*54151Sbostic 		lostconn(0);
77032127Sbostic 
771*54151Sbostic 	cp = rbuf;
772*54151Sbostic 	switch(resp) {
773*54151Sbostic 	case 0:				/* ok */
774*54151Sbostic 		return (0);
775*54151Sbostic 	default:
776*54151Sbostic 		*cp++ = resp;
777*54151Sbostic 		/* FALLTHROUGH */
778*54151Sbostic 	case 1:				/* error, followed by error msg */
779*54151Sbostic 	case 2:				/* fatal error, "" */
780*54151Sbostic 		do {
781*54151Sbostic 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
782*54151Sbostic 				lostconn(0);
783*54151Sbostic 			*cp++ = ch;
784*54151Sbostic 		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
785*54151Sbostic 
786*54151Sbostic 		if (!iamremote)
787*54151Sbostic 			(void)write(STDERR_FILENO, rbuf, cp - rbuf);
788*54151Sbostic 		++errs;
789*54151Sbostic 		if (resp == 1)
790*54151Sbostic 			return (-1);
791*54151Sbostic 		exit(1);
792*54151Sbostic 	}
793*54151Sbostic 	/* NOTREACHED */
79438100Sbostic }
79538100Sbostic 
796*54151Sbostic void
79732127Sbostic usage()
79832127Sbostic {
79938098Sbostic #ifdef KERBEROS
800*54151Sbostic #ifdef CRYPT
80138098Sbostic 	(void)fprintf(stderr, "%s\n\t%s\n",
802*54151Sbostic 	    "usage: rcp [-Kpx] [-k realm] f1 f2",
803*54151Sbostic 	    "or: rcp [-Kprx] [-k realm] f1 ... fn directory");
80445397Smckusick #else
805*54151Sbostic 	(void)fprintf(stderr, "%s\n\t%s\n",
806*54151Sbostic 	    "usage: rcp [-Kp] [-k realm] f1 f2",
807*54151Sbostic 	    "or: rcp [-Kpr] [-k realm] f1 ... fn directory");
808*54151Sbostic #endif
809*54151Sbostic #else
81038098Sbostic 	(void)fprintf(stderr,
811*54151Sbostic 	    "usage: rcp [-p] f1 f2; or: rcp [-pr] f1 ... fn directory\n");
81236626Skfall #endif
81332127Sbostic 	exit(1);
81432127Sbostic }
81538879Skfall 
816*54151Sbostic #if __STDC__
817*54151Sbostic #include <stdarg.h>
818*54151Sbostic #else
819*54151Sbostic #include <varargs.h>
820*54151Sbostic #endif
821*54151Sbostic 
82238879Skfall #ifdef KERBEROS
823*54151Sbostic void
824*54151Sbostic #if __STDC__
825*54151Sbostic oldw(const char *fmt, ...)
826*54151Sbostic #else
827*54151Sbostic oldw(fmt, va_alist)
828*54151Sbostic 	char *fmt;
829*54151Sbostic         va_dcl
830*54151Sbostic #endif
83138879Skfall {
832*54151Sbostic 	va_list ap;
833*54151Sbostic #if __STDC__
834*54151Sbostic 	va_start(ap, fmt);
835*54151Sbostic #else
836*54151Sbostic 	va_start(ap);
837*54151Sbostic #endif
838*54151Sbostic 	(void)fprintf(stderr, "rcp: ");
839*54151Sbostic 	(void)vfprintf(stderr, fmt, ap);
840*54151Sbostic 	(void)fprintf(stderr, ", using standard rcp\n");
841*54151Sbostic 	va_end(ap);
84238879Skfall }
843*54151Sbostic #endif
84438879Skfall 
845*54151Sbostic void
846*54151Sbostic #if __STDC__
847*54151Sbostic err(const char *fmt, ...)
848*54151Sbostic #else
849*54151Sbostic err(fmt, va_alist)
850*54151Sbostic 	char *fmt;
851*54151Sbostic         va_dcl
852*54151Sbostic #endif
85338879Skfall {
854*54151Sbostic 	static FILE *fp;
855*54151Sbostic 	va_list ap;
856*54151Sbostic #if __STDC__
857*54151Sbostic 	va_start(ap, fmt);
858*54151Sbostic #else
859*54151Sbostic 	va_start(ap);
860*54151Sbostic #endif
86138879Skfall 
862*54151Sbostic 	++errs;
863*54151Sbostic 	if (!fp && !(fp = fdopen(rem, "w")))
864*54151Sbostic 		return;
865*54151Sbostic 	(void)fprintf(fp, "%c", 0x01);
866*54151Sbostic 	(void)fprintf(fp, "rcp: ");
867*54151Sbostic 	(void)vfprintf(fp, fmt, ap);
868*54151Sbostic 	(void)fprintf(fp, "\n");
869*54151Sbostic 	if (!iamremote) {
870*54151Sbostic 		(void)fprintf(stderr, "rcp: ");
871*54151Sbostic 		(void)vfprintf(stderr, fmt, ap);
872*54151Sbostic 		(void)fprintf(stderr, "\n");
87338879Skfall 	}
874*54151Sbostic 	va_end(ap);
875*54151Sbostic 	(void)fflush(fp);
87638879Skfall }
877