xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 42666)
122499Sdist /*
236304Skarels  * Copyright (c) 1985, 1988 Regents of the University of California.
333738Sbostic  * All rights reserved.
433738Sbostic  *
5*42666Sbostic  * %sccs.include.redist.c%
622499Sdist  */
722499Sdist 
810275Ssam #ifndef lint
922499Sdist char copyright[] =
1036304Skarels "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\
1122499Sdist  All rights reserved.\n";
1233738Sbostic #endif /* not lint */
1310275Ssam 
1422499Sdist #ifndef lint
15*42666Sbostic static char sccsid[] = "@(#)ftpd.c	5.36	(Berkeley) 06/01/90";
1633738Sbostic #endif /* not lint */
1722499Sdist 
1810275Ssam /*
1910275Ssam  * FTP server.
2010275Ssam  */
2110303Ssam #include <sys/param.h>
2210275Ssam #include <sys/stat.h>
2310275Ssam #include <sys/ioctl.h>
2410275Ssam #include <sys/socket.h>
2513247Ssam #include <sys/file.h>
2613595Ssam #include <sys/wait.h>
2736620Srick #include <sys/dir.h>
2810275Ssam 
2910275Ssam #include <netinet/in.h>
3010275Ssam 
3136933Skarels #define	FTP_NAMES
3213034Ssam #include <arpa/ftp.h>
3313211Sroot #include <arpa/inet.h>
3426044Sminshall #include <arpa/telnet.h>
3513034Ssam 
3636933Skarels #include <ctype.h>
3710275Ssam #include <stdio.h>
3810275Ssam #include <signal.h>
3910275Ssam #include <pwd.h>
4010275Ssam #include <setjmp.h>
4110275Ssam #include <netdb.h>
4210423Ssam #include <errno.h>
4342031Sbostic #include <string.h>
4426493Sminshall #include <syslog.h>
4536435Sbostic #include <varargs.h>
4637459Skarels #include "pathnames.h"
4710275Ssam 
4810695Ssam /*
4910695Ssam  * File containing login names
5010695Ssam  * NOT to be used on this machine.
5110695Ssam  * Commonly used to disallow uucp.
5210695Ssam  */
5310275Ssam extern	int errno;
5410275Ssam extern	char *crypt();
5510275Ssam extern	char version[];
5610275Ssam extern	char *home;		/* pointer to home directory for glob */
5736276Sbostic extern	FILE *ftpd_popen(), *fopen(), *freopen();
5836304Skarels extern	int  ftpd_pclose(), fclose();
5926044Sminshall extern	char *getline();
6026044Sminshall extern	char cbuf[];
6137459Skarels extern	off_t restart_point;
6210275Ssam 
6310275Ssam struct	sockaddr_in ctrl_addr;
6410275Ssam struct	sockaddr_in data_source;
6510275Ssam struct	sockaddr_in data_dest;
6610275Ssam struct	sockaddr_in his_addr;
6736933Skarels struct	sockaddr_in pasv_addr;
6810275Ssam 
6910275Ssam int	data;
7026044Sminshall jmp_buf	errcatch, urgcatch;
7110275Ssam int	logged_in;
7210275Ssam struct	passwd *pw;
7310275Ssam int	debug;
7426493Sminshall int	timeout = 900;    /* timeout after 15 minutes of inactivity */
7536933Skarels int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
7611757Ssam int	logging;
7710275Ssam int	guest;
7810275Ssam int	type;
7910275Ssam int	form;
8010275Ssam int	stru;			/* avoid C keyword */
8110275Ssam int	mode;
8210321Ssam int	usedefault = 1;		/* for data transfers */
8336304Skarels int	pdata = -1;		/* for passive mode */
8426044Sminshall int	transflag;
8536933Skarels off_t	file_size;
8636933Skarels off_t	byte_count;
8736933Skarels #if !defined(CMASK) || CMASK == 0
8836933Skarels #undef CMASK
8936933Skarels #define CMASK 027
9036933Skarels #endif
9136933Skarels int	defumask = CMASK;		/* default umask value */
9226044Sminshall char	tmpline[7];
9336276Sbostic char	hostname[MAXHOSTNAMELEN];
9436276Sbostic char	remotehost[MAXHOSTNAMELEN];
9510275Ssam 
9611653Ssam /*
9711653Ssam  * Timeout intervals for retrying connections
9811653Ssam  * to hosts that don't accept PORT cmds.  This
9911653Ssam  * is a kludge, but given the problems with TCP...
10011653Ssam  */
10111653Ssam #define	SWAITMAX	90	/* wait at most 90 seconds */
10211653Ssam #define	SWAITINT	5	/* interval between retries */
10311653Ssam 
10411653Ssam int	swaitmax = SWAITMAX;
10511653Ssam int	swaitint = SWAITINT;
10611653Ssam 
10710275Ssam int	lostconn();
10826044Sminshall int	myoob();
10910275Ssam FILE	*getdatasock(), *dataconn();
11010275Ssam 
11136620Srick #ifdef SETPROCTITLE
11236620Srick char	**Argv = NULL;		/* pointer to argument vector */
11336620Srick char	*LastArgv = NULL;	/* end of argv */
11436933Skarels char	proctitle[BUFSIZ];	/* initial part of title */
11536620Srick #endif /* SETPROCTITLE */
11636620Srick 
11736620Srick main(argc, argv, envp)
11810275Ssam 	int argc;
11910275Ssam 	char *argv[];
12036620Srick 	char **envp;
12110275Ssam {
12227750Sminshall 	int addrlen, on = 1;
12310275Ssam 	char *cp;
12410275Ssam 
12516339Skarels 	addrlen = sizeof (his_addr);
12636304Skarels 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
12726493Sminshall 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
12810275Ssam 		exit(1);
12910275Ssam 	}
13016339Skarels 	addrlen = sizeof (ctrl_addr);
13136304Skarels 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
13226493Sminshall 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
13316339Skarels 		exit(1);
13416339Skarels 	}
13516339Skarels 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
13610275Ssam 	debug = 0;
13726493Sminshall 	openlog("ftpd", LOG_PID, LOG_DAEMON);
13836620Srick #ifdef SETPROCTITLE
13936620Srick 	/*
14036620Srick 	 *  Save start and extent of argv for setproctitle.
14136620Srick 	 */
14236620Srick 	Argv = argv;
14336620Srick 	while (*envp)
14436620Srick 		envp++;
14536620Srick 	LastArgv = envp[-1] + strlen(envp[-1]);
14636620Srick #endif /* SETPROCTITLE */
14736620Srick 
14810275Ssam 	argc--, argv++;
14910275Ssam 	while (argc > 0 && *argv[0] == '-') {
15010275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
15110275Ssam 
15211653Ssam 		case 'v':
15311653Ssam 			debug = 1;
15411653Ssam 			break;
15511653Ssam 
15610275Ssam 		case 'd':
15710275Ssam 			debug = 1;
15810275Ssam 			break;
15910275Ssam 
16011757Ssam 		case 'l':
16111757Ssam 			logging = 1;
16211757Ssam 			break;
16311757Ssam 
16411653Ssam 		case 't':
16511653Ssam 			timeout = atoi(++cp);
16636933Skarels 			if (maxtimeout < timeout)
16736933Skarels 				maxtimeout = timeout;
16811653Ssam 			goto nextopt;
16911653Ssam 
17036933Skarels 		case 'T':
17136933Skarels 			maxtimeout = atoi(++cp);
17236933Skarels 			if (timeout > maxtimeout)
17336933Skarels 				timeout = maxtimeout;
17436933Skarels 			goto nextopt;
17536933Skarels 
17636933Skarels 		case 'u':
17736933Skarels 		    {
17836933Skarels 			int val = 0;
17936933Skarels 
18036933Skarels 			while (*++cp && *cp >= '0' && *cp <= '9')
18136933Skarels 				val = val*8 + *cp - '0';
18236933Skarels 			if (*cp)
18336933Skarels 				fprintf(stderr, "ftpd: Bad value for -u\n");
18436933Skarels 			else
18536933Skarels 				defumask = val;
18636933Skarels 			goto nextopt;
18736933Skarels 		    }
18836933Skarels 
18910275Ssam 		default:
19016339Skarels 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
19116339Skarels 			     *cp);
19210275Ssam 			break;
19310275Ssam 		}
19411653Ssam nextopt:
19510275Ssam 		argc--, argv++;
19610275Ssam 	}
19737459Skarels 	(void) freopen(_PATH_DEVNULL, "w", stderr);
19826493Sminshall 	(void) signal(SIGPIPE, lostconn);
19926493Sminshall 	(void) signal(SIGCHLD, SIG_IGN);
20035691Sbostic 	if ((int)signal(SIGURG, myoob) < 0)
20126493Sminshall 		syslog(LOG_ERR, "signal: %m");
20235691Sbostic 
20338134Srick 	/* Try to handle urgent data inline */
20427750Sminshall #ifdef SO_OOBINLINE
20536276Sbostic 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
20627750Sminshall 		syslog(LOG_ERR, "setsockopt: %m");
20736276Sbostic #endif
20838134Srick 
20936933Skarels #ifdef	F_SETOWN
21036304Skarels 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
21136304Skarels 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
21236933Skarels #endif
21316760Slepreau 	dolog(&his_addr);
21416339Skarels 	/*
21516339Skarels 	 * Set up default state
21616339Skarels 	 */
21716339Skarels 	data = -1;
21816339Skarels 	type = TYPE_A;
21916339Skarels 	form = FORM_N;
22016339Skarels 	stru = STRU_F;
22116339Skarels 	mode = MODE_S;
22226044Sminshall 	tmpline[0] = '\0';
22326493Sminshall 	(void) gethostname(hostname, sizeof (hostname));
22436276Sbostic 	reply(220, "%s FTP server (%s) ready.", hostname, version);
22536304Skarels 	(void) setjmp(errcatch);
22636304Skarels 	for (;;)
22726493Sminshall 		(void) yyparse();
22836620Srick 	/* NOTREACHED */
22910275Ssam }
23010419Ssam 
23110275Ssam lostconn()
23210275Ssam {
23310275Ssam 
23414089Ssam 	if (debug)
23526493Sminshall 		syslog(LOG_DEBUG, "lost connection");
23614089Ssam 	dologout(-1);
23710275Ssam }
23810275Ssam 
23935672Sbostic static char ttyline[20];
24035672Sbostic 
24136185Sbostic /*
24236185Sbostic  * Helper function for sgetpwnam().
24336185Sbostic  */
24436185Sbostic char *
24536185Sbostic sgetsave(s)
24636185Sbostic 	char *s;
24736185Sbostic {
24836185Sbostic 	char *malloc();
24936185Sbostic 	char *new = malloc((unsigned) strlen(s) + 1);
25036933Skarels 
25136185Sbostic 	if (new == NULL) {
25236620Srick 		perror_reply(421, "Local resource failure: malloc");
25336185Sbostic 		dologout(1);
25436620Srick 		/* NOTREACHED */
25536185Sbostic 	}
25636185Sbostic 	(void) strcpy(new, s);
25736185Sbostic 	return (new);
25836185Sbostic }
25936185Sbostic 
26036185Sbostic /*
26136185Sbostic  * Save the result of a getpwnam.  Used for USER command, since
26236185Sbostic  * the data returned must not be clobbered by any other command
26336185Sbostic  * (e.g., globbing).
26436185Sbostic  */
26536185Sbostic struct passwd *
26636185Sbostic sgetpwnam(name)
26736185Sbostic 	char *name;
26836185Sbostic {
26936185Sbostic 	static struct passwd save;
27036185Sbostic 	register struct passwd *p;
27136185Sbostic 	char *sgetsave();
27236185Sbostic 
27336185Sbostic 	if ((p = getpwnam(name)) == NULL)
27436185Sbostic 		return (p);
27536185Sbostic 	if (save.pw_name) {
27636185Sbostic 		free(save.pw_name);
27736185Sbostic 		free(save.pw_passwd);
27836185Sbostic 		free(save.pw_gecos);
27936185Sbostic 		free(save.pw_dir);
28036185Sbostic 		free(save.pw_shell);
28136185Sbostic 	}
28236185Sbostic 	save = *p;
28336185Sbostic 	save.pw_name = sgetsave(p->pw_name);
28436185Sbostic 	save.pw_passwd = sgetsave(p->pw_passwd);
28536185Sbostic 	save.pw_gecos = sgetsave(p->pw_gecos);
28636185Sbostic 	save.pw_dir = sgetsave(p->pw_dir);
28736185Sbostic 	save.pw_shell = sgetsave(p->pw_shell);
28836185Sbostic 	return (&save);
28936185Sbostic }
29036185Sbostic 
29136304Skarels int login_attempts;		/* number of failed login attempts */
29236304Skarels int askpasswd;			/* had user command, ask for passwd */
29336304Skarels 
29436304Skarels /*
29536304Skarels  * USER command.
29642327Sbostic  * Sets global passwd pointer pw if named account exists and is acceptable;
29742327Sbostic  * sets askpasswd if a PASS command is expected.  If logged in previously,
29842327Sbostic  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
29942327Sbostic  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
30042327Sbostic  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
30142327Sbostic  * requesting login privileges.  Disallow anyone who does not have a standard
30242327Sbostic  * shell as returned by getusershell().  Disallow anyone mentioned in the file
30342327Sbostic  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
30436304Skarels  */
30536304Skarels user(name)
30636304Skarels 	char *name;
30736304Skarels {
30836304Skarels 	register char *cp;
30936304Skarels 	char *shell;
31040183Smckusick 	char *getusershell();
31136304Skarels 
31236304Skarels 	if (logged_in) {
31336304Skarels 		if (guest) {
31436304Skarels 			reply(530, "Can't change user from guest login.");
31536304Skarels 			return;
31636304Skarels 		}
31736304Skarels 		end_login();
31836304Skarels 	}
31936304Skarels 
32036304Skarels 	guest = 0;
32136304Skarels 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
32240183Smckusick 		if (checkuser("ftp") || checkuser("anonymous"))
32340155Smckusick 			reply(530, "User %s access denied.", name);
32440155Smckusick 		else if ((pw = sgetpwnam("ftp")) != NULL) {
32536304Skarels 			guest = 1;
32636304Skarels 			askpasswd = 1;
32736304Skarels 			reply(331, "Guest login ok, send ident as password.");
32836933Skarels 		} else
32936304Skarels 			reply(530, "User %s unknown.", name);
33036304Skarels 		return;
33136304Skarels 	}
33236304Skarels 	if (pw = sgetpwnam(name)) {
33336304Skarels 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
33437459Skarels 			shell = _PATH_BSHELL;
33536304Skarels 		while ((cp = getusershell()) != NULL)
33636304Skarels 			if (strcmp(cp, shell) == 0)
33736304Skarels 				break;
33836304Skarels 		endusershell();
33940183Smckusick 		if (cp == NULL || checkuser(name)) {
34036304Skarels 			reply(530, "User %s access denied.", name);
34136933Skarels 			if (logging)
34236933Skarels 				syslog(LOG_NOTICE,
34336933Skarels 				    "FTP LOGIN REFUSED FROM %s, %s",
34436933Skarels 				    remotehost, name);
34536304Skarels 			pw = (struct passwd *) NULL;
34636304Skarels 			return;
34736304Skarels 		}
34836304Skarels 	}
34936304Skarels 	reply(331, "Password required for %s.", name);
35036304Skarels 	askpasswd = 1;
35136304Skarels 	/*
35236304Skarels 	 * Delay before reading passwd after first failed
35336304Skarels 	 * attempt to slow down passwd-guessing programs.
35436304Skarels 	 */
35536304Skarels 	if (login_attempts)
35636304Skarels 		sleep((unsigned) login_attempts);
35736304Skarels }
35836304Skarels 
35936304Skarels /*
36040183Smckusick  * Check if a user is in the file _PATH_FTPUSERS
36140183Smckusick  */
36240183Smckusick checkuser(name)
36340183Smckusick 	char *name;
36440183Smckusick {
36542327Sbostic 	register FILE *fd;
36642327Sbostic 	register char *p;
36742327Sbostic 	char line[BUFSIZ];
36840183Smckusick 
36940183Smckusick 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
37042327Sbostic 		while (fgets(line, sizeof(line), fd) != NULL)
37142327Sbostic 			if ((p = index(line, '\n')) != NULL) {
37242327Sbostic 				*p = '\0';
37342327Sbostic 				if (line[0] == '#')
37442327Sbostic 					continue;
37542327Sbostic 				if (strcmp(line, name) == 0)
37642327Sbostic 					return (1);
37742327Sbostic 			}
37840183Smckusick 		(void) fclose(fd);
37940183Smckusick 	}
38040183Smckusick 	return (0);
38140183Smckusick }
38240183Smckusick 
38340183Smckusick /*
38436304Skarels  * Terminate login as previous user, if any, resetting state;
38536304Skarels  * used when USER command is given or login fails.
38636304Skarels  */
38736304Skarels end_login()
38836304Skarels {
38936304Skarels 
39036304Skarels 	(void) seteuid((uid_t)0);
39136304Skarels 	if (logged_in)
39236304Skarels 		logwtmp(ttyline, "", "");
39336304Skarels 	pw = NULL;
39436304Skarels 	logged_in = 0;
39536304Skarels 	guest = 0;
39636304Skarels }
39736304Skarels 
39810275Ssam pass(passwd)
39910275Ssam 	char *passwd;
40010275Ssam {
40136304Skarels 	char *xpasswd, *salt;
40210275Ssam 
40336304Skarels 	if (logged_in || askpasswd == 0) {
40410275Ssam 		reply(503, "Login with USER first.");
40510275Ssam 		return;
40610275Ssam 	}
40736304Skarels 	askpasswd = 0;
40810275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
40936304Skarels 		if (pw == NULL)
41036304Skarels 			salt = "xx";
41136304Skarels 		else
41236304Skarels 			salt = pw->pw_passwd;
41336304Skarels 		xpasswd = crypt(passwd, salt);
41416760Slepreau 		/* The strcmp does not catch null passwords! */
41536304Skarels 		if (pw == NULL || *pw->pw_passwd == '\0' ||
41636304Skarels 		    strcmp(xpasswd, pw->pw_passwd)) {
41710275Ssam 			reply(530, "Login incorrect.");
41810275Ssam 			pw = NULL;
41936304Skarels 			if (login_attempts++ >= 5) {
42036933Skarels 				syslog(LOG_NOTICE,
42136304Skarels 				    "repeated login failures from %s",
42236304Skarels 				    remotehost);
42336304Skarels 				exit(0);
42436304Skarels 			}
42510275Ssam 			return;
42610275Ssam 		}
42710275Ssam 	}
42836304Skarels 	login_attempts = 0;		/* this time successful */
42936304Skarels 	(void) setegid((gid_t)pw->pw_gid);
43036304Skarels 	(void) initgroups(pw->pw_name, pw->pw_gid);
43116033Sralph 
43236192Sbostic 	/* open wtmp before chroot */
43336192Sbostic 	(void)sprintf(ttyline, "ftp%d", getpid());
43436192Sbostic 	logwtmp(ttyline, pw->pw_name, remotehost);
43536192Sbostic 	logged_in = 1;
43636192Sbostic 
43736446Sbostic 	if (guest) {
43836620Srick 		/*
43936933Skarels 		 * We MUST do a chdir() after the chroot. Otherwise
44036933Skarels 		 * the old current directory will be accessible as "."
44136933Skarels 		 * outside the new root!
44236620Srick 		 */
44336620Srick 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
44436446Sbostic 			reply(550, "Can't set guest privileges.");
44536446Sbostic 			goto bad;
44636446Sbostic 		}
44736933Skarels 	} else if (chdir(pw->pw_dir) < 0) {
44836620Srick 		if (chdir("/") < 0) {
44936446Sbostic 			reply(530, "User %s: can't change directory to %s.",
45036446Sbostic 			    pw->pw_name, pw->pw_dir);
45136446Sbostic 			goto bad;
45236933Skarels 		} else
45336446Sbostic 			lreply(230, "No directory! Logging in with home=/");
45436620Srick 	}
45536304Skarels 	if (seteuid((uid_t)pw->pw_uid) < 0) {
45636304Skarels 		reply(550, "Can't set uid.");
45736304Skarels 		goto bad;
45836304Skarels 	}
45936550Sbostic 	if (guest) {
46036192Sbostic 		reply(230, "Guest login ok, access restrictions apply.");
46136933Skarels #ifdef SETPROCTITLE
46236933Skarels 		sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
46336933Skarels 		    sizeof(proctitle) - sizeof(remotehost) -
46436933Skarels 		    sizeof(": anonymous/"), passwd);
46536933Skarels 		setproctitle(proctitle);
46636933Skarels #endif /* SETPROCTITLE */
46736933Skarels 		if (logging)
46836933Skarels 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
46936933Skarels 			    remotehost, passwd);
47036933Skarels 	} else {
47110275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
47236933Skarels #ifdef SETPROCTITLE
47336933Skarels 		sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
47436933Skarels 		setproctitle(proctitle);
47536933Skarels #endif /* SETPROCTITLE */
47636933Skarels 		if (logging)
47736933Skarels 			syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
47836933Skarels 			    remotehost, pw->pw_name);
47936550Sbostic 	}
48010303Ssam 	home = pw->pw_dir;		/* home dir for globbing */
48136933Skarels 	(void) umask(defumask);
48210303Ssam 	return;
48310303Ssam bad:
48436304Skarels 	/* Forget all about it... */
48536304Skarels 	end_login();
48610275Ssam }
48710275Ssam 
48810275Ssam retrieve(cmd, name)
48910275Ssam 	char *cmd, *name;
49010275Ssam {
49110275Ssam 	FILE *fin, *dout;
49210275Ssam 	struct stat st;
49336620Srick 	int (*closefunc)();
49410275Ssam 
49536557Sbostic 	if (cmd == 0) {
49636446Sbostic 		fin = fopen(name, "r"), closefunc = fclose;
49736557Sbostic 		st.st_size = 0;
49836557Sbostic 	} else {
49910275Ssam 		char line[BUFSIZ];
50010275Ssam 
50126493Sminshall 		(void) sprintf(line, cmd, name), name = line;
50236304Skarels 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
50336557Sbostic 		st.st_size = -1;
50436933Skarels 		st.st_blksize = BUFSIZ;
50510275Ssam 	}
50610275Ssam 	if (fin == NULL) {
50713152Ssam 		if (errno != 0)
50836304Skarels 			perror_reply(550, name);
50910275Ssam 		return;
51010275Ssam 	}
51110275Ssam 	if (cmd == 0 &&
51236933Skarels 	    (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
51310275Ssam 		reply(550, "%s: not a plain file.", name);
51410275Ssam 		goto done;
51510275Ssam 	}
51637459Skarels 	if (restart_point) {
51737459Skarels 		if (type == TYPE_A) {
51837459Skarels 			register int i, n, c;
51937459Skarels 
52037459Skarels 			n = restart_point;
52137459Skarels 			i = 0;
52237459Skarels 			while (i++ < n) {
52337459Skarels 				if ((c=getc(fin)) == EOF) {
52437459Skarels 					perror_reply(550, name);
52537459Skarels 					goto done;
52637459Skarels 				}
52737459Skarels 				if (c == '\n')
52837459Skarels 					i++;
52937459Skarels 			}
53037459Skarels 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
53137459Skarels 			perror_reply(550, name);
53237459Skarels 			goto done;
53337459Skarels 		}
53437459Skarels 	}
53510275Ssam 	dout = dataconn(name, st.st_size, "w");
53610275Ssam 	if (dout == NULL)
53710275Ssam 		goto done;
53836620Srick 	send_data(fin, dout, st.st_blksize);
53926493Sminshall 	(void) fclose(dout);
54026044Sminshall 	data = -1;
54126044Sminshall 	pdata = -1;
54210275Ssam done:
54310275Ssam 	(*closefunc)(fin);
54410275Ssam }
54510275Ssam 
54636304Skarels store(name, mode, unique)
54710275Ssam 	char *name, *mode;
54836304Skarels 	int unique;
54910275Ssam {
55010275Ssam 	FILE *fout, *din;
55136446Sbostic 	struct stat st;
55236620Srick 	int (*closefunc)();
55336304Skarels 	char *gunique();
55410275Ssam 
55536446Sbostic 	if (unique && stat(name, &st) == 0 &&
55636446Sbostic 	    (name = gunique(name)) == NULL)
55736446Sbostic 		return;
55810303Ssam 
55937459Skarels 	if (restart_point)
56037459Skarels 		mode = "r+w";
56136620Srick 	fout = fopen(name, mode);
56236620Srick 	closefunc = fclose;
56310275Ssam 	if (fout == NULL) {
56436304Skarels 		perror_reply(553, name);
56510275Ssam 		return;
56610275Ssam 	}
56737459Skarels 	if (restart_point) {
56837459Skarels 		if (type == TYPE_A) {
56937459Skarels 			register int i, n, c;
57037459Skarels 
57137459Skarels 			n = restart_point;
57237459Skarels 			i = 0;
57337459Skarels 			while (i++ < n) {
57437459Skarels 				if ((c=getc(fout)) == EOF) {
57537459Skarels 					perror_reply(550, name);
57637459Skarels 					goto done;
57737459Skarels 				}
57837459Skarels 				if (c == '\n')
57937459Skarels 					i++;
58037459Skarels 			}
58137459Skarels 			/*
58237459Skarels 			 * We must do this seek to "current" position
58337459Skarels 			 * because we are changing from reading to
58437459Skarels 			 * writing.
58537459Skarels 			 */
58637459Skarels 			if (fseek(fout, 0L, L_INCR) < 0) {
58737459Skarels 				perror_reply(550, name);
58837459Skarels 				goto done;
58937459Skarels 			}
59037459Skarels 		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
59137459Skarels 			perror_reply(550, name);
59237459Skarels 			goto done;
59337459Skarels 		}
59437459Skarels 	}
59536304Skarels 	din = dataconn(name, (off_t)-1, "r");
59610275Ssam 	if (din == NULL)
59710275Ssam 		goto done;
59836620Srick 	if (receive_data(din, fout) == 0) {
59936620Srick 		if (unique)
60036304Skarels 			reply(226, "Transfer complete (unique file name:%s).",
60136933Skarels 			    name);
60236304Skarels 		else
60336304Skarels 			reply(226, "Transfer complete.");
60426044Sminshall 	}
60526493Sminshall 	(void) fclose(din);
60626044Sminshall 	data = -1;
60726044Sminshall 	pdata = -1;
60810275Ssam done:
60910275Ssam 	(*closefunc)(fout);
61010275Ssam }
61110275Ssam 
61210275Ssam FILE *
61310275Ssam getdatasock(mode)
61410275Ssam 	char *mode;
61510275Ssam {
61637459Skarels 	int s, on = 1, tries;
61710275Ssam 
61810275Ssam 	if (data >= 0)
61910275Ssam 		return (fdopen(data, mode));
62013247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
62110602Ssam 	if (s < 0)
62210275Ssam 		return (NULL);
62336304Skarels 	(void) seteuid((uid_t)0);
62437459Skarels 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
62537459Skarels 	    (char *) &on, sizeof (on)) < 0)
62610602Ssam 		goto bad;
62713152Ssam 	/* anchor socket to avoid multi-homing problems */
62813152Ssam 	data_source.sin_family = AF_INET;
62913152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
63037459Skarels 	for (tries = 1; ; tries++) {
63137459Skarels 		if (bind(s, (struct sockaddr *)&data_source,
63237459Skarels 		    sizeof (data_source)) >= 0)
63337459Skarels 			break;
63437459Skarels 		if (errno != EADDRINUSE || tries > 10)
63537459Skarels 			goto bad;
63637459Skarels 		sleep(tries);
63737459Skarels 	}
63836304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
63910275Ssam 	return (fdopen(s, mode));
64010602Ssam bad:
64136304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
64226493Sminshall 	(void) close(s);
64310602Ssam 	return (NULL);
64410275Ssam }
64510275Ssam 
64610275Ssam FILE *
64710275Ssam dataconn(name, size, mode)
64810275Ssam 	char *name;
64911653Ssam 	off_t size;
65010275Ssam 	char *mode;
65110275Ssam {
65210275Ssam 	char sizebuf[32];
65310275Ssam 	FILE *file;
65411653Ssam 	int retry = 0;
65510275Ssam 
65636933Skarels 	file_size = size;
65736933Skarels 	byte_count = 0;
65836304Skarels 	if (size != (off_t) -1)
65926493Sminshall 		(void) sprintf (sizebuf, " (%ld bytes)", size);
66010275Ssam 	else
66110275Ssam 		(void) strcpy(sizebuf, "");
66236304Skarels 	if (pdata >= 0) {
66326044Sminshall 		struct sockaddr_in from;
66426044Sminshall 		int s, fromlen = sizeof(from);
66526044Sminshall 
66636304Skarels 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
66726044Sminshall 		if (s < 0) {
66826044Sminshall 			reply(425, "Can't open data connection.");
66926044Sminshall 			(void) close(pdata);
67026044Sminshall 			pdata = -1;
67126044Sminshall 			return(NULL);
67226044Sminshall 		}
67326044Sminshall 		(void) close(pdata);
67426044Sminshall 		pdata = s;
67536235Skarels 		reply(150, "Opening %s mode data connection for %s%s.",
67636235Skarels 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
67726044Sminshall 		return(fdopen(pdata, mode));
67826044Sminshall 	}
67910275Ssam 	if (data >= 0) {
68010275Ssam 		reply(125, "Using existing data connection for %s%s.",
68110275Ssam 		    name, sizebuf);
68210321Ssam 		usedefault = 1;
68310275Ssam 		return (fdopen(data, mode));
68410275Ssam 	}
68510566Ssam 	if (usedefault)
68610422Ssam 		data_dest = his_addr;
68710422Ssam 	usedefault = 1;
68810275Ssam 	file = getdatasock(mode);
68910275Ssam 	if (file == NULL) {
69010275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
69113247Ssam 		    inet_ntoa(data_source.sin_addr),
69242412Sbostic 		    ntohs(data_source.sin_port), strerror(errno));
69310275Ssam 		return (NULL);
69410275Ssam 	}
69510275Ssam 	data = fileno(file);
69636304Skarels 	while (connect(data, (struct sockaddr *)&data_dest,
69736304Skarels 	    sizeof (data_dest)) < 0) {
69811653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
69926493Sminshall 			sleep((unsigned) swaitint);
70011653Ssam 			retry += swaitint;
70111653Ssam 			continue;
70211653Ssam 		}
70336304Skarels 		perror_reply(425, "Can't build data connection");
70410275Ssam 		(void) fclose(file);
70510275Ssam 		data = -1;
70610275Ssam 		return (NULL);
70710275Ssam 	}
70836235Skarels 	reply(150, "Opening %s mode data connection for %s%s.",
70936235Skarels 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
71010275Ssam 	return (file);
71110275Ssam }
71210275Ssam 
71310275Ssam /*
71410275Ssam  * Tranfer the contents of "instr" to
71510275Ssam  * "outstr" peer using the appropriate
71636933Skarels  * encapsulation of the data subject
71710275Ssam  * to Mode, Structure, and Type.
71810275Ssam  *
71910275Ssam  * NB: Form isn't handled.
72010275Ssam  */
72136446Sbostic send_data(instr, outstr, blksize)
72210275Ssam 	FILE *instr, *outstr;
72336446Sbostic 	off_t blksize;
72410275Ssam {
72536446Sbostic 	register int c, cnt;
72636446Sbostic 	register char *buf;
72736446Sbostic 	int netfd, filefd;
72810275Ssam 
72926044Sminshall 	transflag++;
73026044Sminshall 	if (setjmp(urgcatch)) {
73126044Sminshall 		transflag = 0;
73236620Srick 		return;
73326044Sminshall 	}
73410275Ssam 	switch (type) {
73510275Ssam 
73610275Ssam 	case TYPE_A:
73710275Ssam 		while ((c = getc(instr)) != EOF) {
73836933Skarels 			byte_count++;
73911220Ssam 			if (c == '\n') {
74036933Skarels 				if (ferror(outstr))
74136620Srick 					goto data_err;
74227750Sminshall 				(void) putc('\r', outstr);
74311220Ssam 			}
74427750Sminshall 			(void) putc(c, outstr);
74510275Ssam 		}
74636933Skarels 		fflush(outstr);
74726044Sminshall 		transflag = 0;
74836933Skarels 		if (ferror(instr))
74936933Skarels 			goto file_err;
75036933Skarels 		if (ferror(outstr))
75136620Srick 			goto data_err;
75236620Srick 		reply(226, "Transfer complete.");
75336620Srick 		return;
75436446Sbostic 
75510275Ssam 	case TYPE_I:
75610275Ssam 	case TYPE_L:
75736446Sbostic 		if ((buf = malloc((u_int)blksize)) == NULL) {
75836446Sbostic 			transflag = 0;
75936933Skarels 			perror_reply(451, "Local resource failure: malloc");
76036933Skarels 			return;
76136446Sbostic 		}
76210275Ssam 		netfd = fileno(outstr);
76310275Ssam 		filefd = fileno(instr);
76436933Skarels 		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
76536620Srick 		    write(netfd, buf, cnt) == cnt)
76636933Skarels 			byte_count += cnt;
76726044Sminshall 		transflag = 0;
76836446Sbostic 		(void)free(buf);
76936933Skarels 		if (cnt != 0) {
77036933Skarels 			if (cnt < 0)
77136933Skarels 				goto file_err;
77236620Srick 			goto data_err;
77336933Skarels 		}
77436620Srick 		reply(226, "Transfer complete.");
77536620Srick 		return;
77636620Srick 	default:
77736620Srick 		transflag = 0;
77836620Srick 		reply(550, "Unimplemented TYPE %d in send_data", type);
77936620Srick 		return;
78010275Ssam 	}
78136620Srick 
78236620Srick data_err:
78326044Sminshall 	transflag = 0;
78436933Skarels 	perror_reply(426, "Data connection");
78536933Skarels 	return;
78636933Skarels 
78736933Skarels file_err:
78836933Skarels 	transflag = 0;
78936933Skarels 	perror_reply(551, "Error on input file");
79010275Ssam }
79110275Ssam 
79210275Ssam /*
79310275Ssam  * Transfer data from peer to
79410275Ssam  * "outstr" using the appropriate
79510275Ssam  * encapulation of the data subject
79610275Ssam  * to Mode, Structure, and Type.
79710275Ssam  *
79810275Ssam  * N.B.: Form isn't handled.
79910275Ssam  */
80010275Ssam receive_data(instr, outstr)
80110275Ssam 	FILE *instr, *outstr;
80210275Ssam {
80310275Ssam 	register int c;
80438134Srick 	int cnt, bare_lfs = 0;
80510275Ssam 	char buf[BUFSIZ];
80610275Ssam 
80726044Sminshall 	transflag++;
80826044Sminshall 	if (setjmp(urgcatch)) {
80926044Sminshall 		transflag = 0;
81036933Skarels 		return (-1);
81126044Sminshall 	}
81210275Ssam 	switch (type) {
81310275Ssam 
81410275Ssam 	case TYPE_I:
81510275Ssam 	case TYPE_L:
81626044Sminshall 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
81736620Srick 			if (write(fileno(outstr), buf, cnt) != cnt)
81836933Skarels 				goto file_err;
81936933Skarels 			byte_count += cnt;
82026044Sminshall 		}
82136933Skarels 		if (cnt < 0)
82236620Srick 			goto data_err;
82326044Sminshall 		transflag = 0;
82436933Skarels 		return (0);
82510275Ssam 
82610275Ssam 	case TYPE_E:
82727106Smckusick 		reply(553, "TYPE E not implemented.");
82826044Sminshall 		transflag = 0;
82927106Smckusick 		return (-1);
83010275Ssam 
83110275Ssam 	case TYPE_A:
83210275Ssam 		while ((c = getc(instr)) != EOF) {
83336933Skarels 			byte_count++;
83438134Srick 			if (c == '\n')
83538134Srick 				bare_lfs++;
83627750Sminshall 			while (c == '\r') {
83736933Skarels 				if (ferror(outstr))
83836620Srick 					goto data_err;
83936933Skarels 				if ((c = getc(instr)) != '\n') {
84027750Sminshall 					(void) putc ('\r', outstr);
84136933Skarels 					if (c == '\0' || c == EOF)
84236933Skarels 						goto contin2;
84336933Skarels 				}
84410275Ssam 			}
84536933Skarels 			(void) putc(c, outstr);
84636933Skarels 	contin2:	;
84710275Ssam 		}
84836620Srick 		fflush(outstr);
84936933Skarels 		if (ferror(instr))
85036620Srick 			goto data_err;
85136933Skarels 		if (ferror(outstr))
85236933Skarels 			goto file_err;
85326044Sminshall 		transflag = 0;
85438134Srick 		if (bare_lfs) {
85538134Srick 			lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
85638134Srick 			printf("   File may not have transferred correctly.\r\n");
85738134Srick 		}
85810275Ssam 		return (0);
85936620Srick 	default:
86036620Srick 		reply(550, "Unimplemented TYPE %d in receive_data", type);
86136620Srick 		transflag = 0;
86236933Skarels 		return (-1);
86310275Ssam 	}
86436620Srick 
86536620Srick data_err:
86626044Sminshall 	transflag = 0;
86736933Skarels 	perror_reply(426, "Data Connection");
86836933Skarels 	return (-1);
86936933Skarels 
87036933Skarels file_err:
87136933Skarels 	transflag = 0;
87236933Skarels 	perror_reply(452, "Error writing file");
87336933Skarels 	return (-1);
87410275Ssam }
87510275Ssam 
87636933Skarels statfilecmd(filename)
87736933Skarels 	char *filename;
87836933Skarels {
87936933Skarels 	char line[BUFSIZ];
88036933Skarels 	FILE *fin;
88136933Skarels 	int c;
88236933Skarels 
88336933Skarels 	(void) sprintf(line, "/bin/ls -lgA %s", filename);
88436933Skarels 	fin = ftpd_popen(line, "r");
88536933Skarels 	lreply(211, "status of %s:", filename);
88636933Skarels 	while ((c = getc(fin)) != EOF) {
88736933Skarels 		if (c == '\n') {
88836933Skarels 			if (ferror(stdout)){
88936933Skarels 				perror_reply(421, "control connection");
89036933Skarels 				(void) ftpd_pclose(fin);
89136933Skarels 				dologout(1);
89236933Skarels 				/* NOTREACHED */
89336933Skarels 			}
89436933Skarels 			if (ferror(fin)) {
89536933Skarels 				perror_reply(551, filename);
89636933Skarels 				(void) ftpd_pclose(fin);
89736933Skarels 				return;
89836933Skarels 			}
89936933Skarels 			(void) putc('\r', stdout);
90036933Skarels 		}
90136933Skarels 		(void) putc(c, stdout);
90236933Skarels 	}
90336933Skarels 	(void) ftpd_pclose(fin);
90436933Skarels 	reply(211, "End of Status");
90536933Skarels }
90636933Skarels 
90736933Skarels statcmd()
90836933Skarels {
90936933Skarels 	struct sockaddr_in *sin;
91036933Skarels 	u_char *a, *p;
91136933Skarels 
91236933Skarels 	lreply(211, "%s FTP server status:", hostname, version);
91336933Skarels 	printf("     %s\r\n", version);
91436933Skarels 	printf("     Connected to %s", remotehost);
91538134Srick 	if (!isdigit(remotehost[0]))
91636933Skarels 		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
91736933Skarels 	printf("\r\n");
91836933Skarels 	if (logged_in) {
91936933Skarels 		if (guest)
92036933Skarels 			printf("     Logged in anonymously\r\n");
92136933Skarels 		else
92236933Skarels 			printf("     Logged in as %s\r\n", pw->pw_name);
92336933Skarels 	} else if (askpasswd)
92436933Skarels 		printf("     Waiting for password\r\n");
92536933Skarels 	else
92636933Skarels 		printf("     Waiting for user name\r\n");
92736933Skarels 	printf("     TYPE: %s", typenames[type]);
92836933Skarels 	if (type == TYPE_A || type == TYPE_E)
92936933Skarels 		printf(", FORM: %s", formnames[form]);
93036933Skarels 	if (type == TYPE_L)
93136933Skarels #if NBBY == 8
93236933Skarels 		printf(" %d", NBBY);
93336933Skarels #else
93436933Skarels 		printf(" %d", bytesize);	/* need definition! */
93536933Skarels #endif
93636933Skarels 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
93736933Skarels 	    strunames[stru], modenames[mode]);
93836933Skarels 	if (data != -1)
93936933Skarels 		printf("     Data connection open\r\n");
94036933Skarels 	else if (pdata != -1) {
94136933Skarels 		printf("     in Passive mode");
94236933Skarels 		sin = &pasv_addr;
94336933Skarels 		goto printaddr;
94436933Skarels 	} else if (usedefault == 0) {
94536933Skarels 		printf("     PORT");
94636933Skarels 		sin = &data_dest;
94736933Skarels printaddr:
94836933Skarels 		a = (u_char *) &sin->sin_addr;
94936933Skarels 		p = (u_char *) &sin->sin_port;
95036933Skarels #define UC(b) (((int) b) & 0xff)
95136933Skarels 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
95236933Skarels 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
95336933Skarels #undef UC
95436933Skarels 	} else
95536933Skarels 		printf("     No data connection\r\n");
95636933Skarels 	reply(211, "End of status");
95736933Skarels }
95836933Skarels 
95910275Ssam fatal(s)
96010275Ssam 	char *s;
96110275Ssam {
96210275Ssam 	reply(451, "Error in server: %s\n", s);
96310275Ssam 	reply(221, "Closing connection due to server error.");
96413247Ssam 	dologout(0);
96536620Srick 	/* NOTREACHED */
96610275Ssam }
96710275Ssam 
96836446Sbostic /* VARARGS2 */
96936446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5)
97010275Ssam 	int n;
97136446Sbostic 	char *fmt;
97210275Ssam {
97310275Ssam 	printf("%d ", n);
97436446Sbostic 	printf(fmt, p0, p1, p2, p3, p4, p5);
97510275Ssam 	printf("\r\n");
97636435Sbostic 	(void)fflush(stdout);
97710275Ssam 	if (debug) {
97826493Sminshall 		syslog(LOG_DEBUG, "<--- %d ", n);
97936446Sbostic 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
98010275Ssam }
98136620Srick }
98210275Ssam 
98336446Sbostic /* VARARGS2 */
98436446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5)
98510275Ssam 	int n;
98636446Sbostic 	char *fmt;
98710275Ssam {
98836446Sbostic 	printf("%d- ", n);
98936446Sbostic 	printf(fmt, p0, p1, p2, p3, p4, p5);
99036446Sbostic 	printf("\r\n");
99136435Sbostic 	(void)fflush(stdout);
99236446Sbostic 	if (debug) {
99336446Sbostic 		syslog(LOG_DEBUG, "<--- %d- ", n);
99436446Sbostic 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
99536446Sbostic 	}
99610275Ssam }
99710275Ssam 
99810275Ssam ack(s)
99910275Ssam 	char *s;
100010275Ssam {
100127106Smckusick 	reply(250, "%s command successful.", s);
100210275Ssam }
100310275Ssam 
100410275Ssam nack(s)
100510275Ssam 	char *s;
100610275Ssam {
100710275Ssam 	reply(502, "%s command not implemented.", s);
100810275Ssam }
100910275Ssam 
101036304Skarels /* ARGSUSED */
101126493Sminshall yyerror(s)
101226493Sminshall 	char *s;
101310275Ssam {
101426044Sminshall 	char *cp;
101526044Sminshall 
101636551Sbostic 	if (cp = index(cbuf,'\n'))
101736551Sbostic 		*cp = '\0';
101836933Skarels 	reply(500, "'%s': command not understood.", cbuf);
101910275Ssam }
102010275Ssam 
102110275Ssam delete(name)
102210275Ssam 	char *name;
102310275Ssam {
102410275Ssam 	struct stat st;
102510275Ssam 
102610275Ssam 	if (stat(name, &st) < 0) {
102736304Skarels 		perror_reply(550, name);
102810275Ssam 		return;
102910275Ssam 	}
103010275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
103110275Ssam 		if (rmdir(name) < 0) {
103236304Skarels 			perror_reply(550, name);
103310275Ssam 			return;
103410275Ssam 		}
103510275Ssam 		goto done;
103610275Ssam 	}
103710275Ssam 	if (unlink(name) < 0) {
103836304Skarels 		perror_reply(550, name);
103910275Ssam 		return;
104010275Ssam 	}
104110275Ssam done:
104210275Ssam 	ack("DELE");
104310275Ssam }
104410275Ssam 
104510275Ssam cwd(path)
104610275Ssam 	char *path;
104710275Ssam {
104836620Srick 	if (chdir(path) < 0)
104936304Skarels 		perror_reply(550, path);
105036620Srick 	else
105136620Srick 		ack("CWD");
105210275Ssam }
105310275Ssam 
105410303Ssam makedir(name)
105510275Ssam 	char *name;
105610275Ssam {
105736276Sbostic 	if (mkdir(name, 0777) < 0)
105836304Skarels 		perror_reply(550, name);
105936276Sbostic 	else
106036276Sbostic 		reply(257, "MKD command successful.");
106110275Ssam }
106210275Ssam 
106310303Ssam removedir(name)
106410275Ssam 	char *name;
106510275Ssam {
106636620Srick 	if (rmdir(name) < 0)
106736304Skarels 		perror_reply(550, name);
106836620Srick 	else
106936620Srick 		ack("RMD");
107010275Ssam }
107110275Ssam 
107210303Ssam pwd()
107310275Ssam {
107410303Ssam 	char path[MAXPATHLEN + 1];
107536304Skarels 	extern char *getwd();
107610275Ssam 
107736620Srick 	if (getwd(path) == (char *)NULL)
107827106Smckusick 		reply(550, "%s.", path);
107936620Srick 	else
108036620Srick 		reply(257, "\"%s\" is current directory.", path);
108110275Ssam }
108210275Ssam 
108310275Ssam char *
108410275Ssam renamefrom(name)
108510275Ssam 	char *name;
108610275Ssam {
108710275Ssam 	struct stat st;
108810275Ssam 
108910275Ssam 	if (stat(name, &st) < 0) {
109036304Skarels 		perror_reply(550, name);
109110275Ssam 		return ((char *)0);
109210275Ssam 	}
109310303Ssam 	reply(350, "File exists, ready for destination name");
109410275Ssam 	return (name);
109510275Ssam }
109610275Ssam 
109710275Ssam renamecmd(from, to)
109810275Ssam 	char *from, *to;
109910275Ssam {
110036620Srick 	if (rename(from, to) < 0)
110136304Skarels 		perror_reply(550, "rename");
110236620Srick 	else
110336620Srick 		ack("RNTO");
110410275Ssam }
110510275Ssam 
110610275Ssam dolog(sin)
110710275Ssam 	struct sockaddr_in *sin;
110810275Ssam {
110936304Skarels 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
111010275Ssam 		sizeof (struct in_addr), AF_INET);
111136304Skarels 	time_t t, time();
111226493Sminshall 	extern char *ctime();
111310275Ssam 
111436304Skarels 	if (hp)
111526493Sminshall 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
111636304Skarels 	else
111726493Sminshall 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
111813247Ssam 		    sizeof (remotehost));
111936620Srick #ifdef SETPROCTITLE
112036933Skarels 	sprintf(proctitle, "%s: connected", remotehost);
112136933Skarels 	setproctitle(proctitle);
112236620Srick #endif /* SETPROCTITLE */
112336933Skarels 
112436933Skarels 	if (logging) {
112536933Skarels 		t = time((time_t *) 0);
112636933Skarels 		syslog(LOG_INFO, "connection from %s at %s",
112736933Skarels 		    remotehost, ctime(&t));
112836933Skarels 	}
112910275Ssam }
113010695Ssam 
113110695Ssam /*
113213247Ssam  * Record logout in wtmp file
113313247Ssam  * and exit with supplied status.
113413247Ssam  */
113513247Ssam dologout(status)
113613247Ssam 	int status;
113713247Ssam {
113817580Ssam 	if (logged_in) {
113936304Skarels 		(void) seteuid((uid_t)0);
114035672Sbostic 		logwtmp(ttyline, "", "");
114113247Ssam 	}
114214436Ssam 	/* beware of flushing buffers after a SIGPIPE */
114314436Ssam 	_exit(status);
114413247Ssam }
114513247Ssam 
114626044Sminshall myoob()
114726044Sminshall {
114827750Sminshall 	char *cp;
114926044Sminshall 
115027750Sminshall 	/* only process if transfer occurring */
115136304Skarels 	if (!transflag)
115226044Sminshall 		return;
115327750Sminshall 	cp = tmpline;
115427750Sminshall 	if (getline(cp, 7, stdin) == NULL) {
115536304Skarels 		reply(221, "You could at least say goodbye.");
115627750Sminshall 		dologout(0);
115726044Sminshall 	}
115826044Sminshall 	upper(cp);
115936933Skarels 	if (strcmp(cp, "ABOR\r\n") == 0) {
116036933Skarels 		tmpline[0] = '\0';
116136933Skarels 		reply(426, "Transfer aborted. Data connection closed.");
116236933Skarels 		reply(226, "Abort successful");
116336933Skarels 		longjmp(urgcatch, 1);
116436933Skarels 	}
116536933Skarels 	if (strcmp(cp, "STAT\r\n") == 0) {
116636933Skarels 		if (file_size != (off_t) -1)
116736933Skarels 			reply(213, "Status: %lu of %lu bytes transferred",
116836933Skarels 			    byte_count, file_size);
116936933Skarels 		else
117036933Skarels 			reply(213, "Status: %lu bytes transferred", byte_count);
117136933Skarels 	}
117226044Sminshall }
117326044Sminshall 
117427106Smckusick /*
117536620Srick  * Note: a response of 425 is not mentioned as a possible response to
117636620Srick  * 	the PASV command in RFC959. However, it has been blessed as
117736620Srick  * 	a legitimate response by Jon Postel in a telephone conversation
117836620Srick  *	with Rick Adams on 25 Jan 89.
117927106Smckusick  */
118026044Sminshall passive()
118126044Sminshall {
118226044Sminshall 	int len;
118326044Sminshall 	register char *p, *a;
118426044Sminshall 
118526044Sminshall 	pdata = socket(AF_INET, SOCK_STREAM, 0);
118626044Sminshall 	if (pdata < 0) {
118736620Srick 		perror_reply(425, "Can't open passive connection");
118826044Sminshall 		return;
118926044Sminshall 	}
119036933Skarels 	pasv_addr = ctrl_addr;
119136933Skarels 	pasv_addr.sin_port = 0;
119236304Skarels 	(void) seteuid((uid_t)0);
119336933Skarels 	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
119436304Skarels 		(void) seteuid((uid_t)pw->pw_uid);
119536620Srick 		goto pasv_error;
119626044Sminshall 	}
119736304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
119836933Skarels 	len = sizeof(pasv_addr);
119936933Skarels 	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
120036620Srick 		goto pasv_error;
120136620Srick 	if (listen(pdata, 1) < 0)
120236620Srick 		goto pasv_error;
120336933Skarels 	a = (char *) &pasv_addr.sin_addr;
120436933Skarels 	p = (char *) &pasv_addr.sin_port;
120526044Sminshall 
120626044Sminshall #define UC(b) (((int) b) & 0xff)
120726044Sminshall 
120826044Sminshall 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
120926044Sminshall 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
121036620Srick 	return;
121136620Srick 
121236620Srick pasv_error:
121336620Srick 	(void) close(pdata);
121436620Srick 	pdata = -1;
121536620Srick 	perror_reply(425, "Can't open passive connection");
121636620Srick 	return;
121726044Sminshall }
121826044Sminshall 
121936304Skarels /*
122036304Skarels  * Generate unique name for file with basename "local".
122136304Skarels  * The file named "local" is already known to exist.
122236304Skarels  * Generates failure reply on error.
122336304Skarels  */
122426044Sminshall char *
122526044Sminshall gunique(local)
122626044Sminshall 	char *local;
122726044Sminshall {
122826044Sminshall 	static char new[MAXPATHLEN];
122936304Skarels 	struct stat st;
123026044Sminshall 	char *cp = rindex(local, '/');
123136933Skarels 	int count = 0;
123226044Sminshall 
123336304Skarels 	if (cp)
123426044Sminshall 		*cp = '\0';
123536620Srick 	if (stat(cp ? local : ".", &st) < 0) {
123636933Skarels 		perror_reply(553, cp ? local : ".");
123726044Sminshall 		return((char *) 0);
123826044Sminshall 	}
123936620Srick 	if (cp)
124036620Srick 		*cp = '/';
124126044Sminshall 	(void) strcpy(new, local);
124226044Sminshall 	cp = new + strlen(new);
124326044Sminshall 	*cp++ = '.';
124436304Skarels 	for (count = 1; count < 100; count++) {
124536304Skarels 		(void) sprintf(cp, "%d", count);
124636304Skarels 		if (stat(new, &st) < 0)
124736304Skarels 			return(new);
124826044Sminshall 	}
124936304Skarels 	reply(452, "Unique file name cannot be created.");
125036304Skarels 	return((char *) 0);
125126044Sminshall }
125236304Skarels 
125336304Skarels /*
125436304Skarels  * Format and send reply containing system error number.
125536304Skarels  */
125636304Skarels perror_reply(code, string)
125736304Skarels 	int code;
125836304Skarels 	char *string;
125936304Skarels {
126042412Sbostic 	reply(code, "%s: %s.", string, strerror(errno));
126136304Skarels }
126236620Srick 
126336620Srick static char *onefile[] = {
126436620Srick 	"",
126536620Srick 	0
126636620Srick };
126736620Srick 
126836620Srick send_file_list(whichfiles)
126936620Srick 	char *whichfiles;
127036620Srick {
127136620Srick 	struct stat st;
127236620Srick 	DIR *dirp = NULL;
127336620Srick 	struct direct *dir;
127436620Srick 	FILE *dout = NULL;
127536620Srick 	register char **dirlist, *dirname;
127637459Skarels 	int simple = 0;
127736620Srick 	char *strpbrk();
127836620Srick 
127936620Srick 	if (strpbrk(whichfiles, "~{[*?") != NULL) {
128036620Srick 		extern char **glob(), *globerr;
128136933Skarels 
128236620Srick 		globerr = NULL;
128336620Srick 		dirlist = glob(whichfiles);
128436620Srick 		if (globerr != NULL) {
128536620Srick 			reply(550, globerr);
128636620Srick 			return;
128736620Srick 		} else if (dirlist == NULL) {
128836620Srick 			errno = ENOENT;
128936620Srick 			perror_reply(550, whichfiles);
129036620Srick 			return;
129136620Srick 		}
129236620Srick 	} else {
129336620Srick 		onefile[0] = whichfiles;
129436620Srick 		dirlist = onefile;
129537459Skarels 		simple = 1;
129636620Srick 	}
129736933Skarels 
129836933Skarels 	if (setjmp(urgcatch)) {
129936933Skarels 		transflag = 0;
130036933Skarels 		return;
130136933Skarels 	}
130236620Srick 	while (dirname = *dirlist++) {
130336620Srick 		if (stat(dirname, &st) < 0) {
130436933Skarels 			/*
130536933Skarels 			 * If user typed "ls -l", etc, and the client
130636933Skarels 			 * used NLST, do what the user meant.
130736933Skarels 			 */
130836933Skarels 			if (dirname[0] == '-' && *dirlist == NULL &&
130936933Skarels 			    transflag == 0) {
131036933Skarels 				retrieve("/bin/ls %s", dirname);
131136933Skarels 				return;
131236933Skarels 			}
131336620Srick 			perror_reply(550, whichfiles);
131436620Srick 			if (dout != NULL) {
131536620Srick 				(void) fclose(dout);
131636933Skarels 				transflag = 0;
131736620Srick 				data = -1;
131836620Srick 				pdata = -1;
131936620Srick 			}
132036620Srick 			return;
132136620Srick 		}
132236933Skarels 
132336620Srick 		if ((st.st_mode&S_IFMT) == S_IFREG) {
132436620Srick 			if (dout == NULL) {
132537459Skarels 				dout = dataconn("file list", (off_t)-1, "w");
132636620Srick 				if (dout == NULL)
132736620Srick 					return;
132836933Skarels 				transflag++;
132936620Srick 			}
133038158Srick 			fprintf(dout, "%s%s\n", dirname,
133138158Srick 				type == TYPE_A ? "\r" : "");
133236933Skarels 			byte_count += strlen(dirname) + 1;
133336620Srick 			continue;
133436933Skarels 		} else if ((st.st_mode&S_IFMT) != S_IFDIR)
133536620Srick 			continue;
133636620Srick 
133736620Srick 		if ((dirp = opendir(dirname)) == NULL)
133836620Srick 			continue;
133936620Srick 
134036620Srick 		while ((dir = readdir(dirp)) != NULL) {
134136933Skarels 			char nbuf[MAXPATHLEN];
134236620Srick 
134336620Srick 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
134436620Srick 				continue;
134536933Skarels 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
134636933Skarels 			    dir->d_namlen == 2)
134736620Srick 				continue;
134836620Srick 
134936933Skarels 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
135036933Skarels 
135136620Srick 			/*
135236933Skarels 			 * We have to do a stat to insure it's
135336933Skarels 			 * not a directory or special file.
135436620Srick 			 */
135537459Skarels 			if (simple || (stat(nbuf, &st) == 0 &&
135637459Skarels 			    (st.st_mode&S_IFMT) == S_IFREG)) {
135736620Srick 				if (dout == NULL) {
135837459Skarels 					dout = dataconn("file list", (off_t)-1,
135936620Srick 						"w");
136036620Srick 					if (dout == NULL)
136136620Srick 						return;
136236933Skarels 					transflag++;
136336620Srick 				}
136436620Srick 				if (nbuf[0] == '.' && nbuf[1] == '/')
136538158Srick 					fprintf(dout, "%s%s\n", &nbuf[2],
136638158Srick 						type == TYPE_A ? "\r" : "");
136736620Srick 				else
136838158Srick 					fprintf(dout, "%s%s\n", nbuf,
136938158Srick 						type == TYPE_A ? "\r" : "");
137036933Skarels 				byte_count += strlen(nbuf) + 1;
137136620Srick 			}
137236620Srick 		}
137336620Srick 		(void) closedir(dirp);
137436620Srick 	}
137536620Srick 
137636933Skarels 	if (dout == NULL)
137736933Skarels 		reply(550, "No files found.");
137836933Skarels 	else if (ferror(dout) != 0)
137936933Skarels 		perror_reply(550, "Data connection");
138036933Skarels 	else
138136620Srick 		reply(226, "Transfer complete.");
138236620Srick 
138336933Skarels 	transflag = 0;
138436933Skarels 	if (dout != NULL)
138536620Srick 		(void) fclose(dout);
138636620Srick 	data = -1;
138736620Srick 	pdata = -1;
138836620Srick }
138936620Srick 
139036620Srick #ifdef SETPROCTITLE
139136620Srick /*
139236620Srick  * clobber argv so ps will show what we're doing.
139336620Srick  * (stolen from sendmail)
139436620Srick  * warning, since this is usually started from inetd.conf, it
139536620Srick  * often doesn't have much of an environment or arglist to overwrite.
139636620Srick  */
139736620Srick 
139836620Srick /*VARARGS1*/
139936620Srick setproctitle(fmt, a, b, c)
140036620Srick char *fmt;
140136620Srick {
140236620Srick 	register char *p, *bp, ch;
140336620Srick 	register int i;
140436620Srick 	char buf[BUFSIZ];
140536620Srick 
140636620Srick 	(void) sprintf(buf, fmt, a, b, c);
140736620Srick 
140836620Srick 	/* make ps print our process name */
140936620Srick 	p = Argv[0];
141036620Srick 	*p++ = '-';
141136620Srick 
141236620Srick 	i = strlen(buf);
141336620Srick 	if (i > LastArgv - p - 2) {
141436620Srick 		i = LastArgv - p - 2;
141536620Srick 		buf[i] = '\0';
141636620Srick 	}
141736620Srick 	bp = buf;
141836620Srick 	while (ch = *bp++)
141936620Srick 		if (ch != '\n' && ch != '\r')
142036620Srick 			*p++ = ch;
142136620Srick 	while (p < LastArgv)
142236620Srick 		*p++ = ' ';
142336620Srick }
142436620Srick #endif /* SETPROCTITLE */
1425