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