ftpd.c revision 6740
11592Srgrimes/*
21592Srgrimes * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
31592Srgrimes *	The Regents of the University of California.  All rights reserved.
41592Srgrimes *
51592Srgrimes * Redistribution and use in source and binary forms, with or without
61592Srgrimes * modification, are permitted provided that the following conditions
71592Srgrimes * are met:
81592Srgrimes * 1. Redistributions of source code must retain the above copyright
91592Srgrimes *    notice, this list of conditions and the following disclaimer.
101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111592Srgrimes *    notice, this list of conditions and the following disclaimer in the
121592Srgrimes *    documentation and/or other materials provided with the distribution.
131592Srgrimes * 3. All advertising materials mentioning features or use of this software
141592Srgrimes *    must display the following acknowledgement:
151592Srgrimes *	This product includes software developed by the University of
161592Srgrimes *	California, Berkeley and its contributors.
171592Srgrimes * 4. Neither the name of the University nor the names of its contributors
181592Srgrimes *    may be used to endorse or promote products derived from this software
191592Srgrimes *    without specific prior written permission.
201592Srgrimes *
211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311592Srgrimes * SUCH DAMAGE.
321592Srgrimes */
331592Srgrimes
341592Srgrimes#ifndef lint
351592Srgrimesstatic char copyright[] =
361592Srgrimes"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
371592Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381592Srgrimes#endif /* not lint */
391592Srgrimes
401592Srgrimes#ifndef lint
411592Srgrimesstatic char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
421592Srgrimes#endif /* not lint */
431592Srgrimes
441592Srgrimes/*
451592Srgrimes * FTP server.
461592Srgrimes */
471592Srgrimes#include <sys/param.h>
481592Srgrimes#include <sys/stat.h>
491592Srgrimes#include <sys/ioctl.h>
501592Srgrimes#include <sys/socket.h>
511592Srgrimes#include <sys/wait.h>
521592Srgrimes
531592Srgrimes#include <netinet/in.h>
541592Srgrimes#include <netinet/in_systm.h>
551592Srgrimes#include <netinet/ip.h>
561592Srgrimes
571592Srgrimes#define	FTP_NAMES
581592Srgrimes#include <arpa/ftp.h>
591592Srgrimes#include <arpa/inet.h>
601592Srgrimes#include <arpa/telnet.h>
611592Srgrimes
621592Srgrimes#include <ctype.h>
631592Srgrimes#include <dirent.h>
641592Srgrimes#include <err.h>
651592Srgrimes#include <errno.h>
661592Srgrimes#include <fcntl.h>
671592Srgrimes#include <glob.h>
681592Srgrimes#include <limits.h>
691592Srgrimes#include <netdb.h>
701592Srgrimes#include <pwd.h>
711592Srgrimes#include <setjmp.h>
721592Srgrimes#include <signal.h>
731592Srgrimes#include <stdio.h>
741592Srgrimes#include <stdlib.h>
751592Srgrimes#include <string.h>
761592Srgrimes#include <syslog.h>
771592Srgrimes#include <time.h>
781592Srgrimes#include <unistd.h>
791592Srgrimes
803938Spst#ifdef	SKEY
813938Spst#include <skey.h>
823938Spst#endif
833938Spst
841592Srgrimes#include "pathnames.h"
851592Srgrimes#include "extern.h"
861592Srgrimes
871592Srgrimes#if __STDC__
881592Srgrimes#include <stdarg.h>
891592Srgrimes#else
901592Srgrimes#include <varargs.h>
911592Srgrimes#endif
921592Srgrimes
931592Srgrimesstatic char version[] = "Version 6.00";
941592Srgrimes
951592Srgrimesextern	off_t restart_point;
961592Srgrimesextern	char cbuf[];
971592Srgrimes
981592Srgrimesstruct	sockaddr_in ctrl_addr;
991592Srgrimesstruct	sockaddr_in data_source;
1001592Srgrimesstruct	sockaddr_in data_dest;
1011592Srgrimesstruct	sockaddr_in his_addr;
1021592Srgrimesstruct	sockaddr_in pasv_addr;
1031592Srgrimes
1041592Srgrimesint	data;
1051592Srgrimesjmp_buf	errcatch, urgcatch;
1061592Srgrimesint	logged_in;
1071592Srgrimesstruct	passwd *pw;
1081592Srgrimesint	debug;
1091592Srgrimesint	timeout = 900;    /* timeout after 15 minutes of inactivity */
1101592Srgrimesint	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
1111592Srgrimesint	logging;
1121592Srgrimesint	guest;
1136740Sguido#ifdef STATS
1146740Sguidoint	stats;
1156740Sguidoint	statfd = -1;
1166740Sguido#endif
1171592Srgrimesint	type;
1181592Srgrimesint	form;
1191592Srgrimesint	stru;			/* avoid C keyword */
1201592Srgrimesint	mode;
1211592Srgrimesint	usedefault = 1;		/* for data transfers */
1221592Srgrimesint	pdata = -1;		/* for passive mode */
1231592Srgrimessig_atomic_t transflag;
1241592Srgrimesoff_t	file_size;
1251592Srgrimesoff_t	byte_count;
1261592Srgrimes#if !defined(CMASK) || CMASK == 0
1271592Srgrimes#undef CMASK
1281592Srgrimes#define CMASK 027
1291592Srgrimes#endif
1301592Srgrimesint	defumask = CMASK;		/* default umask value */
1311592Srgrimeschar	tmpline[7];
1321592Srgrimeschar	hostname[MAXHOSTNAMELEN];
1331592Srgrimeschar	remotehost[MAXHOSTNAMELEN];
1346740Sguido#ifdef STATS
1356740Sguidochar	*ident = NULL;
1366740Sguido#endif
1371592Srgrimes
1381592Srgrimes/*
1391592Srgrimes * Timeout intervals for retrying connections
1401592Srgrimes * to hosts that don't accept PORT cmds.  This
1411592Srgrimes * is a kludge, but given the problems with TCP...
1421592Srgrimes */
1431592Srgrimes#define	SWAITMAX	90	/* wait at most 90 seconds */
1441592Srgrimes#define	SWAITINT	5	/* interval between retries */
1451592Srgrimes
1461592Srgrimesint	swaitmax = SWAITMAX;
1471592Srgrimesint	swaitint = SWAITINT;
1481592Srgrimes
1491592Srgrimes#ifdef SETPROCTITLE
1501592Srgrimeschar	**Argv = NULL;		/* pointer to argument vector */
1511592Srgrimeschar	*LastArgv = NULL;	/* end of argv */
1521592Srgrimeschar	proctitle[LINE_MAX];	/* initial part of title */
1531592Srgrimes#endif /* SETPROCTITLE */
1541592Srgrimes
1552193Sguido#ifdef SKEY
1562193Sguidoint	pwok = 0;
1573938Spstchar	addr_string[20];	/* XXX */
1582193Sguido#endif
1592193Sguido
1601592Srgrimes#define LOGCMD(cmd, file) \
1611592Srgrimes	if (logging > 1) \
1621592Srgrimes	    syslog(LOG_INFO,"%s %s%s", cmd, \
1631592Srgrimes		*(file) == '/' ? "" : curdir(), file);
1641592Srgrimes#define LOGCMD2(cmd, file1, file2) \
1651592Srgrimes	 if (logging > 1) \
1661592Srgrimes	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
1671592Srgrimes		*(file1) == '/' ? "" : curdir(), file1, \
1681592Srgrimes		*(file2) == '/' ? "" : curdir(), file2);
1691592Srgrimes#define LOGBYTES(cmd, file, cnt) \
1701592Srgrimes	if (logging > 1) { \
1711592Srgrimes		if (cnt == (off_t)-1) \
1721592Srgrimes		    syslog(LOG_INFO,"%s %s%s", cmd, \
1731592Srgrimes			*(file) == '/' ? "" : curdir(), file); \
1741592Srgrimes		else \
1751592Srgrimes		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
1761592Srgrimes			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
1771592Srgrimes	}
1781592Srgrimes
1791592Srgrimesstatic void	 ack __P((char *));
1801592Srgrimesstatic void	 myoob __P((int));
1811592Srgrimesstatic int	 checkuser __P((char *));
1821592Srgrimesstatic FILE	*dataconn __P((char *, off_t, char *));
1831592Srgrimesstatic void	 dolog __P((struct sockaddr_in *));
1841592Srgrimesstatic char	*curdir __P((void));
1851592Srgrimesstatic void	 end_login __P((void));
1861592Srgrimesstatic FILE	*getdatasock __P((char *));
1871592Srgrimesstatic char	*gunique __P((char *));
1881592Srgrimesstatic void	 lostconn __P((int));
1891592Srgrimesstatic int	 receive_data __P((FILE *, FILE *));
1901592Srgrimesstatic void	 send_data __P((FILE *, FILE *, off_t));
1911592Srgrimesstatic struct passwd *
1921592Srgrimes		 sgetpwnam __P((char *));
1931592Srgrimesstatic char	*sgetsave __P((char *));
1941592Srgrimes
1951592Srgrimesstatic char *
1961592Srgrimescurdir()
1971592Srgrimes{
1981592Srgrimes	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
1991592Srgrimes
2001592Srgrimes	if (getcwd(path, sizeof(path)-2) == NULL)
2011592Srgrimes		return ("");
2021592Srgrimes	if (path[1] != '\0')		/* special case for root dir. */
2031592Srgrimes		strcat(path, "/");
2041592Srgrimes	/* For guest account, skip / since it's chrooted */
2051592Srgrimes	return (guest ? path+1 : path);
2061592Srgrimes}
2071592Srgrimes
2081592Srgrimesint
2091592Srgrimesmain(argc, argv, envp)
2101592Srgrimes	int argc;
2111592Srgrimes	char *argv[];
2121592Srgrimes	char **envp;
2131592Srgrimes{
2141592Srgrimes	int addrlen, ch, on = 1, tos;
2151592Srgrimes	char *cp, line[LINE_MAX];
2161592Srgrimes	FILE *fd;
2171592Srgrimes
2183938Spst	tzset();		/* in case no timezone database in ~ftp */
2193938Spst
2201592Srgrimes	/*
2211592Srgrimes	 * LOG_NDELAY sets up the logging connection immediately,
2221592Srgrimes	 * necessary for anonymous ftp's that chroot and can't do it later.
2231592Srgrimes	 */
2241592Srgrimes	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
2251592Srgrimes	addrlen = sizeof(his_addr);
2261592Srgrimes	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
2271592Srgrimes		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
2281592Srgrimes		exit(1);
2291592Srgrimes	}
2303938Spst#ifdef SKEY
2313938Spst	strcpy(addr_string, inet_ntoa(his_addr.sin_addr));
2323938Spst#endif
2331592Srgrimes	addrlen = sizeof(ctrl_addr);
2341592Srgrimes	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
2351592Srgrimes		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
2361592Srgrimes		exit(1);
2371592Srgrimes	}
2381592Srgrimes#ifdef IP_TOS
2391592Srgrimes	tos = IPTOS_LOWDELAY;
2401592Srgrimes	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
2411592Srgrimes		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
2421592Srgrimes#endif
2431592Srgrimes	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
2441592Srgrimes	debug = 0;
2451592Srgrimes#ifdef SETPROCTITLE
2461592Srgrimes	/*
2471592Srgrimes	 *  Save start and extent of argv for setproctitle.
2481592Srgrimes	 */
2491592Srgrimes	Argv = argv;
2501592Srgrimes	while (*envp)
2511592Srgrimes		envp++;
2521592Srgrimes	LastArgv = envp[-1] + strlen(envp[-1]);
2531592Srgrimes#endif /* SETPROCTITLE */
2541592Srgrimes
2556740Sguido
2566740Sguido#ifdef STATS
2576740Sguido	while ((ch = getopt(argc, argv, "dlSt:T:u:v")) != EOF) {
2586740Sguido#else
2591592Srgrimes	while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) {
2606740Sguido#endif
2611592Srgrimes		switch (ch) {
2621592Srgrimes		case 'd':
2631592Srgrimes			debug = 1;
2641592Srgrimes			break;
2651592Srgrimes
2661592Srgrimes		case 'l':
2671592Srgrimes			logging++;	/* > 1 == extra logging */
2681592Srgrimes			break;
2691592Srgrimes
2701592Srgrimes		case 't':
2711592Srgrimes			timeout = atoi(optarg);
2721592Srgrimes			if (maxtimeout < timeout)
2731592Srgrimes				maxtimeout = timeout;
2741592Srgrimes			break;
2756740Sguido#ifdef STATS
2766740Sguido		case 'S':
2776740Sguido			stats =  1;
2786740Sguido			break;
2796740Sguido#endif
2801592Srgrimes		case 'T':
2811592Srgrimes			maxtimeout = atoi(optarg);
2821592Srgrimes			if (timeout > maxtimeout)
2831592Srgrimes				timeout = maxtimeout;
2841592Srgrimes			break;
2851592Srgrimes
2861592Srgrimes		case 'u':
2871592Srgrimes		    {
2881592Srgrimes			long val = 0;
2891592Srgrimes
2901592Srgrimes			val = strtol(optarg, &optarg, 8);
2911592Srgrimes			if (*optarg != '\0' || val < 0)
2921592Srgrimes				warnx("bad value for -u");
2931592Srgrimes			else
2941592Srgrimes				defumask = val;
2951592Srgrimes			break;
2961592Srgrimes		    }
2971592Srgrimes
2981592Srgrimes		case 'v':
2991592Srgrimes			debug = 1;
3001592Srgrimes			break;
3011592Srgrimes
3021592Srgrimes		default:
3031592Srgrimes			warnx("unknown flag -%c ignored", optopt);
3041592Srgrimes			break;
3051592Srgrimes		}
3061592Srgrimes	}
3071592Srgrimes	(void) freopen(_PATH_DEVNULL, "w", stderr);
3081592Srgrimes	(void) signal(SIGPIPE, lostconn);
3091592Srgrimes	(void) signal(SIGCHLD, SIG_IGN);
3101592Srgrimes	if ((int)signal(SIGURG, myoob) < 0)
3111592Srgrimes		syslog(LOG_ERR, "signal: %m");
3121592Srgrimes
3131592Srgrimes	/* Try to handle urgent data inline */
3141592Srgrimes#ifdef SO_OOBINLINE
3151592Srgrimes	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
3161592Srgrimes		syslog(LOG_ERR, "setsockopt: %m");
3171592Srgrimes#endif
3181592Srgrimes
3191592Srgrimes#ifdef	F_SETOWN
3201592Srgrimes	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
3211592Srgrimes		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
3221592Srgrimes#endif
3231592Srgrimes	dolog(&his_addr);
3241592Srgrimes	/*
3251592Srgrimes	 * Set up default state
3261592Srgrimes	 */
3271592Srgrimes	data = -1;
3281592Srgrimes	type = TYPE_A;
3291592Srgrimes	form = FORM_N;
3301592Srgrimes	stru = STRU_F;
3311592Srgrimes	mode = MODE_S;
3321592Srgrimes	tmpline[0] = '\0';
3331592Srgrimes
3341592Srgrimes	/* If logins are disabled, print out the message. */
3351592Srgrimes	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
3361592Srgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
3371592Srgrimes			if ((cp = strchr(line, '\n')) != NULL)
3381592Srgrimes				*cp = '\0';
3391592Srgrimes			lreply(530, "%s", line);
3401592Srgrimes		}
3411592Srgrimes		(void) fflush(stdout);
3421592Srgrimes		(void) fclose(fd);
3431592Srgrimes		reply(530, "System not available.");
3441592Srgrimes		exit(0);
3451592Srgrimes	}
3461592Srgrimes	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
3471592Srgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
3481592Srgrimes			if ((cp = strchr(line, '\n')) != NULL)
3491592Srgrimes				*cp = '\0';
3501592Srgrimes			lreply(220, "%s", line);
3511592Srgrimes		}
3521592Srgrimes		(void) fflush(stdout);
3531592Srgrimes		(void) fclose(fd);
3541592Srgrimes		/* reply(220,) must follow */
3551592Srgrimes	}
3561592Srgrimes	(void) gethostname(hostname, sizeof(hostname));
3571592Srgrimes	reply(220, "%s FTP server (%s) ready.", hostname, version);
3581592Srgrimes	(void) setjmp(errcatch);
3591592Srgrimes	for (;;)
3601592Srgrimes		(void) yyparse();
3611592Srgrimes	/* NOTREACHED */
3621592Srgrimes}
3631592Srgrimes
3641592Srgrimesstatic void
3651592Srgrimeslostconn(signo)
3661592Srgrimes	int signo;
3671592Srgrimes{
3681592Srgrimes
3691592Srgrimes	if (debug)
3701592Srgrimes		syslog(LOG_DEBUG, "lost connection");
3711592Srgrimes	dologout(-1);
3721592Srgrimes}
3731592Srgrimes
3741592Srgrimesstatic char ttyline[20];
3751592Srgrimes
3761592Srgrimes/*
3771592Srgrimes * Helper function for sgetpwnam().
3781592Srgrimes */
3791592Srgrimesstatic char *
3801592Srgrimessgetsave(s)
3811592Srgrimes	char *s;
3821592Srgrimes{
3831592Srgrimes	char *new = malloc((unsigned) strlen(s) + 1);
3841592Srgrimes
3851592Srgrimes	if (new == NULL) {
3861592Srgrimes		perror_reply(421, "Local resource failure: malloc");
3871592Srgrimes		dologout(1);
3881592Srgrimes		/* NOTREACHED */
3891592Srgrimes	}
3901592Srgrimes	(void) strcpy(new, s);
3911592Srgrimes	return (new);
3921592Srgrimes}
3931592Srgrimes
3941592Srgrimes/*
3951592Srgrimes * Save the result of a getpwnam.  Used for USER command, since
3961592Srgrimes * the data returned must not be clobbered by any other command
3971592Srgrimes * (e.g., globbing).
3981592Srgrimes */
3991592Srgrimesstatic struct passwd *
4001592Srgrimessgetpwnam(name)
4011592Srgrimes	char *name;
4021592Srgrimes{
4031592Srgrimes	static struct passwd save;
4041592Srgrimes	struct passwd *p;
4051592Srgrimes
4061592Srgrimes	if ((p = getpwnam(name)) == NULL)
4071592Srgrimes		return (p);
4081592Srgrimes	if (save.pw_name) {
4091592Srgrimes		free(save.pw_name);
4101592Srgrimes		free(save.pw_passwd);
4111592Srgrimes		free(save.pw_gecos);
4121592Srgrimes		free(save.pw_dir);
4131592Srgrimes		free(save.pw_shell);
4141592Srgrimes	}
4151592Srgrimes	save = *p;
4161592Srgrimes	save.pw_name = sgetsave(p->pw_name);
4171592Srgrimes	save.pw_passwd = sgetsave(p->pw_passwd);
4181592Srgrimes	save.pw_gecos = sgetsave(p->pw_gecos);
4191592Srgrimes	save.pw_dir = sgetsave(p->pw_dir);
4201592Srgrimes	save.pw_shell = sgetsave(p->pw_shell);
4211592Srgrimes	return (&save);
4221592Srgrimes}
4231592Srgrimes
4241592Srgrimesstatic int login_attempts;	/* number of failed login attempts */
4251592Srgrimesstatic int askpasswd;		/* had user command, ask for passwd */
4261592Srgrimesstatic char curname[10];	/* current USER name */
4271592Srgrimes
4281592Srgrimes/*
4291592Srgrimes * USER command.
4301592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable;
4311592Srgrimes * sets askpasswd if a PASS command is expected.  If logged in previously,
4321592Srgrimes * need to reset state.  If name is "ftp" or "anonymous", the name is not in
4331592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
4341592Srgrimes * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
4351592Srgrimes * requesting login privileges.  Disallow anyone who does not have a standard
4361592Srgrimes * shell as returned by getusershell().  Disallow anyone mentioned in the file
4371592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
4381592Srgrimes */
4391592Srgrimesvoid
4401592Srgrimesuser(name)
4411592Srgrimes	char *name;
4421592Srgrimes{
4431592Srgrimes	char *cp, *shell;
4441592Srgrimes
4451592Srgrimes	if (logged_in) {
4461592Srgrimes		if (guest) {
4471592Srgrimes			reply(530, "Can't change user from guest login.");
4481592Srgrimes			return;
4491592Srgrimes		}
4501592Srgrimes		end_login();
4511592Srgrimes	}
4521592Srgrimes
4531592Srgrimes	guest = 0;
4541592Srgrimes	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
4551592Srgrimes		if (checkuser("ftp") || checkuser("anonymous"))
4561592Srgrimes			reply(530, "User %s access denied.", name);
4571592Srgrimes		else if ((pw = sgetpwnam("ftp")) != NULL) {
4581592Srgrimes			guest = 1;
4591592Srgrimes			askpasswd = 1;
4601592Srgrimes			reply(331,
4613938Spst			"Guest login ok, send your email address as password.");
4621592Srgrimes		} else
4631592Srgrimes			reply(530, "User %s unknown.", name);
4641592Srgrimes		if (!askpasswd && logging)
4651592Srgrimes			syslog(LOG_NOTICE,
4661592Srgrimes			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
4671592Srgrimes		return;
4681592Srgrimes	}
4691592Srgrimes	if (pw = sgetpwnam(name)) {
4701592Srgrimes		if ((shell = pw->pw_shell) == NULL || *shell == 0)
4711592Srgrimes			shell = _PATH_BSHELL;
4721592Srgrimes		while ((cp = getusershell()) != NULL)
4731592Srgrimes			if (strcmp(cp, shell) == 0)
4741592Srgrimes				break;
4751592Srgrimes		endusershell();
4761592Srgrimes
4771592Srgrimes		if (cp == NULL || checkuser(name)) {
4781592Srgrimes			reply(530, "User %s access denied.", name);
4791592Srgrimes			if (logging)
4801592Srgrimes				syslog(LOG_NOTICE,
4811592Srgrimes				    "FTP LOGIN REFUSED FROM %s, %s",
4821592Srgrimes				    remotehost, name);
4831592Srgrimes			pw = (struct passwd *) NULL;
4841592Srgrimes			return;
4851592Srgrimes		}
4861592Srgrimes	}
4871592Srgrimes	if (logging)
4881592Srgrimes		strncpy(curname, name, sizeof(curname)-1);
4892193Sguido#ifdef SKEY
4903938Spst	pwok = skeyaccess(name, NULL, remotehost, addr_string);
4912193Sguido	reply(331, "%s", skey_challenge(name, pw, pwok));
4922193Sguido#else
4931592Srgrimes	reply(331, "Password required for %s.", name);
4942193Sguido#endif
4951592Srgrimes	askpasswd = 1;
4961592Srgrimes	/*
4971592Srgrimes	 * Delay before reading passwd after first failed
4981592Srgrimes	 * attempt to slow down passwd-guessing programs.
4991592Srgrimes	 */
5001592Srgrimes	if (login_attempts)
5011592Srgrimes		sleep((unsigned) login_attempts);
5021592Srgrimes}
5031592Srgrimes
5041592Srgrimes/*
5051592Srgrimes * Check if a user is in the file _PATH_FTPUSERS
5061592Srgrimes */
5071592Srgrimesstatic int
5081592Srgrimescheckuser(name)
5091592Srgrimes	char *name;
5101592Srgrimes{
5111592Srgrimes	FILE *fd;
5121592Srgrimes	int found = 0;
5131592Srgrimes	char *p, line[BUFSIZ];
5141592Srgrimes
5151592Srgrimes	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
5161592Srgrimes		while (fgets(line, sizeof(line), fd) != NULL)
5171592Srgrimes			if ((p = strchr(line, '\n')) != NULL) {
5181592Srgrimes				*p = '\0';
5191592Srgrimes				if (line[0] == '#')
5201592Srgrimes					continue;
5212930Sdg				if (strcmp(line, name) == 0) {
5221592Srgrimes					found = 1;
5231592Srgrimes					break;
5241592Srgrimes				}
5251592Srgrimes			}
5261592Srgrimes		(void) fclose(fd);
5271592Srgrimes	}
5281592Srgrimes	return (found);
5291592Srgrimes}
5301592Srgrimes
5311592Srgrimes/*
5321592Srgrimes * Terminate login as previous user, if any, resetting state;
5331592Srgrimes * used when USER command is given or login fails.
5341592Srgrimes */
5351592Srgrimesstatic void
5361592Srgrimesend_login()
5371592Srgrimes{
5381592Srgrimes
5391592Srgrimes	(void) seteuid((uid_t)0);
5401592Srgrimes	if (logged_in)
5411592Srgrimes		logwtmp(ttyline, "", "");
5421592Srgrimes	pw = NULL;
5431592Srgrimes	logged_in = 0;
5441592Srgrimes	guest = 0;
5451592Srgrimes}
5461592Srgrimes
5471592Srgrimesvoid
5481592Srgrimespass(passwd)
5491592Srgrimes	char *passwd;
5501592Srgrimes{
5511592Srgrimes	char *salt, *xpasswd;
5521592Srgrimes	FILE *fd;
5531592Srgrimes
5541592Srgrimes	if (logged_in || askpasswd == 0) {
5551592Srgrimes		reply(503, "Login with USER first.");
5561592Srgrimes		return;
5571592Srgrimes	}
5581592Srgrimes	askpasswd = 0;
5591592Srgrimes	if (!guest) {		/* "ftp" is only account allowed no password */
5601592Srgrimes		if (pw == NULL)
5611592Srgrimes			salt = "xx";
5621592Srgrimes		else
5631592Srgrimes			salt = pw->pw_passwd;
5642193Sguido#ifdef SKEY
5652193Sguido		xpasswd = skey_crypt(passwd, salt, pw, pwok);
5663206Spst		pwok = 0;
5672193Sguido#else
5681592Srgrimes		xpasswd = crypt(passwd, salt);
5692193Sguido#endif
5701592Srgrimes		/* The strcmp does not catch null passwords! */
5711592Srgrimes		if (pw == NULL || *pw->pw_passwd == '\0' ||
5721592Srgrimes		    strcmp(xpasswd, pw->pw_passwd)) {
5731592Srgrimes			reply(530, "Login incorrect.");
5741592Srgrimes			if (logging)
5751592Srgrimes				syslog(LOG_NOTICE,
5761592Srgrimes				    "FTP LOGIN FAILED FROM %s, %s",
5771592Srgrimes				    remotehost, curname);
5781592Srgrimes			pw = NULL;
5791592Srgrimes			if (login_attempts++ >= 5) {
5801592Srgrimes				syslog(LOG_NOTICE,
5811592Srgrimes				    "repeated login failures from %s",
5821592Srgrimes				    remotehost);
5831592Srgrimes				exit(0);
5841592Srgrimes			}
5851592Srgrimes			return;
5861592Srgrimes		}
5871592Srgrimes	}
5881592Srgrimes	login_attempts = 0;		/* this time successful */
5891592Srgrimes	if (setegid((gid_t)pw->pw_gid) < 0) {
5901592Srgrimes		reply(550, "Can't set gid.");
5911592Srgrimes		return;
5921592Srgrimes	}
5931592Srgrimes	(void) initgroups(pw->pw_name, pw->pw_gid);
5941592Srgrimes
5951592Srgrimes	/* open wtmp before chroot */
5961592Srgrimes	(void)sprintf(ttyline, "ftp%d", getpid());
5971592Srgrimes	logwtmp(ttyline, pw->pw_name, remotehost);
5981592Srgrimes	logged_in = 1;
5991592Srgrimes
6006740Sguido#ifdef STATS
6016740Sguido	if (guest && stats == 1 && statfd < 0)
6026740Sguido		if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
6036740Sguido			stats = 0;
6046740Sguido#endif
6056740Sguido
6061592Srgrimes	if (guest) {
6071592Srgrimes		/*
6081592Srgrimes		 * We MUST do a chdir() after the chroot. Otherwise
6091592Srgrimes		 * the old current directory will be accessible as "."
6101592Srgrimes		 * outside the new root!
6111592Srgrimes		 */
6121592Srgrimes		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
6131592Srgrimes			reply(550, "Can't set guest privileges.");
6141592Srgrimes			goto bad;
6151592Srgrimes		}
6161592Srgrimes	} else if (chdir(pw->pw_dir) < 0) {
6171592Srgrimes		if (chdir("/") < 0) {
6181592Srgrimes			reply(530, "User %s: can't change directory to %s.",
6191592Srgrimes			    pw->pw_name, pw->pw_dir);
6201592Srgrimes			goto bad;
6211592Srgrimes		} else
6221592Srgrimes			lreply(230, "No directory! Logging in with home=/");
6231592Srgrimes	}
6241592Srgrimes	if (seteuid((uid_t)pw->pw_uid) < 0) {
6251592Srgrimes		reply(550, "Can't set uid.");
6261592Srgrimes		goto bad;
6271592Srgrimes	}
6281592Srgrimes	/*
6291592Srgrimes	 * Display a login message, if it exists.
6301592Srgrimes	 * N.B. reply(230,) must follow the message.
6311592Srgrimes	 */
6321592Srgrimes	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
6331592Srgrimes		char *cp, line[LINE_MAX];
6341592Srgrimes
6351592Srgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
6361592Srgrimes			if ((cp = strchr(line, '\n')) != NULL)
6371592Srgrimes				*cp = '\0';
6381592Srgrimes			lreply(230, "%s", line);
6391592Srgrimes		}
6401592Srgrimes		(void) fflush(stdout);
6411592Srgrimes		(void) fclose(fd);
6421592Srgrimes	}
6431592Srgrimes	if (guest) {
6446740Sguido#ifdef STATS
6456740Sguido		char * copy();
6466740Sguido
6476740Sguido		if (ident != NULL)
6486740Sguido			free(ident);
6496740Sguido		ident = (char *) copy(passwd);
6506740Sguido#endif
6511592Srgrimes		reply(230, "Guest login ok, access restrictions apply.");
6521592Srgrimes#ifdef SETPROCTITLE
6531592Srgrimes		snprintf(proctitle, sizeof(proctitle),
6541592Srgrimes		    "%s: anonymous/%.*s", remotehost,
6551592Srgrimes		    sizeof(proctitle) - sizeof(remotehost) -
6561592Srgrimes		    sizeof(": anonymous/"), passwd);
6571592Srgrimes		setproctitle(proctitle);
6581592Srgrimes#endif /* SETPROCTITLE */
6591592Srgrimes		if (logging)
6601592Srgrimes			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
6611592Srgrimes			    remotehost, passwd);
6621592Srgrimes	} else {
6631592Srgrimes		reply(230, "User %s logged in.", pw->pw_name);
6641592Srgrimes#ifdef SETPROCTITLE
6651592Srgrimes		snprintf(proctitle, sizeof(proctitle),
6661592Srgrimes		    "%s: %s", remotehost, pw->pw_name);
6671592Srgrimes		setproctitle(proctitle);
6681592Srgrimes#endif /* SETPROCTITLE */
6691592Srgrimes		if (logging)
6701592Srgrimes			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
6711592Srgrimes			    remotehost, pw->pw_name);
6721592Srgrimes	}
6731592Srgrimes	(void) umask(defumask);
6741592Srgrimes	return;
6751592Srgrimesbad:
6761592Srgrimes	/* Forget all about it... */
6771592Srgrimes	end_login();
6781592Srgrimes}
6791592Srgrimes
6801592Srgrimesvoid
6811592Srgrimesretrieve(cmd, name)
6821592Srgrimes	char *cmd, *name;
6831592Srgrimes{
6841592Srgrimes	FILE *fin, *dout;
6851592Srgrimes	struct stat st;
6861592Srgrimes	int (*closefunc) __P((FILE *));
6876740Sguido#ifdef STATS
6886740Sguido	long start;
6896740Sguido#endif
6901592Srgrimes
6911592Srgrimes	if (cmd == 0) {
6921592Srgrimes		fin = fopen(name, "r"), closefunc = fclose;
6931592Srgrimes		st.st_size = 0;
6941592Srgrimes	} else {
6951592Srgrimes		char line[BUFSIZ];
6961592Srgrimes
6971592Srgrimes		(void) sprintf(line, cmd, name), name = line;
6981592Srgrimes		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
6991592Srgrimes		st.st_size = -1;
7001592Srgrimes		st.st_blksize = BUFSIZ;
7011592Srgrimes	}
7021592Srgrimes	if (fin == NULL) {
7031592Srgrimes		if (errno != 0) {
7041592Srgrimes			perror_reply(550, name);
7051592Srgrimes			if (cmd == 0) {
7061592Srgrimes				LOGCMD("get", name);
7071592Srgrimes			}
7081592Srgrimes		}
7091592Srgrimes		return;
7101592Srgrimes	}
7111592Srgrimes	byte_count = -1;
7121592Srgrimes	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
7131592Srgrimes		reply(550, "%s: not a plain file.", name);
7141592Srgrimes		goto done;
7151592Srgrimes	}
7161592Srgrimes	if (restart_point) {
7171592Srgrimes		if (type == TYPE_A) {
7181592Srgrimes			off_t i, n;
7191592Srgrimes			int c;
7201592Srgrimes
7211592Srgrimes			n = restart_point;
7221592Srgrimes			i = 0;
7231592Srgrimes			while (i++ < n) {
7241592Srgrimes				if ((c=getc(fin)) == EOF) {
7251592Srgrimes					perror_reply(550, name);
7261592Srgrimes					goto done;
7271592Srgrimes				}
7281592Srgrimes				if (c == '\n')
7291592Srgrimes					i++;
7301592Srgrimes			}
7311592Srgrimes		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
7321592Srgrimes			perror_reply(550, name);
7331592Srgrimes			goto done;
7341592Srgrimes		}
7351592Srgrimes	}
7361592Srgrimes	dout = dataconn(name, st.st_size, "w");
7371592Srgrimes	if (dout == NULL)
7381592Srgrimes		goto done;
7396740Sguido#ifdef STATS
7406740Sguido	time(&start);
7416740Sguido#endif
7421592Srgrimes	send_data(fin, dout, st.st_blksize);
7436740Sguido#ifdef STATS
7446740Sguido	if (cmd == 0 && guest && stats)
7456740Sguido		logxfer( name, st.st_size, start);
7466740Sguido#endif
7471592Srgrimes	(void) fclose(dout);
7481592Srgrimes	data = -1;
7491592Srgrimes	pdata = -1;
7501592Srgrimesdone:
7511592Srgrimes	if (cmd == 0)
7521592Srgrimes		LOGBYTES("get", name, byte_count);
7531592Srgrimes	(*closefunc)(fin);
7541592Srgrimes}
7551592Srgrimes
7561592Srgrimesvoid
7571592Srgrimesstore(name, mode, unique)
7581592Srgrimes	char *name, *mode;
7591592Srgrimes	int unique;
7601592Srgrimes{
7611592Srgrimes	FILE *fout, *din;
7621592Srgrimes	struct stat st;
7631592Srgrimes	int (*closefunc) __P((FILE *));
7641592Srgrimes
7651592Srgrimes	if (unique && stat(name, &st) == 0 &&
7661592Srgrimes	    (name = gunique(name)) == NULL) {
7671592Srgrimes		LOGCMD(*mode == 'w' ? "put" : "append", name);
7681592Srgrimes		return;
7691592Srgrimes	}
7701592Srgrimes
7711592Srgrimes	if (restart_point)
7721592Srgrimes		mode = "r+";
7731592Srgrimes	fout = fopen(name, mode);
7741592Srgrimes	closefunc = fclose;
7751592Srgrimes	if (fout == NULL) {
7761592Srgrimes		perror_reply(553, name);
7771592Srgrimes		LOGCMD(*mode == 'w' ? "put" : "append", name);
7781592Srgrimes		return;
7791592Srgrimes	}
7801592Srgrimes	byte_count = -1;
7811592Srgrimes	if (restart_point) {
7821592Srgrimes		if (type == TYPE_A) {
7831592Srgrimes			off_t i, n;
7841592Srgrimes			int c;
7851592Srgrimes
7861592Srgrimes			n = restart_point;
7871592Srgrimes			i = 0;
7881592Srgrimes			while (i++ < n) {
7891592Srgrimes				if ((c=getc(fout)) == EOF) {
7901592Srgrimes					perror_reply(550, name);
7911592Srgrimes					goto done;
7921592Srgrimes				}
7931592Srgrimes				if (c == '\n')
7941592Srgrimes					i++;
7951592Srgrimes			}
7961592Srgrimes			/*
7971592Srgrimes			 * We must do this seek to "current" position
7981592Srgrimes			 * because we are changing from reading to
7991592Srgrimes			 * writing.
8001592Srgrimes			 */
8011592Srgrimes			if (fseek(fout, 0L, L_INCR) < 0) {
8021592Srgrimes				perror_reply(550, name);
8031592Srgrimes				goto done;
8041592Srgrimes			}
8051592Srgrimes		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
8061592Srgrimes			perror_reply(550, name);
8071592Srgrimes			goto done;
8081592Srgrimes		}
8091592Srgrimes	}
8101592Srgrimes	din = dataconn(name, (off_t)-1, "r");
8111592Srgrimes	if (din == NULL)
8121592Srgrimes		goto done;
8131592Srgrimes	if (receive_data(din, fout) == 0) {
8141592Srgrimes		if (unique)
8151592Srgrimes			reply(226, "Transfer complete (unique file name:%s).",
8161592Srgrimes			    name);
8171592Srgrimes		else
8181592Srgrimes			reply(226, "Transfer complete.");
8191592Srgrimes	}
8201592Srgrimes	(void) fclose(din);
8211592Srgrimes	data = -1;
8221592Srgrimes	pdata = -1;
8231592Srgrimesdone:
8241592Srgrimes	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
8251592Srgrimes	(*closefunc)(fout);
8261592Srgrimes}
8271592Srgrimes
8281592Srgrimesstatic FILE *
8291592Srgrimesgetdatasock(mode)
8301592Srgrimes	char *mode;
8311592Srgrimes{
8321592Srgrimes	int on = 1, s, t, tries;
8331592Srgrimes
8341592Srgrimes	if (data >= 0)
8351592Srgrimes		return (fdopen(data, mode));
8361592Srgrimes	(void) seteuid((uid_t)0);
8371592Srgrimes	s = socket(AF_INET, SOCK_STREAM, 0);
8381592Srgrimes	if (s < 0)
8391592Srgrimes		goto bad;
8401592Srgrimes	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
8411592Srgrimes	    (char *) &on, sizeof(on)) < 0)
8421592Srgrimes		goto bad;
8431592Srgrimes	/* anchor socket to avoid multi-homing problems */
8441592Srgrimes	data_source.sin_family = AF_INET;
8451592Srgrimes	data_source.sin_addr = ctrl_addr.sin_addr;
8461592Srgrimes	for (tries = 1; ; tries++) {
8471592Srgrimes		if (bind(s, (struct sockaddr *)&data_source,
8481592Srgrimes		    sizeof(data_source)) >= 0)
8491592Srgrimes			break;
8501592Srgrimes		if (errno != EADDRINUSE || tries > 10)
8511592Srgrimes			goto bad;
8521592Srgrimes		sleep(tries);
8531592Srgrimes	}
8541592Srgrimes	(void) seteuid((uid_t)pw->pw_uid);
8551592Srgrimes#ifdef IP_TOS
8561592Srgrimes	on = IPTOS_THROUGHPUT;
8571592Srgrimes	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
8581592Srgrimes		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
8591592Srgrimes#endif
8601592Srgrimes	return (fdopen(s, mode));
8611592Srgrimesbad:
8621592Srgrimes	/* Return the real value of errno (close may change it) */
8631592Srgrimes	t = errno;
8641592Srgrimes	(void) seteuid((uid_t)pw->pw_uid);
8651592Srgrimes	(void) close(s);
8661592Srgrimes	errno = t;
8671592Srgrimes	return (NULL);
8681592Srgrimes}
8691592Srgrimes
8701592Srgrimesstatic FILE *
8711592Srgrimesdataconn(name, size, mode)
8721592Srgrimes	char *name;
8731592Srgrimes	off_t size;
8741592Srgrimes	char *mode;
8751592Srgrimes{
8761592Srgrimes	char sizebuf[32];
8771592Srgrimes	FILE *file;
8781592Srgrimes	int retry = 0, tos;
8791592Srgrimes
8801592Srgrimes	file_size = size;
8811592Srgrimes	byte_count = 0;
8821592Srgrimes	if (size != (off_t) -1)
8831592Srgrimes		(void) sprintf(sizebuf, " (%qd bytes)", size);
8841592Srgrimes	else
8851592Srgrimes		(void) strcpy(sizebuf, "");
8861592Srgrimes	if (pdata >= 0) {
8871592Srgrimes		struct sockaddr_in from;
8881592Srgrimes		int s, fromlen = sizeof(from);
8891592Srgrimes
8901592Srgrimes		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
8911592Srgrimes		if (s < 0) {
8921592Srgrimes			reply(425, "Can't open data connection.");
8931592Srgrimes			(void) close(pdata);
8941592Srgrimes			pdata = -1;
8951592Srgrimes			return (NULL);
8961592Srgrimes		}
8971592Srgrimes		(void) close(pdata);
8981592Srgrimes		pdata = s;
8991592Srgrimes#ifdef IP_TOS
9001592Srgrimes		tos = IPTOS_LOWDELAY;
9011592Srgrimes		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
9021592Srgrimes		    sizeof(int));
9031592Srgrimes#endif
9041592Srgrimes		reply(150, "Opening %s mode data connection for '%s'%s.",
9051592Srgrimes		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
9061592Srgrimes		return (fdopen(pdata, mode));
9071592Srgrimes	}
9081592Srgrimes	if (data >= 0) {
9091592Srgrimes		reply(125, "Using existing data connection for '%s'%s.",
9101592Srgrimes		    name, sizebuf);
9111592Srgrimes		usedefault = 1;
9121592Srgrimes		return (fdopen(data, mode));
9131592Srgrimes	}
9141592Srgrimes	if (usedefault)
9151592Srgrimes		data_dest = his_addr;
9161592Srgrimes	usedefault = 1;
9171592Srgrimes	file = getdatasock(mode);
9181592Srgrimes	if (file == NULL) {
9191592Srgrimes		reply(425, "Can't create data socket (%s,%d): %s.",
9201592Srgrimes		    inet_ntoa(data_source.sin_addr),
9211592Srgrimes		    ntohs(data_source.sin_port), strerror(errno));
9221592Srgrimes		return (NULL);
9231592Srgrimes	}
9241592Srgrimes	data = fileno(file);
9251592Srgrimes	while (connect(data, (struct sockaddr *)&data_dest,
9261592Srgrimes	    sizeof(data_dest)) < 0) {
9271592Srgrimes		if (errno == EADDRINUSE && retry < swaitmax) {
9281592Srgrimes			sleep((unsigned) swaitint);
9291592Srgrimes			retry += swaitint;
9301592Srgrimes			continue;
9311592Srgrimes		}
9321592Srgrimes		perror_reply(425, "Can't build data connection");
9331592Srgrimes		(void) fclose(file);
9341592Srgrimes		data = -1;
9351592Srgrimes		return (NULL);
9361592Srgrimes	}
9371592Srgrimes	reply(150, "Opening %s mode data connection for '%s'%s.",
9381592Srgrimes	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
9391592Srgrimes	return (file);
9401592Srgrimes}
9411592Srgrimes
9421592Srgrimes/*
9431592Srgrimes * Tranfer the contents of "instr" to "outstr" peer using the appropriate
9441592Srgrimes * encapsulation of the data subject * to Mode, Structure, and Type.
9451592Srgrimes *
9461592Srgrimes * NB: Form isn't handled.
9471592Srgrimes */
9481592Srgrimesstatic void
9491592Srgrimessend_data(instr, outstr, blksize)
9501592Srgrimes	FILE *instr, *outstr;
9511592Srgrimes	off_t blksize;
9521592Srgrimes{
9531592Srgrimes	int c, cnt, filefd, netfd;
9541592Srgrimes	char *buf;
9551592Srgrimes
9561592Srgrimes	transflag++;
9571592Srgrimes	if (setjmp(urgcatch)) {
9581592Srgrimes		transflag = 0;
9591592Srgrimes		return;
9601592Srgrimes	}
9611592Srgrimes	switch (type) {
9621592Srgrimes
9631592Srgrimes	case TYPE_A:
9641592Srgrimes		while ((c = getc(instr)) != EOF) {
9651592Srgrimes			byte_count++;
9661592Srgrimes			if (c == '\n') {
9671592Srgrimes				if (ferror(outstr))
9681592Srgrimes					goto data_err;
9691592Srgrimes				(void) putc('\r', outstr);
9701592Srgrimes			}
9711592Srgrimes			(void) putc(c, outstr);
9721592Srgrimes		}
9731592Srgrimes		fflush(outstr);
9741592Srgrimes		transflag = 0;
9751592Srgrimes		if (ferror(instr))
9761592Srgrimes			goto file_err;
9771592Srgrimes		if (ferror(outstr))
9781592Srgrimes			goto data_err;
9791592Srgrimes		reply(226, "Transfer complete.");
9801592Srgrimes		return;
9811592Srgrimes
9821592Srgrimes	case TYPE_I:
9831592Srgrimes	case TYPE_L:
9841592Srgrimes		if ((buf = malloc((u_int)blksize)) == NULL) {
9851592Srgrimes			transflag = 0;
9861592Srgrimes			perror_reply(451, "Local resource failure: malloc");
9871592Srgrimes			return;
9881592Srgrimes		}
9891592Srgrimes		netfd = fileno(outstr);
9901592Srgrimes		filefd = fileno(instr);
9911592Srgrimes		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
9921592Srgrimes		    write(netfd, buf, cnt) == cnt)
9931592Srgrimes			byte_count += cnt;
9941592Srgrimes		transflag = 0;
9951592Srgrimes		(void)free(buf);
9961592Srgrimes		if (cnt != 0) {
9971592Srgrimes			if (cnt < 0)
9981592Srgrimes				goto file_err;
9991592Srgrimes			goto data_err;
10001592Srgrimes		}
10011592Srgrimes		reply(226, "Transfer complete.");
10021592Srgrimes		return;
10031592Srgrimes	default:
10041592Srgrimes		transflag = 0;
10051592Srgrimes		reply(550, "Unimplemented TYPE %d in send_data", type);
10061592Srgrimes		return;
10071592Srgrimes	}
10081592Srgrimes
10091592Srgrimesdata_err:
10101592Srgrimes	transflag = 0;
10111592Srgrimes	perror_reply(426, "Data connection");
10121592Srgrimes	return;
10131592Srgrimes
10141592Srgrimesfile_err:
10151592Srgrimes	transflag = 0;
10161592Srgrimes	perror_reply(551, "Error on input file");
10171592Srgrimes}
10181592Srgrimes
10191592Srgrimes/*
10201592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of
10211592Srgrimes * the data subject to Mode, Structure, and Type.
10221592Srgrimes *
10231592Srgrimes * N.B.: Form isn't handled.
10241592Srgrimes */
10251592Srgrimesstatic int
10261592Srgrimesreceive_data(instr, outstr)
10271592Srgrimes	FILE *instr, *outstr;
10281592Srgrimes{
10291592Srgrimes	int c;
10301592Srgrimes	int cnt, bare_lfs = 0;
10311592Srgrimes	char buf[BUFSIZ];
10321592Srgrimes
10331592Srgrimes	transflag++;
10341592Srgrimes	if (setjmp(urgcatch)) {
10351592Srgrimes		transflag = 0;
10361592Srgrimes		return (-1);
10371592Srgrimes	}
10381592Srgrimes	switch (type) {
10391592Srgrimes
10401592Srgrimes	case TYPE_I:
10411592Srgrimes	case TYPE_L:
10421592Srgrimes		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
10431592Srgrimes			if (write(fileno(outstr), buf, cnt) != cnt)
10441592Srgrimes				goto file_err;
10451592Srgrimes			byte_count += cnt;
10461592Srgrimes		}
10471592Srgrimes		if (cnt < 0)
10481592Srgrimes			goto data_err;
10491592Srgrimes		transflag = 0;
10501592Srgrimes		return (0);
10511592Srgrimes
10521592Srgrimes	case TYPE_E:
10531592Srgrimes		reply(553, "TYPE E not implemented.");
10541592Srgrimes		transflag = 0;
10551592Srgrimes		return (-1);
10561592Srgrimes
10571592Srgrimes	case TYPE_A:
10581592Srgrimes		while ((c = getc(instr)) != EOF) {
10591592Srgrimes			byte_count++;
10601592Srgrimes			if (c == '\n')
10611592Srgrimes				bare_lfs++;
10621592Srgrimes			while (c == '\r') {
10631592Srgrimes				if (ferror(outstr))
10641592Srgrimes					goto data_err;
10651592Srgrimes				if ((c = getc(instr)) != '\n') {
10661592Srgrimes					(void) putc ('\r', outstr);
10671592Srgrimes					if (c == '\0' || c == EOF)
10681592Srgrimes						goto contin2;
10691592Srgrimes				}
10701592Srgrimes			}
10711592Srgrimes			(void) putc(c, outstr);
10721592Srgrimes	contin2:	;
10731592Srgrimes		}
10741592Srgrimes		fflush(outstr);
10751592Srgrimes		if (ferror(instr))
10761592Srgrimes			goto data_err;
10771592Srgrimes		if (ferror(outstr))
10781592Srgrimes			goto file_err;
10791592Srgrimes		transflag = 0;
10801592Srgrimes		if (bare_lfs) {
10811592Srgrimes			lreply(226,
10821592Srgrimes		"WARNING! %d bare linefeeds received in ASCII mode",
10831592Srgrimes			    bare_lfs);
10841592Srgrimes		(void)printf("   File may not have transferred correctly.\r\n");
10851592Srgrimes		}
10861592Srgrimes		return (0);
10871592Srgrimes	default:
10881592Srgrimes		reply(550, "Unimplemented TYPE %d in receive_data", type);
10891592Srgrimes		transflag = 0;
10901592Srgrimes		return (-1);
10911592Srgrimes	}
10921592Srgrimes
10931592Srgrimesdata_err:
10941592Srgrimes	transflag = 0;
10951592Srgrimes	perror_reply(426, "Data Connection");
10961592Srgrimes	return (-1);
10971592Srgrimes
10981592Srgrimesfile_err:
10991592Srgrimes	transflag = 0;
11001592Srgrimes	perror_reply(452, "Error writing file");
11011592Srgrimes	return (-1);
11021592Srgrimes}
11031592Srgrimes
11041592Srgrimesvoid
11051592Srgrimesstatfilecmd(filename)
11061592Srgrimes	char *filename;
11071592Srgrimes{
11081592Srgrimes	FILE *fin;
11091592Srgrimes	int c;
11101592Srgrimes	char line[LINE_MAX];
11111592Srgrimes
11121592Srgrimes	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
11131592Srgrimes	fin = ftpd_popen(line, "r");
11141592Srgrimes	lreply(211, "status of %s:", filename);
11151592Srgrimes	while ((c = getc(fin)) != EOF) {
11161592Srgrimes		if (c == '\n') {
11171592Srgrimes			if (ferror(stdout)){
11181592Srgrimes				perror_reply(421, "control connection");
11191592Srgrimes				(void) ftpd_pclose(fin);
11201592Srgrimes				dologout(1);
11211592Srgrimes				/* NOTREACHED */
11221592Srgrimes			}
11231592Srgrimes			if (ferror(fin)) {
11241592Srgrimes				perror_reply(551, filename);
11251592Srgrimes				(void) ftpd_pclose(fin);
11261592Srgrimes				return;
11271592Srgrimes			}
11281592Srgrimes			(void) putc('\r', stdout);
11291592Srgrimes		}
11301592Srgrimes		(void) putc(c, stdout);
11311592Srgrimes	}
11321592Srgrimes	(void) ftpd_pclose(fin);
11331592Srgrimes	reply(211, "End of Status");
11341592Srgrimes}
11351592Srgrimes
11361592Srgrimesvoid
11371592Srgrimesstatcmd()
11381592Srgrimes{
11391592Srgrimes	struct sockaddr_in *sin;
11401592Srgrimes	u_char *a, *p;
11411592Srgrimes
11421592Srgrimes	lreply(211, "%s FTP server status:", hostname, version);
11431592Srgrimes	printf("     %s\r\n", version);
11441592Srgrimes	printf("     Connected to %s", remotehost);
11451592Srgrimes	if (!isdigit(remotehost[0]))
11461592Srgrimes		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
11471592Srgrimes	printf("\r\n");
11481592Srgrimes	if (logged_in) {
11491592Srgrimes		if (guest)
11501592Srgrimes			printf("     Logged in anonymously\r\n");
11511592Srgrimes		else
11521592Srgrimes			printf("     Logged in as %s\r\n", pw->pw_name);
11531592Srgrimes	} else if (askpasswd)
11541592Srgrimes		printf("     Waiting for password\r\n");
11551592Srgrimes	else
11561592Srgrimes		printf("     Waiting for user name\r\n");
11571592Srgrimes	printf("     TYPE: %s", typenames[type]);
11581592Srgrimes	if (type == TYPE_A || type == TYPE_E)
11591592Srgrimes		printf(", FORM: %s", formnames[form]);
11601592Srgrimes	if (type == TYPE_L)
11611592Srgrimes#if NBBY == 8
11621592Srgrimes		printf(" %d", NBBY);
11631592Srgrimes#else
11641592Srgrimes		printf(" %d", bytesize);	/* need definition! */
11651592Srgrimes#endif
11661592Srgrimes	printf("; STRUcture: %s; transfer MODE: %s\r\n",
11671592Srgrimes	    strunames[stru], modenames[mode]);
11681592Srgrimes	if (data != -1)
11691592Srgrimes		printf("     Data connection open\r\n");
11701592Srgrimes	else if (pdata != -1) {
11711592Srgrimes		printf("     in Passive mode");
11721592Srgrimes		sin = &pasv_addr;
11731592Srgrimes		goto printaddr;
11741592Srgrimes	} else if (usedefault == 0) {
11751592Srgrimes		printf("     PORT");
11761592Srgrimes		sin = &data_dest;
11771592Srgrimesprintaddr:
11781592Srgrimes		a = (u_char *) &sin->sin_addr;
11791592Srgrimes		p = (u_char *) &sin->sin_port;
11801592Srgrimes#define UC(b) (((int) b) & 0xff)
11811592Srgrimes		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
11821592Srgrimes			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
11831592Srgrimes#undef UC
11841592Srgrimes	} else
11851592Srgrimes		printf("     No data connection\r\n");
11861592Srgrimes	reply(211, "End of status");
11871592Srgrimes}
11881592Srgrimes
11891592Srgrimesvoid
11901592Srgrimesfatal(s)
11911592Srgrimes	char *s;
11921592Srgrimes{
11931592Srgrimes
11941592Srgrimes	reply(451, "Error in server: %s\n", s);
11951592Srgrimes	reply(221, "Closing connection due to server error.");
11961592Srgrimes	dologout(0);
11971592Srgrimes	/* NOTREACHED */
11981592Srgrimes}
11991592Srgrimes
12001592Srgrimesvoid
12011592Srgrimes#if __STDC__
12021592Srgrimesreply(int n, const char *fmt, ...)
12031592Srgrimes#else
12041592Srgrimesreply(n, fmt, va_alist)
12051592Srgrimes	int n;
12061592Srgrimes	char *fmt;
12071592Srgrimes        va_dcl
12081592Srgrimes#endif
12091592Srgrimes{
12101592Srgrimes	va_list ap;
12111592Srgrimes#if __STDC__
12121592Srgrimes	va_start(ap, fmt);
12131592Srgrimes#else
12141592Srgrimes	va_start(ap);
12151592Srgrimes#endif
12161592Srgrimes	(void)printf("%d ", n);
12171592Srgrimes	(void)vprintf(fmt, ap);
12181592Srgrimes	(void)printf("\r\n");
12191592Srgrimes	(void)fflush(stdout);
12201592Srgrimes	if (debug) {
12211592Srgrimes		syslog(LOG_DEBUG, "<--- %d ", n);
12221592Srgrimes		vsyslog(LOG_DEBUG, fmt, ap);
12231592Srgrimes	}
12241592Srgrimes}
12251592Srgrimes
12261592Srgrimesvoid
12271592Srgrimes#if __STDC__
12281592Srgrimeslreply(int n, const char *fmt, ...)
12291592Srgrimes#else
12301592Srgrimeslreply(n, fmt, va_alist)
12311592Srgrimes	int n;
12321592Srgrimes	char *fmt;
12331592Srgrimes        va_dcl
12341592Srgrimes#endif
12351592Srgrimes{
12361592Srgrimes	va_list ap;
12371592Srgrimes#if __STDC__
12381592Srgrimes	va_start(ap, fmt);
12391592Srgrimes#else
12401592Srgrimes	va_start(ap);
12411592Srgrimes#endif
12421592Srgrimes	(void)printf("%d- ", n);
12431592Srgrimes	(void)vprintf(fmt, ap);
12441592Srgrimes	(void)printf("\r\n");
12451592Srgrimes	(void)fflush(stdout);
12461592Srgrimes	if (debug) {
12471592Srgrimes		syslog(LOG_DEBUG, "<--- %d- ", n);
12481592Srgrimes		vsyslog(LOG_DEBUG, fmt, ap);
12491592Srgrimes	}
12501592Srgrimes}
12511592Srgrimes
12521592Srgrimesstatic void
12531592Srgrimesack(s)
12541592Srgrimes	char *s;
12551592Srgrimes{
12561592Srgrimes
12571592Srgrimes	reply(250, "%s command successful.", s);
12581592Srgrimes}
12591592Srgrimes
12601592Srgrimesvoid
12611592Srgrimesnack(s)
12621592Srgrimes	char *s;
12631592Srgrimes{
12641592Srgrimes
12651592Srgrimes	reply(502, "%s command not implemented.", s);
12661592Srgrimes}
12671592Srgrimes
12681592Srgrimes/* ARGSUSED */
12691592Srgrimesvoid
12701592Srgrimesyyerror(s)
12711592Srgrimes	char *s;
12721592Srgrimes{
12731592Srgrimes	char *cp;
12741592Srgrimes
12751592Srgrimes	if (cp = strchr(cbuf,'\n'))
12761592Srgrimes		*cp = '\0';
12771592Srgrimes	reply(500, "'%s': command not understood.", cbuf);
12781592Srgrimes}
12791592Srgrimes
12801592Srgrimesvoid
12811592Srgrimesdelete(name)
12821592Srgrimes	char *name;
12831592Srgrimes{
12841592Srgrimes	struct stat st;
12851592Srgrimes
12861592Srgrimes	LOGCMD("delete", name);
12871592Srgrimes	if (stat(name, &st) < 0) {
12881592Srgrimes		perror_reply(550, name);
12891592Srgrimes		return;
12901592Srgrimes	}
12911592Srgrimes	if ((st.st_mode&S_IFMT) == S_IFDIR) {
12921592Srgrimes		if (rmdir(name) < 0) {
12931592Srgrimes			perror_reply(550, name);
12941592Srgrimes			return;
12951592Srgrimes		}
12961592Srgrimes		goto done;
12971592Srgrimes	}
12981592Srgrimes	if (unlink(name) < 0) {
12991592Srgrimes		perror_reply(550, name);
13001592Srgrimes		return;
13011592Srgrimes	}
13021592Srgrimesdone:
13031592Srgrimes	ack("DELE");
13041592Srgrimes}
13051592Srgrimes
13061592Srgrimesvoid
13071592Srgrimescwd(path)
13081592Srgrimes	char *path;
13091592Srgrimes{
13101592Srgrimes
13111592Srgrimes	if (chdir(path) < 0)
13121592Srgrimes		perror_reply(550, path);
13131592Srgrimes	else
13141592Srgrimes		ack("CWD");
13151592Srgrimes}
13161592Srgrimes
13171592Srgrimesvoid
13181592Srgrimesmakedir(name)
13191592Srgrimes	char *name;
13201592Srgrimes{
13211592Srgrimes
13221592Srgrimes	LOGCMD("mkdir", name);
13231592Srgrimes	if (mkdir(name, 0777) < 0)
13241592Srgrimes		perror_reply(550, name);
13251592Srgrimes	else
13261592Srgrimes		reply(257, "MKD command successful.");
13271592Srgrimes}
13281592Srgrimes
13291592Srgrimesvoid
13301592Srgrimesremovedir(name)
13311592Srgrimes	char *name;
13321592Srgrimes{
13331592Srgrimes
13341592Srgrimes	LOGCMD("rmdir", name);
13351592Srgrimes	if (rmdir(name) < 0)
13361592Srgrimes		perror_reply(550, name);
13371592Srgrimes	else
13381592Srgrimes		ack("RMD");
13391592Srgrimes}
13401592Srgrimes
13411592Srgrimesvoid
13421592Srgrimespwd()
13431592Srgrimes{
13441592Srgrimes	char path[MAXPATHLEN + 1];
13451592Srgrimes
13461592Srgrimes	if (getwd(path) == (char *)NULL)
13471592Srgrimes		reply(550, "%s.", path);
13481592Srgrimes	else
13491592Srgrimes		reply(257, "\"%s\" is current directory.", path);
13501592Srgrimes}
13511592Srgrimes
13521592Srgrimeschar *
13531592Srgrimesrenamefrom(name)
13541592Srgrimes	char *name;
13551592Srgrimes{
13561592Srgrimes	struct stat st;
13571592Srgrimes
13581592Srgrimes	if (stat(name, &st) < 0) {
13591592Srgrimes		perror_reply(550, name);
13601592Srgrimes		return ((char *)0);
13611592Srgrimes	}
13621592Srgrimes	reply(350, "File exists, ready for destination name");
13631592Srgrimes	return (name);
13641592Srgrimes}
13651592Srgrimes
13661592Srgrimesvoid
13671592Srgrimesrenamecmd(from, to)
13681592Srgrimes	char *from, *to;
13691592Srgrimes{
13701592Srgrimes
13711592Srgrimes	LOGCMD2("rename", from, to);
13721592Srgrimes	if (rename(from, to) < 0)
13731592Srgrimes		perror_reply(550, "rename");
13741592Srgrimes	else
13751592Srgrimes		ack("RNTO");
13761592Srgrimes}
13771592Srgrimes
13781592Srgrimesstatic void
13791592Srgrimesdolog(sin)
13801592Srgrimes	struct sockaddr_in *sin;
13811592Srgrimes{
13821592Srgrimes	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
13831592Srgrimes		sizeof(struct in_addr), AF_INET);
13841592Srgrimes
13851592Srgrimes	if (hp)
13861592Srgrimes		(void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
13871592Srgrimes	else
13881592Srgrimes		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
13891592Srgrimes		    sizeof(remotehost));
13901592Srgrimes#ifdef SETPROCTITLE
13911592Srgrimes	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
13921592Srgrimes	setproctitle(proctitle);
13931592Srgrimes#endif /* SETPROCTITLE */
13941592Srgrimes
13951592Srgrimes	if (logging)
13961592Srgrimes		syslog(LOG_INFO, "connection from %s", remotehost);
13971592Srgrimes}
13981592Srgrimes
13991592Srgrimes/*
14001592Srgrimes * Record logout in wtmp file
14011592Srgrimes * and exit with supplied status.
14021592Srgrimes */
14031592Srgrimesvoid
14041592Srgrimesdologout(status)
14051592Srgrimes	int status;
14061592Srgrimes{
14071592Srgrimes
14081592Srgrimes	if (logged_in) {
14091592Srgrimes		(void) seteuid((uid_t)0);
14101592Srgrimes		logwtmp(ttyline, "", "");
14111592Srgrimes	}
14121592Srgrimes	/* beware of flushing buffers after a SIGPIPE */
14131592Srgrimes	_exit(status);
14141592Srgrimes}
14151592Srgrimes
14161592Srgrimesstatic void
14171592Srgrimesmyoob(signo)
14181592Srgrimes	int signo;
14191592Srgrimes{
14201592Srgrimes	char *cp;
14211592Srgrimes
14221592Srgrimes	/* only process if transfer occurring */
14231592Srgrimes	if (!transflag)
14241592Srgrimes		return;
14251592Srgrimes	cp = tmpline;
14261592Srgrimes	if (getline(cp, 7, stdin) == NULL) {
14271592Srgrimes		reply(221, "You could at least say goodbye.");
14281592Srgrimes		dologout(0);
14291592Srgrimes	}
14301592Srgrimes	upper(cp);
14311592Srgrimes	if (strcmp(cp, "ABOR\r\n") == 0) {
14321592Srgrimes		tmpline[0] = '\0';
14331592Srgrimes		reply(426, "Transfer aborted. Data connection closed.");
14341592Srgrimes		reply(226, "Abort successful");
14351592Srgrimes		longjmp(urgcatch, 1);
14361592Srgrimes	}
14371592Srgrimes	if (strcmp(cp, "STAT\r\n") == 0) {
14381592Srgrimes		if (file_size != (off_t) -1)
14391592Srgrimes			reply(213, "Status: %qd of %qd bytes transferred",
14401592Srgrimes			    byte_count, file_size);
14411592Srgrimes		else
14421592Srgrimes			reply(213, "Status: %qd bytes transferred", byte_count);
14431592Srgrimes	}
14441592Srgrimes}
14451592Srgrimes
14461592Srgrimes/*
14471592Srgrimes * Note: a response of 425 is not mentioned as a possible response to
14481592Srgrimes *	the PASV command in RFC959. However, it has been blessed as
14491592Srgrimes *	a legitimate response by Jon Postel in a telephone conversation
14501592Srgrimes *	with Rick Adams on 25 Jan 89.
14511592Srgrimes */
14521592Srgrimesvoid
14531592Srgrimespassive()
14541592Srgrimes{
14551592Srgrimes	int len;
14561592Srgrimes	char *p, *a;
14571592Srgrimes
14581592Srgrimes	pdata = socket(AF_INET, SOCK_STREAM, 0);
14591592Srgrimes	if (pdata < 0) {
14601592Srgrimes		perror_reply(425, "Can't open passive connection");
14611592Srgrimes		return;
14621592Srgrimes	}
14631592Srgrimes	pasv_addr = ctrl_addr;
14641592Srgrimes	pasv_addr.sin_port = 0;
14651592Srgrimes	(void) seteuid((uid_t)0);
14661592Srgrimes	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
14671592Srgrimes		(void) seteuid((uid_t)pw->pw_uid);
14681592Srgrimes		goto pasv_error;
14691592Srgrimes	}
14701592Srgrimes	(void) seteuid((uid_t)pw->pw_uid);
14711592Srgrimes	len = sizeof(pasv_addr);
14721592Srgrimes	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
14731592Srgrimes		goto pasv_error;
14741592Srgrimes	if (listen(pdata, 1) < 0)
14751592Srgrimes		goto pasv_error;
14761592Srgrimes	a = (char *) &pasv_addr.sin_addr;
14771592Srgrimes	p = (char *) &pasv_addr.sin_port;
14781592Srgrimes
14791592Srgrimes#define UC(b) (((int) b) & 0xff)
14801592Srgrimes
14811592Srgrimes	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
14821592Srgrimes		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
14831592Srgrimes	return;
14841592Srgrimes
14851592Srgrimespasv_error:
14861592Srgrimes	(void) close(pdata);
14871592Srgrimes	pdata = -1;
14881592Srgrimes	perror_reply(425, "Can't open passive connection");
14891592Srgrimes	return;
14901592Srgrimes}
14911592Srgrimes
14921592Srgrimes/*
14931592Srgrimes * Generate unique name for file with basename "local".
14941592Srgrimes * The file named "local" is already known to exist.
14951592Srgrimes * Generates failure reply on error.
14961592Srgrimes */
14971592Srgrimesstatic char *
14981592Srgrimesgunique(local)
14991592Srgrimes	char *local;
15001592Srgrimes{
15011592Srgrimes	static char new[MAXPATHLEN];
15021592Srgrimes	struct stat st;
15031592Srgrimes	int count;
15041592Srgrimes	char *cp;
15051592Srgrimes
15061592Srgrimes	cp = strrchr(local, '/');
15071592Srgrimes	if (cp)
15081592Srgrimes		*cp = '\0';
15091592Srgrimes	if (stat(cp ? local : ".", &st) < 0) {
15101592Srgrimes		perror_reply(553, cp ? local : ".");
15111592Srgrimes		return ((char *) 0);
15121592Srgrimes	}
15131592Srgrimes	if (cp)
15141592Srgrimes		*cp = '/';
15151592Srgrimes	(void) strcpy(new, local);
15161592Srgrimes	cp = new + strlen(new);
15171592Srgrimes	*cp++ = '.';
15181592Srgrimes	for (count = 1; count < 100; count++) {
15191592Srgrimes		(void)sprintf(cp, "%d", count);
15201592Srgrimes		if (stat(new, &st) < 0)
15211592Srgrimes			return (new);
15221592Srgrimes	}
15231592Srgrimes	reply(452, "Unique file name cannot be created.");
15241592Srgrimes	return (NULL);
15251592Srgrimes}
15261592Srgrimes
15271592Srgrimes/*
15281592Srgrimes * Format and send reply containing system error number.
15291592Srgrimes */
15301592Srgrimesvoid
15311592Srgrimesperror_reply(code, string)
15321592Srgrimes	int code;
15331592Srgrimes	char *string;
15341592Srgrimes{
15351592Srgrimes
15361592Srgrimes	reply(code, "%s: %s.", string, strerror(errno));
15371592Srgrimes}
15381592Srgrimes
15391592Srgrimesstatic char *onefile[] = {
15401592Srgrimes	"",
15411592Srgrimes	0
15421592Srgrimes};
15431592Srgrimes
15441592Srgrimesvoid
15451592Srgrimessend_file_list(whichf)
15461592Srgrimes	char *whichf;
15471592Srgrimes{
15481592Srgrimes	struct stat st;
15491592Srgrimes	DIR *dirp = NULL;
15501592Srgrimes	struct dirent *dir;
15511592Srgrimes	FILE *dout = NULL;
15521592Srgrimes	char **dirlist, *dirname;
15531592Srgrimes	int simple = 0;
15541592Srgrimes	int freeglob = 0;
15551592Srgrimes	glob_t gl;
15561592Srgrimes
15571592Srgrimes	if (strpbrk(whichf, "~{[*?") != NULL) {
15581592Srgrimes		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
15591592Srgrimes
15601592Srgrimes		memset(&gl, 0, sizeof(gl));
15611592Srgrimes		freeglob = 1;
15621592Srgrimes		if (glob(whichf, flags, 0, &gl)) {
15631592Srgrimes			reply(550, "not found");
15641592Srgrimes			goto out;
15651592Srgrimes		} else if (gl.gl_pathc == 0) {
15661592Srgrimes			errno = ENOENT;
15671592Srgrimes			perror_reply(550, whichf);
15681592Srgrimes			goto out;
15691592Srgrimes		}
15701592Srgrimes		dirlist = gl.gl_pathv;
15711592Srgrimes	} else {
15721592Srgrimes		onefile[0] = whichf;
15731592Srgrimes		dirlist = onefile;
15741592Srgrimes		simple = 1;
15751592Srgrimes	}
15761592Srgrimes
15771592Srgrimes	if (setjmp(urgcatch)) {
15781592Srgrimes		transflag = 0;
15791592Srgrimes		goto out;
15801592Srgrimes	}
15811592Srgrimes	while (dirname = *dirlist++) {
15821592Srgrimes		if (stat(dirname, &st) < 0) {
15831592Srgrimes			/*
15841592Srgrimes			 * If user typed "ls -l", etc, and the client
15851592Srgrimes			 * used NLST, do what the user meant.
15861592Srgrimes			 */
15871592Srgrimes			if (dirname[0] == '-' && *dirlist == NULL &&
15881592Srgrimes			    transflag == 0) {
15891592Srgrimes				retrieve("/bin/ls %s", dirname);
15901592Srgrimes				goto out;
15911592Srgrimes			}
15921592Srgrimes			perror_reply(550, whichf);
15931592Srgrimes			if (dout != NULL) {
15941592Srgrimes				(void) fclose(dout);
15951592Srgrimes				transflag = 0;
15961592Srgrimes				data = -1;
15971592Srgrimes				pdata = -1;
15981592Srgrimes			}
15991592Srgrimes			goto out;
16001592Srgrimes		}
16011592Srgrimes
16021592Srgrimes		if (S_ISREG(st.st_mode)) {
16031592Srgrimes			if (dout == NULL) {
16041592Srgrimes				dout = dataconn("file list", (off_t)-1, "w");
16051592Srgrimes				if (dout == NULL)
16061592Srgrimes					goto out;
16071592Srgrimes				transflag++;
16081592Srgrimes			}
16091592Srgrimes			fprintf(dout, "%s%s\n", dirname,
16101592Srgrimes				type == TYPE_A ? "\r" : "");
16111592Srgrimes			byte_count += strlen(dirname) + 1;
16121592Srgrimes			continue;
16131592Srgrimes		} else if (!S_ISDIR(st.st_mode))
16141592Srgrimes			continue;
16151592Srgrimes
16161592Srgrimes		if ((dirp = opendir(dirname)) == NULL)
16171592Srgrimes			continue;
16181592Srgrimes
16191592Srgrimes		while ((dir = readdir(dirp)) != NULL) {
16201592Srgrimes			char nbuf[MAXPATHLEN];
16211592Srgrimes
16221592Srgrimes			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
16231592Srgrimes				continue;
16241592Srgrimes			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
16251592Srgrimes			    dir->d_namlen == 2)
16261592Srgrimes				continue;
16271592Srgrimes
16281592Srgrimes			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
16291592Srgrimes
16301592Srgrimes			/*
16311592Srgrimes			 * We have to do a stat to insure it's
16321592Srgrimes			 * not a directory or special file.
16331592Srgrimes			 */
16341592Srgrimes			if (simple || (stat(nbuf, &st) == 0 &&
16351592Srgrimes			    S_ISREG(st.st_mode))) {
16361592Srgrimes				if (dout == NULL) {
16371592Srgrimes					dout = dataconn("file list", (off_t)-1,
16381592Srgrimes						"w");
16391592Srgrimes					if (dout == NULL)
16401592Srgrimes						goto out;
16411592Srgrimes					transflag++;
16421592Srgrimes				}
16431592Srgrimes				if (nbuf[0] == '.' && nbuf[1] == '/')
16441592Srgrimes					fprintf(dout, "%s%s\n", &nbuf[2],
16451592Srgrimes						type == TYPE_A ? "\r" : "");
16461592Srgrimes				else
16471592Srgrimes					fprintf(dout, "%s%s\n", nbuf,
16481592Srgrimes						type == TYPE_A ? "\r" : "");
16491592Srgrimes				byte_count += strlen(nbuf) + 1;
16501592Srgrimes			}
16511592Srgrimes		}
16521592Srgrimes		(void) closedir(dirp);
16531592Srgrimes	}
16541592Srgrimes
16551592Srgrimes	if (dout == NULL)
16561592Srgrimes		reply(550, "No files found.");
16571592Srgrimes	else if (ferror(dout) != 0)
16581592Srgrimes		perror_reply(550, "Data connection");
16591592Srgrimes	else
16601592Srgrimes		reply(226, "Transfer complete.");
16611592Srgrimes
16621592Srgrimes	transflag = 0;
16631592Srgrimes	if (dout != NULL)
16641592Srgrimes		(void) fclose(dout);
16651592Srgrimes	data = -1;
16661592Srgrimes	pdata = -1;
16671592Srgrimesout:
16681592Srgrimes	if (freeglob) {
16691592Srgrimes		freeglob = 0;
16701592Srgrimes		globfree(&gl);
16711592Srgrimes	}
16721592Srgrimes}
16731592Srgrimes
16741592Srgrimes#ifdef SETPROCTITLE
16751592Srgrimes/*
16761592Srgrimes * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
16771592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't
16781592Srgrimes * have much of an environment or arglist to overwrite.
16791592Srgrimes */
16801592Srgrimesvoid
16811592Srgrimes#if __STDC__
16821592Srgrimessetproctitle(const char *fmt, ...)
16831592Srgrimes#else
16841592Srgrimessetproctitle(fmt, va_alist)
16851592Srgrimes	char *fmt;
16861592Srgrimes        va_dcl
16871592Srgrimes#endif
16881592Srgrimes{
16891592Srgrimes	int i;
16901592Srgrimes	va_list ap;
16911592Srgrimes	char *p, *bp, ch;
16921592Srgrimes	char buf[LINE_MAX];
16931592Srgrimes
16941592Srgrimes#if __STDC__
16951592Srgrimes	va_start(ap, fmt);
16961592Srgrimes#else
16971592Srgrimes	va_start(ap);
16981592Srgrimes#endif
16991592Srgrimes	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
17001592Srgrimes
17011592Srgrimes	/* make ps print our process name */
17021592Srgrimes	p = Argv[0];
17031592Srgrimes	*p++ = '-';
17041592Srgrimes
17051592Srgrimes	i = strlen(buf);
17061592Srgrimes	if (i > LastArgv - p - 2) {
17071592Srgrimes		i = LastArgv - p - 2;
17081592Srgrimes		buf[i] = '\0';
17091592Srgrimes	}
17101592Srgrimes	bp = buf;
17111592Srgrimes	while (ch = *bp++)
17121592Srgrimes		if (ch != '\n' && ch != '\r')
17131592Srgrimes			*p++ = ch;
17141592Srgrimes	while (p < LastArgv)
17151592Srgrimes		*p++ = ' ';
17161592Srgrimes}
17171592Srgrimes#endif /* SETPROCTITLE */
17186740Sguido
17196740Sguido#ifdef STATS
17206740Sguidologxfer(name, size, start)
17216740Sguido	char *name;
17226740Sguido	long size;
17236740Sguido	long start;
17246740Sguido{
17256740Sguido	char buf[1024];
17266740Sguido	char path[MAXPATHLEN + 1];
17276740Sguido	long now;
17286740Sguido
17296740Sguido	if (statfd >= 0 && getwd(path) != NULL) {
17306740Sguido		time(&now);
17316740Sguido		sprintf(buf, "%.20s!%s!%s!%s/%s!%ld!%ld\n",
17326740Sguido			ctime(&now)+4, ident, remotehost,
17336740Sguido			path, name, size, now - start + (now == start));
17346740Sguido		write(statfd, buf, strlen(buf));
17356740Sguido	}
17366740Sguido}
17376740Sguido
17386740Sguidochar *
17396740Sguidocopy(s)
17406740Sguido	char *s;
17416740Sguido{
17426740Sguido	char *p;
17436740Sguido
17446740Sguido	p = malloc((unsigned) strlen(s) + 1);
17456740Sguido	if (p == NULL)
17466740Sguido		fatal("Ran out of memory.");
17476740Sguido	(void) strcpy(p, s);
17486740Sguido	return (p);
17496740Sguido}
17506740Sguido#endif
1751