summaryrefslogtreecommitdiffstats
path: root/contrib/opie/opieftpd.c
diff options
context:
space:
mode:
authorpst <pst@FreeBSD.org>1997-02-06 17:52:29 +0000
committerpst <pst@FreeBSD.org>1997-02-06 17:52:29 +0000
commit2dfcbf193123fd16b26454eeffa4bbd014e52c53 (patch)
treeec9d150c9da4390c2d223a04ac002523cbfd7f36 /contrib/opie/opieftpd.c
downloadFreeBSD-src-2dfcbf193123fd16b26454eeffa4bbd014e52c53.zip
FreeBSD-src-2dfcbf193123fd16b26454eeffa4bbd014e52c53.tar.gz
Initial import of OPIE v2.3 from
ftp://ftp.nrl.navy.mil/pub/security/opie/
Diffstat (limited to 'contrib/opie/opieftpd.c')
-rw-r--r--contrib/opie/opieftpd.c1643
1 files changed, 1643 insertions, 0 deletions
diff --git a/contrib/opie/opieftpd.c b/contrib/opie/opieftpd.c
new file mode 100644
index 0000000..b11ece4
--- /dev/null
+++ b/contrib/opie/opieftpd.c
@@ -0,0 +1,1643 @@
+/* opieftpd.c: Main program for an FTP daemon.
+
+%%% portions-copyright-cmetz
+Portions of this software are Copyright 1996 by Craig Metz, All Rights
+Reserved. The Inner Net License Version 2 applies to these portions of
+the software.
+You should have received a copy of the license with this software. If
+you didn't get a copy, you may request one from <license@inner.net>.
+
+Portions of this software are Copyright 1995 by Randall Atkinson and Dan
+McDonald, All Rights Reserved. All Rights under this copyright are assigned
+to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
+License Agreement applies to this software.
+
+ History:
+
+ Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
+ Moved LS_COMMAND here.
+ Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
+ Removed useless strings (I don't think that removing the
+ ucb copyright one is a problem -- please let me know if
+ I'm wrong). Changed default CMASK to 077. Removed random
+ comments. Use ANSI stdargs for reply/lreply if we can,
+ added stdargs version of reply/lreply. Don't declare the
+ tos variable unless IP_TOS defined. Include stdargs headers
+ early. More headers ifdefed. Made everything static.
+ Got rid of gethostname() call and use of hostname. Pared
+ down status response for places where header files frequently
+ cause trouble. Made logging of user logins (ala -l)
+ non-optional. Moved reply()/lrepy(). Fixed some prototypes.
+ Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
+ result of opiechallenge (allows access control to work).
+ Added patches for AIX. Symbol changes for autoconf.
+ Modified at NRL for OPIE 2.01. Changed password lookup handling
+ to avoid problems with drain-bamaged shadow password packages.
+ Properly handle internal state for anonymous FTP. Unlock
+ user accounts properly if login fails because of /etc/shells.
+ Make sure to close syslog by function to avoid problems with
+ drain bamaged syslog implementations.
+ Modified at NRL for OPIE 2.0.
+ Originally from BSD Net/2.
+
+ There is some really, really ugly code in here.
+*/
+/*
+ * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opie_cfg.h"
+
+#if HAVE_ANSISTDARG
+#include <stdarg.h>
+#endif /* HAVE_ANSISTDARG */
+
+/*
+ * FTP server.
+ */
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+#include <sys/stat.h>
+/* #include <sys/ioctl.h> */
+#include <sys/socket.h>
+#include <sys/wait.h>
+#ifdef SYS_FCNTL_H
+#include <sys/fcntl.h>
+#else
+#include <fcntl.h>
+#endif /* SYS_FCNTL_H */
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#define FTP_NAMES
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include <signal.h>
+#include <dirent.h>
+#include <fcntl.h>
+#if HAVE_TIME_H
+#include <time.h>
+#endif /* HAVE_TIME_H */
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif /* HAVE_PWD_H */
+#include <setjmp.h>
+#include <netdb.h>
+#include <errno.h>
+#include <syslog.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <grp.h>
+
+#include "opie.h"
+
+#if HAVE_SHADOW_H
+#include <shadow.h>
+#endif /* HAVE_SHADOW_H */
+
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#endif /* HAVE_CRYPT_H */
+
+#if HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif /* HAVE_SYS_UTSNAME_H */
+
+#ifdef _AIX
+#include <sys/id.h>
+#include <sys/priv.h>
+#endif /* _AIX */
+
+#ifdef IP_TOS
+#ifndef IPTOS_THROUGHPUT
+#undef IP_TOS
+#endif /* !IPTOS_THROUGHPUT */
+#ifndef IPTOS_LOWDELAY
+#undef IP_TOS
+#endif /* !IPTOS_LOWDELAY */
+#endif /* IP_TOS */
+
+extern int errno;
+extern char *home; /* pointer to home directory for glob */
+extern FILE *ftpd_popen __P((char *, char *));
+extern int ftpd_pclose __P((FILE *));
+extern char cbuf[];
+extern off_t restart_point;
+
+static struct sockaddr_in ctrl_addr;
+static struct sockaddr_in data_source;
+struct sockaddr_in data_dest;
+struct sockaddr_in his_addr;
+static struct sockaddr_in pasv_addr;
+
+static int data;
+jmp_buf errcatch;
+static jmp_buf urgcatch;
+int logged_in;
+struct passwd *pw;
+int debug;
+int timeout = 900; /* timeout after 15 minutes of inactivity */
+int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */
+
+#if DOANONYMOUS
+static int guest;
+#endif /* DOANONYMOUS */
+int type;
+int form;
+static int stru; /* avoid C keyword */
+static int mode;
+int usedefault = 1; /* for data transfers */
+int pdata = -1; /* for passive mode */
+static int transflag;
+static off_t file_size;
+static off_t byte_count;
+
+#if (!defined(CMASK) || CMASK == 0)
+#undef CMASK
+#define CMASK 077
+#endif
+
+static int defumask = CMASK; /* default umask value */
+char tmpline[7];
+char remotehost[MAXHOSTNAMELEN];
+
+/*
+ * Timeout intervals for retrying connections
+ * to hosts that don't accept PORT cmds. This
+ * is a kludge, but given the problems with TCP...
+ */
+#define SWAITMAX 90 /* wait at most 90 seconds */
+#define SWAITINT 5 /* interval between retries */
+
+static int swaitmax = SWAITMAX;
+static int swaitint = SWAITINT;
+
+#if DOTITLE
+static char **Argv = NULL; /* pointer to argument vector */
+static char *LastArgv = NULL; /* end of argv */
+static char proctitle[BUFSIZ]; /* initial part of title */
+#endif /* DOTITLE */
+
+static int af_pwok = 0, pwok = 0;
+static struct opie opiestate;
+
+VOIDRET perror_reply __P((int, char *));
+VOIDRET dologout __P((int));
+char *getline __P((char *, int, FILE *));
+VOIDRET upper __P((char *));
+
+static VOIDRET lostconn __P((int));
+static FILE *getdatasock __P((char *));
+static FILE *dataconn __P((char *, off_t, char *));
+static int checkuser __P((char *));
+static VOIDRET end_login __P((void));
+static VOIDRET send_data __P((FILE *, FILE *, off_t));
+static int receive_data __P((FILE *, FILE *));
+static char *gunique __P((char *));
+static char *sgetsave __P((char *));
+
+int logwtmp __P((char *, char *, char *));
+
+int fclose __P((FILE *));
+
+#ifdef HAVE_ANSISTDARG
+VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
+{
+ va_list ap;
+ char buffer[1024];
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+
+ printf("%d %s\r\n", n, buffer);
+ fflush(stdout);
+ if (debug)
+ syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
+}
+#else /* HAVE_ANSISTDARG */
+VOIDRET reply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
+{
+ printf("%d ", n);
+ printf(fmt, p0, p1, p2, p3, p4, p5);
+ printf("\r\n");
+ fflush(stdout);
+ if (debug) {
+ syslog(LOG_DEBUG, "<--- %d ", n);
+ syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
+ }
+}
+#endif /* HAVE_ANSISTDARG */
+
+#ifdef HAVE_ANSISTDARG
+VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
+{
+ va_list ap;
+ char buffer[1024];
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+
+ printf("%d- %s\r\n", n, buffer);
+ fflush(stdout);
+ if (debug)
+ syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
+}
+#else /* HAVE_ANSISTDARG */
+VOIDRET lreply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
+{
+ printf("%d- ", n);
+ printf(fmt, p0, p1, p2, p3, p4, p5);
+ printf("\r\n");
+ fflush(stdout);
+ if (debug) {
+ syslog(LOG_DEBUG, "<--- %d- ", n);
+ syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
+ }
+}
+#endif /* HAVE_ANSISTDARG */
+
+static VOIDRET lostconn FUNCTION((input), int input)
+{
+ if (debug)
+ syslog(LOG_DEBUG, "lost connection");
+ dologout(-1);
+}
+
+static char ttyline[20];
+
+/*
+ * Helper function for sgetpwnam().
+ */
+static char *sgetsave FUNCTION((s), char *s)
+{
+ char *new = malloc((unsigned) strlen(s) + 1);
+
+ if (new == NULL) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ /* NOTREACHED */
+ }
+ strcpy(new, s);
+ return (new);
+}
+
+/*
+ * Save the result of a getpwnam. Used for USER command, since
+ * the data returned must not be clobbered by any other command
+ * (e.g., globbing).
+ */
+static struct passwd *sgetpwnam FUNCTION((name), char *name)
+{
+ static struct passwd save;
+ register struct passwd *p;
+
+#if HAVE_SHADOW
+ struct spwd *spwd;
+#endif /* HAVE_SHADOW */
+
+ if ((p = getpwnam(name)) == NULL)
+ return (p);
+
+#if HAVE_SHADOW
+ if ((spwd = getspnam(name)) == NULL)
+ return NULL;
+
+ endspent();
+
+ p->pw_passwd = spwd->sp_pwdp;
+#endif /* HAVE_SHADOW */
+
+ endpwent();
+
+ if (save.pw_name) {
+ free(save.pw_name);
+ free(save.pw_passwd);
+ free(save.pw_gecos);
+ free(save.pw_dir);
+ free(save.pw_shell);
+ }
+ save = *p;
+ save.pw_name = sgetsave(p->pw_name);
+ save.pw_passwd = sgetsave(p->pw_passwd);
+ save.pw_gecos = sgetsave(p->pw_gecos);
+ save.pw_dir = sgetsave(p->pw_dir);
+ save.pw_shell = sgetsave(p->pw_shell);
+ return (&save);
+}
+
+int login_attempts; /* number of failed login attempts */
+int askpasswd; /* had user command, ask for passwd */
+
+/*
+ * USER command.
+ * Sets global passwd pointer pw if named account exists and is acceptable;
+ * sets askpasswd if a PASS command is expected. If logged in previously,
+ * need to reset state. If name is "ftp" or "anonymous", the name is not in
+ * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
+ * If account doesn't exist, ask for passwd anyway. Otherwise, check user
+ * requesting login privileges. Disallow anyone who does not have a standard
+ * shell as returned by getusershell(). Disallow anyone mentioned in the file
+ * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
+ */
+int user FUNCTION((name), char *name)
+{
+ register char *cp;
+ char *shell;
+
+ if (logged_in) {
+#if DOANONYMOUS
+ if (guest) {
+ reply(530, "Can't change user from guest login.");
+ return -1;
+ }
+#endif /* DOANONMOUS */
+ end_login();
+ }
+ askpasswd = 1;
+#if DOANONYMOUS
+ guest = 0;
+ if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
+ if (!checkuser("ftp") && !checkuser("anonymous"))
+ if ((pw = sgetpwnam("ftp")) != NULL) {
+ guest = 1;
+ askpasswd = 1;
+ reply(331, "Guest login ok, send ident as password.");
+ syslog(LOG_INFO, "Anonymous FTP connection made from host %s.",
+ remotehost);
+ return 0;
+ }
+#endif /* DOANONYMOUS */
+ if (pw = sgetpwnam(name)) {
+ if ((shell = pw->pw_shell) == NULL || *shell == 0)
+ shell = _PATH_BSHELL;
+ while ((cp = getusershell()) != NULL)
+ if (!strcmp(cp, shell))
+ break;
+ endusershell();
+ if (cp == NULL || checkuser(name) ||
+ ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
+#if DEBUG
+ if (!cp)
+ syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
+ if (checkuser(name))
+ syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
+ if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
+ syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
+#endif /* DEBUG */
+ pw = (struct passwd *) NULL;
+ askpasswd = -1;
+ }
+ }
+ {
+ char prompt[OPIE_CHALLENGE_MAX + 1];
+
+ opiechallenge(&opiestate, name, prompt);
+
+ if (askpasswd == -1) {
+ syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
+ pwok = 0;
+ } else
+ pwok = af_pwok && opiealways(pw->pw_dir);
+
+#if NEW_PROMPTS
+ reply(331, "Response to %s %s for %s.", prompt,
+#else /* NEW_PROMPTS */
+ reply(331, "OTP response %s %s for %s.", prompt,
+#endif /* NEW_PROMPTS */
+ pwok ? "requested" : "required", name);
+ }
+ /* Delay before reading passwd after first failed attempt to slow down
+ passwd-guessing programs. */
+ if (login_attempts)
+ sleep((unsigned) login_attempts);
+
+ return 0;
+}
+
+/*
+ * Check if a user is in the file _PATH_FTPUSERS
+ */
+static int checkuser FUNCTION((name), char *name)
+{
+ register FILE *fd;
+ register char *p;
+ char line[BUFSIZ];
+
+ if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL)
+ if ((p = strchr(line, '\n')) != NULL) {
+ *p = '\0';
+ if (line[0] == '#')
+ continue;
+ if (strcmp(line, name) == 0)
+ return (1);
+ }
+ fclose(fd);
+ }
+ return (0);
+}
+
+/*
+ * Terminate login as previous user, if any, resetting state;
+ * used when USER command is given or login fails.
+ */
+static VOIDRET end_login FUNCTION_NOARGS
+{
+ if (seteuid((uid_t) 0))
+ syslog(LOG_ERR, "Can't set euid");
+ if (logged_in)
+ logwtmp(ttyline, "", "");
+ pw = NULL;
+ logged_in = 0;
+#if DOANONYMOUS
+ guest = 0;
+#endif /* DOANONYMOUS */
+}
+
+VOIDRET pass FUNCTION((passwd), char *passwd)
+{
+ int legit = askpasswd + 1, i;
+
+ if (logged_in || askpasswd == 0) {
+ reply(503, "Login with USER first.");
+ return;
+ }
+ askpasswd = 0;
+
+#if DOANONYMOUS
+ if (!guest) { /* "ftp" is only account allowed no password */
+#endif /* DOANONYMOUS */
+ i = opieverify(&opiestate, passwd);
+ if (legit && i && pwok)
+ i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
+ if (!legit || i) {
+ reply(530, "Login incorrect.");
+ pw = NULL;
+ if (login_attempts++ >= 5) {
+ syslog(LOG_WARNING,
+ "Repeated login failures for user %s from %s",
+ pw->pw_name, remotehost);
+ exit(0);
+ }
+ return;
+ }
+#if DOANONYMOUS
+ }
+#endif /* DOANONYMOUS */
+ login_attempts = 0; /* this time successful */
+ setegid((gid_t) pw->pw_gid);
+ initgroups(pw->pw_name, pw->pw_gid);
+
+ /* open wtmp before chroot */
+ sprintf(ttyline, "ftp%d", getpid());
+ logwtmp(ttyline, pw->pw_name, remotehost);
+ logged_in = 1;
+
+#if DOANONYMOUS
+ if (guest) {
+ /* We MUST do a chdir() after the chroot. Otherwise the old current
+ directory will be accessible as "." outside the new root! */
+ if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+ reply(550, "Can't set guest privileges.");
+ goto bad;
+ }
+ } else
+#endif /* DOANONYMOUS */
+ if (chdir(pw->pw_dir) < 0) {
+ if (chdir("/") < 0) {
+ reply(530, "User %s: can't change directory to %s.",
+ pw->pw_name, pw->pw_dir);
+ goto bad;
+ } else
+ lreply(230, "No directory! Logging in with home=/");
+ }
+/* This patch was contributed by an OPIE user. We don't know what it
+ does, exactly. It may or may not work. */
+#ifdef _AIX
+ {
+ priv_t priv;
+ priv.pv_priv[0] = 0;
+ priv.pv_priv[1] = 0;
+ setgroups(NULL, NULL);
+ if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
+ &priv, sizeof(priv_t)) < 0 ||
+ setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
+ setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
+ seteuid((uid_t)pw->pw_uid) < 0) {
+ reply(550, "Can't set uid (_AIX3).");
+ goto bad;
+ }
+ }
+#else /* _AIX */
+ if (seteuid((uid_t) pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
+ goto bad;
+ }
+#endif /* _AIX */
+#if DOANONYMOUS
+ if (guest) {
+ reply(230, "Guest login ok, access restrictions apply.");
+#if DOTITLE
+ sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
+ sizeof(proctitle) - sizeof(remotehost) -
+ sizeof(": anonymous/"), passwd);
+ setproctitle(proctitle);
+#endif /* DOTITLE */
+ syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
+ remotehost, passwd);
+ } else
+#endif /* DOANONYMOUS */
+ {
+ reply(230, "User %s logged in.", pw->pw_name);
+
+#if DOTITLE
+ sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
+ setproctitle(proctitle);
+#endif /* DOTITLE */
+ syslog(LOG_NOTICE, "FTP login from %s with user name %s",
+ remotehost, pw->pw_name);
+ }
+ home = pw->pw_dir; /* home dir for globbing */
+ umask(defumask);
+ return;
+
+bad:
+ /* Forget all about it... */
+ end_login();
+}
+
+VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
+{
+ FILE *fin, *dout;
+ struct stat st;
+ int (*closefunc) ();
+
+ if (cmd == 0) {
+ fin = fopen(name, "r"), closefunc = fclose;
+ st.st_size = 0;
+ } else {
+ char line[BUFSIZ];
+
+ sprintf(line, cmd, name), name = line;
+ fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
+ st.st_size = -1;
+#if HAVE_ST_BLKSIZE
+ st.st_blksize = BUFSIZ;
+#endif /* HAVE_ST_BLKSIZE */
+ }
+ if (fin == NULL) {
+ if (errno != 0)
+ perror_reply(550, name);
+ return;
+ }
+ if (cmd == 0 &&
+ (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
+ reply(550, "%s: not a plain file.", name);
+ goto done;
+ }
+ if (restart_point) {
+ if (type == TYPE_A) {
+ register int i, n, c;
+
+ n = restart_point;
+ i = 0;
+ while (i++ < n) {
+ if ((c = getc(fin)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ } else
+ if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ dout = dataconn(name, st.st_size, "w");
+ if (dout == NULL)
+ goto done;
+#if HAVE_ST_BLKSIZE
+ send_data(fin, dout, st.st_blksize);
+#else /* HAVE_ST_BLKSIZE */
+ send_data(fin, dout, BUFSIZ);
+#endif /* HAVE_ST_BLKSIZE */
+ fclose(dout);
+ data = -1;
+ pdata = -1;
+done:
+ (*closefunc) (fin);
+}
+
+VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
+{
+ FILE *fout, *din;
+ struct stat st;
+ int (*closefunc) ();
+
+ if (unique && stat(name, &st) == 0 &&
+ (name = gunique(name)) == NULL)
+ return;
+
+ if (restart_point)
+ mode = "r+w";
+ fout = fopen(name, mode);
+ closefunc = fclose;
+ if (fout == NULL) {
+ perror_reply(553, name);
+ return;
+ }
+ if (restart_point) {
+ if (type == TYPE_A) {
+ register int i, n, c;
+
+ n = restart_point;
+ i = 0;
+ while (i++ < n) {
+ if ((c = getc(fout)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ /* We must do this seek to "current" position because we are changing
+ from reading to writing. */
+ if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ } else
+ if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ din = dataconn(name, (off_t) - 1, "r");
+ if (din == NULL)
+ goto done;
+ if (receive_data(din, fout) == 0) {
+ if (unique)
+ reply(226, "Transfer complete (unique file name:%s).",
+ name);
+ else
+ reply(226, "Transfer complete.");
+ }
+ fclose(din);
+ data = -1;
+ pdata = -1;
+done:
+ (*closefunc) (fout);
+}
+
+static FILE *getdatasock FUNCTION((mode), char *mode)
+{
+ int s, on = 1, tries;
+
+ if (data >= 0)
+ return (fdopen(data, mode));
+ if (seteuid((uid_t) 0))
+ syslog(LOG_ERR, "Can't set euid");
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ goto bad;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &on, sizeof(on)) < 0)
+ goto bad;
+ /* anchor socket to avoid multi-homing problems */
+ data_source.sin_family = AF_INET;
+ data_source.sin_addr = ctrl_addr.sin_addr;
+ for (tries = 1;; tries++) {
+ if (bind(s, (struct sockaddr *) & data_source,
+ sizeof(data_source)) >= 0)
+ break;
+ if (errno != EADDRINUSE || tries > 10)
+ goto bad;
+ sleep(tries);
+ }
+ if (seteuid((uid_t) pw->pw_uid))
+ syslog(LOG_ERR, "Can't set euid");
+#ifdef IP_TOS
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+ return (fdopen(s, mode));
+bad:
+ if (seteuid((uid_t) pw->pw_uid))
+ syslog(LOG_ERR, "Can't set euid");
+ close(s);
+ return (NULL);
+}
+
+static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
+{
+ char sizebuf[32];
+ FILE *file;
+ int retry = 0;
+#ifdef IP_TOS
+ int tos;
+#endif /* IP_TOS */
+
+ file_size = size;
+ byte_count = 0;
+ if (size != (off_t) - 1)
+ sprintf(sizebuf, " (%ld bytes)", size);
+ else
+ strcpy(sizebuf, "");
+ if (pdata >= 0) {
+ struct sockaddr_in from;
+ int s, fromlen = sizeof(from);
+
+ s = accept(pdata, (struct sockaddr *) & from, &fromlen);
+ if (s < 0) {
+ reply(425, "Can't open data connection.");
+ close(pdata);
+ pdata = -1;
+ return (NULL);
+ }
+ close(pdata);
+ pdata = s;
+#ifdef IP_TOS
+ tos = IPTOS_LOWDELAY;
+ setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
+ sizeof(int));
+
+#endif
+ reply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (fdopen(pdata, mode));
+ }
+ if (data >= 0) {
+ reply(125, "Using existing data connection for %s%s.",
+ name, sizebuf);
+ usedefault = 1;
+ return (fdopen(data, mode));
+ }
+ if (usedefault)
+ data_dest = his_addr;
+ usedefault = 1;
+ file = getdatasock(mode);
+ if (file == NULL) {
+ reply(425, "Can't create data socket (%s,%d): %s.",
+ inet_ntoa(data_source.sin_addr),
+ ntohs(data_source.sin_port), strerror(errno));
+ return (NULL);
+ }
+ data = fileno(file);
+ while (connect(data, (struct sockaddr *) & data_dest,
+ sizeof(data_dest)) < 0) {
+ if (errno == EADDRINUSE && retry < swaitmax) {
+ sleep((unsigned) swaitint);
+ retry += swaitint;
+ continue;
+ }
+ perror_reply(425, "Can't build data connection");
+ fclose(file);
+ data = -1;
+ return (NULL);
+ }
+ reply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (file);
+}
+
+/*
+ * Tranfer the contents of "instr" to
+ * "outstr" peer using the appropriate
+ * encapsulation of the data subject
+ * to Mode, Structure, and Type.
+ *
+ * NB: Form isn't handled.
+ */
+static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
+{
+ register int c, cnt;
+ register char *buf;
+ int netfd, filefd;
+
+ transflag++;
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return;
+ }
+ switch (type) {
+
+ case TYPE_A:
+ while ((c = getc(instr)) != EOF) {
+ byte_count++;
+ if (c == '\n') {
+ if (ferror(outstr))
+ goto data_err;
+ putc('\r', outstr);
+ }
+ putc(c, outstr);
+ }
+ fflush(outstr);
+ transflag = 0;
+ if (ferror(instr))
+ goto file_err;
+ if (ferror(outstr))
+ goto data_err;
+ reply(226, "Transfer complete.");
+ return;
+
+ case TYPE_I:
+ case TYPE_L:
+ if ((buf = malloc((u_int) blksize)) == NULL) {
+ transflag = 0;
+ perror_reply(451, "Local resource failure: malloc");
+ return;
+ }
+ netfd = fileno(outstr);
+ filefd = fileno(instr);
+ while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
+ write(netfd, buf, cnt) == cnt)
+ byte_count += cnt;
+ transflag = 0;
+ free(buf);
+ if (cnt != 0) {
+ if (cnt < 0)
+ goto file_err;
+ goto data_err;
+ }
+ reply(226, "Transfer complete.");
+ return;
+ default:
+ transflag = 0;
+ reply(550, "Unimplemented TYPE %d in send_data", type);
+ return;
+ }
+
+data_err:
+ transflag = 0;
+ perror_reply(426, "Data connection");
+ return;
+
+file_err:
+ transflag = 0;
+ perror_reply(551, "Error on input file");
+}
+
+/*
+ * Transfer data from peer to
+ * "outstr" using the appropriate
+ * encapulation of the data subject
+ * to Mode, Structure, and Type.
+ *
+ * N.B.: Form isn't handled.
+ */
+static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
+{
+ register int c;
+ int cnt, bare_lfs = 0;
+ char buf[BUFSIZ];
+
+ transflag++;
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return (-1);
+ }
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+ while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
+ if (write(fileno(outstr), buf, cnt) != cnt)
+ goto file_err;
+ byte_count += cnt;
+ }
+ if (cnt < 0)
+ goto data_err;
+ transflag = 0;
+ return (0);
+
+ case TYPE_E:
+ reply(553, "TYPE E not implemented.");
+ transflag = 0;
+ return (-1);
+
+ case TYPE_A:
+ while ((c = getc(instr)) != EOF) {
+ byte_count++;
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ if (ferror(outstr))
+ goto data_err;
+ if ((c = getc(instr)) != '\n') {
+ putc('\r', outstr);
+ if (c == '\0' || c == EOF)
+ goto contin2;
+ }
+ }
+ putc(c, outstr);
+ contin2:;
+ }
+ fflush(outstr);
+ if (ferror(instr))
+ goto data_err;
+ if (ferror(outstr))
+ goto file_err;
+ transflag = 0;
+ if (bare_lfs) {
+ lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
+ printf(" File may not have transferred correctly.\r\n");
+ }
+ return (0);
+ default:
+ reply(550, "Unimplemented TYPE %d in receive_data", type);
+ transflag = 0;
+ return (-1);
+ }
+
+data_err:
+ transflag = 0;
+ perror_reply(426, "Data Connection");
+ return (-1);
+
+file_err:
+ transflag = 0;
+ perror_reply(452, "Error writing file");
+ return (-1);
+}
+
+VOIDRET statfilecmd FUNCTION((filename), char *filename)
+{
+ char line[BUFSIZ];
+ FILE *fin;
+ int c;
+
+#if HAVE_LS_G_FLAG
+ sprintf(line, "%s %s", "/bin/ls -lgA", filename);
+#else /* HAVE_LS_G_FLAG */
+ sprintf(line, "%s %s", "/bin/ls -lA", filename);
+#endif /* HAVE_LS_G_FLAG */
+ fin = ftpd_popen(line, "r");
+ lreply(211, "status of %s:", filename);
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ if (ferror(stdout)) {
+ perror_reply(421, "control connection");
+ ftpd_pclose(fin);
+ dologout(1);
+ /* NOTREACHED */
+ }
+ if (ferror(fin)) {
+ perror_reply(551, filename);
+ ftpd_pclose(fin);
+ return;
+ }
+ putc('\r', stdout);
+ }
+ putc(c, stdout);
+ }
+ ftpd_pclose(fin);
+ reply(211, "End of Status");
+}
+
+VOIDRET statcmd FUNCTION_NOARGS
+{
+/* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
+ struct sockaddr_in *sin;
+ u_char *a, *p;
+
+ lreply(211, "FTP server status:");
+ printf(" \r\n");
+ printf(" Connected to %s", remotehost);
+ if (!isdigit(remotehost[0]))
+ printf(" (%s)", inet_ntoa(his_addr.sin_addr));
+ printf("\r\n");
+ if (logged_in) {
+#if DOANONYMOUS
+ if (guest)
+ printf(" Logged in anonymously\r\n");
+ else
+#endif /* DOANONYMOUS */
+ printf(" Logged in as %s\r\n", pw->pw_name);
+ } else
+ if (askpasswd)
+ printf(" Waiting for password\r\n");
+ else
+ printf(" Waiting for user name\r\n");
+ if (data != -1)
+ printf(" Data connection open\r\n");
+ else
+ if (pdata != -1) {
+ printf(" in Passive mode");
+ sin = &pasv_addr;
+ goto printaddr;
+ } else
+ if (usedefault == 0) {
+ printf(" PORT");
+ sin = &data_dest;
+ printaddr:
+ a = (u_char *) & sin->sin_addr;
+ p = (u_char *) & sin->sin_port;
+#define UC(b) (((int) b) & 0xff)
+ printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
+ UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+#undef UC
+ } else
+ printf(" No data connection\r\n");
+ reply(211, "End of status");
+}
+
+VOIDRET opiefatal FUNCTION((s), char *s)
+{
+ reply(451, "Error in server: %s\n", s);
+ reply(221, "Closing connection due to server error.");
+ dologout(0);
+ /* NOTREACHED */
+}
+
+static VOIDRET ack FUNCTION((s), char *s)
+{
+ reply(250, "%s command successful.", s);
+}
+
+VOIDRET nack FUNCTION((s), char *s)
+{
+ reply(502, "%s command not implemented.", s);
+}
+
+VOIDRET yyerror FUNCTION((s), char *s)
+{
+ char *cp;
+
+ if (cp = strchr(cbuf, '\n'))
+ *cp = '\0';
+ reply(500, "'%s': command not understood.", cbuf);
+}
+
+VOIDRET delete FUNCTION((name), char *name)
+{
+ struct stat st;
+
+ if (stat(name, &st) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ if ((st.st_mode & S_IFMT) == S_IFDIR) {
+ if (rmdir(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ goto done;
+ }
+ if (unlink(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+done:
+ ack("DELE");
+}
+
+VOIDRET cwd FUNCTION((path), char *path)
+{
+ if (chdir(path) < 0)
+ perror_reply(550, path);
+ else
+ ack("CWD");
+}
+
+VOIDRET makedir FUNCTION((name), char *name)
+{
+ if (mkdir(name, 0777) < 0)
+ perror_reply(550, name);
+ else
+ reply(257, "MKD command successful.");
+}
+
+VOIDRET removedir FUNCTION((name), char *name)
+{
+ if (rmdir(name) < 0)
+ perror_reply(550, name);
+ else
+ ack("RMD");
+}
+
+VOIDRET pwd FUNCTION_NOARGS
+{
+ char path[MAXPATHLEN + 1];
+
+ if (getcwd(path, MAXPATHLEN) == (char *) NULL)
+ reply(550, "%s.", path);
+ else
+ reply(257, "\"%s\" is current directory.", path);
+}
+
+char *renamefrom FUNCTION((name), char *name)
+{
+ struct stat st;
+
+ if (stat(name, &st) < 0) {
+ perror_reply(550, name);
+ return ((char *) 0);
+ }
+ reply(350, "File exists, ready for destination name");
+ return (name);
+}
+
+VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
+{
+ if (rename(from, to) < 0)
+ perror_reply(550, "rename");
+ else
+ ack("RNTO");
+}
+
+static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
+{
+ struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ time_t t, time();
+
+ if (hp)
+ strncpy(remotehost, hp->h_name, sizeof(remotehost));
+ else
+ strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
+#if DOTITLE
+ sprintf(proctitle, "%s: connected", remotehost);
+ setproctitle(proctitle);
+#endif /* DOTITLE */
+
+ t = time((time_t *) 0);
+ syslog(LOG_INFO, "connection from %s at %s",
+ remotehost, ctime(&t));
+}
+
+/*
+ * Record logout in wtmp file
+ * and exit with supplied status.
+ */
+VOIDRET dologout FUNCTION((status), int status)
+{
+ if (logged_in) {
+ if (seteuid((uid_t) 0))
+ syslog(LOG_ERR, "Can't set euid");
+ logwtmp(ttyline, "", "");
+ }
+ /* beware of flushing buffers after a SIGPIPE */
+ _exit(status);
+}
+
+static VOIDRET myoob FUNCTION((input), int input)
+{
+ char *cp;
+
+ /* only process if transfer occurring */
+ if (!transflag)
+ return;
+ cp = tmpline;
+ if (getline(cp, 7, stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ upper(cp);
+ if (strcmp(cp, "ABOR\r\n") == 0) {
+ tmpline[0] = '\0';
+ reply(426, "Transfer aborted. Data connection closed.");
+ reply(226, "Abort successful");
+ longjmp(urgcatch, 1);
+ }
+ if (strcmp(cp, "STAT\r\n") == 0) {
+ if (file_size != (off_t) - 1)
+ reply(213, "Status: %lu of %lu bytes transferred",
+ byte_count, file_size);
+ else
+ reply(213, "Status: %lu bytes transferred", byte_count);
+ }
+}
+
+/*
+ * Note: a response of 425 is not mentioned as a possible response to
+ * the PASV command in RFC959. However, it has been blessed as
+ * a legitimate response by Jon Postel in a telephone conversation
+ * with Rick Adams on 25 Jan 89.
+ */
+VOIDRET passive FUNCTION_NOARGS
+{
+ int len;
+ register char *p, *a;
+
+ pdata = socket(AF_INET, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+ pasv_addr = ctrl_addr;
+ pasv_addr.sin_port = 0;
+ if (seteuid((uid_t) 0))
+ syslog(LOG_ERR, "Can't set euid");
+ if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
+ seteuid((uid_t) pw->pw_uid);
+ goto pasv_error;
+ }
+ if (seteuid((uid_t) pw->pw_uid))
+ syslog(LOG_ERR, "Can't set euid");
+ len = sizeof(pasv_addr);
+ if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+ a = (char *) &pasv_addr.sin_addr;
+ p = (char *) &pasv_addr.sin_port;
+
+#define UC(b) (((int) b) & 0xff)
+
+ reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
+ UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+ return;
+
+pasv_error:
+ close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
+ * Generate unique name for file with basename "local".
+ * The file named "local" is already known to exist.
+ * Generates failure reply on error.
+ */
+static char *gunique FUNCTION((local), char *local)
+{
+ static char new[MAXPATHLEN];
+ struct stat st;
+ char *cp = strrchr(local, '/');
+ int count = 0;
+
+ if (cp)
+ *cp = '\0';
+ if (stat(cp ? local : ".", &st) < 0) {
+ perror_reply(553, cp ? local : ".");
+ return ((char *) 0);
+ }
+ if (cp)
+ *cp = '/';
+ strcpy(new, local);
+ cp = new + strlen(new);
+ *cp++ = '.';
+ for (count = 1; count < 100; count++) {
+ sprintf(cp, "%d", count);
+ if (stat(new, &st) < 0)
+ return (new);
+ }
+ reply(452, "Unique file name cannot be created.");
+ return ((char *) 0);
+}
+
+/*
+ * Format and send reply containing system error number.
+ */
+VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
+{
+ reply(code, "%s: %s.", string, strerror(errno));
+}
+
+static char *onefile[] =
+{
+ "",
+ 0
+};
+
+VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
+{
+ struct stat st;
+ DIR *dirp = NULL;
+ struct dirent *dir;
+ FILE *dout = NULL;
+ register char **dirlist, *dirname;
+ int simple = 0;
+
+ if (strpbrk(whichfiles, "~{[*?") != NULL) {
+ extern char **ftpglob(), *globerr;
+
+ globerr = NULL;
+ dirlist = ftpglob(whichfiles);
+ if (globerr != NULL) {
+ reply(550, globerr);
+ return;
+ } else
+ if (dirlist == NULL) {
+ errno = ENOENT;
+ perror_reply(550, whichfiles);
+ return;
+ }
+ } else {
+ onefile[0] = whichfiles;
+ dirlist = onefile;
+ simple = 1;
+ }
+
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return;
+ }
+ while (dirname = *dirlist++) {
+ if (stat(dirname, &st) < 0) {
+ /* If user typed "ls -l", etc, and the client used NLST, do what the
+ user meant. */
+ if (dirname[0] == '-' && *dirlist == NULL &&
+ transflag == 0) {
+ retrieve("/bin/ls %s", dirname);
+ return;
+ }
+ perror_reply(550, whichfiles);
+ if (dout != NULL) {
+ fclose(dout);
+ transflag = 0;
+ data = -1;
+ pdata = -1;
+ }
+ return;
+ }
+ if ((st.st_mode & S_IFMT) == S_IFREG) {
+ if (dout == NULL) {
+ dout = dataconn("file list", (off_t) - 1, "w");
+ if (dout == NULL)
+ return;
+ transflag++;
+ }
+ fprintf(dout, "%s%s\n", dirname,
+ type == TYPE_A ? "\r" : "");
+ byte_count += strlen(dirname) + 1;
+ continue;
+ } else
+ if ((st.st_mode & S_IFMT) != S_IFDIR)
+ continue;
+
+ if ((dirp = opendir(dirname)) == NULL)
+ continue;
+
+ while ((dir = readdir(dirp)) != NULL) {
+ char nbuf[MAXPATHLEN];
+
+ if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
+ continue;
+ if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
+ (strlen(dir->d_name) == 2))
+ continue;
+
+ sprintf(nbuf, "%s/%s", dirname, dir->d_name);
+
+ /* We have to do a stat to insure it's not a directory or special file. */
+ if (simple || (stat(nbuf, &st) == 0 &&
+ (st.st_mode & S_IFMT) == S_IFREG)) {
+ if (dout == NULL) {
+ dout = dataconn("file list", (off_t) - 1, "w");
+ if (dout == NULL)
+ return;
+ transflag++;
+ }
+ if (nbuf[0] == '.' && nbuf[1] == '/')
+ fprintf(dout, "%s%s\n", &nbuf[2],
+ type == TYPE_A ? "\r" : "");
+ else
+ fprintf(dout, "%s%s\n", nbuf,
+ type == TYPE_A ? "\r" : "");
+ byte_count += strlen(nbuf) + 1;
+ }
+ }
+ closedir(dirp);
+ }
+
+ if (dout == NULL)
+ reply(550, "No files found.");
+ else
+ if (ferror(dout) != 0)
+ perror_reply(550, "Data connection");
+ else
+ reply(226, "Transfer complete.");
+
+ transflag = 0;
+ if (dout != NULL)
+ fclose(dout);
+ data = -1;
+ pdata = -1;
+}
+
+#if DOTITLE
+/*
+ * clobber argv so ps will show what we're doing.
+ * (stolen from sendmail)
+ * warning, since this is usually started from inetd.conf, it
+ * often doesn't have much of an environment or arglist to overwrite.
+ */
+VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
+{
+ register char *p, *bp, ch;
+ register int i;
+ char buf[BUFSIZ];
+
+ sprintf(buf, fmt, a, b, c);
+
+ /* make ps print our process name */
+ p = Argv[0];
+ *p++ = '-';
+
+ i = strlen(buf);
+ if (i > LastArgv - p - 2) {
+ i = LastArgv - p - 2;
+ buf[i] = '\0';
+ }
+ bp = buf;
+ while (ch = *bp++)
+ if (ch != '\n' && ch != '\r')
+ *p++ = ch;
+ while (p < LastArgv)
+ *p++ = ' ';
+}
+#endif /* DOTITLE */
+
+void catchexit FUNCTION_NOARGS
+{
+ closelog();
+}
+
+int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
+{
+ int addrlen, on = 1;
+ char *cp;
+#ifdef IP_TOS
+ int tos;
+#endif /* IP_TOS */
+
+ {
+ int i;
+
+ for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
+ close(i);
+ }
+
+ /* LOG_NDELAY sets up the logging connection immediately, necessary for
+ anonymous ftp's that chroot and can't do it later. */
+ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ atexit(catchexit);
+ addrlen = sizeof(his_addr);
+ if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
+ exit(1);
+ }
+ addrlen = sizeof(ctrl_addr);
+ if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
+ exit(1);
+ }
+#ifdef IP_TOS
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+ data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+ debug = 0;
+#if DOTITLE
+ /* Save start and extent of argv for setproctitle. */
+ Argv = argv;
+ while (*envp)
+ envp++;
+ LastArgv = envp[-1] + strlen(envp[-1]);
+#endif /* DOTITLE */
+
+ argc--, argv++;
+ while (argc > 0 && *argv[0] == '-') {
+ for (cp = &argv[0][1]; *cp; cp++)
+ switch (*cp) {
+
+ case 'v':
+ debug = 1;
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+
+ case 'l':
+ break;
+
+ case 't':
+ timeout = atoi(++cp);
+ if (maxtimeout < timeout)
+ maxtimeout = timeout;
+ goto nextopt;
+
+ case 'T':
+ maxtimeout = atoi(++cp);
+ if (timeout > maxtimeout)
+ timeout = maxtimeout;
+ goto nextopt;
+
+ case 'u':
+ {
+ int val = 0;
+
+ while (*++cp && *cp >= '0' && *cp <= '9')
+ val = val * 8 + *cp - '0';
+ if (*cp)
+ fprintf(stderr, "ftpd: Bad value for -u\n");
+ else
+ defumask = val;
+ goto nextopt;
+ }
+
+ default:
+ fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
+ *cp);
+ break;
+ }
+nextopt:
+ argc--, argv++;
+ }
+ freopen(_PATH_DEVNULL, "w", stderr);
+ signal(SIGPIPE, lostconn);
+ signal(SIGCHLD, SIG_IGN);
+ if ((int) signal(SIGURG, myoob) < 0)
+ syslog(LOG_ERR, "signal: %m");
+
+ /* Try to handle urgent data inline */
+#ifdef SO_OOBINLINE
+ if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt: %m");
+#endif
+
+#ifdef F_SETOWN
+ if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
+ syslog(LOG_ERR, "fcntl F_SETOWN: %m");
+#endif
+ dolog(&his_addr);
+ /* Set up default state */
+ data = -1;
+ type = TYPE_A;
+ form = FORM_N;
+ stru = STRU_F;
+ mode = MODE_S;
+ tmpline[0] = '\0';
+ af_pwok = opieaccessfile(remotehost);
+
+#if 0
+ {
+ struct utsname utsname;
+
+ if (uname(&utsname) < 0) {
+ syslog(LOG_ERR, "uname() failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ reply(220, "%s FTP server ready.", utsname.nodename);
+ }
+#else /* 0 */
+ reply(220, "FTP server ready.");
+#endif /* 0 */
+
+ setjmp(errcatch);
+ for (;;)
+ yyparse();
+ /* NOTREACHED */
+ return 0;
+}
OpenPOWER on IntegriCloud