ftpd.c revision 1.25
1132751Skan/*	$OpenBSD: ftpd.c,v 1.25 1996/12/03 03:07:17 deraadt Exp $	*/
2132751Skan/*	$NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $	*/
3169718Skan
445299Sobrien/*
5132751Skan * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
6169718Skan *	The Regents of the University of California.  All rights reserved.
7132751Skan *
8169718Skan * Redistribution and use in source and binary forms, with or without
952914Sobrien * modification, are permitted provided that the following conditions
10169718Skan * are met:
11132751Skan * 1. Redistributions of source code must retain the above copyright
12169718Skan *    notice, this list of conditions and the following disclaimer.
13132751Skan * 2. Redistributions in binary form must reproduce the above copyright
14169718Skan *    notice, this list of conditions and the following disclaimer in the
1552914Sobrien *    documentation and/or other materials provided with the distribution.
16169718Skan * 3. All advertising materials mentioning features or use of this software
17132751Skan *    must display the following acknowledgement:
18169718Skan *	This product includes software developed by the University of
19132751Skan *	California, Berkeley and its contributors.
20169718Skan * 4. Neither the name of the University nor the names of its contributors
2196340Sobrien *    may be used to endorse or promote products derived from this software
22169718Skan *    without specific prior written permission.
23132751Skan *
24169718Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25132751Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26169718Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2796340Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28169718Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29132751Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30132751Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31132751Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32132751Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33169718Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34132751Skan * SUCH DAMAGE.
35169718Skan */
3652914Sobrien
3752914Sobrien#ifndef lint
38169718Skanstatic char copyright[] =
39169718Skan"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
40169718Skan	The Regents of the University of California.  All rights reserved.\n";
41169718Skan#endif /* not lint */
4245299Sobrien
43169718Skan#ifndef lint
44132751Skan#if 0
45132751Skanstatic char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
46169718Skan#else
47169718Skanstatic char rcsid[] = "$NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $";
48169718Skan#endif
4952914Sobrien#endif /* not lint */
50169718Skan
51169718Skan/*
52169718Skan * FTP server.
53169718Skan */
54169718Skan#include <sys/param.h>
55169718Skan#include <sys/stat.h>
56169718Skan#include <sys/ioctl.h>
57132751Skan#include <sys/socket.h>
58132751Skan#include <sys/wait.h>
59169718Skan#include <sys/mman.h>
60169718Skan
61169718Skan#include <netinet/in.h>
6252914Sobrien#include <netinet/in_systm.h>
63169718Skan#include <netinet/ip.h>
64132751Skan#include <netinet/tcp.h>
65132751Skan
66132751Skan#define	FTP_NAMES
67169718Skan#include <arpa/ftp.h>
68169718Skan#include <arpa/inet.h>
69169718Skan#include <arpa/telnet.h>
7052914Sobrien
71169718Skan#include <ctype.h>
72132751Skan#include <dirent.h>
73132751Skan#include <err.h>
74169718Skan#include <errno.h>
75169718Skan#include <fcntl.h>
76169718Skan#include <glob.h>
7796340Sobrien#include <limits.h>
78169718Skan#include <netdb.h>
79169718Skan#include <pwd.h>
80169718Skan#include <setjmp.h>
81169718Skan#include <signal.h>
82169718Skan#include <stdio.h>
83169718Skan#include <stdlib.h>
84169718Skan#include <string.h>
85169718Skan#include <syslog.h>
86132751Skan#include <time.h>
87132751Skan#include <vis.h>
88132751Skan#include <unistd.h>
89169718Skan#include <utmp.h>
90169718Skan
91169718Skan#include "pathnames.h"
9252914Sobrien#include "extern.h"
93169718Skan
94132751Skan#if __STDC__
95132751Skan#include <stdarg.h>
96169718Skan#else
97169718Skan#include <varargs.h>
98169718Skan#endif
9952914Sobrien
100169718Skanstatic char version[] = "Version 6.1/OpenBSD";
101169718Skan
102169718Skanextern	off_t restart_point;
103169718Skanextern	char cbuf[];
104132751Skan
105132751Skanstruct	sockaddr_in server_addr;
106169718Skanstruct	sockaddr_in ctrl_addr;
107169718Skanstruct	sockaddr_in data_source;
108169718Skanstruct	sockaddr_in data_dest;
109169718Skanstruct	sockaddr_in his_addr;
110169718Skanstruct	sockaddr_in pasv_addr;
11152914Sobrien
112169718Skanint	daemon_mode = 0;
113132751Skanint	data;
114132751Skanjmp_buf	errcatch, urgcatch;
115169718Skanint	logged_in;
116132751Skanstruct	passwd *pw;
117169718Skanint	debug = 0;
11852914Sobrienint	timeout = 900;    /* timeout after 15 minutes of inactivity */
11945299Sobrienint	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
120169718Skanint	logging;
121169718Skanint	high_data_ports = 0;
122169718Skanint	anon_only = 0;
123132751Skanint	multihome = 0;
124169718Skanint	guest;
12545299Sobrienint	stats;
126169718Skanint	statfd = -1;
127169718Skanint	dochroot;
128169718Skanint	type;
129169718Skanint	form;
130169718Skanint	stru;			/* avoid C keyword */
131169718Skanint	mode;
132169718Skanint	doutmp = 0;		/* update utmp file */
133169718Skanint	usedefault = 1;		/* for data transfers */
134132751Skanint	pdata = -1;		/* for passive mode */
135169718Skansig_atomic_t transflag;
136132751Skanoff_t	file_size;
137169718Skanoff_t	byte_count;
13896340Sobrien#if !defined(CMASK) || CMASK == 0
139169718Skan#undef CMASK
140132751Skan#define CMASK 027
141132751Skan#endif
142169718Skanint	defumask = CMASK;		/* default umask value */
143132751Skanchar	tmpline[7];
144169718Skanchar	hostname[MAXHOSTNAMELEN];
14552914Sobrienchar	remotehost[MAXHOSTNAMELEN];
146169718Skanchar	dhostname[MAXHOSTNAMELEN];
147169718Skanchar	*guestpw;
148169718Skanstatic char ttyline[20];
149169718Skanchar	*tty = ttyline;		/* for klogin */
150169718Skanstatic struct utmp utmp;	/* for utmp */
151169718Skan
152169718Skan#if defined(KERBEROS)
153169718Skanint	notickets = 1;
154169718Skanchar	*krbtkfile_env = NULL;
155169718Skan#endif
156169718Skan
157169718Skanchar	*ident = NULL;
158169718Skan
159132751Skan
160169718Skan/*
161132751Skan * Timeout intervals for retrying connections
162169718Skan * to hosts that don't accept PORT cmds.  This
16396340Sobrien * is a kludge, but given the problems with TCP...
164169718Skan */
165132751Skan#define	SWAITMAX	90	/* wait at most 90 seconds */
166132751Skan#define	SWAITINT	5	/* interval between retries */
167169718Skan
168132751Skanint	swaitmax = SWAITMAX;
169169718Skanint	swaitint = SWAITINT;
17096340Sobrien
171169718Skan#ifdef HASSETPROCTITLE
172132751Skanchar	proctitle[BUFSIZ];	/* initial part of title */
173169718Skan#endif /* HASSETPROCTITLE */
174132751Skan
175169718Skan#define LOGCMD(cmd, file) \
17696340Sobrien	if (logging > 1) \
177169718Skan	    syslog(LOG_INFO,"%s %s%s", cmd, \
178169718Skan		*(file) == '/' ? "" : curdir(), file);
179169718Skan#define LOGCMD2(cmd, file1, file2) \
180169718Skan	 if (logging > 1) \
181169718Skan	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
182169718Skan		*(file1) == '/' ? "" : curdir(), file1, \
183169718Skan		*(file2) == '/' ? "" : curdir(), file2);
184132751Skan#define LOGBYTES(cmd, file, cnt) \
185169718Skan	if (logging > 1) { \
186132751Skan		if (cnt == (off_t)-1) \
187169718Skan		    syslog(LOG_INFO,"%s %s%s", cmd, \
18896340Sobrien			*(file) == '/' ? "" : curdir(), file); \
189169718Skan		else \
190132751Skan		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
191169718Skan			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
192132751Skan	}
193169718Skan
19496340Sobrienstatic void	 ack __P((char *));
195169718Skanstatic void	 myoob __P((int));
196132751Skanstatic int	 checkuser __P((char *, char *));
197169718Skanstatic FILE	*dataconn __P((char *, off_t, char *));
198132751Skanstatic void	 dolog __P((struct sockaddr_in *));
199169718Skanstatic char	*curdir __P((void));
20052914Sobrienstatic void	 end_login __P((void));
201169718Skanstatic FILE	*getdatasock __P((char *));
202132751Skanstatic char	*gunique __P((char *));
203169718Skanstatic void	 lostconn __P((int));
204132751Skanstatic void	 sigquit __P((int));
205169718Skanstatic int	 receive_data __P((FILE *, FILE *));
20652914Sobrienstatic void	 send_data __P((FILE *, FILE *, off_t, off_t, int));
207169718Skanstatic struct passwd *
208169718Skan		 sgetpwnam __P((char *));
209169718Skanstatic char	*sgetsave __P((char *));
210169718Skanstatic void	 reapchild __P((int));
211169718Skan
212169718Skanvoid	 logxfer __P((char *, off_t, time_t));
213169718Skan
214169718Skanstatic char *
215169718Skancurdir()
216169718Skan{
217169718Skan	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
218169718Skan
219169718Skan	if (getcwd(path, sizeof(path)-2) == NULL)
220132751Skan		return ("");
221169718Skan	if (path[1] != '\0')		/* special case for root dir. */
222132751Skan		strcat(path, "/");
223169718Skan	/* For guest account, skip / since it's chrooted */
22452914Sobrien	return (guest ? path+1 : path);
225169718Skan}
226132751Skan
227169718Skanint
228169718Skanmain(argc, argv, envp)
229169718Skan	int argc;
230117428Skan	char *argv[];
231169718Skan	char **envp;
232132751Skan{
233169718Skan	int addrlen, ch, on = 1, tos;
234169718Skan	char *cp, line[LINE_MAX];
235169718Skan	FILE *fd;
23645299Sobrien	char *argstr = "AdDhlMSt:T:u:Uv";
237169718Skan	struct hostent *hp;
238132751Skan
239169718Skan	tzset();	/* in case no timezone database in ~ftp */
240132751Skan
241169718Skan	/* set this here so klogin can use it... */
24245299Sobrien	(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
243169718Skan
244132751Skan	while ((ch = getopt(argc, argv, argstr)) != EOF) {
245169718Skan		switch (ch) {
246169718Skan		case 'A':
247169718Skan			anon_only = 1;
24845299Sobrien			break;
249169718Skan
250169718Skan		case 'd':
251169718Skan			debug = 1;
252169718Skan			break;
253169718Skan
254169718Skan		case 'D':
255169718Skan			daemon_mode = 1;
256132751Skan			break;
257169718Skan
258169718Skan		case 'h':
259169718Skan			high_data_ports = 1;
26045299Sobrien			break;
261169718Skan
262169718Skan		case 'l':
263169718Skan			logging++;	/* > 1 == extra logging */
264169718Skan			break;
265169718Skan
266169718Skan		case 'M':
267169718Skan			multihome = 1;
268132751Skan			break;
269169718Skan
270218822Sdim		case 'S':
271169718Skan			stats = 1;
27245299Sobrien			break;
273169718Skan
274132751Skan		case 't':
275132751Skan			timeout = atoi(optarg);
276169718Skan			if (maxtimeout < timeout)
277169718Skan				maxtimeout = timeout;
278169718Skan			break;
27945299Sobrien
280169718Skan		case 'T':
281132751Skan			maxtimeout = atoi(optarg);
282132751Skan			if (timeout > maxtimeout)
283169718Skan				timeout = maxtimeout;
284169718Skan			break;
285169718Skan
28652914Sobrien		case 'u':
287169718Skan		    {
288132751Skan			long val = 0;
289169718Skan
290172473Smarius			val = strtol(optarg, &optarg, 8);
291132751Skan			if (*optarg != '\0' || val < 0)
292169718Skan				warnx("bad value for -u");
293172473Smarius			else
29496340Sobrien				defumask = val;
295169718Skan			break;
296132751Skan		    }
297169718Skan
298132751Skan		case 'U':
299132751Skan			doutmp = 1;
30052914Sobrien			break;
301169718Skan
302132751Skan		case 'v':
303169718Skan			debug = 1;
304132751Skan			break;
305169718Skan
30652914Sobrien		default:
307169718Skan			warnx("unknown flag -%c ignored", optopt);
308169718Skan			break;
309169718Skan		}
310169718Skan	}
311169718Skan
312169718Skan	(void) freopen(_PATH_DEVNULL, "w", stderr);
313169718Skan
314132751Skan	/*
315169718Skan	 * LOG_NDELAY sets up the logging connection immediately,
316132751Skan	 * necessary for anonymous ftp's that chroot and can't do it later.
317169718Skan	 */
31896340Sobrien	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
319169718Skan
320132751Skan	if (daemon_mode) {
321169718Skan		int ctl_sock, fd;
322132751Skan		struct servent *sv;
323169718Skan
32452914Sobrien		/*
325169718Skan		 * Detach from parent.
326169718Skan		 */
327169718Skan		if (daemon(1, 1) < 0) {
328218822Sdim			syslog(LOG_ERR, "failed to become a daemon");
329169718Skan			exit(1);
330169718Skan		}
331169718Skan		(void) signal(SIGCHLD, reapchild);
332169718Skan		/*
333169718Skan		 * Get port number for ftp/tcp.
334169718Skan		 */
335132751Skan		sv = getservbyname("ftp", "tcp");
336169718Skan		if (sv == NULL) {
33796340Sobrien			syslog(LOG_ERR, "getservbyname for ftp failed");
338169718Skan			exit(1);
339169718Skan		}
340169718Skan		/*
341169718Skan		 * Open a socket, bind it to the FTP port, and start
342169718Skan		 * listening.
343169718Skan		 */
344169718Skan		ctl_sock = socket(AF_INET, SOCK_STREAM, 0);
345169718Skan		if (ctl_sock < 0) {
346169718Skan			syslog(LOG_ERR, "control socket: %m");
347169718Skan			exit(1);
348132751Skan		}
349169718Skan		if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
35096340Sobrien		    (char *)&on, sizeof(on)) < 0)
351169718Skan			syslog(LOG_ERR, "control setsockopt: %m");;
352169718Skan		server_addr.sin_family = AF_INET;
353169718Skan		server_addr.sin_addr.s_addr = INADDR_ANY;
354132751Skan		server_addr.sin_port = sv->s_port;
355169718Skan		if (bind(ctl_sock, (struct sockaddr *)&server_addr,
35696340Sobrien			 sizeof(server_addr))) {
357169718Skan			syslog(LOG_ERR, "control bind: %m");
358169718Skan			exit(1);
359169718Skan		}
360169718Skan		if (listen(ctl_sock, 32) < 0) {
361132751Skan			syslog(LOG_ERR, "control listen: %m");
362169718Skan			exit(1);
36352914Sobrien		}
364169718Skan		/*
365169718Skan		 * Loop forever accepting connection requests and forking off
366169718Skan		 * children to handle them.
367169718Skan		 */
368132751Skan		while (1) {
369169718Skan			addrlen = sizeof(his_addr);
37045299Sobrien			fd = accept(ctl_sock, (struct sockaddr *)&his_addr,
371169718Skan				    &addrlen);
372169718Skan			if (fork() == 0) {
373169718Skan				/* child */
374169718Skan				(void) dup2(fd, 0);
375169718Skan				(void) dup2(fd, 1);
376169718Skan				close(ctl_sock);
377169718Skan				break;
378169718Skan			}
379169718Skan			close(fd);
380169718Skan		}
381169718Skan	} else {
382132751Skan		addrlen = sizeof(his_addr);
383169718Skan		if (getpeername(0, (struct sockaddr *)&his_addr,
38445299Sobrien			        &addrlen) < 0) {
385169718Skan			syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
386169718Skan			exit(1);
387169718Skan		}
388169718Skan	}
389132751Skan
390169718Skan	(void) signal(SIGHUP, sigquit);
39145299Sobrien	(void) signal(SIGINT, sigquit);
392169718Skan	(void) signal(SIGQUIT, sigquit);
393169718Skan	(void) signal(SIGTERM, sigquit);
394169718Skan	(void) signal(SIGPIPE, lostconn);
395169718Skan	(void) signal(SIGCHLD, SIG_IGN);
396169718Skan	if ((long)signal(SIGURG, myoob) < 0)
397169718Skan		syslog(LOG_ERR, "signal: %m");
398169718Skan
399169718Skan	addrlen = sizeof(ctrl_addr);
400169718Skan	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
401169718Skan		syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
402169718Skan		exit(1);
403169718Skan	}
404169718Skan#ifdef IP_TOS
405169718Skan	tos = IPTOS_LOWDELAY;
406169718Skan	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
407169718Skan		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
408169718Skan#endif
409169718Skan	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
410169718Skan
411169718Skan	/* Try to handle urgent data inline */
412169718Skan#ifdef SO_OOBINLINE
413169718Skan	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
414169718Skan		syslog(LOG_ERR, "setsockopt: %m");
415169718Skan#endif
416169718Skan
417169718Skan#ifdef	F_SETOWN
418169718Skan	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
419169718Skan		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
420169718Skan#endif
421169718Skan	dolog(&his_addr);
422169718Skan	/*
423169718Skan	 * Set up default state
424169718Skan	 */
425169718Skan	data = -1;
426169718Skan	type = TYPE_A;
427169718Skan	form = FORM_N;
428169718Skan	stru = STRU_F;
429169718Skan	mode = MODE_S;
430169718Skan	tmpline[0] = '\0';
431169718Skan
432169718Skan	/* If logins are disabled, print out the message. */
433169718Skan	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
434169718Skan		while (fgets(line, sizeof(line), fd) != NULL) {
435169718Skan			if ((cp = strchr(line, '\n')) != NULL)
436169718Skan				*cp = '\0';
437169718Skan			lreply(530, "%s", line);
438132751Skan		}
439169718Skan		(void) fflush(stdout);
44045299Sobrien		(void) fclose(fd);
441169718Skan		reply(530, "System not available.");
442169718Skan		exit(0);
443169718Skan	}
444169718Skan	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
445169718Skan		while (fgets(line, sizeof(line), fd) != NULL) {
446169718Skan			if ((cp = strchr(line, '\n')) != NULL)
447169718Skan				*cp = '\0';
448169718Skan			lreply(220, "%s", line);
449169718Skan		}
450169718Skan		(void) fflush(stdout);
451169718Skan		(void) fclose(fd);
452132751Skan		/* reply(220,) must follow */
453169718Skan	}
45496340Sobrien	(void) gethostname(hostname, sizeof(hostname));
455169718Skan
456169718Skan	/* Make sure hostname is fully qualified. */
457169718Skan	hp = gethostbyname(hostname);
458169718Skan	if (hp != NULL)
459169718Skan		strcpy (hostname, hp->h_name);
460169718Skan
461169718Skan	if (multihome) {
462169718Skan		hp = gethostbyaddr((char *) &ctrl_addr.sin_addr,
463169718Skan				   sizeof (struct in_addr), AF_INET);
464169718Skan		if (hp != NULL) {
465132751Skan			strcpy (dhostname, hp->h_name);
466169718Skan		} else {
46796340Sobrien			/* Default. */
468169718Skan			strcpy (dhostname, inet_ntoa(ctrl_addr.sin_addr));
469169718Skan		}
470169718Skan	}
471169718Skan
472132751Skan	reply(220, "%s FTP server (%s) ready.",
473169718Skan	      (multihome ? dhostname : hostname), version);
47452914Sobrien	(void) setjmp(errcatch);
475169718Skan	for (;;)
476169718Skan		(void) yyparse();
477169718Skan	/* NOTREACHED */
478169718Skan}
479169718Skan
480169718Skan/*
481169718Skan * Signal handlers.
482169718Skan */
483169718Skan
484169718Skanstatic void
485169718Skanlostconn(signo)
486132751Skan	int signo;
487169718Skan{
48845299Sobrien
489169718Skan	if (debug)
490169718Skan		syslog(LOG_DEBUG, "lost connection");
491169718Skan	dologout(-1);
492169718Skan}
493169718Skan
494169718Skanstatic void
495169718Skansigquit(signo)
496169718Skan	int signo;
497169718Skan{
498169718Skan	syslog(LOG_ERR, "got signal %s", strsignal(signo));
499169718Skan
500132751Skan	dologout(-1);
501169718Skan}
50252914Sobrien
503169718Skan/*
504169718Skan * Helper function for sgetpwnam().
505169718Skan */
506169718Skanstatic char *
507132751Skansgetsave(s)
508169718Skan	char *s;
50945299Sobrien{
510169718Skan	char *new = malloc((unsigned) strlen(s) + 1);
511169718Skan
512169718Skan	if (new == NULL) {
513169718Skan		perror_reply(421, "Local resource failure: malloc");
514169718Skan		dologout(1);
515169718Skan		/* NOTREACHED */
516169718Skan	}
517169718Skan	(void) strcpy(new, s);
518169718Skan	return (new);
519169718Skan}
520169718Skan
521132751Skan/*
522169718Skan * Save the result of a getpwnam.  Used for USER command, since
523117428Skan * the data returned must not be clobbered by any other command
524169718Skan * (e.g., globbing).
525169718Skan */
526169718Skanstatic struct passwd *
527169718Skansgetpwnam(name)
528132751Skan	char *name;
529169718Skan{
53052914Sobrien	static struct passwd save;
531169718Skan	struct passwd *p;
532169718Skan
533169718Skan	if ((p = getpwnam(name)) == NULL)
534169718Skan		return (p);
535132751Skan	if (save.pw_name) {
536169718Skan		free(save.pw_name);
53752914Sobrien		memset(save.pw_passwd, 0, strlen(save.pw_passwd));
538169718Skan		free(save.pw_passwd);
539169718Skan		free(save.pw_gecos);
540169718Skan		free(save.pw_dir);
541169718Skan		free(save.pw_shell);
542132751Skan	}
543169718Skan	save = *p;
54445299Sobrien	save.pw_name = sgetsave(p->pw_name);
545169718Skan	save.pw_passwd = sgetsave(p->pw_passwd);
546169718Skan	save.pw_gecos = sgetsave(p->pw_gecos);
547169718Skan	save.pw_dir = sgetsave(p->pw_dir);
548169718Skan	save.pw_shell = sgetsave(p->pw_shell);
549132751Skan	return (&save);
550169718Skan}
55152914Sobrien
552169718Skanstatic int login_attempts;	/* number of failed login attempts */
553169718Skanstatic int askpasswd;		/* had user command, ask for passwd */
554169718Skanstatic char curname[16];	/* current USER name */
555169718Skan
556169718Skan/*
557169718Skan * USER command.
558169718Skan * Sets global passwd pointer pw if named account exists and is acceptable;
559169718Skan * sets askpasswd if a PASS command is expected.  If logged in previously,
560169718Skan * need to reset state.  If name is "ftp" or "anonymous", the name is not in
561169718Skan * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
562169718Skan * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
563132751Skan * requesting login privileges.  Disallow anyone who does not have a standard
564169718Skan * shell as returned by getusershell().  Disallow anyone mentioned in the file
56552914Sobrien * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
566169718Skan */
567169718Skanvoid
568169718Skanuser(name)
569169718Skan	char *name;
570132751Skan{
571169718Skan	char *cp, *shell;
57245299Sobrien
573169718Skan	if (logged_in) {
574169718Skan		if (guest) {
575169718Skan			reply(530, "Can't change user from guest login.");
576132751Skan			return;
577169718Skan		} else if (dochroot) {
57852914Sobrien			reply(530, "Can't change user from chroot user.");
579169718Skan			return;
580169718Skan		}
581169718Skan		end_login();
582169718Skan	}
583132751Skan
584169718Skan	guest = 0;
58552914Sobrien	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
586169718Skan		if (checkuser(_PATH_FTPUSERS, "ftp") ||
587169718Skan		    checkuser(_PATH_FTPUSERS, "anonymous"))
588169718Skan			reply(530, "User %s access denied.", name);
589169718Skan		else if ((pw = sgetpwnam("ftp")) != NULL) {
590169718Skan			guest = 1;
591169718Skan			askpasswd = 1;
592169718Skan			reply(331,
593169718Skan			    "Guest login ok, type your name as password.");
594169718Skan		} else
595169718Skan			reply(530, "User %s unknown.", name);
596169718Skan		if (!askpasswd && logging)
597132751Skan			syslog(LOG_NOTICE,
598169718Skan			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
59945299Sobrien		return;
600169718Skan	}
601169718Skan	if (anon_only != 0) {
602169718Skan		reply(530, "Sorry, only anonymous ftp allowed.");
603169718Skan		return;
604132751Skan	}
605169718Skan
60645299Sobrien	if (pw = sgetpwnam(name)) {
607169718Skan		if ((shell = pw->pw_shell) == NULL || *shell == 0)
608169718Skan			shell = _PATH_BSHELL;
609169718Skan		while ((cp = getusershell()) != NULL)
610169718Skan			if (strcmp(cp, shell) == 0)
611132751Skan				break;
612169718Skan		endusershell();
61396340Sobrien
614169718Skan		if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) {
615169718Skan			reply(530, "User %s access denied.", name);
616169718Skan			if (logging)
617169718Skan				syslog(LOG_NOTICE,
618169718Skan				    "FTP LOGIN REFUSED FROM %s, %s",
619169718Skan				    remotehost, name);
620169718Skan			pw = (struct passwd *) NULL;
621169718Skan			return;
622169718Skan		}
623169718Skan	}
624169718Skan	if (logging) {
625132751Skan		strncpy(curname, name, sizeof(curname)-1);
626169718Skan		curname[sizeof(curname)-1] = '\0';
62796340Sobrien	}
628169718Skan#ifdef SKEY
629169718Skan	if (!skey_haskey(name)) {
630169718Skan		char *myskey, *skey_keyinfo __P((char *name));
631169718Skan
632132751Skan		myskey = skey_keyinfo(name);
633169718Skan		reply(331, "Password [ %s ] for %s required.",
63452914Sobrien		    myskey ? myskey : "error getting challenge", name);
635169718Skan	} else
636169718Skan#endif
637169718Skan		reply(331, "Password required for %s.", name);
638169718Skan
639169718Skan	askpasswd = 1;
640169718Skan	/*
641169718Skan	 * Delay before reading passwd after first failed
642169718Skan	 * attempt to slow down passwd-guessing programs.
643132751Skan	 */
644169718Skan	if (login_attempts)
64552914Sobrien		sleep((unsigned) login_attempts);
646169718Skan}
64752914Sobrien
648132751Skan/*
649132751Skan * Check if a user is in the file "fname"
650169718Skan */
65145299Sobrienstatic int
652169718Skancheckuser(fname, name)
65345299Sobrien	char *fname;
654169718Skan	char *name;
655169718Skan{
656169718Skan	FILE *fd;
657169718Skan	int found = 0;
658169718Skan	char *p, line[BUFSIZ];
659169718Skan
660169718Skan	if ((fd = fopen(fname, "r")) != NULL) {
661169718Skan		while (fgets(line, sizeof(line), fd) != NULL)
662169718Skan			if ((p = strchr(line, '\n')) != NULL) {
663169718Skan				*p = '\0';
664169718Skan				if (line[0] == '#')
665169718Skan					continue;
666169718Skan				if (strcmp(line, name) == 0) {
667169718Skan					found = 1;
668169718Skan					break;
669169718Skan				}
670169718Skan			}
671169718Skan		(void) fclose(fd);
672169718Skan	}
673169718Skan	return (found);
674169718Skan}
675169718Skan
676169718Skan/*
677169718Skan * Terminate login as previous user, if any, resetting state;
678169718Skan * used when USER command is given or login fails.
679169718Skan */
680169718Skanstatic void
681169718Skanend_login()
682169718Skan{
683169718Skan
684169718Skan	(void) seteuid((uid_t)0);
685169718Skan	if (logged_in) {
686169718Skan		logwtmp(ttyline, "", "");
687169718Skan		if (doutmp)
688169718Skan			logout(utmp.ut_line);
689169718Skan	}
690169718Skan	pw = NULL;
691132751Skan	logged_in = 0;
692169718Skan	guest = 0;
693132751Skan	dochroot = 0;
694169718Skan}
69596340Sobrien
696169718Skanvoid
697132751Skanpass(passwd)
698169718Skan	char *passwd;
699132751Skan{
700169718Skan	int rval;
70145299Sobrien	FILE *fd;
702169718Skan	static char homedir[MAXPATHLEN];
703132751Skan	char rootdir[MAXPATHLEN];
704169718Skan
705132751Skan	if (logged_in || askpasswd == 0) {
706169718Skan		reply(503, "Login with USER first.");
70752914Sobrien		return;
708169718Skan	}
709132751Skan	askpasswd = 0;
710169718Skan	if (!guest) {		/* "ftp" is only account allowed no password */
711132751Skan		if (pw == NULL) {
712169718Skan			rval = 1;	/* failure below */
71352914Sobrien			goto skip;
714169718Skan		}
715169718Skan#if defined(KERBEROS)
716169718Skan		rval = klogin(pw, "", hostname, passwd);
717169718Skan		if (rval == 0)
718169718Skan			goto skip;
719169718Skan#endif
720169718Skan#ifdef SKEY
721132751Skan		if (skey_haskey(pw->pw_name) == 0 &&
722169718Skan		   (skey_passcheck(pw->pw_name, passwd) != -1)) {
723132751Skan			rval = 0;
724169718Skan			goto skip;
72552914Sobrien		}
726169718Skan#endif
727132751Skan		/* the strcmp does not catch null passwords! */
728169718Skan		if (pw == NULL || *pw->pw_passwd == '\0' ||
729132751Skan		    strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) {
730169718Skan			rval = 1;	 /* failure */
73145299Sobrien			goto skip;
732169718Skan		}
733132751Skan		rval = 0;
734169718Skan
735132751Skanskip:
736169718Skan		/*
73745299Sobrien		 * If rval == 1, the user failed the authentication check
738169718Skan		 * above.  If rval == 0, either Kerberos or local authentication
739132751Skan		 * succeeded.
740132751Skan		 */
74145299Sobrien		if (rval) {
742132751Skan			reply(530, "Login incorrect.");
743132751Skan			if (logging)
744169718Skan				syslog(LOG_NOTICE,
745132751Skan				    "FTP LOGIN FAILED FROM %s, %s",
746169718Skan				    remotehost, curname);
74745299Sobrien			pw = NULL;
748169718Skan			if (login_attempts++ >= 5) {
749169718Skan				syslog(LOG_NOTICE,
750169718Skan				    "repeated login failures from %s",
751169718Skan				    remotehost);
752169718Skan				exit(0);
753169718Skan			}
754169718Skan			return;
755169718Skan		}
756169718Skan	} else {
757169718Skan		/* Save anonymous' password. */
758169718Skan		guestpw = strdup(passwd);
759169718Skan		if (guestpw == (char *)NULL)
760169718Skan			fatal("Out of memory");
761169718Skan	}
762132751Skan	login_attempts = 0;		/* this time successful */
763132751Skan	if (setegid((gid_t)pw->pw_gid) < 0) {
764169718Skan		reply(550, "Can't set gid.");
765132751Skan		return;
766169718Skan	}
76745299Sobrien	(void) initgroups(pw->pw_name, pw->pw_gid);
768169718Skan
769132751Skan	/* open wtmp before chroot */
770132751Skan	logwtmp(ttyline, pw->pw_name, remotehost);
771169718Skan
772132751Skan	/* open utmp before chroot */
773169718Skan	if (doutmp) {
77445299Sobrien		memset((void *)&utmp, 0, sizeof(utmp));
775169718Skan		(void)time(&utmp.ut_time);
776132751Skan		(void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name));
777169718Skan		(void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host));
778132751Skan		(void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line));
779169718Skan		login(&utmp);
78045299Sobrien	}
781169718Skan
782169718Skan	/* open stats file before chroot */
783169718Skan	if (guest && (stats == 1) && (statfd < 0))
784218822Sdim		if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
785169718Skan			stats = 0;
786169718Skan
787169718Skan	logged_in = 1;
788169718Skan
789169718Skan	dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
790169718Skan	if (guest || dochroot) {
791169718Skan		if (multihome) {
792169718Skan			struct stat ts;
793169718Skan
794169718Skan			/* Compute root directory. */
795169718Skan			snprintf (rootdir, sizeof(rootdir), "%s/%s",
796169718Skan				  pw->pw_dir, dhostname);
797169718Skan			if (stat(rootdir, &ts) < 0) {
798169718Skan				snprintf (rootdir, sizeof(rootdir), "%s/%s",
799169718Skan					  pw->pw_dir, hostname);
800169718Skan			}
801169718Skan		} else
802169718Skan			strcpy (rootdir, pw->pw_dir);
803132751Skan	}
804169718Skan	if (guest) {
805132751Skan		/*
806169718Skan		 * We MUST do a chdir() after the chroot. Otherwise
80752914Sobrien		 * the old current directory will be accessible as "."
808169718Skan		 * outside the new root!
809132751Skan		 */
810169718Skan		if (chroot(rootdir) < 0 || chdir("/") < 0) {
811132751Skan			reply(550, "Can't set guest privileges.");
812169718Skan			goto bad;
81345299Sobrien		}
814169718Skan		strcpy(pw->pw_dir, "/");
815132751Skan		setenv("HOME", "/", 1);
816169718Skan	} else if (dochroot) {
817132751Skan		if (chroot(rootdir) < 0 || chdir("/") < 0) {
818169718Skan			reply(550, "Can't change root.");
81945299Sobrien			goto bad;
820169718Skan		}
821132751Skan		strcpy(pw->pw_dir, "/");
822169718Skan		setenv("HOME", "/", 1);
823169718Skan	} else if (chdir(pw->pw_dir) < 0) {
824169718Skan		if (chdir("/") < 0) {
82545299Sobrien			reply(530, "User %s: can't change directory to %s.",
826169718Skan			    pw->pw_name, pw->pw_dir);
827132751Skan			goto bad;
828169718Skan		} else
829132751Skan			lreply(230, "No directory! Logging in with home=/");
830169718Skan	}
83145299Sobrien	if (seteuid((uid_t)pw->pw_uid) < 0) {
832169718Skan		reply(550, "Can't set uid.");
833132751Skan		goto bad;
834169718Skan	}
835132751Skan
836169718Skan	/*
83796340Sobrien	 * Set home directory so that use of ~ (tilde) works correctly.
838169718Skan	 */
839132751Skan	if (getcwd(homedir, MAXPATHLEN) != NULL)
840169718Skan		setenv("HOME", homedir, 1);
841132751Skan
842169718Skan	/*
84396340Sobrien	 * Display a login message, if it exists.
844169718Skan	 * N.B. reply(230,) must follow the message.
845132751Skan	 */
846169718Skan	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
847132751Skan		char *cp, line[LINE_MAX];
848169718Skan
84996340Sobrien		while (fgets(line, sizeof(line), fd) != NULL) {
850169718Skan			if ((cp = strchr(line, '\n')) != NULL)
851169718Skan				*cp = '\0';
852169718Skan			lreply(230, "%s", line);
853169718Skan		}
854169718Skan		(void) fflush(stdout);
855169718Skan		(void) fclose(fd);
856169718Skan	}
857132751Skan	if (guest) {
858169718Skan		if (ident != NULL)
859132751Skan			free(ident);
860169718Skan		ident = strdup(passwd);
86196340Sobrien		if (ident == (char *)NULL)
862169718Skan			fatal("Ran out of memory.");
863132751Skan		reply(230, "Guest login ok, access restrictions apply.");
864169718Skan#ifdef HASSETPROCTITLE
865132751Skan		snprintf(proctitle, sizeof(proctitle),
866169718Skan		    "%s: anonymous/%.*s", remotehost,
86796340Sobrien		    sizeof(proctitle) - sizeof(remotehost) -
868169718Skan		    sizeof(": anonymous/"), passwd);
869132751Skan		setproctitle(proctitle);
870169718Skan#endif /* HASSETPROCTITLE */
871132751Skan		if (logging)
872169718Skan			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
87396340Sobrien			    remotehost, passwd);
874169718Skan	} else {
875132751Skan		reply(230, "User %s logged in.", pw->pw_name);
876169718Skan#ifdef HASSETPROCTITLE
877169718Skan		snprintf(proctitle, sizeof(proctitle),
878169718Skan		    "%s: %s", remotehost, pw->pw_name);
87996340Sobrien		setproctitle(proctitle);
880169718Skan#endif /* HASSETPROCTITLE */
881169718Skan		if (logging)
882169718Skan			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
883169718Skan			    remotehost, pw->pw_name);
884169718Skan	}
885169718Skan	(void) umask(defumask);
886169718Skan	return;
887132751Skanbad:
888132751Skan	/* Forget all about it... */
88996340Sobrien	end_login();
890169718Skan}
891169718Skan
892218822Sdimvoid
893169718Skanretrieve(cmd, name)
894169718Skan	char *cmd, *name;
895169718Skan{
896132751Skan	FILE *fin, *dout;
897169718Skan	struct stat st;
898169718Skan	int (*closefunc) __P((FILE *));
899169718Skan	time_t start;
90096340Sobrien
901169718Skan	if (cmd == 0) {
902132751Skan		fin = fopen(name, "r"), closefunc = fclose;
903132751Skan		st.st_size = 0;
904169718Skan	} else {
905132751Skan		char line[BUFSIZ];
906169718Skan
90796340Sobrien		(void) snprintf(line, sizeof(line), cmd, name);
908169718Skan		name = line;
909169718Skan		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
910169718Skan		st.st_size = -1;
911169718Skan		st.st_blksize = BUFSIZ;
912169718Skan	}
913169718Skan	if (fin == NULL) {
914169718Skan		if (errno != 0) {
915169718Skan			perror_reply(550, name);
916169718Skan			if (cmd == 0) {
917218822Sdim				LOGCMD("get", name);
918169718Skan			}
919169718Skan		}
920169718Skan		return;
921132751Skan	}
922169718Skan	byte_count = -1;
923132751Skan	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
924169718Skan		reply(550, "%s: not a plain file.", name);
92596340Sobrien		goto done;
926169718Skan	}
927132751Skan	if (restart_point) {
928169718Skan		if (type == TYPE_A) {
929132751Skan			off_t i, n;
930169718Skan			int c;
93196340Sobrien
932169718Skan			n = restart_point;
933169718Skan			i = 0;
934169718Skan			while (i++ < n) {
935132751Skan				if ((c=getc(fin)) == EOF) {
936169718Skan					perror_reply(550, name);
93796340Sobrien					goto done;
938169718Skan				}
939132751Skan				if (c == '\n')
940169718Skan					i++;
941132751Skan			}
942169718Skan		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
943117428Skan			perror_reply(550, name);
944169718Skan			goto done;
945132751Skan		}
946169718Skan	}
947132751Skan	dout = dataconn(name, st.st_size, "w");
948169718Skan	if (dout == NULL)
949117428Skan		goto done;
950169718Skan	time(&start);
951132751Skan	send_data(fin, dout, st.st_blksize, st.st_size,
952169718Skan		  (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)));
953132751Skan	if ((cmd == 0) && stats)
954169718Skan		logxfer(name, st.st_size, start);
955117428Skan	(void) fclose(dout);
956169718Skan	data = -1;
957132751Skan	pdata = -1;
958169718Skandone:
959132751Skan	if (cmd == 0)
960169718Skan		LOGBYTES("get", name, byte_count);
961117428Skan	(*closefunc)(fin);
962169718Skan}
963132751Skan
964169718Skanvoid
965132751Skanstore(name, mode, unique)
966169718Skan	char *name, *mode;
967117428Skan	int unique;
968169718Skan{
969132751Skan	FILE *fout, *din;
970169718Skan	struct stat st;
971132751Skan	int (*closefunc) __P((FILE *));
972169718Skan
973117428Skan	if (unique && stat(name, &st) == 0 &&
974169718Skan	    (name = gunique(name)) == NULL) {
975132751Skan		LOGCMD(*mode == 'w' ? "put" : "append", name);
976169718Skan		return;
977132751Skan	}
978169718Skan
979117428Skan	if (restart_point)
980169718Skan		mode = "r+";
981132751Skan	fout = fopen(name, mode);
982169718Skan	closefunc = fclose;
983132751Skan	if (fout == NULL) {
984169718Skan		perror_reply(553, name);
985117428Skan		LOGCMD(*mode == 'w' ? "put" : "append", name);
986169718Skan		return;
987132751Skan	}
988169718Skan	byte_count = -1;
989132751Skan	if (restart_point) {
990169718Skan		if (type == TYPE_A) {
991117428Skan			off_t i, n;
992169718Skan			int c;
993132751Skan
994169718Skan			n = restart_point;
995132751Skan			i = 0;
996169718Skan			while (i++ < n) {
997117428Skan				if ((c=getc(fout)) == EOF) {
998117428Skan					perror_reply(550, name);
999169718Skan					goto done;
1000169718Skan				}
1001169718Skan				if (c == '\n')
1002169718Skan					i++;
1003169718Skan			}
1004169718Skan			/*
1005132751Skan			 * We must do this seek to "current" position
1006169718Skan			 * because we are changing from reading to
1007132751Skan			 * writing.
1008169718Skan			 */
100996340Sobrien			if (fseek(fout, 0L, L_INCR) < 0) {
1010169718Skan				perror_reply(550, name);
1011132751Skan				goto done;
1012169718Skan			}
1013132751Skan		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
1014169718Skan			perror_reply(550, name);
1015132751Skan			goto done;
1016169718Skan		}
1017132751Skan	}
1018169718Skan	din = dataconn(name, (off_t)-1, "r");
1019132751Skan	if (din == NULL)
1020169718Skan		goto done;
1021132751Skan	if (receive_data(din, fout) == 0) {
1022169718Skan		if (unique)
1023132751Skan			reply(226, "Transfer complete (unique file name:%s).",
1024169718Skan			    name);
1025132751Skan		else
1026106848Sru			reply(226, "Transfer complete.");
102796340Sobrien	}
1028169718Skan	(void) fclose(din);
1029132751Skan	data = -1;
1030169718Skan	pdata = -1;
1031132751Skandone:
1032169718Skan	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
103396340Sobrien	(*closefunc)(fout);
1034169718Skan}
1035132751Skan
1036169718Skanstatic FILE *
1037132751Skangetdatasock(mode)
1038169718Skan	char *mode;
103996340Sobrien{
1040169718Skan	int on = 1, s, t, tries;
1041132751Skan
1042169718Skan	if (data >= 0)
1043132751Skan		return (fdopen(data, mode));
1044169718Skan	(void) seteuid((uid_t)0);
104596340Sobrien	s = socket(AF_INET, SOCK_STREAM, 0);
1046169718Skan	if (s < 0)
1047132751Skan		goto bad;
1048169718Skan	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
1049132751Skan	    (char *) &on, sizeof(on)) < 0)
1050169718Skan		goto bad;
105196340Sobrien	/* anchor socket to avoid multi-homing problems */
1052169718Skan	data_source.sin_len = sizeof(struct sockaddr_in);
1053132751Skan	data_source.sin_family = AF_INET;
1054169718Skan	data_source.sin_addr = ctrl_addr.sin_addr;
1055132751Skan	for (tries = 1; ; tries++) {
1056169718Skan		if (bind(s, (struct sockaddr *)&data_source,
105796340Sobrien		    sizeof(data_source)) >= 0)
1058169718Skan			break;
1059132751Skan		if (errno != EADDRINUSE || tries > 10)
1060169718Skan			goto bad;
1061132751Skan		sleep(tries);
1062169718Skan	}
106396340Sobrien	(void) seteuid((uid_t)pw->pw_uid);
1064169718Skan#ifdef IP_TOS
1065132751Skan	on = IPTOS_THROUGHPUT;
1066169718Skan	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
1067132751Skan		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1068169718Skan#endif
106996340Sobrien#ifdef TCP_NOPUSH
1070169718Skan	/*
1071132751Skan	 * Turn off push flag to keep sender TCP from sending short packets
1072169718Skan	 * at the boundaries of each write().  Should probably do a SO_SNDBUF
1073132751Skan	 * to set the send buffer size as well, but that may not be desirable
1074169718Skan	 * in heavy-load situations.
107596340Sobrien	 */
1076169718Skan	on = 1;
1077132751Skan	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
1078169718Skan		syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
1079132751Skan#endif
1080169718Skan#ifdef SO_SNDBUF
108196340Sobrien	on = 65536;
1082169718Skan	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
1083132751Skan		syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
1084169718Skan#endif
1085132751Skan
1086169718Skan	return (fdopen(s, mode));
108796340Sobrienbad:
1088169718Skan	/* Return the real value of errno (close may change it) */
1089132751Skan	t = errno;
1090169718Skan	(void) seteuid((uid_t)pw->pw_uid);
1091132751Skan	(void) close(s);
1092169718Skan	errno = t;
109396340Sobrien	return (NULL);
1094169718Skan}
1095132751Skan
1096169718Skanstatic FILE *
1097132751Skandataconn(name, size, mode)
1098169718Skan	char *name;
109996340Sobrien	off_t size;
1100169718Skan	char *mode;
1101132751Skan{
1102169718Skan	char sizebuf[32];
1103132751Skan	FILE *file;
1104169718Skan	int retry = 0, tos;
110596340Sobrien
1106169718Skan	file_size = size;
1107132751Skan	byte_count = 0;
1108169718Skan	if (size != (off_t) -1) {
1109132751Skan		(void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)",
1110169718Skan				size);
111196340Sobrien	} else
1112169718Skan		sizebuf[0] = '\0';
1113132751Skan	if (pdata >= 0) {
1114169718Skan		struct sockaddr_in from;
1115132751Skan		int s, fromlen = sizeof(from);
1116169718Skan
111796340Sobrien		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
1118169718Skan		if (s < 0) {
1119132751Skan			reply(425, "Can't open data connection.");
1120169718Skan			(void) close(pdata);
1121132751Skan			pdata = -1;
1122169718Skan			return (NULL);
112396340Sobrien		}
1124169718Skan		if (ntohs(from.sin_port) < IPPORT_RESERVED) {
1125132751Skan			perror_reply(425, "Can't build data connection");
1126169718Skan			(void) close(pdata);
1127132751Skan			(void) close(s);
1128169718Skan			pdata = -1;
112996340Sobrien			return (NULL);
1130169718Skan		}
1131132751Skan		if (from.sin_addr.s_addr != his_addr.sin_addr.s_addr) {
1132169718Skan			perror_reply(435, "Can't build data connection");
1133132751Skan			(void) close(pdata);
1134169718Skan			(void) close(s);
113596340Sobrien			pdata = -1;
1136169718Skan			return (NULL);
1137132751Skan		}
1138169718Skan		(void) close(pdata);
1139132751Skan		pdata = s;
1140169718Skan#ifdef IP_TOS
114196340Sobrien		tos = IPTOS_THROUGHPUT;
114296340Sobrien		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
1143132751Skan		    sizeof(int));
1144169718Skan#endif
1145132751Skan		reply(150, "Opening %s mode data connection for '%s'%s.",
1146169718Skan		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
114796340Sobrien		return (fdopen(pdata, mode));
1148169718Skan	}
1149132751Skan	if (data >= 0) {
1150169718Skan		reply(125, "Using existing data connection for '%s'%s.",
1151132751Skan		    name, sizebuf);
1152169718Skan		usedefault = 1;
115396340Sobrien		return (fdopen(data, mode));
1154169718Skan	}
1155132751Skan	if (usedefault)
1156169718Skan		data_dest = his_addr;
1157132751Skan	usedefault = 1;
1158169718Skan	file = getdatasock(mode);
115996340Sobrien	if (file == NULL) {
1160169718Skan		reply(425, "Can't create data socket (%s,%d): %s.",
1161132751Skan		    inet_ntoa(data_source.sin_addr),
1162169718Skan		    ntohs(data_source.sin_port), strerror(errno));
1163132751Skan		return (NULL);
1164169718Skan	}
116596340Sobrien	data = fileno(file);
1166169718Skan
1167132751Skan	/*
1168169718Skan	 * attempt to connect to reserved port on client machine;
1169132751Skan	 * this looks like an attack
1170169718Skan	 */
117196340Sobrien	if (ntohs(data_dest.sin_port) < IPPORT_RESERVED) {
1172169718Skan		perror_reply(425, "Can't build data connection");
1173132751Skan		(void) fclose(file);
1174169718Skan		data = -1;
1175132751Skan		return NULL;
1176169718Skan	}
117796340Sobrien	if (data_dest.sin_addr.s_addr != his_addr.sin_addr.s_addr) {
1178169718Skan		perror_reply(435, "Can't build data connection");
1179132751Skan		(void) fclose(file);
1180169718Skan		data = -1;
1181132751Skan		return NULL;
1182169718Skan	}
118396340Sobrien	while (connect(data, (struct sockaddr *)&data_dest,
1184169718Skan	    sizeof(data_dest)) < 0) {
1185132751Skan		if (errno == EADDRINUSE && retry < swaitmax) {
1186132751Skan			sleep((unsigned) swaitint);
1187169718Skan			retry += swaitint;
1188132751Skan			continue;
1189169718Skan		}
1190117428Skan		perror_reply(425, "Can't build data connection");
1191169718Skan		(void) fclose(file);
1192132751Skan		data = -1;
1193169718Skan		return (NULL);
1194132751Skan	}
1195169718Skan	reply(150, "Opening %s mode data connection for '%s'%s.",
119696340Sobrien	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
119796340Sobrien	return (file);
1198169718Skan}
1199169718Skan
1200132751Skan/*
1201132751Skan * Tranfer the contents of "instr" to "outstr" peer using the appropriate
120296340Sobrien * encapsulation of the data subject to Mode, Structure, and Type.
1203169718Skan *
1204132751Skan * NB: Form isn't handled.
1205169718Skan */
1206169718Skanstatic void
1207169718Skansend_data(instr, outstr, blksize, filesize, isreg)
120896340Sobrien	FILE *instr, *outstr;
1209169718Skan	off_t blksize;
1210132751Skan	off_t filesize;
1211169718Skan	int isreg;
1212132751Skan{
1213169718Skan	int c, cnt, filefd, netfd;
121496340Sobrien	char *buf, *bp;
1215169718Skan	size_t len;
1216132751Skan
1217169718Skan	transflag++;
1218132751Skan	if (setjmp(urgcatch)) {
1219169718Skan		transflag = 0;
122096340Sobrien		return;
1221169718Skan	}
1222132751Skan	switch (type) {
1223169718Skan
1224132751Skan	case TYPE_A:
1225169718Skan		while ((c = getc(instr)) != EOF) {
122696340Sobrien			byte_count++;
1227169718Skan			if (c == '\n') {
1228132751Skan				if (ferror(outstr))
1229169718Skan					goto data_err;
1230132751Skan				(void) putc('\r', outstr);
1231169718Skan			}
123296340Sobrien			(void) putc(c, outstr);
1233169718Skan		}
1234132751Skan		fflush(outstr);
1235169718Skan		transflag = 0;
1236132751Skan		if (ferror(instr))
1237169718Skan			goto file_err;
123896340Sobrien		if (ferror(outstr))
1239169718Skan			goto data_err;
1240132751Skan		reply(226, "Transfer complete.");
1241169718Skan		return;
1242132751Skan
1243169718Skan	case TYPE_I:
1244117428Skan	case TYPE_L:
1245169718Skan		/*
1246132751Skan		 * isreg is only set if we are not doing restart and we
1247169718Skan		 * are sending a regular file
1248132751Skan		 */
1249169718Skan		netfd = fileno(outstr);
125096340Sobrien		filefd = fileno(instr);
1251169718Skan
1252132751Skan		if (isreg && filesize < (off_t)16 * 1024 * 1024) {
1253169718Skan			buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd,
1254132751Skan				   (off_t)0);
1255169718Skan			if (!buf) {
125696340Sobrien				syslog(LOG_WARNING, "mmap(%lu): %m",
1257169718Skan				       (unsigned long)filesize);
1258132751Skan				goto oldway;
1259169718Skan			}
1260132751Skan			bp = buf;
1261169718Skan			len = filesize;
126296340Sobrien			do {
1263169718Skan				cnt = write(netfd, bp, len);
1264169718Skan				len -= cnt;
1265169718Skan				bp += cnt;
1266132751Skan				if (cnt > 0) byte_count += cnt;
1267169718Skan			} while(cnt > 0 && len > 0);
1268117428Skan
1269169718Skan			transflag = 0;
1270169718Skan			munmap(buf, (size_t)filesize);
1271169718Skan			if (cnt < 0)
1272169718Skan				goto data_err;
1273132751Skan			reply(226, "Transfer complete.");
127496340Sobrien			return;
1275169718Skan		}
1276169718Skan
1277169718Skanoldway:
1278132751Skan		if ((buf = malloc((u_int)blksize)) == NULL) {
1279169718Skan			transflag = 0;
128096340Sobrien			perror_reply(451, "Local resource failure: malloc");
1281169718Skan			return;
1282169718Skan		}
1283169718Skan
1284132751Skan		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1285169718Skan		    write(netfd, buf, cnt) == cnt)
128696340Sobrien			byte_count += cnt;
128796340Sobrien		transflag = 0;
1288169718Skan		(void)free(buf);
1289169718Skan		if (cnt != 0) {
1290169718Skan			if (cnt < 0)
1291169718Skan				goto file_err;
1292169718Skan			goto data_err;
1293169718Skan		}
1294169718Skan		reply(226, "Transfer complete.");
1295169718Skan		return;
1296132751Skan	default:
1297169718Skan		transflag = 0;
129896340Sobrien		reply(550, "Unimplemented TYPE %d in send_data", type);
1299169718Skan		return;
1300132751Skan	}
1301169718Skan
1302132751Skandata_err:
1303169718Skan	transflag = 0;
130496340Sobrien	perror_reply(426, "Data connection");
1305169718Skan	return;
1306132751Skan
1307169718Skanfile_err:
1308132751Skan	transflag = 0;
1309169718Skan	perror_reply(551, "Error on input file");
131096340Sobrien}
1311169718Skan
1312169718Skan/*
1313169718Skan * Transfer data from peer to "outstr" using the appropriate encapulation of
1314169718Skan * the data subject to Mode, Structure, and Type.
1315169718Skan *
1316169718Skan * N.B.: Form isn't handled.
1317169718Skan */
1318169718Skanstatic int
1319169718Skanreceive_data(instr, outstr)
1320169718Skan	FILE *instr, *outstr;
1321169718Skan{
1322169718Skan	int c;
1323169718Skan	int cnt, bare_lfs = 0;
1324132751Skan	char buf[BUFSIZ];
1325169718Skan
1326132751Skan	transflag++;
1327169718Skan	if (setjmp(urgcatch)) {
132896340Sobrien		transflag = 0;
1329169718Skan		return (-1);
1330132751Skan	}
1331169718Skan	switch (type) {
1332132751Skan
1333169718Skan	case TYPE_I:
133496340Sobrien	case TYPE_L:
133596340Sobrien		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1336169718Skan			if (write(fileno(outstr), buf, cnt) != cnt)
1337169718Skan				goto file_err;
1338169718Skan			byte_count += cnt;
1339169718Skan		}
1340169718Skan		if (cnt < 0)
1341169718Skan			goto data_err;
1342169718Skan		transflag = 0;
1343169718Skan		return (0);
1344169718Skan
1345169718Skan	case TYPE_E:
1346169718Skan		reply(553, "TYPE E not implemented.");
1347169718Skan		transflag = 0;
1348169718Skan		return (-1);
1349169718Skan
1350169718Skan	case TYPE_A:
1351169718Skan		while ((c = getc(instr)) != EOF) {
1352169718Skan			byte_count++;
1353169718Skan			if (c == '\n')
135496340Sobrien				bare_lfs++;
1355169718Skan			while (c == '\r') {
135696340Sobrien				if (ferror(outstr))
1357169718Skan					goto data_err;
1358169718Skan				if ((c = getc(instr)) != '\n') {
1359169718Skan					(void) putc ('\r', outstr);
1360169718Skan					if (c == '\0' || c == EOF)
1361132751Skan						goto contin2;
1362169718Skan				}
136396340Sobrien			}
1364169718Skan			(void) putc(c, outstr);
1365132751Skan	contin2:	;
1366169718Skan		}
1367132751Skan		fflush(outstr);
1368169718Skan		if (ferror(instr))
136996340Sobrien			goto data_err;
1370169718Skan		if (ferror(outstr))
1371132751Skan			goto file_err;
1372169718Skan		transflag = 0;
1373132751Skan		if (bare_lfs) {
1374169718Skan			lreply(226,
137596340Sobrien		"WARNING! %d bare linefeeds received in ASCII mode",
1376169718Skan			    bare_lfs);
1377169718Skan		(void)printf("   File may not have transferred correctly.\r\n");
1378169718Skan		}
1379169718Skan		return (0);
1380132751Skan	default:
1381169718Skan		reply(550, "Unimplemented TYPE %d in receive_data", type);
138296340Sobrien		transflag = 0;
1383132751Skan		return (-1);
1384169718Skan	}
1385132751Skan
1386169718Skandata_err:
138796340Sobrien	transflag = 0;
1388169718Skan	perror_reply(426, "Data Connection");
1389132751Skan	return (-1);
1390169718Skan
1391132751Skanfile_err:
1392169718Skan	transflag = 0;
139396340Sobrien	perror_reply(452, "Error writing file");
1394169718Skan	return (-1);
1395132751Skan}
1396169718Skan
1397132751Skanvoid
1398169718Skanstatfilecmd(filename)
1399117428Skan	char *filename;
1400169718Skan{
1401132751Skan	FILE *fin;
1402169718Skan	int c;
1403132751Skan	char line[LINE_MAX];
1404169718Skan
140596340Sobrien	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1406169718Skan	fin = ftpd_popen(line, "r");
1407132751Skan	lreply(211, "status of %s:", filename);
1408169718Skan	while ((c = getc(fin)) != EOF) {
1409132751Skan		if (c == '\n') {
1410169718Skan			if (ferror(stdout)){
1411169718Skan				perror_reply(421, "control connection");
1412169718Skan				(void) ftpd_pclose(fin);
1413169718Skan				dologout(1);
1414				/* NOTREACHED */
1415			}
1416			if (ferror(fin)) {
1417				perror_reply(551, filename);
1418				(void) ftpd_pclose(fin);
1419				return;
1420			}
1421			(void) putc('\r', stdout);
1422		}
1423		(void) putc(c, stdout);
1424	}
1425	(void) ftpd_pclose(fin);
1426	reply(211, "End of Status");
1427}
1428
1429void
1430statcmd()
1431{
1432	struct sockaddr_in *sin;
1433	u_char *a, *p;
1434
1435	lreply(211, "%s FTP server status:", hostname, version);
1436	printf("     %s\r\n", version);
1437	printf("     Connected to %s", remotehost);
1438	if (!isdigit(remotehost[0]))
1439		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1440	printf("\r\n");
1441	if (logged_in) {
1442		if (guest)
1443			printf("     Logged in anonymously\r\n");
1444		else
1445			printf("     Logged in as %s\r\n", pw->pw_name);
1446	} else if (askpasswd)
1447		printf("     Waiting for password\r\n");
1448	else
1449		printf("     Waiting for user name\r\n");
1450	printf("     TYPE: %s", typenames[type]);
1451	if (type == TYPE_A || type == TYPE_E)
1452		printf(", FORM: %s", formnames[form]);
1453	if (type == TYPE_L)
1454#if NBBY == 8
1455		printf(" %d", NBBY);
1456#else
1457		printf(" %d", bytesize);	/* need definition! */
1458#endif
1459	printf("; STRUcture: %s; transfer MODE: %s\r\n",
1460	    strunames[stru], modenames[mode]);
1461	if (data != -1)
1462		printf("     Data connection open\r\n");
1463	else if (pdata != -1) {
1464		printf("     in Passive mode");
1465		sin = &pasv_addr;
1466		goto printaddr;
1467	} else if (usedefault == 0) {
1468		printf("     PORT");
1469		sin = &data_dest;
1470printaddr:
1471		a = (u_char *) &sin->sin_addr;
1472		p = (u_char *) &sin->sin_port;
1473#define UC(b) (((int) b) & 0xff)
1474		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1475			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1476#undef UC
1477	} else
1478		printf("     No data connection\r\n");
1479	reply(211, "End of status");
1480}
1481
1482void
1483fatal(s)
1484	char *s;
1485{
1486
1487	reply(451, "Error in server: %s\n", s);
1488	reply(221, "Closing connection due to server error.");
1489	dologout(0);
1490	/* NOTREACHED */
1491}
1492
1493void
1494#if __STDC__
1495reply(int n, const char *fmt, ...)
1496#else
1497reply(n, fmt, va_alist)
1498	int n;
1499	char *fmt;
1500        va_dcl
1501#endif
1502{
1503	va_list ap;
1504#if __STDC__
1505	va_start(ap, fmt);
1506#else
1507	va_start(ap);
1508#endif
1509	(void)printf("%d ", n);
1510	(void)vprintf(fmt, ap);
1511	(void)printf("\r\n");
1512	(void)fflush(stdout);
1513	if (debug) {
1514		syslog(LOG_DEBUG, "<--- %d ", n);
1515		vsyslog(LOG_DEBUG, fmt, ap);
1516	}
1517}
1518
1519void
1520#if __STDC__
1521lreply(int n, const char *fmt, ...)
1522#else
1523lreply(n, fmt, va_alist)
1524	int n;
1525	char *fmt;
1526        va_dcl
1527#endif
1528{
1529	va_list ap;
1530#if __STDC__
1531	va_start(ap, fmt);
1532#else
1533	va_start(ap);
1534#endif
1535	(void)printf("%d- ", n);
1536	(void)vprintf(fmt, ap);
1537	(void)printf("\r\n");
1538	(void)fflush(stdout);
1539	if (debug) {
1540		syslog(LOG_DEBUG, "<--- %d- ", n);
1541		vsyslog(LOG_DEBUG, fmt, ap);
1542	}
1543}
1544
1545static void
1546ack(s)
1547	char *s;
1548{
1549
1550	reply(250, "%s command successful.", s);
1551}
1552
1553void
1554nack(s)
1555	char *s;
1556{
1557
1558	reply(502, "%s command not implemented.", s);
1559}
1560
1561/* ARGSUSED */
1562void
1563yyerror(s)
1564	char *s;
1565{
1566	char *cp;
1567
1568	if (cp = strchr(cbuf,'\n'))
1569		*cp = '\0';
1570	reply(500, "'%s': command not understood.", cbuf);
1571}
1572
1573void
1574delete(name)
1575	char *name;
1576{
1577	struct stat st;
1578
1579	LOGCMD("delete", name);
1580	if (stat(name, &st) < 0) {
1581		perror_reply(550, name);
1582		return;
1583	}
1584	if ((st.st_mode&S_IFMT) == S_IFDIR) {
1585		if (rmdir(name) < 0) {
1586			perror_reply(550, name);
1587			return;
1588		}
1589		goto done;
1590	}
1591	if (unlink(name) < 0) {
1592		perror_reply(550, name);
1593		return;
1594	}
1595done:
1596	ack("DELE");
1597}
1598
1599void
1600cwd(path)
1601	char *path;
1602{
1603
1604	if (chdir(path) < 0)
1605		perror_reply(550, path);
1606	else
1607		ack("CWD");
1608}
1609
1610void
1611makedir(name)
1612	char *name;
1613{
1614
1615	LOGCMD("mkdir", name);
1616	if (mkdir(name, 0777) < 0)
1617		perror_reply(550, name);
1618	else
1619		reply(257, "MKD command successful.");
1620}
1621
1622void
1623removedir(name)
1624	char *name;
1625{
1626
1627	LOGCMD("rmdir", name);
1628	if (rmdir(name) < 0)
1629		perror_reply(550, name);
1630	else
1631		ack("RMD");
1632}
1633
1634void
1635pwd()
1636{
1637	char path[MAXPATHLEN + 1];
1638
1639	if (getwd(path) == (char *)NULL)
1640		reply(550, "%s.", path);
1641	else
1642		reply(257, "\"%s\" is current directory.", path);
1643}
1644
1645char *
1646renamefrom(name)
1647	char *name;
1648{
1649	struct stat st;
1650
1651	if (stat(name, &st) < 0) {
1652		perror_reply(550, name);
1653		return ((char *)0);
1654	}
1655	reply(350, "File exists, ready for destination name");
1656	return (name);
1657}
1658
1659void
1660renamecmd(from, to)
1661	char *from, *to;
1662{
1663
1664	LOGCMD2("rename", from, to);
1665	if (rename(from, to) < 0)
1666		perror_reply(550, "rename");
1667	else
1668		ack("RNTO");
1669}
1670
1671static void
1672dolog(sin)
1673	struct sockaddr_in *sin;
1674{
1675	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1676		sizeof(struct in_addr), AF_INET);
1677
1678	if (hp)
1679		(void) strncpy(remotehost, hp->h_name, sizeof(remotehost)-1);
1680	else
1681		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1682		    sizeof(remotehost)-1);
1683	remotehost[sizeof(remotehost)-1] = '\0';
1684#ifdef HASSETPROCTITLE
1685	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1686	setproctitle(proctitle);
1687#endif /* HASSETPROCTITLE */
1688
1689	if (logging)
1690		syslog(LOG_INFO, "connection from %s", remotehost);
1691}
1692
1693/*
1694 * Record logout in wtmp file
1695 * and exit with supplied status.
1696 */
1697void
1698dologout(status)
1699	int status;
1700{
1701
1702	if (logged_in) {
1703		(void) seteuid((uid_t)0);
1704		logwtmp(ttyline, "", "");
1705		if (doutmp)
1706			logout(utmp.ut_line);
1707#if defined(KERBEROS)
1708		if (!notickets && krbtkfile_env)
1709			unlink(krbtkfile_env);
1710#endif
1711	}
1712	/* beware of flushing buffers after a SIGPIPE */
1713	_exit(status);
1714}
1715
1716static void
1717myoob(signo)
1718	int signo;
1719{
1720	char *cp;
1721
1722	/* only process if transfer occurring */
1723	if (!transflag)
1724		return;
1725	cp = tmpline;
1726	if (getline(cp, 7, stdin) == NULL) {
1727		reply(221, "You could at least say goodbye.");
1728		dologout(0);
1729	}
1730	upper(cp);
1731	if (strcmp(cp, "ABOR\r\n") == 0) {
1732		tmpline[0] = '\0';
1733		reply(426, "Transfer aborted. Data connection closed.");
1734		reply(226, "Abort successful");
1735		longjmp(urgcatch, 1);
1736	}
1737	if (strcmp(cp, "STAT\r\n") == 0) {
1738		if (file_size != (off_t) -1)
1739			reply(213, "Status: %qd of %qd bytes transferred",
1740			    byte_count, file_size);
1741		else
1742			reply(213, "Status: %qd bytes transferred", byte_count);
1743	}
1744}
1745
1746/*
1747 * Note: a response of 425 is not mentioned as a possible response to
1748 *	the PASV command in RFC959. However, it has been blessed as
1749 *	a legitimate response by Jon Postel in a telephone conversation
1750 *	with Rick Adams on 25 Jan 89.
1751 */
1752void
1753passive()
1754{
1755	int len, on;
1756	u_short port;
1757	char *p, *a;
1758
1759	if (pw == NULL) {
1760		reply(530, "Please login with USER and PASS");
1761		return;
1762	}
1763	if (pdata >= 0)
1764		close(pdata);
1765	pdata = socket(AF_INET, SOCK_STREAM, 0);
1766	if (pdata < 0) {
1767		perror_reply(425, "Can't open passive connection");
1768		return;
1769	}
1770
1771#ifdef IP_PORTRANGE
1772	on = high_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
1773	if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
1774		       (char *)&on, sizeof(on)) < 0)
1775		goto pasv_error;
1776#endif
1777
1778	pasv_addr = ctrl_addr;
1779	pasv_addr.sin_port = 0;
1780	if (bind(pdata, (struct sockaddr *)&pasv_addr,
1781		 sizeof(pasv_addr)) < 0)
1782		goto pasv_error;
1783
1784	len = sizeof(pasv_addr);
1785	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1786		goto pasv_error;
1787	if (listen(pdata, 1) < 0)
1788		goto pasv_error;
1789	a = (char *) &pasv_addr.sin_addr;
1790	p = (char *) &pasv_addr.sin_port;
1791
1792#define UC(b) (((int) b) & 0xff)
1793
1794	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1795		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1796	return;
1797
1798pasv_error:
1799	(void) close(pdata);
1800	pdata = -1;
1801	perror_reply(425, "Can't open passive connection");
1802	return;
1803}
1804
1805/*
1806 * Generate unique name for file with basename "local".
1807 * The file named "local" is already known to exist.
1808 * Generates failure reply on error.
1809 */
1810static char *
1811gunique(local)
1812	char *local;
1813{
1814	static char new[MAXPATHLEN];
1815	struct stat st;
1816	int count, len;
1817	char *cp;
1818
1819	cp = strrchr(local, '/');
1820	if (cp)
1821		*cp = '\0';
1822	if (stat(cp ? local : ".", &st) < 0) {
1823		perror_reply(553, cp ? local : ".");
1824		return ((char *) 0);
1825	}
1826	if (cp)
1827		*cp = '/';
1828	(void) strncpy(new, local, sizeof(new)-1);
1829	new[sizeof(new)-1] = '\0';
1830	len = strlen(new);
1831	if (len+2+1 >= sizeof(new)-1)
1832		return (NULL);
1833	cp = new + len;
1834	*cp++ = '.';
1835	for (count = 1; count < 100; count++) {
1836		(void)snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1837		if (stat(new, &st) < 0)
1838			return (new);
1839	}
1840	reply(452, "Unique file name cannot be created.");
1841	return (NULL);
1842}
1843
1844/*
1845 * Format and send reply containing system error number.
1846 */
1847void
1848perror_reply(code, string)
1849	int code;
1850	char *string;
1851{
1852
1853	reply(code, "%s: %s.", string, strerror(errno));
1854}
1855
1856static char *onefile[] = {
1857	"",
1858	0
1859};
1860
1861void
1862send_file_list(whichf)
1863	char *whichf;
1864{
1865	struct stat st;
1866	DIR *dirp = NULL;
1867	struct dirent *dir;
1868	FILE *dout = NULL;
1869	char **dirlist, *dirname;
1870	int simple = 0;
1871	int freeglob = 0;
1872	glob_t gl;
1873
1874	if (strpbrk(whichf, "~{[*?") != NULL) {
1875		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1876
1877		memset(&gl, 0, sizeof(gl));
1878		freeglob = 1;
1879		if (glob(whichf, flags, 0, &gl)) {
1880			reply(550, "not found");
1881			goto out;
1882		} else if (gl.gl_pathc == 0) {
1883			errno = ENOENT;
1884			perror_reply(550, whichf);
1885			goto out;
1886		}
1887		dirlist = gl.gl_pathv;
1888	} else {
1889		onefile[0] = whichf;
1890		dirlist = onefile;
1891		simple = 1;
1892	}
1893
1894	if (setjmp(urgcatch)) {
1895		transflag = 0;
1896		goto out;
1897	}
1898	while (dirname = *dirlist++) {
1899		if (stat(dirname, &st) < 0) {
1900			/*
1901			 * If user typed "ls -l", etc, and the client
1902			 * used NLST, do what the user meant.
1903			 */
1904			if (dirname[0] == '-' && *dirlist == NULL &&
1905			    transflag == 0) {
1906				retrieve("/bin/ls %s", dirname);
1907				goto out;
1908			}
1909			perror_reply(550, whichf);
1910			if (dout != NULL) {
1911				(void) fclose(dout);
1912				transflag = 0;
1913				data = -1;
1914				pdata = -1;
1915			}
1916			goto out;
1917		}
1918
1919		if (S_ISREG(st.st_mode)) {
1920			if (dout == NULL) {
1921				dout = dataconn("file list", (off_t)-1, "w");
1922				if (dout == NULL)
1923					goto out;
1924				transflag++;
1925			}
1926			fprintf(dout, "%s%s\n", dirname,
1927				type == TYPE_A ? "\r" : "");
1928			byte_count += strlen(dirname) + 1;
1929			continue;
1930		} else if (!S_ISDIR(st.st_mode))
1931			continue;
1932
1933		if ((dirp = opendir(dirname)) == NULL)
1934			continue;
1935
1936		while ((dir = readdir(dirp)) != NULL) {
1937			char nbuf[MAXPATHLEN];
1938
1939			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1940				continue;
1941			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1942			    dir->d_namlen == 2)
1943				continue;
1944
1945			snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname,
1946				 dir->d_name);
1947
1948			/*
1949			 * We have to do a stat to insure it's
1950			 * not a directory or special file.
1951			 */
1952			if (simple || (stat(nbuf, &st) == 0 &&
1953			    S_ISREG(st.st_mode))) {
1954				if (dout == NULL) {
1955					dout = dataconn("file list", (off_t)-1,
1956						"w");
1957					if (dout == NULL)
1958						goto out;
1959					transflag++;
1960				}
1961				if (nbuf[0] == '.' && nbuf[1] == '/')
1962					fprintf(dout, "%s%s\n", &nbuf[2],
1963						type == TYPE_A ? "\r" : "");
1964				else
1965					fprintf(dout, "%s%s\n", nbuf,
1966						type == TYPE_A ? "\r" : "");
1967				byte_count += strlen(nbuf) + 1;
1968			}
1969		}
1970		(void) closedir(dirp);
1971	}
1972
1973	if (dout == NULL)
1974		reply(550, "No files found.");
1975	else if (ferror(dout) != 0)
1976		perror_reply(550, "Data connection");
1977	else
1978		reply(226, "Transfer complete.");
1979
1980	transflag = 0;
1981	if (dout != NULL)
1982		(void) fclose(dout);
1983	data = -1;
1984	pdata = -1;
1985out:
1986	if (freeglob) {
1987		freeglob = 0;
1988		globfree(&gl);
1989	}
1990}
1991
1992static void
1993reapchild(signo)
1994	int signo;
1995{
1996	while (wait3(NULL, WNOHANG, NULL) > 0);
1997}
1998
1999void
2000logxfer(name, size, start)
2001	char *name;
2002	off_t size;
2003	time_t start;
2004{
2005	char buf[2048];
2006	char path[MAXPATHLEN];
2007	char vremotehost[MAXHOSTNAMELEN*4], vpath[MAXPATHLEN*4];
2008	char *vname, *vpw;
2009	time_t now;
2010
2011	if ((statfd >= 0) && (getcwd(path, sizeof(path)) != NULL)) {
2012		time(&now);
2013
2014		vname = (char *)malloc(strlen(name)*4+1);
2015		if (vname == NULL)
2016			return;
2017		vpw = (char *)malloc(strlen((guest) ? guestpw : pw->pw_name)*4+1);
2018		if (vpw == NULL) {
2019			free(vname);
2020			return;
2021		}
2022
2023		strvis(vremotehost, remotehost, VIS_SAFE|VIS_NOSLASH);
2024		strvis(vpath, path, VIS_SAFE|VIS_NOSLASH);
2025		strvis(vname, name, VIS_SAFE|VIS_NOSLASH);
2026		strvis(vpw, (guest) ? guestpw : pw->pw_name, VIS_SAFE|VIS_NOSLASH);
2027
2028		snprintf(buf, sizeof(buf),
2029			 "%.24s %d %s %qd %s/%s %c %s %c %c %s ftp %d %s %s\n",
2030			 ctime(&now), now - start + (now == start),
2031			 vremotehost, size, vpath, vname,
2032			 ((type == TYPE_A) ? 'a' : 'b'), "*" /* none yet */,
2033			 'o', ((guest) ? 'a' : 'r'),
2034			 vpw, 0 /* none yet */,
2035			 ((guest) ? "*" : pw->pw_name), dhostname);
2036		write(statfd, buf, strlen(buf));
2037		free(vname);
2038		free(vpw);
2039	}
2040}
2041