ftpd.c revision 13139
1/*
2 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 *	$Id: ftpd.c,v 1.13 1995/11/29 19:52:30 guido Exp $
34 */
35
36#ifndef lint
37static char copyright[] =
38"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
39	The Regents of the University of California.  All rights reserved.\n";
40#endif /* not lint */
41
42#ifndef lint
43static char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
44#endif /* not lint */
45
46/*
47 * FTP server.
48 */
49#include <sys/param.h>
50#include <sys/stat.h>
51#include <sys/ioctl.h>
52#include <sys/socket.h>
53#include <sys/wait.h>
54#include <sys/mman.h>
55
56#include <netinet/in.h>
57#include <netinet/in_systm.h>
58#include <netinet/ip.h>
59#include <netinet/tcp.h>
60
61#define	FTP_NAMES
62#include <arpa/ftp.h>
63#include <arpa/inet.h>
64#include <arpa/telnet.h>
65
66#include <ctype.h>
67#include <dirent.h>
68#include <err.h>
69#include <errno.h>
70#include <fcntl.h>
71#include <glob.h>
72#include <limits.h>
73#include <netdb.h>
74#include <pwd.h>
75#include <setjmp.h>
76#include <signal.h>
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80#include <syslog.h>
81#include <time.h>
82#include <unistd.h>
83#include <libutil.h>
84
85#ifdef	SKEY
86#include <skey.h>
87#endif
88
89#include "pathnames.h"
90#include "extern.h"
91
92#if __STDC__
93#include <stdarg.h>
94#else
95#include <varargs.h>
96#endif
97
98static char version[] = "Version 6.00";
99
100extern	off_t restart_point;
101extern	char cbuf[];
102
103struct	sockaddr_in ctrl_addr;
104struct	sockaddr_in data_source;
105struct	sockaddr_in data_dest;
106struct	sockaddr_in his_addr;
107struct	sockaddr_in pasv_addr;
108
109int	data;
110jmp_buf	errcatch, urgcatch;
111int	logged_in;
112struct	passwd *pw;
113int	debug;
114int	timeout = 900;    /* timeout after 15 minutes of inactivity */
115int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
116int	logging;
117int	restricted_data_ports = 1;
118int	guest;
119#ifdef STATS
120int	stats;
121int	statfd = -1;
122#endif
123int	type;
124int	form;
125int	stru;			/* avoid C keyword */
126int	mode;
127int	usedefault = 1;		/* for data transfers */
128int	pdata = -1;		/* for passive mode */
129sig_atomic_t transflag;
130off_t	file_size;
131off_t	byte_count;
132#if !defined(CMASK) || CMASK == 0
133#undef CMASK
134#define CMASK 027
135#endif
136int	defumask = CMASK;		/* default umask value */
137char	tmpline[7];
138char	hostname[MAXHOSTNAMELEN];
139char	remotehost[MAXHOSTNAMELEN];
140#ifdef STATS
141char	*ident = NULL;
142#endif
143
144/*
145 * Timeout intervals for retrying connections
146 * to hosts that don't accept PORT cmds.  This
147 * is a kludge, but given the problems with TCP...
148 */
149#define	SWAITMAX	90	/* wait at most 90 seconds */
150#define	SWAITINT	5	/* interval between retries */
151
152int	swaitmax = SWAITMAX;
153int	swaitint = SWAITINT;
154
155#ifdef SETPROCTITLE
156#ifdef OLD_SETPROCTITLE
157char	**Argv = NULL;		/* pointer to argument vector */
158char	*LastArgv = NULL;	/* end of argv */
159#endif /* OLD_SETPROCTITLE */
160char	proctitle[LINE_MAX];	/* initial part of title */
161#endif /* SETPROCTITLE */
162
163#ifdef SKEY
164int	pwok = 0;
165char	addr_string[20];	/* XXX */
166#endif
167
168#define LOGCMD(cmd, file) \
169	if (logging > 1) \
170	    syslog(LOG_INFO,"%s %s%s", cmd, \
171		*(file) == '/' ? "" : curdir(), file);
172#define LOGCMD2(cmd, file1, file2) \
173	 if (logging > 1) \
174	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
175		*(file1) == '/' ? "" : curdir(), file1, \
176		*(file2) == '/' ? "" : curdir(), file2);
177#define LOGBYTES(cmd, file, cnt) \
178	if (logging > 1) { \
179		if (cnt == (off_t)-1) \
180		    syslog(LOG_INFO,"%s %s%s", cmd, \
181			*(file) == '/' ? "" : curdir(), file); \
182		else \
183		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
184			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
185	}
186
187static void	 ack __P((char *));
188static void	 myoob __P((int));
189static int	 checkuser __P((char *));
190static FILE	*dataconn __P((char *, off_t, char *));
191static void	 dolog __P((struct sockaddr_in *));
192static char	*curdir __P((void));
193static void	 end_login __P((void));
194static FILE	*getdatasock __P((char *));
195static char	*gunique __P((char *));
196static void	 lostconn __P((int));
197static int	 receive_data __P((FILE *, FILE *));
198static void	 send_data __P((FILE *, FILE *, off_t, off_t, int));
199static struct passwd *
200		 sgetpwnam __P((char *));
201static char	*sgetsave __P((char *));
202
203static char *
204curdir()
205{
206	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
207
208	if (getcwd(path, sizeof(path)-2) == NULL)
209		return ("");
210	if (path[1] != '\0')		/* special case for root dir. */
211		strcat(path, "/");
212	/* For guest account, skip / since it's chrooted */
213	return (guest ? path+1 : path);
214}
215
216int
217main(argc, argv, envp)
218	int argc;
219	char *argv[];
220	char **envp;
221{
222	int addrlen, ch, on = 1, tos;
223	char *cp, line[LINE_MAX];
224	FILE *fd;
225
226	tzset();		/* in case no timezone database in ~ftp */
227
228	/*
229	 * LOG_NDELAY sets up the logging connection immediately,
230	 * necessary for anonymous ftp's that chroot and can't do it later.
231	 */
232	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
233	addrlen = sizeof(his_addr);
234	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
235		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
236		exit(1);
237	}
238#ifdef SKEY
239	strcpy(addr_string, inet_ntoa(his_addr.sin_addr));
240#endif
241	addrlen = sizeof(ctrl_addr);
242	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
243		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
244		exit(1);
245	}
246#ifdef IP_TOS
247	tos = IPTOS_LOWDELAY;
248	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
249		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
250#endif
251	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
252	debug = 0;
253#ifdef OLD_SETPROCTITLE
254	/*
255	 *  Save start and extent of argv for setproctitle.
256	 */
257	Argv = argv;
258	while (*envp)
259		envp++;
260	LastArgv = envp[-1] + strlen(envp[-1]);
261#endif /* OLD_SETPROCTITLE */
262
263
264#ifdef STATS
265	while ((ch = getopt(argc, argv, "dlSt:T:u:v")) != EOF) {
266#else
267	while ((ch = getopt(argc, argv, "dlUt:T:u:v")) != EOF) {
268#endif
269		switch (ch) {
270		case 'd':
271			debug = 1;
272			break;
273
274		case 'l':
275			logging++;	/* > 1 == extra logging */
276			break;
277
278		case 'U':
279			restricted_data_ports = 0;
280			break;
281
282		case 't':
283			timeout = atoi(optarg);
284			if (maxtimeout < timeout)
285				maxtimeout = timeout;
286			break;
287#ifdef STATS
288		case 'S':
289			stats =  1;
290			break;
291#endif
292		case 'T':
293			maxtimeout = atoi(optarg);
294			if (timeout > maxtimeout)
295				timeout = maxtimeout;
296			break;
297
298		case 'u':
299		    {
300			long val = 0;
301
302			val = strtol(optarg, &optarg, 8);
303			if (*optarg != '\0' || val < 0)
304				warnx("bad value for -u");
305			else
306				defumask = val;
307			break;
308		    }
309
310		case 'v':
311			debug = 1;
312			break;
313
314		default:
315			warnx("unknown flag -%c ignored", optopt);
316			break;
317		}
318	}
319	(void) freopen(_PATH_DEVNULL, "w", stderr);
320	(void) signal(SIGPIPE, lostconn);
321	(void) signal(SIGCHLD, SIG_IGN);
322	if ((int)signal(SIGURG, myoob) < 0)
323		syslog(LOG_ERR, "signal: %m");
324
325	/* Try to handle urgent data inline */
326#ifdef SO_OOBINLINE
327	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
328		syslog(LOG_ERR, "setsockopt: %m");
329#endif
330
331#ifdef	F_SETOWN
332	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
333		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
334#endif
335	dolog(&his_addr);
336	/*
337	 * Set up default state
338	 */
339	data = -1;
340	type = TYPE_A;
341	form = FORM_N;
342	stru = STRU_F;
343	mode = MODE_S;
344	tmpline[0] = '\0';
345
346	/* If logins are disabled, print out the message. */
347	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
348		while (fgets(line, sizeof(line), fd) != NULL) {
349			if ((cp = strchr(line, '\n')) != NULL)
350				*cp = '\0';
351			lreply(530, "%s", line);
352		}
353		(void) fflush(stdout);
354		(void) fclose(fd);
355		reply(530, "System not available.");
356		exit(0);
357	}
358	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
359		while (fgets(line, sizeof(line), fd) != NULL) {
360			if ((cp = strchr(line, '\n')) != NULL)
361				*cp = '\0';
362			lreply(220, "%s", line);
363		}
364		(void) fflush(stdout);
365		(void) fclose(fd);
366		/* reply(220,) must follow */
367	}
368	(void) gethostname(hostname, sizeof(hostname));
369	reply(220, "%s FTP server (%s) ready.", hostname, version);
370	(void) setjmp(errcatch);
371	for (;;)
372		(void) yyparse();
373	/* NOTREACHED */
374}
375
376static void
377lostconn(signo)
378	int signo;
379{
380
381	if (debug)
382		syslog(LOG_DEBUG, "lost connection");
383	dologout(-1);
384}
385
386static char ttyline[20];
387
388/*
389 * Helper function for sgetpwnam().
390 */
391static char *
392sgetsave(s)
393	char *s;
394{
395	char *new = malloc((unsigned) strlen(s) + 1);
396
397	if (new == NULL) {
398		perror_reply(421, "Local resource failure: malloc");
399		dologout(1);
400		/* NOTREACHED */
401	}
402	(void) strcpy(new, s);
403	return (new);
404}
405
406/*
407 * Save the result of a getpwnam.  Used for USER command, since
408 * the data returned must not be clobbered by any other command
409 * (e.g., globbing).
410 */
411static struct passwd *
412sgetpwnam(name)
413	char *name;
414{
415	static struct passwd save;
416	struct passwd *p;
417
418	if ((p = getpwnam(name)) == NULL)
419		return (p);
420	if (save.pw_name) {
421		free(save.pw_name);
422		free(save.pw_passwd);
423		free(save.pw_gecos);
424		free(save.pw_dir);
425		free(save.pw_shell);
426	}
427	save = *p;
428	save.pw_name = sgetsave(p->pw_name);
429	save.pw_passwd = sgetsave(p->pw_passwd);
430	save.pw_gecos = sgetsave(p->pw_gecos);
431	save.pw_dir = sgetsave(p->pw_dir);
432	save.pw_shell = sgetsave(p->pw_shell);
433	return (&save);
434}
435
436static int login_attempts;	/* number of failed login attempts */
437static int askpasswd;		/* had user command, ask for passwd */
438static char curname[10];	/* current USER name */
439
440/*
441 * USER command.
442 * Sets global passwd pointer pw if named account exists and is acceptable;
443 * sets askpasswd if a PASS command is expected.  If logged in previously,
444 * need to reset state.  If name is "ftp" or "anonymous", the name is not in
445 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
446 * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
447 * requesting login privileges.  Disallow anyone who does not have a standard
448 * shell as returned by getusershell().  Disallow anyone mentioned in the file
449 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
450 */
451void
452user(name)
453	char *name;
454{
455	char *cp, *shell;
456
457	if (logged_in) {
458		if (guest) {
459			reply(530, "Can't change user from guest login.");
460			return;
461		}
462		end_login();
463	}
464
465	guest = 0;
466	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
467		if (checkuser("ftp") || checkuser("anonymous"))
468			reply(530, "User %s access denied.", name);
469		else if ((pw = sgetpwnam("ftp")) != NULL) {
470			guest = 1;
471			askpasswd = 1;
472			reply(331,
473			"Guest login ok, send your email address as password.");
474		} else
475			reply(530, "User %s unknown.", name);
476		if (!askpasswd && logging)
477			syslog(LOG_NOTICE,
478			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
479		return;
480	}
481	if (pw = sgetpwnam(name)) {
482		if ((shell = pw->pw_shell) == NULL || *shell == 0)
483			shell = _PATH_BSHELL;
484		while ((cp = getusershell()) != NULL)
485			if (strcmp(cp, shell) == 0)
486				break;
487		endusershell();
488
489		if (cp == NULL || checkuser(name)) {
490			reply(530, "User %s access denied.", name);
491			if (logging)
492				syslog(LOG_NOTICE,
493				    "FTP LOGIN REFUSED FROM %s, %s",
494				    remotehost, name);
495			pw = (struct passwd *) NULL;
496			return;
497		}
498	}
499	if (logging)
500		strncpy(curname, name, sizeof(curname)-1);
501#ifdef SKEY
502	pwok = skeyaccess(name, NULL, remotehost, addr_string);
503	reply(331, "%s", skey_challenge(name, pw, pwok));
504#else
505	reply(331, "Password required for %s.", name);
506#endif
507	askpasswd = 1;
508	/*
509	 * Delay before reading passwd after first failed
510	 * attempt to slow down passwd-guessing programs.
511	 */
512	if (login_attempts)
513		sleep((unsigned) login_attempts);
514}
515
516/*
517 * Check if a user is in the file _PATH_FTPUSERS
518 */
519static int
520checkuser(name)
521	char *name;
522{
523	FILE *fd;
524	int found = 0;
525	char *p, line[BUFSIZ];
526
527	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
528		while (fgets(line, sizeof(line), fd) != NULL)
529			if ((p = strchr(line, '\n')) != NULL) {
530				*p = '\0';
531				if (line[0] == '#')
532					continue;
533				if (strcmp(line, name) == 0) {
534					found = 1;
535					break;
536				}
537			}
538		(void) fclose(fd);
539	}
540	return (found);
541}
542
543/*
544 * Terminate login as previous user, if any, resetting state;
545 * used when USER command is given or login fails.
546 */
547static void
548end_login()
549{
550
551	(void) seteuid((uid_t)0);
552	if (logged_in)
553		logwtmp(ttyline, "", "");
554	pw = NULL;
555	logged_in = 0;
556	guest = 0;
557}
558
559void
560pass(passwd)
561	char *passwd;
562{
563	char *salt, *xpasswd;
564	FILE *fd;
565	static char homedir[MAXPATHLEN];
566
567	if (logged_in || askpasswd == 0) {
568		reply(503, "Login with USER first.");
569		return;
570	}
571	askpasswd = 0;
572	if (!guest) {		/* "ftp" is only account allowed no password */
573		if (pw == NULL)
574			salt = "xx";
575		else
576			salt = pw->pw_passwd;
577#ifdef SKEY
578		xpasswd = skey_crypt(passwd, salt, pw, pwok);
579		pwok = 0;
580#else
581		xpasswd = crypt(passwd, salt);
582#endif
583		/* The strcmp does not catch null passwords! */
584		if (pw == NULL || *pw->pw_passwd == '\0' ||
585		    (pw->pw_expire && time(NULL) >= pw->pw_expire) ||
586		    strcmp(xpasswd, pw->pw_passwd)) {
587			reply(530, "Login incorrect.");
588			if (logging)
589				syslog(LOG_NOTICE,
590				    "FTP LOGIN FAILED FROM %s, %s",
591				    remotehost, curname);
592			pw = NULL;
593			if (login_attempts++ >= 5) {
594				syslog(LOG_NOTICE,
595				    "repeated login failures from %s",
596				    remotehost);
597				exit(0);
598			}
599			return;
600		}
601	}
602	login_attempts = 0;		/* this time successful */
603	if (setegid((gid_t)pw->pw_gid) < 0) {
604		reply(550, "Can't set gid.");
605		return;
606	}
607	(void) initgroups(pw->pw_name, pw->pw_gid);
608
609	/* open wtmp before chroot */
610	(void)sprintf(ttyline, "ftp%d", getpid());
611	logwtmp(ttyline, pw->pw_name, remotehost);
612	logged_in = 1;
613
614#ifdef STATS
615	if (guest && stats == 1 && statfd < 0)
616		if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
617			stats = 0;
618#endif
619
620	if (guest) {
621		/*
622		 * We MUST do a chdir() after the chroot. Otherwise
623		 * the old current directory will be accessible as "."
624		 * outside the new root!
625		 */
626		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
627			reply(550, "Can't set guest privileges.");
628			goto bad;
629		}
630	} else if (chdir(pw->pw_dir) < 0) {
631		if (chdir("/") < 0) {
632			reply(530, "User %s: can't change directory to %s.",
633			    pw->pw_name, pw->pw_dir);
634			goto bad;
635		} else
636			lreply(230, "No directory! Logging in with home=/");
637	}
638	if (seteuid((uid_t)pw->pw_uid) < 0) {
639		reply(550, "Can't set uid.");
640		goto bad;
641	}
642
643	/*
644	 * Set home directory so that use of ~ (tilde) works correctly.
645	 */
646	if (getcwd(homedir, MAXPATHLEN) != NULL)
647		setenv("HOME", homedir, 1);
648
649	/*
650	 * Display a login message, if it exists.
651	 * N.B. reply(230,) must follow the message.
652	 */
653	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
654		char *cp, line[LINE_MAX];
655
656		while (fgets(line, sizeof(line), fd) != NULL) {
657			if ((cp = strchr(line, '\n')) != NULL)
658				*cp = '\0';
659			lreply(230, "%s", line);
660		}
661		(void) fflush(stdout);
662		(void) fclose(fd);
663	}
664	if (guest) {
665#ifdef STATS
666		char * copy();
667
668		if (ident != NULL)
669			free(ident);
670		ident = (char *) copy(passwd);
671#endif
672		reply(230, "Guest login ok, access restrictions apply.");
673#ifdef SETPROCTITLE
674		snprintf(proctitle, sizeof(proctitle),
675		    "%s: anonymous/%.*s", remotehost,
676		    sizeof(proctitle) - sizeof(remotehost) -
677		    sizeof(": anonymous/"), passwd);
678		setproctitle("%s", proctitle);
679#endif /* SETPROCTITLE */
680		if (logging)
681			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
682			    remotehost, passwd);
683	} else {
684		reply(230, "User %s logged in.", pw->pw_name);
685#ifdef SETPROCTITLE
686		snprintf(proctitle, sizeof(proctitle),
687		    "%s: %s", remotehost, pw->pw_name);
688		setproctitle("%s", proctitle);
689#endif /* SETPROCTITLE */
690		if (logging)
691			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
692			    remotehost, pw->pw_name);
693	}
694	(void) umask(defumask);
695	return;
696bad:
697	/* Forget all about it... */
698	end_login();
699}
700
701void
702retrieve(cmd, name)
703	char *cmd, *name;
704{
705	FILE *fin, *dout;
706	struct stat st;
707	int (*closefunc) __P((FILE *));
708#ifdef STATS
709	long start;
710#endif
711
712	if (cmd == 0) {
713		fin = fopen(name, "r"), closefunc = fclose;
714		st.st_size = 0;
715	} else {
716		char line[BUFSIZ];
717
718		(void) sprintf(line, cmd, name), name = line;
719		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
720		st.st_size = -1;
721		st.st_blksize = BUFSIZ;
722	}
723	if (fin == NULL) {
724		if (errno != 0) {
725			perror_reply(550, name);
726			if (cmd == 0) {
727				LOGCMD("get", name);
728			}
729		}
730		return;
731	}
732	byte_count = -1;
733	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
734		reply(550, "%s: not a plain file.", name);
735		goto done;
736	}
737	if (restart_point) {
738		if (type == TYPE_A) {
739			off_t i, n;
740			int c;
741
742			n = restart_point;
743			i = 0;
744			while (i++ < n) {
745				if ((c=getc(fin)) == EOF) {
746					perror_reply(550, name);
747					goto done;
748				}
749				if (c == '\n')
750					i++;
751			}
752		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
753			perror_reply(550, name);
754			goto done;
755		}
756	}
757	dout = dataconn(name, st.st_size, "w");
758	if (dout == NULL)
759		goto done;
760#ifdef STATS
761	time(&start);
762#endif
763	send_data(fin, dout, st.st_blksize, st.st_size,
764		  restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
765#ifdef STATS
766	if (cmd == 0 && guest && stats)
767		logxfer( name, st.st_size, start);
768#endif
769	(void) fclose(dout);
770	data = -1;
771	pdata = -1;
772done:
773	if (cmd == 0)
774		LOGBYTES("get", name, byte_count);
775	(*closefunc)(fin);
776}
777
778void
779store(name, mode, unique)
780	char *name, *mode;
781	int unique;
782{
783	FILE *fout, *din;
784	struct stat st;
785	int (*closefunc) __P((FILE *));
786
787	if (unique && stat(name, &st) == 0 &&
788	    (name = gunique(name)) == NULL) {
789		LOGCMD(*mode == 'w' ? "put" : "append", name);
790		return;
791	}
792
793	if (restart_point)
794		mode = "r+";
795	fout = fopen(name, mode);
796	closefunc = fclose;
797	if (fout == NULL) {
798		perror_reply(553, name);
799		LOGCMD(*mode == 'w' ? "put" : "append", name);
800		return;
801	}
802	byte_count = -1;
803	if (restart_point) {
804		if (type == TYPE_A) {
805			off_t i, n;
806			int c;
807
808			n = restart_point;
809			i = 0;
810			while (i++ < n) {
811				if ((c=getc(fout)) == EOF) {
812					perror_reply(550, name);
813					goto done;
814				}
815				if (c == '\n')
816					i++;
817			}
818			/*
819			 * We must do this seek to "current" position
820			 * because we are changing from reading to
821			 * writing.
822			 */
823			if (fseek(fout, 0L, L_INCR) < 0) {
824				perror_reply(550, name);
825				goto done;
826			}
827		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
828			perror_reply(550, name);
829			goto done;
830		}
831	}
832	din = dataconn(name, (off_t)-1, "r");
833	if (din == NULL)
834		goto done;
835	if (receive_data(din, fout) == 0) {
836		if (unique)
837			reply(226, "Transfer complete (unique file name:%s).",
838			    name);
839		else
840			reply(226, "Transfer complete.");
841	}
842	(void) fclose(din);
843	data = -1;
844	pdata = -1;
845done:
846	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
847	(*closefunc)(fout);
848}
849
850static FILE *
851getdatasock(mode)
852	char *mode;
853{
854	int on = 1, s, t, tries;
855
856	if (data >= 0)
857		return (fdopen(data, mode));
858	(void) seteuid((uid_t)0);
859	s = socket(AF_INET, SOCK_STREAM, 0);
860	if (s < 0)
861		goto bad;
862	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
863	    (char *) &on, sizeof(on)) < 0)
864		goto bad;
865	/* anchor socket to avoid multi-homing problems */
866	data_source.sin_family = AF_INET;
867	data_source.sin_addr = ctrl_addr.sin_addr;
868	for (tries = 1; ; tries++) {
869		if (bind(s, (struct sockaddr *)&data_source,
870		    sizeof(data_source)) >= 0)
871			break;
872		if (errno != EADDRINUSE || tries > 10)
873			goto bad;
874		sleep(tries);
875	}
876	(void) seteuid((uid_t)pw->pw_uid);
877#ifdef IP_TOS
878	on = IPTOS_THROUGHPUT;
879	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
880		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
881#endif
882#ifdef TCP_NOPUSH
883	/*
884	 * Turn off push flag to keep sender TCP from sending short packets
885	 * at the boundaries of each write().  Should probably do a SO_SNDBUF
886	 * to set the send buffer size as well, but that may not be desirable
887	 * in heavy-load situations.
888	 */
889	on = 1;
890	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
891		syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
892#endif
893#ifdef SO_SNDBUF
894	on = 65536;
895	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
896		syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
897#endif
898
899	return (fdopen(s, mode));
900bad:
901	/* Return the real value of errno (close may change it) */
902	t = errno;
903	(void) seteuid((uid_t)pw->pw_uid);
904	(void) close(s);
905	errno = t;
906	return (NULL);
907}
908
909static FILE *
910dataconn(name, size, mode)
911	char *name;
912	off_t size;
913	char *mode;
914{
915	char sizebuf[32];
916	FILE *file;
917	int retry = 0, tos;
918
919	file_size = size;
920	byte_count = 0;
921	if (size != (off_t) -1)
922		(void) sprintf(sizebuf, " (%qd bytes)", size);
923	else
924		(void) strcpy(sizebuf, "");
925	if (pdata >= 0) {
926		struct sockaddr_in from;
927		int s, fromlen = sizeof(from);
928		struct timeval timeout;
929		fd_set set;
930
931		FD_ZERO(&set);
932		FD_SET(pdata, &set);
933
934		timeout.tv_usec = 0;
935		timeout.tv_sec = 120;
936
937		if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 ||
938		    (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) {
939			reply(425, "Can't open data connection.");
940			(void) close(pdata);
941			pdata = -1;
942			return (NULL);
943		}
944		(void) close(pdata);
945		pdata = s;
946#ifdef IP_TOS
947		tos = IPTOS_LOWDELAY;
948		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
949		    sizeof(int));
950#endif
951		reply(150, "Opening %s mode data connection for '%s'%s.",
952		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
953		return (fdopen(pdata, mode));
954	}
955	if (data >= 0) {
956		reply(125, "Using existing data connection for '%s'%s.",
957		    name, sizebuf);
958		usedefault = 1;
959		return (fdopen(data, mode));
960	}
961	if (usedefault)
962		data_dest = his_addr;
963	usedefault = 1;
964	file = getdatasock(mode);
965	if (file == NULL) {
966		reply(425, "Can't create data socket (%s,%d): %s.",
967		    inet_ntoa(data_source.sin_addr),
968		    ntohs(data_source.sin_port), strerror(errno));
969		return (NULL);
970	}
971	data = fileno(file);
972	while (connect(data, (struct sockaddr *)&data_dest,
973	    sizeof(data_dest)) < 0) {
974		if (errno == EADDRINUSE && retry < swaitmax) {
975			sleep((unsigned) swaitint);
976			retry += swaitint;
977			continue;
978		}
979		perror_reply(425, "Can't build data connection");
980		(void) fclose(file);
981		data = -1;
982		return (NULL);
983	}
984	reply(150, "Opening %s mode data connection for '%s'%s.",
985	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
986	return (file);
987}
988
989/*
990 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
991 * encapsulation of the data subject to Mode, Structure, and Type.
992 *
993 * NB: Form isn't handled.
994 */
995static void
996send_data(instr, outstr, blksize, filesize, isreg)
997	FILE *instr, *outstr;
998	off_t blksize;
999	off_t filesize;
1000	int isreg;
1001{
1002	int c, cnt, filefd, netfd;
1003	char *buf, *bp;
1004	size_t len;
1005
1006	transflag++;
1007	if (setjmp(urgcatch)) {
1008		transflag = 0;
1009		return;
1010	}
1011	switch (type) {
1012
1013	case TYPE_A:
1014		while ((c = getc(instr)) != EOF) {
1015			byte_count++;
1016			if (c == '\n') {
1017				if (ferror(outstr))
1018					goto data_err;
1019				(void) putc('\r', outstr);
1020			}
1021			(void) putc(c, outstr);
1022		}
1023		fflush(outstr);
1024		transflag = 0;
1025		if (ferror(instr))
1026			goto file_err;
1027		if (ferror(outstr))
1028			goto data_err;
1029		reply(226, "Transfer complete.");
1030		return;
1031
1032	case TYPE_I:
1033	case TYPE_L:
1034		/*
1035		 * isreg is only set if we are not doing restart and we
1036		 * are sending a regular file
1037		 */
1038		netfd = fileno(outstr);
1039		filefd = fileno(instr);
1040
1041		if (isreg && filesize < (off_t)16 * 1024 * 1024) {
1042			buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd,
1043				   (off_t)0);
1044			if (!buf) {
1045				syslog(LOG_WARNING, "mmap(%lu): %m",
1046				       (unsigned long)filesize);
1047				goto oldway;
1048			}
1049			bp = buf;
1050			len = filesize;
1051			do {
1052				cnt = write(netfd, bp, len);
1053				len -= cnt;
1054				bp += cnt;
1055				if (cnt > 0) byte_count += cnt;
1056			} while(cnt > 0 && len > 0);
1057
1058			transflag = 0;
1059			munmap(buf, (size_t)filesize);
1060			if (cnt < 0)
1061				goto data_err;
1062			reply(226, "Transfer complete.");
1063			return;
1064		}
1065
1066oldway:
1067		if ((buf = malloc((u_int)blksize)) == NULL) {
1068			transflag = 0;
1069			perror_reply(451, "Local resource failure: malloc");
1070			return;
1071		}
1072
1073		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1074		    write(netfd, buf, cnt) == cnt)
1075			byte_count += cnt;
1076		transflag = 0;
1077		(void)free(buf);
1078		if (cnt != 0) {
1079			if (cnt < 0)
1080				goto file_err;
1081			goto data_err;
1082		}
1083		reply(226, "Transfer complete.");
1084		return;
1085	default:
1086		transflag = 0;
1087		reply(550, "Unimplemented TYPE %d in send_data", type);
1088		return;
1089	}
1090
1091data_err:
1092	transflag = 0;
1093	perror_reply(426, "Data connection");
1094	return;
1095
1096file_err:
1097	transflag = 0;
1098	perror_reply(551, "Error on input file");
1099}
1100
1101/*
1102 * Transfer data from peer to "outstr" using the appropriate encapulation of
1103 * the data subject to Mode, Structure, and Type.
1104 *
1105 * N.B.: Form isn't handled.
1106 */
1107static int
1108receive_data(instr, outstr)
1109	FILE *instr, *outstr;
1110{
1111	int c;
1112	int cnt, bare_lfs = 0;
1113	char buf[BUFSIZ];
1114
1115	transflag++;
1116	if (setjmp(urgcatch)) {
1117		transflag = 0;
1118		return (-1);
1119	}
1120	switch (type) {
1121
1122	case TYPE_I:
1123	case TYPE_L:
1124		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1125			if (write(fileno(outstr), buf, cnt) != cnt)
1126				goto file_err;
1127			byte_count += cnt;
1128		}
1129		if (cnt < 0)
1130			goto data_err;
1131		transflag = 0;
1132		return (0);
1133
1134	case TYPE_E:
1135		reply(553, "TYPE E not implemented.");
1136		transflag = 0;
1137		return (-1);
1138
1139	case TYPE_A:
1140		while ((c = getc(instr)) != EOF) {
1141			byte_count++;
1142			if (c == '\n')
1143				bare_lfs++;
1144			while (c == '\r') {
1145				if (ferror(outstr))
1146					goto data_err;
1147				if ((c = getc(instr)) != '\n') {
1148					(void) putc ('\r', outstr);
1149					if (c == '\0' || c == EOF)
1150						goto contin2;
1151				}
1152			}
1153			(void) putc(c, outstr);
1154	contin2:	;
1155		}
1156		fflush(outstr);
1157		if (ferror(instr))
1158			goto data_err;
1159		if (ferror(outstr))
1160			goto file_err;
1161		transflag = 0;
1162		if (bare_lfs) {
1163			lreply(226,
1164		"WARNING! %d bare linefeeds received in ASCII mode",
1165			    bare_lfs);
1166		(void)printf("   File may not have transferred correctly.\r\n");
1167		}
1168		return (0);
1169	default:
1170		reply(550, "Unimplemented TYPE %d in receive_data", type);
1171		transflag = 0;
1172		return (-1);
1173	}
1174
1175data_err:
1176	transflag = 0;
1177	perror_reply(426, "Data Connection");
1178	return (-1);
1179
1180file_err:
1181	transflag = 0;
1182	perror_reply(452, "Error writing file");
1183	return (-1);
1184}
1185
1186void
1187statfilecmd(filename)
1188	char *filename;
1189{
1190	FILE *fin;
1191	int c;
1192	char line[LINE_MAX];
1193
1194	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1195	fin = ftpd_popen(line, "r");
1196	lreply(211, "status of %s:", filename);
1197	while ((c = getc(fin)) != EOF) {
1198		if (c == '\n') {
1199			if (ferror(stdout)){
1200				perror_reply(421, "control connection");
1201				(void) ftpd_pclose(fin);
1202				dologout(1);
1203				/* NOTREACHED */
1204			}
1205			if (ferror(fin)) {
1206				perror_reply(551, filename);
1207				(void) ftpd_pclose(fin);
1208				return;
1209			}
1210			(void) putc('\r', stdout);
1211		}
1212		(void) putc(c, stdout);
1213	}
1214	(void) ftpd_pclose(fin);
1215	reply(211, "End of Status");
1216}
1217
1218void
1219statcmd()
1220{
1221	struct sockaddr_in *sin;
1222	u_char *a, *p;
1223
1224	lreply(211, "%s FTP server status:", hostname, version);
1225	printf("     %s\r\n", version);
1226	printf("     Connected to %s", remotehost);
1227	if (!isdigit(remotehost[0]))
1228		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1229	printf("\r\n");
1230	if (logged_in) {
1231		if (guest)
1232			printf("     Logged in anonymously\r\n");
1233		else
1234			printf("     Logged in as %s\r\n", pw->pw_name);
1235	} else if (askpasswd)
1236		printf("     Waiting for password\r\n");
1237	else
1238		printf("     Waiting for user name\r\n");
1239	printf("     TYPE: %s", typenames[type]);
1240	if (type == TYPE_A || type == TYPE_E)
1241		printf(", FORM: %s", formnames[form]);
1242	if (type == TYPE_L)
1243#if NBBY == 8
1244		printf(" %d", NBBY);
1245#else
1246		printf(" %d", bytesize);	/* need definition! */
1247#endif
1248	printf("; STRUcture: %s; transfer MODE: %s\r\n",
1249	    strunames[stru], modenames[mode]);
1250	if (data != -1)
1251		printf("     Data connection open\r\n");
1252	else if (pdata != -1) {
1253		printf("     in Passive mode");
1254		sin = &pasv_addr;
1255		goto printaddr;
1256	} else if (usedefault == 0) {
1257		printf("     PORT");
1258		sin = &data_dest;
1259printaddr:
1260		a = (u_char *) &sin->sin_addr;
1261		p = (u_char *) &sin->sin_port;
1262#define UC(b) (((int) b) & 0xff)
1263		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1264			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1265#undef UC
1266	} else
1267		printf("     No data connection\r\n");
1268	reply(211, "End of status");
1269}
1270
1271void
1272fatal(s)
1273	char *s;
1274{
1275
1276	reply(451, "Error in server: %s\n", s);
1277	reply(221, "Closing connection due to server error.");
1278	dologout(0);
1279	/* NOTREACHED */
1280}
1281
1282void
1283#if __STDC__
1284reply(int n, const char *fmt, ...)
1285#else
1286reply(n, fmt, va_alist)
1287	int n;
1288	char *fmt;
1289        va_dcl
1290#endif
1291{
1292	va_list ap;
1293#if __STDC__
1294	va_start(ap, fmt);
1295#else
1296	va_start(ap);
1297#endif
1298	(void)printf("%d ", n);
1299	(void)vprintf(fmt, ap);
1300	(void)printf("\r\n");
1301	(void)fflush(stdout);
1302	if (debug) {
1303		syslog(LOG_DEBUG, "<--- %d ", n);
1304		vsyslog(LOG_DEBUG, fmt, ap);
1305	}
1306}
1307
1308void
1309#if __STDC__
1310lreply(int n, const char *fmt, ...)
1311#else
1312lreply(n, fmt, va_alist)
1313	int n;
1314	char *fmt;
1315        va_dcl
1316#endif
1317{
1318	va_list ap;
1319#if __STDC__
1320	va_start(ap, fmt);
1321#else
1322	va_start(ap);
1323#endif
1324	(void)printf("%d- ", n);
1325	(void)vprintf(fmt, ap);
1326	(void)printf("\r\n");
1327	(void)fflush(stdout);
1328	if (debug) {
1329		syslog(LOG_DEBUG, "<--- %d- ", n);
1330		vsyslog(LOG_DEBUG, fmt, ap);
1331	}
1332}
1333
1334static void
1335ack(s)
1336	char *s;
1337{
1338
1339	reply(250, "%s command successful.", s);
1340}
1341
1342void
1343nack(s)
1344	char *s;
1345{
1346
1347	reply(502, "%s command not implemented.", s);
1348}
1349
1350/* ARGSUSED */
1351void
1352yyerror(s)
1353	char *s;
1354{
1355	char *cp;
1356
1357	if (cp = strchr(cbuf,'\n'))
1358		*cp = '\0';
1359	reply(500, "'%s': command not understood.", cbuf);
1360}
1361
1362void
1363delete(name)
1364	char *name;
1365{
1366	struct stat st;
1367
1368	LOGCMD("delete", name);
1369	if (stat(name, &st) < 0) {
1370		perror_reply(550, name);
1371		return;
1372	}
1373	if ((st.st_mode&S_IFMT) == S_IFDIR) {
1374		if (rmdir(name) < 0) {
1375			perror_reply(550, name);
1376			return;
1377		}
1378		goto done;
1379	}
1380	if (unlink(name) < 0) {
1381		perror_reply(550, name);
1382		return;
1383	}
1384done:
1385	ack("DELE");
1386}
1387
1388void
1389cwd(path)
1390	char *path;
1391{
1392
1393	if (chdir(path) < 0)
1394		perror_reply(550, path);
1395	else
1396		ack("CWD");
1397}
1398
1399void
1400makedir(name)
1401	char *name;
1402{
1403
1404	LOGCMD("mkdir", name);
1405	if (mkdir(name, 0777) < 0)
1406		perror_reply(550, name);
1407	else
1408		reply(257, "MKD command successful.");
1409}
1410
1411void
1412removedir(name)
1413	char *name;
1414{
1415
1416	LOGCMD("rmdir", name);
1417	if (rmdir(name) < 0)
1418		perror_reply(550, name);
1419	else
1420		ack("RMD");
1421}
1422
1423void
1424pwd()
1425{
1426	char path[MAXPATHLEN + 1];
1427
1428	if (getwd(path) == (char *)NULL)
1429		reply(550, "%s.", path);
1430	else
1431		reply(257, "\"%s\" is current directory.", path);
1432}
1433
1434char *
1435renamefrom(name)
1436	char *name;
1437{
1438	struct stat st;
1439
1440	if (stat(name, &st) < 0) {
1441		perror_reply(550, name);
1442		return ((char *)0);
1443	}
1444	reply(350, "File exists, ready for destination name");
1445	return (name);
1446}
1447
1448void
1449renamecmd(from, to)
1450	char *from, *to;
1451{
1452
1453	LOGCMD2("rename", from, to);
1454	if (rename(from, to) < 0)
1455		perror_reply(550, "rename");
1456	else
1457		ack("RNTO");
1458}
1459
1460static void
1461dolog(sin)
1462	struct sockaddr_in *sin;
1463{
1464	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1465		sizeof(struct in_addr), AF_INET);
1466
1467	if (hp)
1468		(void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
1469	else
1470		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1471		    sizeof(remotehost));
1472#ifdef SETPROCTITLE
1473	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1474	setproctitle("%s", proctitle);
1475#endif /* SETPROCTITLE */
1476
1477	if (logging)
1478		syslog(LOG_INFO, "connection from %s", remotehost);
1479}
1480
1481/*
1482 * Record logout in wtmp file
1483 * and exit with supplied status.
1484 */
1485void
1486dologout(status)
1487	int status;
1488{
1489
1490	if (logged_in) {
1491		(void) seteuid((uid_t)0);
1492		logwtmp(ttyline, "", "");
1493	}
1494	/* beware of flushing buffers after a SIGPIPE */
1495	_exit(status);
1496}
1497
1498static void
1499myoob(signo)
1500	int signo;
1501{
1502	char *cp;
1503
1504	/* only process if transfer occurring */
1505	if (!transflag)
1506		return;
1507	cp = tmpline;
1508	if (getline(cp, 7, stdin) == NULL) {
1509		reply(221, "You could at least say goodbye.");
1510		dologout(0);
1511	}
1512	upper(cp);
1513	if (strcmp(cp, "ABOR\r\n") == 0) {
1514		tmpline[0] = '\0';
1515		reply(426, "Transfer aborted. Data connection closed.");
1516		reply(226, "Abort successful");
1517		longjmp(urgcatch, 1);
1518	}
1519	if (strcmp(cp, "STAT\r\n") == 0) {
1520		if (file_size != (off_t) -1)
1521			reply(213, "Status: %qd of %qd bytes transferred",
1522			    byte_count, file_size);
1523		else
1524			reply(213, "Status: %qd bytes transferred", byte_count);
1525	}
1526}
1527
1528/*
1529 * Note: a response of 425 is not mentioned as a possible response to
1530 *	the PASV command in RFC959. However, it has been blessed as
1531 *	a legitimate response by Jon Postel in a telephone conversation
1532 *	with Rick Adams on 25 Jan 89.
1533 */
1534void
1535passive()
1536{
1537	int len;
1538	u_short port;
1539	char *p, *a;
1540
1541	pdata = socket(AF_INET, SOCK_STREAM, 0);
1542	if (pdata < 0) {
1543		perror_reply(425, "Can't open passive connection");
1544		return;
1545	}
1546
1547	if (restricted_data_ports) {
1548		for (port = FTP_DATA_BOTTOM; port <= FTP_DATA_TOP; port++) {
1549			pasv_addr = ctrl_addr;
1550			pasv_addr.sin_port = htons(port);
1551			(void) seteuid((uid_t)0);
1552			if (bind(pdata, (struct sockaddr *)&pasv_addr,
1553				 sizeof(pasv_addr)) < 0) {
1554				(void) seteuid((uid_t)pw->pw_uid);
1555				if (errno == EADDRINUSE)
1556					continue;
1557				else
1558					goto pasv_error;
1559			}
1560			(void) seteuid((uid_t)pw->pw_uid);
1561			break;
1562		}
1563		if (port > FTP_DATA_TOP)
1564			goto pasv_error;
1565	} else {
1566		pasv_addr = ctrl_addr;
1567		pasv_addr.sin_port = 0;
1568		(void) seteuid((uid_t)0);
1569		if (bind(pdata, (struct sockaddr *)&pasv_addr,
1570			 sizeof(pasv_addr)) < 0) {
1571			(void) seteuid((uid_t)pw->pw_uid);
1572			goto pasv_error;
1573		}
1574		(void) seteuid((uid_t)pw->pw_uid);
1575	}
1576
1577	len = sizeof(pasv_addr);
1578	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1579		goto pasv_error;
1580	if (listen(pdata, 1) < 0)
1581		goto pasv_error;
1582	a = (char *) &pasv_addr.sin_addr;
1583	p = (char *) &pasv_addr.sin_port;
1584
1585#define UC(b) (((int) b) & 0xff)
1586
1587	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1588		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1589	return;
1590
1591pasv_error:
1592	(void) close(pdata);
1593	pdata = -1;
1594	perror_reply(425, "Can't open passive connection");
1595	return;
1596}
1597
1598/*
1599 * Generate unique name for file with basename "local".
1600 * The file named "local" is already known to exist.
1601 * Generates failure reply on error.
1602 */
1603static char *
1604gunique(local)
1605	char *local;
1606{
1607	static char new[MAXPATHLEN];
1608	struct stat st;
1609	int count;
1610	char *cp;
1611
1612	cp = strrchr(local, '/');
1613	if (cp)
1614		*cp = '\0';
1615	if (stat(cp ? local : ".", &st) < 0) {
1616		perror_reply(553, cp ? local : ".");
1617		return ((char *) 0);
1618	}
1619	if (cp)
1620		*cp = '/';
1621	(void) strcpy(new, local);
1622	cp = new + strlen(new);
1623	*cp++ = '.';
1624	for (count = 1; count < 100; count++) {
1625		(void)sprintf(cp, "%d", count);
1626		if (stat(new, &st) < 0)
1627			return (new);
1628	}
1629	reply(452, "Unique file name cannot be created.");
1630	return (NULL);
1631}
1632
1633/*
1634 * Format and send reply containing system error number.
1635 */
1636void
1637perror_reply(code, string)
1638	int code;
1639	char *string;
1640{
1641
1642	reply(code, "%s: %s.", string, strerror(errno));
1643}
1644
1645static char *onefile[] = {
1646	"",
1647	0
1648};
1649
1650void
1651send_file_list(whichf)
1652	char *whichf;
1653{
1654	struct stat st;
1655	DIR *dirp = NULL;
1656	struct dirent *dir;
1657	FILE *dout = NULL;
1658	char **dirlist, *dirname;
1659	int simple = 0;
1660	int freeglob = 0;
1661	glob_t gl;
1662
1663	if (strpbrk(whichf, "~{[*?") != NULL) {
1664		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1665
1666		memset(&gl, 0, sizeof(gl));
1667		freeglob = 1;
1668		if (glob(whichf, flags, 0, &gl)) {
1669			reply(550, "not found");
1670			goto out;
1671		} else if (gl.gl_pathc == 0) {
1672			errno = ENOENT;
1673			perror_reply(550, whichf);
1674			goto out;
1675		}
1676		dirlist = gl.gl_pathv;
1677	} else {
1678		onefile[0] = whichf;
1679		dirlist = onefile;
1680		simple = 1;
1681	}
1682
1683	if (setjmp(urgcatch)) {
1684		transflag = 0;
1685		goto out;
1686	}
1687	while (dirname = *dirlist++) {
1688		if (stat(dirname, &st) < 0) {
1689			/*
1690			 * If user typed "ls -l", etc, and the client
1691			 * used NLST, do what the user meant.
1692			 */
1693			if (dirname[0] == '-' && *dirlist == NULL &&
1694			    transflag == 0) {
1695				retrieve("/bin/ls %s", dirname);
1696				goto out;
1697			}
1698			perror_reply(550, whichf);
1699			if (dout != NULL) {
1700				(void) fclose(dout);
1701				transflag = 0;
1702				data = -1;
1703				pdata = -1;
1704			}
1705			goto out;
1706		}
1707
1708		if (S_ISREG(st.st_mode)) {
1709			if (dout == NULL) {
1710				dout = dataconn("file list", (off_t)-1, "w");
1711				if (dout == NULL)
1712					goto out;
1713				transflag++;
1714			}
1715			fprintf(dout, "%s%s\n", dirname,
1716				type == TYPE_A ? "\r" : "");
1717			byte_count += strlen(dirname) + 1;
1718			continue;
1719		} else if (!S_ISDIR(st.st_mode))
1720			continue;
1721
1722		if ((dirp = opendir(dirname)) == NULL)
1723			continue;
1724
1725		while ((dir = readdir(dirp)) != NULL) {
1726			char nbuf[MAXPATHLEN];
1727
1728			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1729				continue;
1730			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1731			    dir->d_namlen == 2)
1732				continue;
1733
1734			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1735
1736			/*
1737			 * We have to do a stat to insure it's
1738			 * not a directory or special file.
1739			 */
1740			if (simple || (stat(nbuf, &st) == 0 &&
1741			    S_ISREG(st.st_mode))) {
1742				if (dout == NULL) {
1743					dout = dataconn("file list", (off_t)-1,
1744						"w");
1745					if (dout == NULL)
1746						goto out;
1747					transflag++;
1748				}
1749				if (nbuf[0] == '.' && nbuf[1] == '/')
1750					fprintf(dout, "%s%s\n", &nbuf[2],
1751						type == TYPE_A ? "\r" : "");
1752				else
1753					fprintf(dout, "%s%s\n", nbuf,
1754						type == TYPE_A ? "\r" : "");
1755				byte_count += strlen(nbuf) + 1;
1756			}
1757		}
1758		(void) closedir(dirp);
1759	}
1760
1761	if (dout == NULL)
1762		reply(550, "No files found.");
1763	else if (ferror(dout) != 0)
1764		perror_reply(550, "Data connection");
1765	else
1766		reply(226, "Transfer complete.");
1767
1768	transflag = 0;
1769	if (dout != NULL)
1770		(void) fclose(dout);
1771	data = -1;
1772	pdata = -1;
1773out:
1774	if (freeglob) {
1775		freeglob = 0;
1776		globfree(&gl);
1777	}
1778}
1779
1780#ifdef OLD_SETPROCTITLE
1781/*
1782 * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
1783 * Warning, since this is usually started from inetd.conf, it often doesn't
1784 * have much of an environment or arglist to overwrite.
1785 */
1786void
1787#if __STDC__
1788setproctitle(const char *fmt, ...)
1789#else
1790setproctitle(fmt, va_alist)
1791	char *fmt;
1792        va_dcl
1793#endif
1794{
1795	int i;
1796	va_list ap;
1797	char *p, *bp, ch;
1798	char buf[LINE_MAX];
1799
1800#if __STDC__
1801	va_start(ap, fmt);
1802#else
1803	va_start(ap);
1804#endif
1805	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
1806
1807	/* make ps print our process name */
1808	p = Argv[0];
1809	*p++ = '-';
1810
1811	i = strlen(buf);
1812	if (i > LastArgv - p - 2) {
1813		i = LastArgv - p - 2;
1814		buf[i] = '\0';
1815	}
1816	bp = buf;
1817	while (ch = *bp++)
1818		if (ch != '\n' && ch != '\r')
1819			*p++ = ch;
1820	while (p < LastArgv)
1821		*p++ = ' ';
1822}
1823#endif /* OLD_SETPROCTITLE */
1824
1825#ifdef STATS
1826logxfer(name, size, start)
1827	char *name;
1828	long size;
1829	long start;
1830{
1831	char buf[1024];
1832	char path[MAXPATHLEN + 1];
1833	long now;
1834
1835	if (statfd >= 0 && getwd(path) != NULL) {
1836		time(&now);
1837		sprintf(buf, "%.20s!%s!%s!%s/%s!%ld!%ld\n",
1838			ctime(&now)+4, ident, remotehost,
1839			path, name, size, now - start + (now == start));
1840		write(statfd, buf, strlen(buf));
1841	}
1842}
1843
1844char *
1845copy(s)
1846	char *s;
1847{
1848	char *p;
1849
1850	p = malloc((unsigned) strlen(s) + 1);
1851	if (p == NULL)
1852		fatal("Ran out of memory.");
1853	(void) strcpy(p, s);
1854	return (p);
1855}
1856#endif
1857