xref: /csrg-svn/bin/rcp/rcp.c (revision 37037)
121569Sdist /*
235619Sbostic  * Copyright (c) 1983 The Regents of the University of California.
335619Sbostic  * All rights reserved.
435619Sbostic  *
535619Sbostic  * Redistribution and use in source and binary forms are permitted
635619Sbostic  * provided that the above copyright notice and this paragraph are
735619Sbostic  * duplicated in all such forms and that any documentation,
835619Sbostic  * advertising materials, and other materials related to such
935619Sbostic  * distribution and use acknowledge that the software was developed
1035619Sbostic  * by the University of California, Berkeley.  The name of the
1135619Sbostic  * University may not be used to endorse or promote products derived
1235619Sbostic  * from this software without specific prior written permission.
1335619Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1435619Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1535619Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621569Sdist  */
1721569Sdist 
186440Swnj #ifndef lint
1921569Sdist char copyright[] =
2035619Sbostic "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
2121569Sdist  All rights reserved.\n";
2235619Sbostic #endif /* not lint */
236440Swnj 
2421569Sdist #ifndef lint
2536624Skfall static char sccsid[] = "@(#)rcp.c	5.11 (Berkeley) 9/22/88";
2635619Sbostic #endif /* not lint */
2721569Sdist 
2812989Ssam /*
2912989Ssam  * rcp
3012989Ssam  */
316720Smckusick #include <sys/param.h>
3232127Sbostic #include <sys/file.h>
336440Swnj #include <sys/stat.h>
3423101Slepreau #include <sys/time.h>
356440Swnj #include <sys/ioctl.h>
369199Ssam 
379199Ssam #include <netinet/in.h>
389199Ssam 
399199Ssam #include <stdio.h>
409199Ssam #include <signal.h>
416440Swnj #include <pwd.h>
426440Swnj #include <ctype.h>
4318026Sralph #include <netdb.h>
446440Swnj #include <errno.h>
45*37037Sbostic #include "pathnames.h"
4612989Ssam 
4736624Skfall #ifdef	KERBEROS
4836624Skfall #include <kerberos/krb.h>
4936624Skfall char	krb_realm[REALM_SZ];
5036624Skfall int	use_kerberos = 1, encrypt = 0;
5136624Skfall CREDENTIALS	cred;
5236624Skfall Key_schedule	schedule;
5336624Skfall #endif
5436624Skfall 
556440Swnj int	rem;
5632419Sbostic char	*colon(), *index(), *rindex(), *malloc(), *strcpy();
576440Swnj int	errs;
586440Swnj int	lostconn();
596440Swnj int	errno;
606440Swnj char	*sys_errlist[];
616440Swnj int	iamremote, targetshouldbedirectory;
626440Swnj int	iamrecursive;
6323101Slepreau int	pflag;
646440Swnj struct	passwd *pwd;
656440Swnj struct	passwd *getpwuid();
6618126Sralph int	userid;
6718126Sralph int	port;
686440Swnj 
6921254Smckusick struct buffer {
7021254Smckusick 	int	cnt;
7121254Smckusick 	char	*buf;
7221254Smckusick } *allocbuf();
7321254Smckusick 
746440Swnj /*VARARGS*/
756440Swnj int	error();
766440Swnj 
776440Swnj #define	ga()	 	(void) write(rem, "", 1)
786440Swnj 
796440Swnj main(argc, argv)
806440Swnj 	int argc;
816440Swnj 	char **argv;
826440Swnj {
836440Swnj 	char *targ, *host, *src;
8418259Sralph 	char *suser, *tuser, *thost;
856440Swnj 	int i;
866440Swnj 	char buf[BUFSIZ], cmd[16];
8718126Sralph 	struct servent *sp;
8818026Sralph 
8936626Skfall #ifdef	KERBEROS
9036626Skfall 	sp = getservbyname("kshell", "tcp");
9136626Skfall 	if(sp == NULL) {
9236626Skfall 		use_kerberos = 0;
9336626Skfall 		old_warning("kshell service unknown");
9436626Skfall 		sp = getservbyname("kshell", "tcp");
9536626Skfall 	}
9636626Skfall #else
9718026Sralph 	sp = getservbyname("shell", "tcp");
9836626Skfall #endif
9936626Skfall 
10018026Sralph 	if (sp == NULL) {
10118026Sralph 		fprintf(stderr, "rcp: shell/tcp: unknown service\n");
10218026Sralph 		exit(1);
10318026Sralph 	}
10418126Sralph 	port = sp->s_port;
10518126Sralph 	pwd = getpwuid(userid = getuid());
1066440Swnj 	if (pwd == 0) {
1076440Swnj 		fprintf(stderr, "who are you?\n");
1086440Swnj 		exit(1);
1096440Swnj 	}
11023101Slepreau 
11123101Slepreau 	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
11223101Slepreau 		(*argv)++;
11323101Slepreau 		while (**argv) switch (*(*argv)++) {
11423101Slepreau 
11523101Slepreau 		    case 'r':
11623101Slepreau 			iamrecursive++;
11723101Slepreau 			break;
11823101Slepreau 
11936624Skfall #ifdef	KERBEROS
12036624Skfall 		    case 'x':
12136624Skfall 			encrypt = 1;
12236624Skfall 			des_set_key(cred.session, schedule);
12336624Skfall 			break;
12436624Skfall 
12536624Skfall 		    case 'k':
12636624Skfall 			strncpy(krb_realm, ++argv, REALM_SZ);
12736624Skfall 			break;
12836624Skfall #endif
12936624Skfall 
13023101Slepreau 		    case 'p':		/* preserve mtimes and atimes */
13123101Slepreau 			pflag++;
13223101Slepreau 			break;
13323101Slepreau 
13423101Slepreau 		    /* The rest of these are not for users. */
13523101Slepreau 		    case 'd':
13623101Slepreau 			targetshouldbedirectory = 1;
13723101Slepreau 			break;
13823101Slepreau 
13923101Slepreau 		    case 'f':		/* "from" */
14023101Slepreau 			iamremote = 1;
14123101Slepreau 			(void) response();
14223101Slepreau 			(void) setuid(userid);
14323101Slepreau 			source(--argc, ++argv);
14423101Slepreau 			exit(errs);
14523101Slepreau 
14623101Slepreau 		    case 't':		/* "to" */
14723101Slepreau 			iamremote = 1;
14823101Slepreau 			(void) setuid(userid);
14923101Slepreau 			sink(--argc, ++argv);
15023101Slepreau 			exit(errs);
15123101Slepreau 
15223101Slepreau 		    default:
15332127Sbostic 			usage();
15423101Slepreau 		}
1556440Swnj 	}
15632127Sbostic 	if (argc < 2)
15732127Sbostic 		usage();
1586440Swnj 	if (argc > 2)
1596440Swnj 		targetshouldbedirectory = 1;
16032127Sbostic 	rem = -1;
16123101Slepreau 	(void) sprintf(cmd, "rcp%s%s%s",
16223101Slepreau 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
16323101Slepreau 	    targetshouldbedirectory ? " -d" : "");
16423101Slepreau 	(void) signal(SIGPIPE, lostconn);
1656440Swnj 	targ = colon(argv[argc - 1]);
16623101Slepreau 	if (targ) {				/* ... to remote */
1676440Swnj 		*targ++ = 0;
16813542Ssam 		if (*targ == 0)
16913542Ssam 			targ = ".";
17018259Sralph 		thost = index(argv[argc - 1], '@');
17118259Sralph 		if (thost) {
17218259Sralph 			*thost++ = 0;
17318259Sralph 			tuser = argv[argc - 1];
17418259Sralph 			if (*tuser == '\0')
17524711Sbloom 				tuser = NULL;
17618259Sralph 			else if (!okname(tuser))
1776440Swnj 				exit(1);
17818259Sralph 		} else {
17918259Sralph 			thost = argv[argc - 1];
18024711Sbloom 			tuser = NULL;
18118259Sralph 		}
1826440Swnj 		for (i = 0; i < argc - 1; i++) {
1836440Swnj 			src = colon(argv[i]);
18423101Slepreau 			if (src) {		/* remote to remote */
1856440Swnj 				*src++ = 0;
18613542Ssam 				if (*src == 0)
18713738Ssam 					src = ".";
18818259Sralph 				host = index(argv[i], '@');
18918259Sralph 				if (host) {
19018259Sralph 					*host++ = 0;
19118259Sralph 					suser = argv[i];
19218259Sralph 					if (*suser == '\0')
19318259Sralph 						suser = pwd->pw_name;
19418259Sralph 					else if (!okname(suser))
1956440Swnj 						continue;
196*37037Sbostic 		(void) sprintf(buf, "%s %s -l %s -n %s %s '%s%s%s:%s'",
197*37037Sbostic 					    _PATH_RSH, host, suser, cmd, src,
19831945Sbostic 					    tuser ? tuser : "",
19924711Sbloom 					    tuser ? "@" : "",
20024711Sbloom 					    thost, targ);
20118631Sralph 				} else
202*37037Sbostic 		(void) sprintf(buf, "%s %s -n %s %s '%s%s%s:%s'",
203*37037Sbostic 					    _PATH_RSH, argv[i], cmd, src,
20431945Sbostic 					    tuser ? tuser : "",
20524711Sbloom 					    tuser ? "@" : "",
20624711Sbloom 					    thost, targ);
2076440Swnj 				(void) susystem(buf);
20823101Slepreau 			} else {		/* local to remote */
2096440Swnj 				if (rem == -1) {
2106440Swnj 					(void) sprintf(buf, "%s -t %s",
2116440Swnj 					    cmd, targ);
21218259Sralph 					host = thost;
21336624Skfall #ifdef	KERBEROS
21436624Skfall try_again:
21536624Skfall 					if(use_kerberos) {
21636624Skfall 					    rem = KSUCCESS;
21736624Skfall 					    if(krb_realm[0] == '\0')
21836624Skfall 						    rem = krb_get_lrealm(krb_realm,1);
21936624Skfall 					    if(rem == KSUCCESS) {
22036624Skfall 						    if(encrypt) {
22136624Skfall 						        rem = krcmd_mutual(
22236624Skfall 							    &host, port,
22336624Skfall 							    tuser ? tuser
22436624Skfall 								    : pwd->pw_name,
22536624Skfall 							    buf, 0, krb_realm,
22636624Skfall 							    &cred, schedule);
22736624Skfall 						    } else {
22836624Skfall 
22936624Skfall 						        rem = krcmd(
23036624Skfall 							    &host,
23136624Skfall 							    port,
23236624Skfall 					    		    tuser ? tuser
23336624Skfall 							       : pwd->pw_name,
23436624Skfall 					    		    buf, 0,
23536624Skfall 							    krb_realm
23636624Skfall 					    	        );
23736624Skfall 						    }
23836624Skfall 					    } else {
23936624Skfall 						    fprintf(stderr,
24036624Skfall 						      "rcp: error getting local realm %s\n",
24136624Skfall 						      krb_err_txt[rem]);
24236624Skfall 						    exit(1);
24336624Skfall 					    }
24436624Skfall 					    if((rem < 0) && errno == ECONNREFUSED) {
24536624Skfall 						    use_kerberos = 0;
24636624Skfall 						    old_warning("remote host doesn't support Kerberos");
24736626Skfall 						    sp = getservbyname("shell", "tcp");
24836626Skfall 						    if(sp == NULL) {
24936626Skfall 							fprintf(stderr, "unknown service shell/tcp\n");
25036626Skfall 							exit(1);
25136626Skfall 						    }
25236626Skfall 						    port = sp->s_port;
25336624Skfall 						    goto try_again;
25436624Skfall 					    }
25536624Skfall 					} else {
25636624Skfall 					    rem = rcmd(&host, port, pwd->pw_name,
25736624Skfall 					        tuser ? tuser : pwd->pw_name,
25836624Skfall 					        buf, 0);
25936624Skfall 					}
26036624Skfall #else
26136624Skfall 
26224711Sbloom 					rem = rcmd(&host, port, pwd->pw_name,
26324711Sbloom 					    tuser ? tuser : pwd->pw_name,
2646440Swnj 					    buf, 0);
26536624Skfall #endif
2666440Swnj 					if (rem < 0)
2676440Swnj 						exit(1);
2686440Swnj 					if (response() < 0)
2696440Swnj 						exit(1);
27018126Sralph 					(void) setuid(userid);
2716440Swnj 				}
2726440Swnj 				source(1, argv+i);
2736440Swnj 			}
2746440Swnj 		}
27523101Slepreau 	} else {				/* ... to local */
2766440Swnj 		if (targetshouldbedirectory)
2776440Swnj 			verifydir(argv[argc - 1]);
2786440Swnj 		for (i = 0; i < argc - 1; i++) {
2796440Swnj 			src = colon(argv[i]);
28023101Slepreau 			if (src == 0) {		/* local to local */
281*37037Sbostic 				(void) sprintf(buf, "%s%s%s %s %s",
282*37037Sbostic 				    _PATH_CP, iamrecursive ? " -r" : "",
28323101Slepreau 				    pflag ? " -p" : "",
2846440Swnj 				    argv[i], argv[argc - 1]);
2856440Swnj 				(void) susystem(buf);
28623101Slepreau 			} else {		/* remote to local */
2876440Swnj 				*src++ = 0;
28813542Ssam 				if (*src == 0)
28913542Ssam 					src = ".";
29018259Sralph 				host = index(argv[i], '@');
29118259Sralph 				if (host) {
29218259Sralph 					*host++ = 0;
29318259Sralph 					suser = argv[i];
29418259Sralph 					if (*suser == '\0')
29518259Sralph 						suser = pwd->pw_name;
29618259Sralph 					else if (!okname(suser))
2976440Swnj 						continue;
29818259Sralph 				} else {
29918259Sralph 					host = argv[i];
3006440Swnj 					suser = pwd->pw_name;
30118259Sralph 				}
3026440Swnj 				(void) sprintf(buf, "%s -f %s", cmd, src);
30336624Skfall #ifdef	KERBEROS
30436624Skfall one_more_time:
30536624Skfall 				if(use_kerberos) {
30636624Skfall 				    rem = KSUCCESS;
30736624Skfall 				    if(krb_realm[0] == '\0')
30836624Skfall 					    rem = krb_get_lrealm(krb_realm,1);
30936624Skfall 				    if(rem == KSUCCESS) {
31036624Skfall 					if(encrypt) {
31136624Skfall 					    rem = krcmd_mutual(
31236624Skfall 						    &host,
31336624Skfall 						    port,
31436624Skfall 						    suser,
31536624Skfall 						    buf, 0,
31636624Skfall 						    krb_realm,
31736624Skfall 						    &cred, schedule
31836624Skfall 					    );
31936624Skfall 					} else {
32036624Skfall 					    rem = krcmd(
32136624Skfall 						    &host,
32236624Skfall 						    port,
32336624Skfall 						    suser,
32436624Skfall 						    buf, 0,
32536624Skfall 						    krb_realm
32636624Skfall 					    );
32736624Skfall 					}
32836624Skfall 				    } else {
32936624Skfall 					    fprintf(stderr,
33036624Skfall 					      "rcp: error getting local realm %s\n",
33136624Skfall 					      krb_err_txt[rem]);
33236624Skfall 					    exit(1);
33336624Skfall 				    }
33436624Skfall 				    if((rem < 0) && errno == ECONNREFUSED) {
33536624Skfall 					use_kerberos = 0;
33636624Skfall 					old_warning("remote host doesn't suport Kerberos");
33736626Skfall 					sp = getservbyname("shell", "tcp");
33836626Skfall 					if(sp == NULL) {
33936626Skfall 						fprintf(stderr, "unknown service shell/tcp\n");
34036626Skfall 						exit(1);
34136626Skfall 					}
34236626Skfall 					port = sp->s_port;
34336624Skfall 					goto one_more_time;
34436624Skfall 				    }
34536624Skfall 				} else {
34636624Skfall 					rem = rcmd(&host, port, pwd->pw_name, suser,
34736624Skfall 			    			buf, 0);
34836624Skfall 				}
34936624Skfall #else
35018126Sralph 				rem = rcmd(&host, port, pwd->pw_name, suser,
35136624Skfall 			    		buf, 0);
35236624Skfall #endif
3536440Swnj 				if (rem < 0)
35418259Sralph 					continue;
35518126Sralph 				(void) setreuid(0, userid);
3566440Swnj 				sink(1, argv+argc-1);
35718126Sralph 				(void) setreuid(userid, 0);
3586440Swnj 				(void) close(rem);
3596440Swnj 				rem = -1;
3606440Swnj 			}
3616440Swnj 		}
3626440Swnj 	}
3636440Swnj 	exit(errs);
3646440Swnj }
3656440Swnj 
3666440Swnj verifydir(cp)
3676440Swnj 	char *cp;
3686440Swnj {
3696440Swnj 	struct stat stb;
3706440Swnj 
37118126Sralph 	if (stat(cp, &stb) >= 0) {
37218126Sralph 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
37318126Sralph 			return;
37418126Sralph 		errno = ENOTDIR;
37518126Sralph 	}
3766440Swnj 	error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
3776440Swnj 	exit(1);
3786440Swnj }
3796440Swnj 
3806440Swnj char *
3816440Swnj colon(cp)
3826440Swnj 	char *cp;
3836440Swnj {
3846440Swnj 
3856440Swnj 	while (*cp) {
3866440Swnj 		if (*cp == ':')
3876440Swnj 			return (cp);
3886440Swnj 		if (*cp == '/')
3896440Swnj 			return (0);
3906440Swnj 		cp++;
3916440Swnj 	}
3926440Swnj 	return (0);
3936440Swnj }
3946440Swnj 
3956440Swnj okname(cp0)
3966440Swnj 	char *cp0;
3976440Swnj {
3986440Swnj 	register char *cp = cp0;
3996440Swnj 	register int c;
4006440Swnj 
4016440Swnj 	do {
4026440Swnj 		c = *cp;
4036440Swnj 		if (c & 0200)
4046440Swnj 			goto bad;
4056440Swnj 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
4066440Swnj 			goto bad;
4076440Swnj 		cp++;
4086440Swnj 	} while (*cp);
4096440Swnj 	return (1);
4106440Swnj bad:
4116440Swnj 	fprintf(stderr, "rcp: invalid user name %s\n", cp0);
4126440Swnj 	return (0);
4136440Swnj }
4146440Swnj 
41517999Sserge susystem(s)
41617999Sserge 	char *s;
4176440Swnj {
41817999Sserge 	int status, pid, w;
41917999Sserge 	register int (*istat)(), (*qstat)();
4206440Swnj 
42117999Sserge 	if ((pid = vfork()) == 0) {
42223101Slepreau 		(void) setuid(userid);
423*37037Sbostic 		execl(_PATH_BSHELL, "sh", "-c", s, (char *)0);
42417999Sserge 		_exit(127);
42517999Sserge 	}
42617999Sserge 	istat = signal(SIGINT, SIG_IGN);
42717999Sserge 	qstat = signal(SIGQUIT, SIG_IGN);
42817999Sserge 	while ((w = wait(&status)) != pid && w != -1)
42917999Sserge 		;
43017999Sserge 	if (w == -1)
43117999Sserge 		status = -1;
43223101Slepreau 	(void) signal(SIGINT, istat);
43323101Slepreau 	(void) signal(SIGQUIT, qstat);
43417999Sserge 	return (status);
4356440Swnj }
4366440Swnj 
4376440Swnj source(argc, argv)
4386440Swnj 	int argc;
4396440Swnj 	char **argv;
4406440Swnj {
4416440Swnj 	char *last, *name;
4426440Swnj 	struct stat stb;
44321254Smckusick 	static struct buffer buffer;
44421254Smckusick 	struct buffer *bp;
44532268Sbostic 	int x, readerr, f, amt;
44621254Smckusick 	off_t i;
4476440Swnj 	char buf[BUFSIZ];
4486440Swnj 
4496440Swnj 	for (x = 0; x < argc; x++) {
4506440Swnj 		name = argv[x];
45118126Sralph 		if ((f = open(name, 0)) < 0) {
4526440Swnj 			error("rcp: %s: %s\n", name, sys_errlist[errno]);
4536440Swnj 			continue;
4546440Swnj 		}
4556440Swnj 		if (fstat(f, &stb) < 0)
4566440Swnj 			goto notreg;
4576440Swnj 		switch (stb.st_mode&S_IFMT) {
4586440Swnj 
4596440Swnj 		case S_IFREG:
4606440Swnj 			break;
4616440Swnj 
4626440Swnj 		case S_IFDIR:
4636440Swnj 			if (iamrecursive) {
4646440Swnj 				(void) close(f);
46523101Slepreau 				rsource(name, &stb);
4666440Swnj 				continue;
4676440Swnj 			}
4686440Swnj 			/* fall into ... */
4696440Swnj 		default:
4706440Swnj notreg:
4716440Swnj 			(void) close(f);
4726440Swnj 			error("rcp: %s: not a plain file\n", name);
4736440Swnj 			continue;
4746440Swnj 		}
4756440Swnj 		last = rindex(name, '/');
4766440Swnj 		if (last == 0)
4776440Swnj 			last = name;
4786440Swnj 		else
4796440Swnj 			last++;
48023101Slepreau 		if (pflag) {
48123101Slepreau 			/*
48223101Slepreau 			 * Make it compatible with possible future
48323101Slepreau 			 * versions expecting microseconds.
48423101Slepreau 			 */
48523101Slepreau 			(void) sprintf(buf, "T%ld 0 %ld 0\n",
48623101Slepreau 			    stb.st_mtime, stb.st_atime);
48723101Slepreau 			(void) write(rem, buf, strlen(buf));
48823101Slepreau 			if (response() < 0) {
48923101Slepreau 				(void) close(f);
49023101Slepreau 				continue;
49123101Slepreau 			}
49223101Slepreau 		}
49323101Slepreau 		(void) sprintf(buf, "C%04o %ld %s\n",
4946440Swnj 		    stb.st_mode&07777, stb.st_size, last);
4956440Swnj 		(void) write(rem, buf, strlen(buf));
4966440Swnj 		if (response() < 0) {
4976440Swnj 			(void) close(f);
4986440Swnj 			continue;
4996440Swnj 		}
50035689Sbostic 		if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) {
50121254Smckusick 			(void) close(f);
50221254Smckusick 			continue;
50321254Smckusick 		}
50432268Sbostic 		readerr = 0;
50521254Smckusick 		for (i = 0; i < stb.st_size; i += bp->cnt) {
50621254Smckusick 			amt = bp->cnt;
5076440Swnj 			if (i + amt > stb.st_size)
5086440Swnj 				amt = stb.st_size - i;
50932268Sbostic 			if (readerr == 0 && read(f, bp->buf, amt) != amt)
51032268Sbostic 				readerr = errno;
51121254Smckusick 			(void) write(rem, bp->buf, amt);
5126440Swnj 		}
5136440Swnj 		(void) close(f);
51432268Sbostic 		if (readerr == 0)
5156440Swnj 			ga();
5166440Swnj 		else
51732268Sbostic 			error("rcp: %s: %s\n", name, sys_errlist[readerr]);
5186440Swnj 		(void) response();
5196440Swnj 	}
5206440Swnj }
5216440Swnj 
52213542Ssam #include <sys/dir.h>
5236440Swnj 
52423101Slepreau rsource(name, statp)
5256440Swnj 	char *name;
52623101Slepreau 	struct stat *statp;
5276440Swnj {
5286440Swnj 	DIR *d = opendir(name);
5296440Swnj 	char *last;
5306440Swnj 	struct direct *dp;
5316440Swnj 	char buf[BUFSIZ];
5326440Swnj 	char *bufv[1];
5336440Swnj 
5346440Swnj 	if (d == 0) {
53518126Sralph 		error("rcp: %s: %s\n", name, sys_errlist[errno]);
5366440Swnj 		return;
5376440Swnj 	}
5386440Swnj 	last = rindex(name, '/');
5396440Swnj 	if (last == 0)
5406440Swnj 		last = name;
5416440Swnj 	else
5426440Swnj 		last++;
54323101Slepreau 	if (pflag) {
54423101Slepreau 		(void) sprintf(buf, "T%ld 0 %ld 0\n",
54523101Slepreau 		    statp->st_mtime, statp->st_atime);
54623101Slepreau 		(void) write(rem, buf, strlen(buf));
54723101Slepreau 		if (response() < 0) {
54823101Slepreau 			closedir(d);
54923101Slepreau 			return;
55023101Slepreau 		}
55123101Slepreau 	}
55223101Slepreau 	(void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
5536440Swnj 	(void) write(rem, buf, strlen(buf));
5546440Swnj 	if (response() < 0) {
5556440Swnj 		closedir(d);
5566440Swnj 		return;
5576440Swnj 	}
5586440Swnj 	while (dp = readdir(d)) {
5596440Swnj 		if (dp->d_ino == 0)
5606440Swnj 			continue;
5616440Swnj 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
5626440Swnj 			continue;
5636440Swnj 		if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
5646440Swnj 			error("%s/%s: Name too long.\n", name, dp->d_name);
5656440Swnj 			continue;
5666440Swnj 		}
5676440Swnj 		(void) sprintf(buf, "%s/%s", name, dp->d_name);
5686440Swnj 		bufv[0] = buf;
5696440Swnj 		source(1, bufv);
5706440Swnj 	}
5716440Swnj 	closedir(d);
5726440Swnj 	(void) write(rem, "E\n", 2);
5736440Swnj 	(void) response();
5746440Swnj }
5756440Swnj 
5766440Swnj response()
5776440Swnj {
5786440Swnj 	char resp, c, rbuf[BUFSIZ], *cp = rbuf;
5796440Swnj 
5806440Swnj 	if (read(rem, &resp, 1) != 1)
5816440Swnj 		lostconn();
5826440Swnj 	switch (resp) {
5836440Swnj 
58423101Slepreau 	case 0:				/* ok */
5856440Swnj 		return (0);
5866440Swnj 
5876440Swnj 	default:
5886440Swnj 		*cp++ = resp;
5896440Swnj 		/* fall into... */
59023101Slepreau 	case 1:				/* error, followed by err msg */
59123101Slepreau 	case 2:				/* fatal error, "" */
5926440Swnj 		do {
5936440Swnj 			if (read(rem, &c, 1) != 1)
5946440Swnj 				lostconn();
5956440Swnj 			*cp++ = c;
5966440Swnj 		} while (cp < &rbuf[BUFSIZ] && c != '\n');
5976440Swnj 		if (iamremote == 0)
5986440Swnj 			(void) write(2, rbuf, cp - rbuf);
5996440Swnj 		errs++;
6006440Swnj 		if (resp == 1)
6016440Swnj 			return (-1);
6026440Swnj 		exit(1);
6036440Swnj 	}
6046440Swnj 	/*NOTREACHED*/
6056440Swnj }
6066440Swnj 
6076440Swnj lostconn()
6086440Swnj {
6096440Swnj 
6106440Swnj 	if (iamremote == 0)
6116440Swnj 		fprintf(stderr, "rcp: lost connection\n");
6126440Swnj 	exit(1);
6136440Swnj }
6146440Swnj 
6156440Swnj sink(argc, argv)
6166440Swnj 	int argc;
6176440Swnj 	char **argv;
6186440Swnj {
61921254Smckusick 	off_t i, j;
62021254Smckusick 	char *targ, *whopp, *cp;
62121254Smckusick 	int of, mode, wrerr, exists, first, count, amt, size;
62221254Smckusick 	struct buffer *bp;
62321254Smckusick 	static struct buffer buffer;
62421254Smckusick 	struct stat stb;
62521254Smckusick 	int targisdir = 0;
6266440Swnj 	int mask = umask(0);
6276440Swnj 	char *myargv[1];
62823101Slepreau 	char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
62923101Slepreau 	int setimes = 0;
63023101Slepreau 	struct timeval tv[2];
63123101Slepreau #define atime	tv[0]
63223101Slepreau #define mtime	tv[1]
63321254Smckusick #define	SCREWUP(str)	{ whopp = str; goto screwup; }
6346440Swnj 
63523112Slepreau 	if (!pflag)
63623112Slepreau 		(void) umask(mask);
63718126Sralph 	if (argc != 1) {
6386440Swnj 		error("rcp: ambiguous target\n");
6396440Swnj 		exit(1);
6406440Swnj 	}
6416440Swnj 	targ = *argv;
6426440Swnj 	if (targetshouldbedirectory)
6436440Swnj 		verifydir(targ);
6446440Swnj 	ga();
6456440Swnj 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
6466440Swnj 		targisdir = 1;
64714580Sralph 	for (first = 1; ; first = 0) {
6486440Swnj 		cp = cmdbuf;
6496440Swnj 		if (read(rem, cp, 1) <= 0)
6506440Swnj 			return;
6516440Swnj 		if (*cp++ == '\n')
6526440Swnj 			SCREWUP("unexpected '\\n'");
6536440Swnj 		do {
6546440Swnj 			if (read(rem, cp, 1) != 1)
6556440Swnj 				SCREWUP("lost connection");
6566440Swnj 		} while (*cp++ != '\n');
6576440Swnj 		*cp = 0;
6586440Swnj 		if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
6596440Swnj 			if (iamremote == 0)
66013542Ssam 				(void) write(2, cmdbuf+1, strlen(cmdbuf+1));
6616440Swnj 			if (cmdbuf[0] == '\02')
6626440Swnj 				exit(1);
6636440Swnj 			errs++;
6646440Swnj 			continue;
6656440Swnj 		}
6666440Swnj 		*--cp = 0;
6676440Swnj 		cp = cmdbuf;
6686440Swnj 		if (*cp == 'E') {
6696440Swnj 			ga();
6706440Swnj 			return;
6716440Swnj 		}
67223101Slepreau 
67323101Slepreau #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
67423101Slepreau 		if (*cp == 'T') {
67523101Slepreau 			setimes++;
67623101Slepreau 			cp++;
67723101Slepreau 			getnum(mtime.tv_sec);
67823101Slepreau 			if (*cp++ != ' ')
67923101Slepreau 				SCREWUP("mtime.sec not delimited");
68023101Slepreau 			getnum(mtime.tv_usec);
68123101Slepreau 			if (*cp++ != ' ')
68223101Slepreau 				SCREWUP("mtime.usec not delimited");
68323101Slepreau 			getnum(atime.tv_sec);
68423101Slepreau 			if (*cp++ != ' ')
68523101Slepreau 				SCREWUP("atime.sec not delimited");
68623101Slepreau 			getnum(atime.tv_usec);
68723101Slepreau 			if (*cp++ != '\0')
68823101Slepreau 				SCREWUP("atime.usec not delimited");
68923101Slepreau 			ga();
69023101Slepreau 			continue;
69123101Slepreau 		}
69214580Sralph 		if (*cp != 'C' && *cp != 'D') {
69314580Sralph 			/*
69414580Sralph 			 * Check for the case "rcp remote:foo\* local:bar".
69514580Sralph 			 * In this case, the line "No match." can be returned
69614580Sralph 			 * by the shell before the rcp command on the remote is
69714580Sralph 			 * executed so the ^Aerror_message convention isn't
69814580Sralph 			 * followed.
69914580Sralph 			 */
70014580Sralph 			if (first) {
70114580Sralph 				error("%s\n", cp);
70214580Sralph 				exit(1);
70314580Sralph 			}
7046440Swnj 			SCREWUP("expected control record");
70514580Sralph 		}
7066440Swnj 		cp++;
7076440Swnj 		mode = 0;
7086440Swnj 		for (; cp < cmdbuf+5; cp++) {
7096440Swnj 			if (*cp < '0' || *cp > '7')
7106440Swnj 				SCREWUP("bad mode");
7116440Swnj 			mode = (mode << 3) | (*cp - '0');
7126440Swnj 		}
7136440Swnj 		if (*cp++ != ' ')
7146440Swnj 			SCREWUP("mode not delimited");
7156440Swnj 		size = 0;
71623112Slepreau 		while (isdigit(*cp))
7176440Swnj 			size = size * 10 + (*cp++ - '0');
7186440Swnj 		if (*cp++ != ' ')
7196440Swnj 			SCREWUP("size not delimited");
7206440Swnj 		if (targisdir)
7216440Swnj 			(void) sprintf(nambuf, "%s%s%s", targ,
7226440Swnj 			    *targ ? "/" : "", cp);
7236440Swnj 		else
7246440Swnj 			(void) strcpy(nambuf, targ);
7256440Swnj 		exists = stat(nambuf, &stb) == 0;
72618126Sralph 		if (cmdbuf[0] == 'D') {
72718126Sralph 			if (exists) {
7286440Swnj 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
7296440Swnj 					errno = ENOTDIR;
7306440Swnj 					goto bad;
7316440Swnj 				}
73223112Slepreau 				if (pflag)
73323112Slepreau 					(void) chmod(nambuf, mode);
73418126Sralph 			} else if (mkdir(nambuf, mode) < 0)
7356440Swnj 				goto bad;
7366440Swnj 			myargv[0] = nambuf;
7376440Swnj 			sink(1, myargv);
73823101Slepreau 			if (setimes) {
73923101Slepreau 				setimes = 0;
74023101Slepreau 				if (utimes(nambuf, tv) < 0)
74123101Slepreau 					error("rcp: can't set times on %s: %s\n",
74223101Slepreau 					    nambuf, sys_errlist[errno]);
74323101Slepreau 			}
7446440Swnj 			continue;
74518126Sralph 		}
74632127Sbostic 		if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) {
7476440Swnj 	bad:
7486440Swnj 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
7496440Swnj 			continue;
7506440Swnj 		}
75123112Slepreau 		if (exists && pflag)
75223112Slepreau 			(void) fchmod(of, mode);
7536440Swnj 		ga();
75435689Sbostic 		if ((bp = allocbuf(&buffer, of, BUFSIZ)) == 0) {
75523101Slepreau 			(void) close(of);
75621254Smckusick 			continue;
75721254Smckusick 		}
75821254Smckusick 		cp = bp->buf;
75921254Smckusick 		count = 0;
7606440Swnj 		wrerr = 0;
7616440Swnj 		for (i = 0; i < size; i += BUFSIZ) {
76221254Smckusick 			amt = BUFSIZ;
7636440Swnj 			if (i + amt > size)
7646440Swnj 				amt = size - i;
76521254Smckusick 			count += amt;
7666440Swnj 			do {
76721254Smckusick 				j = read(rem, cp, amt);
76824711Sbloom 				if (j <= 0) {
76924711Sbloom 					if (j == 0)
77024711Sbloom 					    error("rcp: dropped connection");
77124711Sbloom 					else
77224711Sbloom 					    error("rcp: %s\n",
77324711Sbloom 						sys_errlist[errno]);
7746440Swnj 					exit(1);
77524711Sbloom 				}
7766440Swnj 				amt -= j;
7776440Swnj 				cp += j;
7786440Swnj 			} while (amt > 0);
77921254Smckusick 			if (count == bp->cnt) {
78021254Smckusick 				if (wrerr == 0 &&
78121254Smckusick 				    write(of, bp->buf, count) != count)
78221254Smckusick 					wrerr++;
78321254Smckusick 				count = 0;
78421254Smckusick 				cp = bp->buf;
78521254Smckusick 			}
7866440Swnj 		}
78721254Smckusick 		if (count != 0 && wrerr == 0 &&
78821254Smckusick 		    write(of, bp->buf, count) != count)
78921254Smckusick 			wrerr++;
79032127Sbostic 		if (ftruncate(of, size))
79132127Sbostic 			error("rcp: can't truncate %s: %s\n",
79232127Sbostic 			    nambuf, sys_errlist[errno]);
7936440Swnj 		(void) close(of);
7946440Swnj 		(void) response();
79523101Slepreau 		if (setimes) {
79623101Slepreau 			setimes = 0;
79723101Slepreau 			if (utimes(nambuf, tv) < 0)
79823101Slepreau 				error("rcp: can't set times on %s: %s\n",
79923101Slepreau 				    nambuf, sys_errlist[errno]);
80023101Slepreau 		}
8016440Swnj 		if (wrerr)
80223101Slepreau 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
8036440Swnj 		else
8046440Swnj 			ga();
8056440Swnj 	}
8066440Swnj screwup:
8076440Swnj 	error("rcp: protocol screwup: %s\n", whopp);
8086440Swnj 	exit(1);
8096440Swnj }
8106440Swnj 
81121254Smckusick struct buffer *
81221254Smckusick allocbuf(bp, fd, blksize)
81321254Smckusick 	struct buffer *bp;
81421254Smckusick 	int fd, blksize;
81521254Smckusick {
81621254Smckusick 	struct stat stb;
81721254Smckusick 	int size;
81821254Smckusick 
81921254Smckusick 	if (fstat(fd, &stb) < 0) {
82021254Smckusick 		error("rcp: fstat: %s\n", sys_errlist[errno]);
82135689Sbostic 		return ((struct buffer *)0);
82221254Smckusick 	}
82321254Smckusick 	size = roundup(stb.st_blksize, blksize);
82421254Smckusick 	if (size == 0)
82521254Smckusick 		size = blksize;
82621254Smckusick 	if (bp->cnt < size) {
82721254Smckusick 		if (bp->buf != 0)
82821254Smckusick 			free(bp->buf);
82923101Slepreau 		bp->buf = (char *)malloc((unsigned) size);
83021254Smckusick 		if (bp->buf == 0) {
83121254Smckusick 			error("rcp: malloc: out of memory\n");
83235689Sbostic 			return ((struct buffer *)0);
83321254Smckusick 		}
83421254Smckusick 	}
83521254Smckusick 	bp->cnt = size;
83621254Smckusick 	return (bp);
83721254Smckusick }
83821254Smckusick 
83923101Slepreau /*VARARGS1*/
8406440Swnj error(fmt, a1, a2, a3, a4, a5)
8416440Swnj 	char *fmt;
8426440Swnj 	int a1, a2, a3, a4, a5;
8436440Swnj {
8446440Swnj 	char buf[BUFSIZ], *cp = buf;
8456440Swnj 
8466440Swnj 	errs++;
8476440Swnj 	*cp++ = 1;
8486440Swnj 	(void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
8496440Swnj 	(void) write(rem, buf, strlen(buf));
8506440Swnj 	if (iamremote == 0)
8516440Swnj 		(void) write(2, buf+1, strlen(buf+1));
8526440Swnj }
85332127Sbostic 
85432127Sbostic usage()
85532127Sbostic {
85636626Skfall #ifdef	KERBEROS
85736626Skfall 	fputs("usage: rcp [-k realm] [-x] [-p] f1 f2;\n", stderr);
85836626Skfall 	fputs("   or: rcp [-k realm] [-x] [-rp] f1 ... fn d2\n", stderr);
85936626Skfall #else
86032127Sbostic 	fputs("usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n", stderr);
86136626Skfall #endif
86232127Sbostic 	exit(1);
86332127Sbostic }
86436624Skfall 
86536624Skfall #ifdef	KERBEROS
86636624Skfall old_warning(str)
86736624Skfall 	char	*str;
86836624Skfall {
86936626Skfall 	fprintf(stderr,"Warning: %s, using standard rcp\n", str);
87036624Skfall }
87136624Skfall #endif
872