1872b698pfg/*-
2872b698pfg * SPDX-License-Identifier: BSD-3-Clause
3872b698pfg *
47d07d2drgrimes * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
57d07d2drgrimes *	The Regents of the University of California.  All rights reserved.
67d07d2drgrimes *
77d07d2drgrimes * Redistribution and use in source and binary forms, with or without
87d07d2drgrimes * modification, are permitted provided that the following conditions
97d07d2drgrimes * are met:
107d07d2drgrimes * 1. Redistributions of source code must retain the above copyright
117d07d2drgrimes *    notice, this list of conditions and the following disclaimer.
127d07d2drgrimes * 2. Redistributions in binary form must reproduce the above copyright
137d07d2drgrimes *    notice, this list of conditions and the following disclaimer in the
147d07d2drgrimes *    documentation and/or other materials provided with the distribution.
1573e4798brueffer * 3. Neither the name of the University nor the names of its contributors
167d07d2drgrimes *    may be used to endorse or promote products derived from this software
177d07d2drgrimes *    without specific prior written permission.
187d07d2drgrimes *
197d07d2drgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
207d07d2drgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
217d07d2drgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
227d07d2drgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
237d07d2drgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
247d07d2drgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
257d07d2drgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
267d07d2drgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
277d07d2drgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
287d07d2drgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
297d07d2drgrimes * SUCH DAMAGE.
307d07d2drgrimes */
317d07d2drgrimes
32a81e90dmarkm#if 0
337d07d2drgrimes#ifndef lint
347d07d2drgrimesstatic char copyright[] =
357d07d2drgrimes"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
367d07d2drgrimes	The Regents of the University of California.  All rights reserved.\n";
377d07d2drgrimes#endif /* not lint */
38a81e90dmarkm#endif
397d07d2drgrimes
407d07d2drgrimes#ifndef lint
4117df8f3charnier#if 0
427d07d2drgrimesstatic char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
43a81e90dmarkm#endif
4417df8f3charnier#endif /* not lint */
457d07d2drgrimes
46b18d29fyar#include <sys/cdefs.h>
47b18d29fyar__FBSDID("$FreeBSD$");
48b18d29fyar
497d07d2drgrimes/*
507d07d2drgrimes * FTP server.
517d07d2drgrimes */
527d07d2drgrimes#include <sys/param.h>
537d07d2drgrimes#include <sys/ioctl.h>
54c5c43cewollman#include <sys/mman.h>
557d07d2drgrimes#include <sys/socket.h>
56c5c43cewollman#include <sys/stat.h>
57c5c43cewollman#include <sys/time.h>
587d07d2drgrimes#include <sys/wait.h>
597d07d2drgrimes
607d07d2drgrimes#include <netinet/in.h>
617d07d2drgrimes#include <netinet/in_systm.h>
627d07d2drgrimes#include <netinet/ip.h>
63687339awollman#include <netinet/tcp.h>
647d07d2drgrimes
657d07d2drgrimes#define	FTP_NAMES
667d07d2drgrimes#include <arpa/ftp.h>
677d07d2drgrimes#include <arpa/inet.h>
687d07d2drgrimes#include <arpa/telnet.h>
697d07d2drgrimes
707d07d2drgrimes#include <ctype.h>
717d07d2drgrimes#include <dirent.h>
727d07d2drgrimes#include <err.h>
737d07d2drgrimes#include <errno.h>
747d07d2drgrimes#include <fcntl.h>
757d07d2drgrimes#include <glob.h>
767d07d2drgrimes#include <limits.h>
777d07d2drgrimes#include <netdb.h>
787d07d2drgrimes#include <pwd.h>
79004ba69davidn#include <grp.h>
8050483c2ache#include <opie.h>
817d07d2drgrimes#include <signal.h>
82634a2a6yar#include <stdint.h>
837d07d2drgrimes#include <stdio.h>
847d07d2drgrimes#include <stdlib.h>
857d07d2drgrimes#include <string.h>
867d07d2drgrimes#include <syslog.h>
877d07d2drgrimes#include <time.h>
887d07d2drgrimes#include <unistd.h>
89eee47fepeter#include <libutil.h>
90a3b6ff7davidn#ifdef	LOGIN_CAP
91a3b6ff7davidn#include <login_cap.h>
92a3b6ff7davidn#endif
937d07d2drgrimes
94f767ca7markm#ifdef USE_PAM
950b2fe68markm#include <security/pam_appl.h>
960b2fe68markm#endif
970b2fe68markm
98a9d9ad7lidl#include "blacklist_client.h"
997d07d2drgrimes#include "pathnames.h"
1007d07d2drgrimes#include "extern.h"
1017d07d2drgrimes
1027d07d2drgrimes#include <stdarg.h>
1037d07d2drgrimes
104a1afe71davidnstatic char version[] = "Version 6.00LS";
105a1afe71davidn#undef main
1067d07d2drgrimes
107ce15efbshinunion sockunion ctrl_addr;
108ce15efbshinunion sockunion data_source;
109ce15efbshinunion sockunion data_dest;
110ce15efbshinunion sockunion his_addr;
111ce15efbshinunion sockunion pasv_addr;
1127d07d2drgrimes
11353e950cdgint	daemon_mode;
1147d07d2drgrimesint	data;
1153d2488fyarint	dataport;
1166284753yarint	hostinfo = 1;	/* print host-specific info in messages */
1177d07d2drgrimesint	logged_in;
1187d07d2drgrimesstruct	passwd *pw;
119e6e9500yarchar	*homedir;
120fefaa70markmint	ftpdebug;
1217d07d2drgrimesint	timeout = 900;    /* timeout after 15 minutes of inactivity */
1227d07d2drgrimesint	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
1237d07d2drgrimesint	logging;
1240e79ca4pstint	restricted_data_ports = 1;
125fca5bc3pstint	paranoid = 1;	  /* be extra careful about security */
126ad7885atorstenbint	anon_only = 0;    /* Only anonymous ftp allowed */
127021deabyarint	assumeutf8 = 0;   /* Assume that server file names are in UTF-8 */
1287d07d2drgrimesint	guest;
129fca5bc3pstint	dochroot;
130604b588yarchar	*chrootdir;
1318edf222yarint	dowtmp = 1;
132ee5cff5guidoint	stats;
133ee5cff5guidoint	statfd = -1;
1347d07d2drgrimesint	type;
1357d07d2drgrimesint	form;
1367d07d2drgrimesint	stru;			/* avoid C keyword */
1377d07d2drgrimesint	mode;
1387d07d2drgrimesint	usedefault = 1;		/* for data transfers */
1397d07d2drgrimesint	pdata = -1;		/* for passive mode */
140d46c701yarint	readonly = 0;		/* Server is in readonly mode.	*/
141d46c701yarint	noepsv = 0;		/* EPSV command is disabled.	*/
142d46c701yarint	noretr = 0;		/* RETR command is disabled.	*/
143d46c701yarint	noguestretr = 0;	/* RETR command is disabled for anon users. */
144d46c701yarint	noguestmkd = 0;		/* MKD command is disabled for anon users. */
145d46c701yarint	noguestmod = 1;		/* anon users may not modify existing files. */
14670c24c2lidlint	use_blacklist = 0;
14728e8743nik
1487d07d2drgrimesoff_t	file_size;
1497d07d2drgrimesoff_t	byte_count;
1507d07d2drgrimes#if !defined(CMASK) || CMASK == 0
1517d07d2drgrimes#undef CMASK
1527d07d2drgrimes#define CMASK 027
1537d07d2drgrimes#endif
1547d07d2drgrimesint	defumask = CMASK;		/* default umask value */
1557d07d2drgrimeschar	tmpline[7];
15655c9dacdavidnchar	*hostname;
15761b8c1cddint	epsvall = 0;
15861b8c1cdd
159d10a613davidn#ifdef VIRTUAL_HOSTING
16055c9dacdavidnchar	*ftpuser;
16155c9dacdavidn
16255c9dacdavidnstatic struct ftphost {
16355c9dacdavidn	struct ftphost	*next;
1646c4d71eshin	struct addrinfo *hostinfo;
16555c9dacdavidn	char		*hostname;
16655c9dacdavidn	char		*anonuser;
16755c9dacdavidn	char		*statfile;
16855c9dacdavidn	char		*welcome;
16955c9dacdavidn	char		*loginmsg;
17055c9dacdavidn} *thishost, *firsthost;
17155c9dacdavidn
17255c9dacdavidn#endif
173806e7b8yarchar	remotehost[NI_MAXHOST];
174ee5cff5guidochar	*ident = NULL;
175fca5bc3pst
176a0af351edstatic char	wtmpid[20];
177fca5bc3pst
178f767ca7markm#ifdef USE_PAM
179f15fa6aimpstatic int	auth_pam(struct passwd**, const char*);
180d46c701yarpam_handle_t	*pamh = NULL;
18150483c2ache#endif
1823b3e620markm
183d46c701yarstatic struct opie	opiedata;
184d46c701yarstatic char		opieprompt[OPIE_CHALLENGE_MAX+1];
185d46c701yarstatic int		pwok;
186a81e90dmarkm
187c860311yarchar	*pid_file = NULL; /* means default location to pidfile(3) */
188773f3ebjulian
1897d07d2drgrimes/*
190e62162bjlemon * Limit number of pathnames that glob can return.
191e62162bjlemon * A limit of 0 indicates the number of pathnames is unlimited.
192e62162bjlemon */
193e62162bjlemon#define MAXGLOBARGS	16384
194e62162bjlemon#
195e62162bjlemon
196e62162bjlemon/*
1977d07d2drgrimes * Timeout intervals for retrying connections
1987d07d2drgrimes * to hosts that don't accept PORT cmds.  This
1997d07d2drgrimes * is a kludge, but given the problems with TCP...
2007d07d2drgrimes */
2017d07d2drgrimes#define	SWAITMAX	90	/* wait at most 90 seconds */
2027d07d2drgrimes#define	SWAITINT	5	/* interval between retries */
2037d07d2drgrimes
2047d07d2drgrimesint	swaitmax = SWAITMAX;
2057d07d2drgrimesint	swaitint = SWAITINT;
2067d07d2drgrimes
2077d07d2drgrimes#ifdef SETPROCTITLE
208eee47fepeter#ifdef OLD_SETPROCTITLE
2097d07d2drgrimeschar	**Argv = NULL;		/* pointer to argument vector */
2107d07d2drgrimeschar	*LastArgv = NULL;	/* end of argv */
211eee47fepeter#endif /* OLD_SETPROCTITLE */
2127d07d2drgrimeschar	proctitle[LINE_MAX];	/* initial part of title */
2137d07d2drgrimes#endif /* SETPROCTITLE */
2147d07d2drgrimes
215750c661yar#define LOGCMD(cmd, file)		logcmd((cmd), (file), NULL, -1)
216750c661yar#define LOGCMD2(cmd, file1, file2)	logcmd((cmd), (file1), (file2), -1)
217750c661yar#define LOGBYTES(cmd, file, cnt)	logcmd((cmd), (file), NULL, (cnt))
2187d07d2drgrimes
219e3e66eeyarstatic	volatile sig_atomic_t recvurg;
220e3e66eeyarstatic	int transflag;		/* NB: for debugging only */
221e3e66eeyar
222e3e66eeyar#define STARTXFER	flagxfer(1)
223e3e66eeyar#define ENDXFER		flagxfer(0)
224e3e66eeyar
225e3e66eeyar#define START_UNSAFE	maskurg(1)
226e3e66eeyar#define END_UNSAFE	maskurg(0)
227e3e66eeyar
228e3e66eeyar/* It's OK to put an `else' clause after this macro. */
229e3e66eeyar#define CHECKOOB(action)						\
230e3e66eeyar	if (recvurg) {							\
231e3e66eeyar		recvurg = 0;						\
232e3e66eeyar		if (myoob()) {						\
233e3e66eeyar			ENDXFER;					\
234e3e66eeyar			action;						\
235e3e66eeyar		}							\
236e3e66eeyar	}
237e3e66eeyar
23855c9dacdavidn#ifdef VIRTUAL_HOSTING
2392fdbdd8umestatic void	 inithosts(int);
240d46c701yarstatic void	 selecthost(union sockunion *);
24155c9dacdavidn#endif
242f15fa6aimpstatic void	 ack(char *);
243f15fa6aimpstatic void	 sigurg(int);
244e3e66eeyarstatic void	 maskurg(int);
245e3e66eeyarstatic void	 flagxfer(int);
246e3e66eeyarstatic int	 myoob(void);
24748ae57ecsjpstatic int	 checkuser(char *, char *, int, char **, int *);
248f15fa6aimpstatic FILE	*dataconn(char *, off_t, char *);
249f15fa6aimpstatic void	 dolog(struct sockaddr *);
250f15fa6aimpstatic void	 end_login(void);
251f15fa6aimpstatic FILE	*getdatasock(char *);
2521402e3cyarstatic int	 guniquefd(char *, char **);
253f15fa6aimpstatic void	 lostconn(int);
254f15fa6aimpstatic void	 sigquit(int);
255f15fa6aimpstatic int	 receive_data(FILE *, FILE *);
256c9044cfyarstatic int	 send_data(FILE *, FILE *, size_t, off_t, int);
2577d07d2drgrimesstatic struct passwd *
258f15fa6aimp		 sgetpwnam(char *);
259f15fa6aimpstatic char	*sgetsave(char *);
260f15fa6aimpstatic void	 reapchild(int);
26181121beyarstatic void	 appendf(char **, char *, ...) __printflike(2, 3);
262750c661yarstatic void	 logcmd(char *, char *, char *, off_t);
263f15fa6aimpstatic void      logxfer(char *, off_t, time_t);
2647825a53yarstatic char	*doublequote(char *);
265d25cb60umestatic int	*socksetup(int, char *, const char *);
2667d07d2drgrimes
2677d07d2drgrimesint
268f15fa6aimpmain(int argc, char *argv[], char **envp)
2697d07d2drgrimes{
27003a2de3stefanf	socklen_t addrlen;
2715e937d1lidl	int ch, on = 1, tos, s = STDIN_FILENO;
2727d07d2drgrimes	char *cp, line[LINE_MAX];
2737d07d2drgrimes	FILE *fd;
274ce15efbshin	char	*bindname = NULL;
2753d2488fyar	const char *bindport = "ftp";
276ce15efbshin	int	family = AF_UNSPEC;
277dc82fedyar	struct sigaction sa;
2787d07d2drgrimes
279649e775ache	tzset();		/* in case no timezone database in ~ftp */
280dc82fedyar	sigemptyset(&sa.sa_mask);
281dc82fedyar	sa.sa_flags = SA_RESTART;
282649e775ache
283eee47fepeter#ifdef OLD_SETPROCTITLE
2847d07d2drgrimes	/*
2857d07d2drgrimes	 *  Save start and extent of argv for setproctitle.
2867d07d2drgrimes	 */
2877d07d2drgrimes	Argv = argv;
2887d07d2drgrimes	while (*envp)
2897d07d2drgrimes		envp++;
2907d07d2drgrimes	LastArgv = envp[-1] + strlen(envp[-1]);
291eee47fepeter#endif /* OLD_SETPROCTITLE */
2927d07d2drgrimes
2931a2e300yar	/*
2941a2e300yar	 * Prevent diagnostic messages from appearing on stderr.
2951a2e300yar	 * We run as a daemon or from inetd; in both cases, there's
2961a2e300yar	 * more reason in logging to syslog.
2971a2e300yar	 */
2981a2e300yar	(void) freopen(_PATH_DEVNULL, "w", stderr);
2991a2e300yar	opterr = 0;
3001a2e300yar
3011a2e300yar	/*
3021a2e300yar	 * LOG_NDELAY sets up the logging connection immediately,
3031a2e300yar	 * necessary for anonymous ftp's that chroot and can't do it later.
3041a2e300yar	 */
3051a2e300yar	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
306ee5cff5guido
3076284753yar	while ((ch = getopt(argc, argv,
30870c24c2lidl	                    "468a:ABdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) {
3097d07d2drgrimes		switch (ch) {
31011df7e3yar		case '4':
311d25cb60ume			family = (family == AF_INET6) ? AF_UNSPEC : AF_INET;
31211df7e3yar			break;
31311df7e3yar
31411df7e3yar		case '6':
315d25cb60ume			family = (family == AF_INET) ? AF_UNSPEC : AF_INET6;
31611df7e3yar			break;
31711df7e3yar
318021deabyar		case '8':
319021deabyar			assumeutf8 = 1;
320021deabyar			break;
321021deabyar
32211df7e3yar		case 'a':
32311df7e3yar			bindname = optarg;
32411df7e3yar			break;
32511df7e3yar
32611df7e3yar		case 'A':
32711df7e3yar			anon_only = 1;
32853e950cdg			break;
32953e950cdg
33070c24c2lidl		case 'B':
33170c24c2lidl#ifdef USE_BLACKLIST
33270c24c2lidl			use_blacklist = 1;
33370c24c2lidl#else
33470c24c2lidl			syslog(LOG_WARNING, "not compiled with USE_BLACKLIST support");
33570c24c2lidl#endif
33670c24c2lidl			break;
33770c24c2lidl
3387d07d2drgrimes		case 'd':
339fefaa70markm			ftpdebug++;
3407d07d2drgrimes			break;
3417d07d2drgrimes
34211df7e3yar		case 'D':
34311df7e3yar			daemon_mode++;
34411df7e3yar			break;
34511df7e3yar
34610d3e99phk		case 'E':
34710d3e99phk			noepsv = 1;
34810d3e99phk			break;
34910d3e99phk
3506284753yar		case 'h':
3516284753yar			hostinfo = 0;
3526284753yar			break;
3536284753yar
3547d07d2drgrimes		case 'l':
3557d07d2drgrimes			logging++;	/* > 1 == extra logging */
3567d07d2drgrimes			break;
3577d07d2drgrimes
3581402e3cyar		case 'm':
3591402e3cyar			noguestmod = 0;
3601402e3cyar			break;
3611402e3cyar
36211df7e3yar		case 'M':
36311df7e3yar			noguestmkd = 1;
36411df7e3yar			break;
36511df7e3yar
36611df7e3yar		case 'o':
36711df7e3yar			noretr = 1;
36811df7e3yar			break;
36911df7e3yar
37011df7e3yar		case 'O':
37111df7e3yar			noguestretr = 1;
37211df7e3yar			break;
37311df7e3yar
37411df7e3yar		case 'p':
37511df7e3yar			pid_file = optarg;
37611df7e3yar			break;
37711df7e3yar
3783d2488fyar		case 'P':
3793d2488fyar			bindport = optarg;
3803d2488fyar			break;
3813d2488fyar
38210d3e99phk		case 'r':
38310d3e99phk			readonly = 1;
38410d3e99phk			break;
38510d3e99phk
386fca5bc3pst		case 'R':
387fca5bc3pst			paranoid = 0;
3880e79ca4pst			break;
3890e79ca4pst
390ee5cff5guido		case 'S':
391fca5bc3pst			stats++;
392ee5cff5guido			break;
393fca5bc3pst
394fca5bc3pst		case 't':
395fca5bc3pst			timeout = atoi(optarg);
396fca5bc3pst			if (maxtimeout < timeout)
397fca5bc3pst				maxtimeout = timeout;
398fca5bc3pst			break;
399fca5bc3pst
40011df7e3yar		case 'T':
40111df7e3yar			maxtimeout = atoi(optarg);
40211df7e3yar			if (timeout > maxtimeout)
40311df7e3yar				timeout = maxtimeout;
404773f3ebjulian			break;
405773f3ebjulian
4067d07d2drgrimes		case 'u':
4077d07d2drgrimes		    {
4087d07d2drgrimes			long val = 0;
4097d07d2drgrimes
4107d07d2drgrimes			val = strtol(optarg, &optarg, 8);
4117d07d2drgrimes			if (*optarg != '\0' || val < 0)
4121a2e300yar				syslog(LOG_WARNING, "bad value for -u");
4137d07d2drgrimes			else
4147d07d2drgrimes				defumask = val;
4157d07d2drgrimes			break;
4167d07d2drgrimes		    }
41711df7e3yar		case 'U':
41811df7e3yar			restricted_data_ports = 0;
419ad7885atorstenb			break;
4207d07d2drgrimes
4217d07d2drgrimes		case 'v':
422b0f2856yar			ftpdebug++;
4237d07d2drgrimes			break;
4247d07d2drgrimes
4258edf222yar		case 'W':
4268edf222yar			dowtmp = 0;
4278edf222yar			break;
4288edf222yar
4297d07d2drgrimes		default:
4301a2e300yar			syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
4317d07d2drgrimes			break;
4327d07d2drgrimes		}
4337d07d2drgrimes	}
43453e950cdg
43582b41a4eugen	/* handle filesize limit gracefully */
43682b41a4eugen	sa.sa_handler = SIG_IGN;
43782b41a4eugen	(void)sigaction(SIGXFSZ, &sa, NULL);
43882b41a4eugen
43953e950cdg	if (daemon_mode) {
440d25cb60ume		int *ctl_sock, fd, maxfd = -1, nfds, i;
441d25cb60ume		fd_set defreadfds, readfds;
442d25cb60ume		pid_t pid;
443c860311yar		struct pidfh *pfh;
444c860311yar
445c860311yar		if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) {
446c860311yar			if (errno == EEXIST) {
447c860311yar				syslog(LOG_ERR, "%s already running, pid %d",
448c860311yar				       getprogname(), (int)pid);
449c860311yar				exit(1);
450c860311yar			}
451c860311yar			syslog(LOG_WARNING, "pidfile_open: %m");
452c860311yar		}
45353e950cdg
45453e950cdg		/*
45553e950cdg		 * Detach from parent.
45653e950cdg		 */
45753e950cdg		if (daemon(1, 1) < 0) {
45853e950cdg			syslog(LOG_ERR, "failed to become a daemon");
45953e950cdg			exit(1);
46053e950cdg		}
461c860311yar
462c860311yar		if (pfh != NULL && pidfile_write(pfh) == -1)
463c860311yar			syslog(LOG_WARNING, "pidfile_write: %m");
464c860311yar
465dc82fedyar		sa.sa_handler = reapchild;
466dc82fedyar		(void)sigaction(SIGCHLD, &sa, NULL);
467d25cb60ume
4682fdbdd8ume#ifdef VIRTUAL_HOSTING
4692fdbdd8ume		inithosts(family);
4702fdbdd8ume#endif
4712fdbdd8ume
47253e950cdg		/*
47353e950cdg		 * Open a socket, bind it to the FTP port, and start
47453e950cdg		 * listening.
47553e950cdg		 */
476d25cb60ume		ctl_sock = socksetup(family, bindname, bindport);
477d25cb60ume		if (ctl_sock == NULL)
47853e950cdg			exit(1);
479d25cb60ume
480d25cb60ume		FD_ZERO(&defreadfds);
481d25cb60ume		for (i = 1; i <= *ctl_sock; i++) {
482d25cb60ume			FD_SET(ctl_sock[i], &defreadfds);
483d25cb60ume			if (listen(ctl_sock[i], 32) < 0) {
484d25cb60ume				syslog(LOG_ERR, "control listen: %m");
485d25cb60ume				exit(1);
486d25cb60ume			}
487d25cb60ume			if (maxfd < ctl_sock[i])
488d25cb60ume				maxfd = ctl_sock[i];
48953e950cdg		}
490d25cb60ume
49153e950cdg		/*
49253e950cdg		 * Loop forever accepting connection requests and forking off
49353e950cdg		 * children to handle them.
49453e950cdg		 */
49553e950cdg		while (1) {
496d25cb60ume			FD_COPY(&defreadfds, &readfds);
497d25cb60ume			nfds = select(maxfd + 1, &readfds, NULL, NULL, 0);
498d25cb60ume			if (nfds <= 0) {
499d25cb60ume				if (nfds < 0 && errno != EINTR)
500d25cb60ume					syslog(LOG_WARNING, "select: %m");
501d25cb60ume				continue;
50253e950cdg			}
503d25cb60ume
504d25cb60ume			pid = -1;
505d25cb60ume                        for (i = 1; i <= *ctl_sock; i++)
506d25cb60ume				if (FD_ISSET(ctl_sock[i], &readfds)) {
507d25cb60ume					addrlen = sizeof(his_addr);
508d25cb60ume					fd = accept(ctl_sock[i],
509d25cb60ume					    (struct sockaddr *)&his_addr,
510d25cb60ume					    &addrlen);
5111e8c9a7yar					if (fd == -1) {
5121e8c9a7yar						syslog(LOG_WARNING,
5131e8c9a7yar						       "accept: %m");
5141e8c9a7yar						continue;
5151e8c9a7yar					}
5161e8c9a7yar					switch (pid = fork()) {
5171e8c9a7yar					case 0:
5181e8c9a7yar						/* child */
5195e937d1lidl						(void) dup2(fd, s);
5205e937d1lidl						(void) dup2(fd, STDOUT_FILENO);
5211e8c9a7yar						(void) close(fd);
5221e8c9a7yar						for (i = 1; i <= *ctl_sock; i++)
5231e8c9a7yar							close(ctl_sock[i]);
5241e8c9a7yar						if (pfh != NULL)
5251e8c9a7yar							pidfile_close(pfh);
5261e8c9a7yar						goto gotchild;
5271e8c9a7yar					case -1:
5281e8c9a7yar						syslog(LOG_WARNING, "fork: %m");
5291e8c9a7yar						/* FALLTHROUGH */
5301e8c9a7yar					default:
5311e8c9a7yar						close(fd);
532b218fbfmaxim					}
533d25cb60ume				}
53453e950cdg		}
53553e950cdg	} else {
53653e950cdg		addrlen = sizeof(his_addr);
5375e937d1lidl		if (getpeername(s, (struct sockaddr *)&his_addr, &addrlen) < 0) {
53853e950cdg			syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
53953e950cdg			exit(1);
54053e950cdg		}
5412fdbdd8ume
5422fdbdd8ume#ifdef VIRTUAL_HOSTING
5432fdbdd8ume		if (his_addr.su_family == AF_INET6 &&
5442fdbdd8ume		    IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
5452fdbdd8ume			family = AF_INET;
5462fdbdd8ume		else
5472fdbdd8ume			family = his_addr.su_family;
5482fdbdd8ume		inithosts(family);
5492fdbdd8ume#endif
55053e950cdg	}
55153e950cdg
5521e8c9a7yargotchild:
553dc82fedyar	sa.sa_handler = SIG_DFL;
554dc82fedyar	(void)sigaction(SIGCHLD, &sa, NULL);
555dc82fedyar
556dc82fedyar	sa.sa_handler = sigurg;
557dc82fedyar	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
558dc82fedyar	(void)sigaction(SIGURG, &sa, NULL);
559dc82fedyar
560dc82fedyar	sigfillset(&sa.sa_mask);	/* block all signals in handler */
561dc82fedyar	sa.sa_flags = SA_RESTART;
562dc82fedyar	sa.sa_handler = sigquit;
563dc82fedyar	(void)sigaction(SIGHUP, &sa, NULL);
564dc82fedyar	(void)sigaction(SIGINT, &sa, NULL);
565dc82fedyar	(void)sigaction(SIGQUIT, &sa, NULL);
566dc82fedyar	(void)sigaction(SIGTERM, &sa, NULL);
567dc82fedyar
568dc82fedyar	sa.sa_handler = lostconn;
569dc82fedyar	(void)sigaction(SIGPIPE, &sa, NULL);
5707d07d2drgrimes
57153e950cdg	addrlen = sizeof(ctrl_addr);
5725e937d1lidl	if (getsockname(s, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
57353e950cdg		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
57453e950cdg		exit(1);
57553e950cdg	}
5763d2488fyar	dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */
57755c9dacdavidn#ifdef VIRTUAL_HOSTING
57855c9dacdavidn	/* select our identity from virtual host table */
579ce15efbshin	selecthost(&ctrl_addr);
58055c9dacdavidn#endif
58153e950cdg#ifdef IP_TOS
582ce15efbshin	if (ctrl_addr.su_family == AF_INET)
583ce15efbshin      {
58453e950cdg	tos = IPTOS_LOWDELAY;
5855e937d1lidl	if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
586d19478cyar		syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m");
587ce15efbshin      }
58853e950cdg#endif
5893f47a32dg	/*
5903f47a32dg	 * Disable Nagle on the control channel so that we don't have to wait
5913f47a32dg	 * for peer's ACK before issuing our next reply.
5923f47a32dg	 */
5935e937d1lidl	if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
594d19478cyar		syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m");
5953f47a32dg
596ce15efbshin	data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
59753e950cdg
598a0af351ed	(void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid());
599fca5bc3pst
6007d07d2drgrimes	/* Try to handle urgent data inline */
6017d07d2drgrimes#ifdef SO_OOBINLINE
6025e937d1lidl	if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0)
603d19478cyar		syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m");
6047d07d2drgrimes#endif
6057d07d2drgrimes
6067d07d2drgrimes#ifdef	F_SETOWN
6075e937d1lidl	if (fcntl(s, F_SETOWN, getpid()) == -1)
6087d07d2drgrimes		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
6097d07d2drgrimes#endif
610ce15efbshin	dolog((struct sockaddr *)&his_addr);
6117d07d2drgrimes	/*
6127d07d2drgrimes	 * Set up default state
6137d07d2drgrimes	 */
6147d07d2drgrimes	data = -1;
6157d07d2drgrimes	type = TYPE_A;
6167d07d2drgrimes	form = FORM_N;
6177d07d2drgrimes	stru = STRU_F;
6187d07d2drgrimes	mode = MODE_S;
6197d07d2drgrimes	tmpline[0] = '\0';
6207d07d2drgrimes
6217d07d2drgrimes	/* If logins are disabled, print out the message. */
6227d07d2drgrimes	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
6237d07d2drgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
6247d07d2drgrimes			if ((cp = strchr(line, '\n')) != NULL)
6257d07d2drgrimes				*cp = '\0';
6267d07d2drgrimes			lreply(530, "%s", line);
6277d07d2drgrimes		}
6287d07d2drgrimes		(void) fflush(stdout);
6297d07d2drgrimes		(void) fclose(fd);
6307d07d2drgrimes		reply(530, "System not available.");
6317d07d2drgrimes		exit(0);
6327d07d2drgrimes	}
63355c9dacdavidn#ifdef VIRTUAL_HOSTING
634fc65dfcobrien	fd = fopen(thishost->welcome, "r");
63555c9dacdavidn#else
636fc65dfcobrien	fd = fopen(_PATH_FTPWELCOME, "r");
63755c9dacdavidn#endif
638fc65dfcobrien	if (fd != NULL) {
6397d07d2drgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
6407d07d2drgrimes			if ((cp = strchr(line, '\n')) != NULL)
6417d07d2drgrimes				*cp = '\0';
6427d07d2drgrimes			lreply(220, "%s", line);
6437d07d2drgrimes		}
6447d07d2drgrimes		(void) fflush(stdout);
6457d07d2drgrimes		(void) fclose(fd);
6467d07d2drgrimes		/* reply(220,) must follow */
6477d07d2drgrimes	}
64855c9dacdavidn#ifndef VIRTUAL_HOSTING
649d10a613davidn	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
650fefaa70markm		fatalerror("Ran out of memory.");
651806e7b8yar	if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0)
652806e7b8yar		hostname[0] = '\0';
653c6c6c7dbrian	hostname[MAXHOSTNAMELEN - 1] = '\0';
65455c9dacdavidn#endif
6556284753yar	if (hostinfo)
6566284753yar		reply(220, "%s FTP server (%s) ready.", hostname, version);
6576284753yar	else
6586284753yar		reply(220, "FTP server ready.");
65970c24c2lidl	BLACKLIST_INIT();
6607d07d2drgrimes	for (;;)
6617d07d2drgrimes		(void) yyparse();
6627d07d2drgrimes	/* NOTREACHED */
6637d07d2drgrimes}
6647d07d2drgrimes
6657d07d2drgrimesstatic void
666f15fa6aimplostconn(int signo)
6677d07d2drgrimes{
6687d07d2drgrimes
669fefaa70markm	if (ftpdebug)
6707d07d2drgrimes		syslog(LOG_DEBUG, "lost connection");
67117df8f3charnier	dologout(1);
6727d07d2drgrimes}
6737d07d2drgrimes
674dc82fedyarstatic void
675f15fa6aimpsigquit(int signo)
676dc82fedyar{
677dc82fedyar
678dc82fedyar	syslog(LOG_ERR, "got signal %d", signo);
679dc82fedyar	dologout(1);
680dc82fedyar}
681dc82fedyar
68255c9dacdavidn#ifdef VIRTUAL_HOSTING
68355c9dacdavidn/*
68455c9dacdavidn * read in virtual host tables (if they exist)
68555c9dacdavidn */
68655c9dacdavidn
68755c9dacdavidnstatic void
6882fdbdd8umeinithosts(int family)
68955c9dacdavidn{
690c2dfbe8yar	int insert;
691eba11c7yar	size_t len;
69255c9dacdavidn	FILE *fp;
693eba11c7yar	char *cp, *mp, *line;
694eba11c7yar	char *hostname;
695c2dfbe8yar	char *vhost, *anonuser, *statfile, *welcome, *loginmsg;
69655c9dacdavidn	struct ftphost *hrp, *lhrp;
697ce15efbshin	struct addrinfo hints, *res, *ai;
69855c9dacdavidn
69955c9dacdavidn	/*
70055c9dacdavidn	 * Fill in the default host information
70155c9dacdavidn	 */
702eba11c7yar	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
703eba11c7yar		fatalerror("Ran out of memory.");
704806e7b8yar	if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0)
705eba11c7yar		hostname[0] = '\0';
706eba11c7yar	hostname[MAXHOSTNAMELEN - 1] = '\0';
707eba11c7yar	if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
708fefaa70markm		fatalerror("Ran out of memory.");
709eba11c7yar	hrp->hostname = hostname;
7106c4d71eshin	hrp->hostinfo = NULL;
711ce15efbshin
712ce15efbshin	memset(&hints, 0, sizeof(hints));
7132fdbdd8ume	hints.ai_flags = AI_PASSIVE;
7142fdbdd8ume	hints.ai_family = family;
7152fdbdd8ume	hints.ai_socktype = SOCK_STREAM;
716a5929f9yar	if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0)
7176c4d71eshin		hrp->hostinfo = res;
71855c9dacdavidn	hrp->statfile = _PATH_FTPDSTATFILE;
71955c9dacdavidn	hrp->welcome  = _PATH_FTPWELCOME;
72055c9dacdavidn	hrp->loginmsg = _PATH_FTPLOGINMESG;
72155c9dacdavidn	hrp->anonuser = "ftp";
72255c9dacdavidn	hrp->next = NULL;
72355c9dacdavidn	thishost = firsthost = lhrp = hrp;
72455c9dacdavidn	if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
725906476byar		int addrsize, gothost;
726ce15efbshin		void *addr;
727ce15efbshin		struct hostent *hp;
728ce15efbshin
729eba11c7yar		while ((line = fgetln(fp, &len)) != NULL) {
730ce15efbshin			int	i, hp_error;
73155c9dacdavidn
732eba11c7yar			/* skip comments */
733eba11c7yar			if (line[0] == '#')
73455c9dacdavidn				continue;
735eba11c7yar			if (line[len - 1] == '\n') {
736eba11c7yar				line[len - 1] = '\0';
737eba11c7yar				mp = NULL;
738eba11c7yar			} else {
739eba11c7yar				if ((mp = malloc(len + 1)) == NULL)
740eba11c7yar					fatalerror("Ran out of memory.");
741eba11c7yar				memcpy(mp, line, len);
742eba11c7yar				mp[len] = '\0';
743eba11c7yar				line = mp;
74455c9dacdavidn			}
74555c9dacdavidn			cp = strtok(line, " \t");
746eba11c7yar			/* skip empty lines */
747eba11c7yar			if (cp == NULL)
748eba11c7yar				goto nextline;
749c2dfbe8yar			vhost = cp;
750c2dfbe8yar
751c2dfbe8yar			/* set defaults */
752c2dfbe8yar			anonuser = "ftp";
753c2dfbe8yar			statfile = _PATH_FTPDSTATFILE;
754c2dfbe8yar			welcome  = _PATH_FTPWELCOME;
755c2dfbe8yar			loginmsg = _PATH_FTPLOGINMESG;
756c2dfbe8yar
757c2dfbe8yar			/*
758c2dfbe8yar			 * Preparse the line so we can use its info
759c2dfbe8yar			 * for all the addresses associated with
760c2dfbe8yar			 * the virtual host name.
761c2dfbe8yar			 * Field 0, the virtual host name, is special:
762c2dfbe8yar			 * it's already parsed off and will be strdup'ed
763c2dfbe8yar			 * later, after we know its canonical form.
764c2dfbe8yar			 */
765c2dfbe8yar			for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++)
766c2dfbe8yar				if (*cp != '-' && (cp = strdup(cp)))
767c2dfbe8yar					switch (i) {
768c2dfbe8yar					case 1:	/* anon user permissions */
769c2dfbe8yar						anonuser = cp;
770c2dfbe8yar						break;
771c2dfbe8yar					case 2: /* statistics file */
772c2dfbe8yar						statfile = cp;
773c2dfbe8yar						break;
774c2dfbe8yar					case 3: /* welcome message */
775c2dfbe8yar						welcome  = cp;
776c2dfbe8yar						break;
777c2dfbe8yar					case 4: /* login message */
778c2dfbe8yar						loginmsg = cp;
779c2dfbe8yar						break;
780c2dfbe8yar					default: /* programming error */
781c2dfbe8yar						abort();
782c2dfbe8yar						/* NOTREACHED */
783c2dfbe8yar					}
784ce15efbshin
785ce15efbshin			hints.ai_flags = AI_PASSIVE;
7862fdbdd8ume			hints.ai_family = family;
7872fdbdd8ume			hints.ai_socktype = SOCK_STREAM;
788a5929f9yar			if (getaddrinfo(vhost, NULL, &hints, &res) != 0)
789eba11c7yar				goto nextline;
790ce15efbshin			for (ai = res; ai != NULL && ai->ai_addr != NULL;
7911f6b02cdavidn			     ai = ai->ai_next) {
792ce15efbshin
7931f6b02cdavidn			gothost = 0;
79455c9dacdavidn			for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
7956c4d71eshin				struct addrinfo *hi;
7966c4d71eshin
7976c4d71eshin				for (hi = hrp->hostinfo; hi != NULL;
7986c4d71eshin				     hi = hi->ai_next)
7996c4d71eshin					if (hi->ai_addrlen == ai->ai_addrlen &&
8006c4d71eshin					    memcmp(hi->ai_addr,
8016c4d71eshin						   ai->ai_addr,
8021f6b02cdavidn						   ai->ai_addr->sa_len) == 0) {
8031f6b02cdavidn						gothost++;
8046c4d71eshin						break;
8054410d83yar					}
8061f6b02cdavidn				if (gothost)
8071f6b02cdavidn					break;
80855c9dacdavidn			}
80955c9dacdavidn			if (hrp == NULL) {
81055c9dacdavidn				if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
811eba11c7yar					goto nextline;
812a5929f9yar				hrp->hostname = NULL;
813c2dfbe8yar				insert = 1;
8149841ff7yar			} else {
815e2616c6yar				if (hrp->hostinfo && hrp->hostinfo != res)
8169841ff7yar					freeaddrinfo(hrp->hostinfo);
817c2dfbe8yar				insert = 0; /* host already in the chain */
8189841ff7yar			}
8196c4d71eshin			hrp->hostinfo = res;
8206c4d71eshin
82155c9dacdavidn			/*
82255c9dacdavidn			 * determine hostname to use.
823ce15efbshin			 * force defined name if there is a valid alias
82455c9dacdavidn			 * otherwise fallback to primary hostname
82555c9dacdavidn			 */
826ce15efbshin			/* XXX: getaddrinfo() can't do alias check */
8276c4d71eshin			switch(hrp->hostinfo->ai_family) {
828ce15efbshin			case AF_INET:
829ab8d11dyar				addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr;
830ab8d11dyar				addrsize = sizeof(struct in_addr);
831ce15efbshin				break;
832ce15efbshin			case AF_INET6:
833ab8d11dyar				addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr;
834ab8d11dyar				addrsize = sizeof(struct in6_addr);
835ce15efbshin				break;
836ce15efbshin			default:
837ce15efbshin				/* should not reach here */
8389841ff7yar				freeaddrinfo(hrp->hostinfo);
8399841ff7yar				if (insert)
8409841ff7yar					free(hrp); /*not in chain, can free*/
8419841ff7yar				else
8429841ff7yar					hrp->hostinfo = NULL; /*mark as blank*/
843eba11c7yar				goto nextline;
844ce15efbshin				/* NOTREACHED */
845ce15efbshin			}
846b55ffafyar			if ((hp = getipnodebyaddr(addr, addrsize,
8476c4d71eshin						  hrp->hostinfo->ai_family,
848ce15efbshin						  &hp_error)) != NULL) {
849c2dfbe8yar				if (strcmp(vhost, hp->h_name) != 0) {
85055c9dacdavidn					if (hp->h_aliases == NULL)
851c2dfbe8yar						vhost = hp->h_name;
85255c9dacdavidn					else {
85355c9dacdavidn						i = 0;
85455c9dacdavidn						while (hp->h_aliases[i] &&
855c2dfbe8yar						       strcmp(vhost, hp->h_aliases[i]) != 0)
85655c9dacdavidn							++i;
85755c9dacdavidn						if (hp->h_aliases[i] == NULL)
858c2dfbe8yar							vhost = hp->h_name;
85955c9dacdavidn					}
86055c9dacdavidn				}
86155c9dacdavidn			}
862a5929f9yar			if (hrp->hostname &&
863a5929f9yar			    strcmp(hrp->hostname, vhost) != 0) {
864a5929f9yar				free(hrp->hostname);
865a5929f9yar				hrp->hostname = NULL;
866a5929f9yar			}
867a5929f9yar			if (hrp->hostname == NULL &&
8689841ff7yar			    (hrp->hostname = strdup(vhost)) == NULL) {
8699841ff7yar				freeaddrinfo(hrp->hostinfo);
8709841ff7yar				hrp->hostinfo = NULL; /* mark as blank */
8719841ff7yar				if (hp)
8729841ff7yar					freehostent(hp);
873c2dfbe8yar				goto nextline;
8749841ff7yar			}
875c2dfbe8yar			hrp->anonuser = anonuser;
876c2dfbe8yar			hrp->statfile = statfile;
877c2dfbe8yar			hrp->welcome  = welcome;
878c2dfbe8yar			hrp->loginmsg = loginmsg;
879c2dfbe8yar			if (insert) {
880c2dfbe8yar				hrp->next  = NULL;
881c2dfbe8yar				lhrp->next = hrp;
882c2dfbe8yar				lhrp = hrp;
88355c9dacdavidn			}
8840e4b896yar			if (hp)
8850e4b896yar				freehostent(hp);
886ce15efbshin		      }
887eba11c7yarnextline:
888eba11c7yar			if (mp)
889eba11c7yar				free(mp);
89055c9dacdavidn		}
89155c9dacdavidn		(void) fclose(fp);
89255c9dacdavidn	}
89355c9dacdavidn}
89455c9dacdavidn
89555c9dacdavidnstatic void
896f15fa6aimpselecthost(union sockunion *su)
89755c9dacdavidn{
89855c9dacdavidn	struct ftphost	*hrp;
899ce15efbshin	u_int16_t port;
900ce15efbshin#ifdef INET6
901ce15efbshin	struct in6_addr *mapped_in6 = NULL;
902ce15efbshin#endif
9036c4d71eshin	struct addrinfo *hi;
904ce15efbshin
905ce15efbshin#ifdef INET6
906ce15efbshin	/*
907ce15efbshin	 * XXX IPv4 mapped IPv6 addr consideraton,
908ce15efbshin	 * specified in rfc2373.
909ce15efbshin	 */
910ce15efbshin	if (su->su_family == AF_INET6 &&
911ce15efbshin	    IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr))
912ce15efbshin		mapped_in6 = &su->su_sin6.sin6_addr;
913ce15efbshin#endif
91455c9dacdavidn
91555c9dacdavidn	hrp = thishost = firsthost;	/* default */
916ce15efbshin	port = su->su_port;
917ce15efbshin	su->su_port = 0;
91855c9dacdavidn	while (hrp != NULL) {
9191f6b02cdavidn	    for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) {
9206c4d71eshin		if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) {
92155c9dacdavidn			thishost = hrp;
922a4b5efdyar			goto found;
92355c9dacdavidn		}
924ce15efbshin#ifdef INET6
925ce15efbshin		/* XXX IPv4 mapped IPv6 addr consideraton */
9266c4d71eshin		if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL &&
927ce15efbshin		    (memcmp(&mapped_in6->s6_addr[12],
9286c4d71eshin			    &((struct sockaddr_in *)hi->ai_addr)->sin_addr,
929ce15efbshin			    sizeof(struct in_addr)) == 0)) {
930ce15efbshin			thishost = hrp;
931a4b5efdyar			goto found;
932ce15efbshin		}
933ce15efbshin#endif
9341f6b02cdavidn	    }
9351f6b02cdavidn	    hrp = hrp->next;
93655c9dacdavidn	}
937a4b5efdyarfound:
938ce15efbshin	su->su_port = port;
93955c9dacdavidn	/* setup static variables as appropriate */
94055c9dacdavidn	hostname = thishost->hostname;
94155c9dacdavidn	ftpuser = thishost->anonuser;
94255c9dacdavidn}
943