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