📄 ftpd.c
字号:
/* - Ftp Server * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994, 2002 * The 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. * 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. */#if 0static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";#endif/* * FTP server. */#ifdef HAVE_CONFIG_H# include <config.h>#endif#if !defined (__GNUC__) && defined (_AIX)#pragma alloca#endif#ifndef alloca /* Make alloca work the best possible way. */# ifdef __GNUC__# define alloca __builtin_alloca# else /* not __GNUC__ */# if HAVE_ALLOCA_H# include <alloca.h># else /* not __GNUC__ or HAVE_ALLOCA_H */# ifndef _AIX /* Already did AIX, up at the top. */ char *alloca ();# endif /* not _AIX */# endif /* not HAVE_ALLOCA_H */# endif /* not __GNUC__ */#endif /* not alloca */#include <sys/param.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/socket.h>#ifdef HAVE_SYS_WAIT_H# include <sys/wait.h>#endif#include <netinet/in.h>#ifdef HAVE_NETINET_IN_SYSTM_H# include <netinet/in_systm.h>#endif#ifdef HAVE_NETINET_IP_H# include <netinet/ip.h>#endif#define FTP_NAMES#include <arpa/ftp.h>#include <arpa/inet.h>#include <arpa/telnet.h>#include <ctype.h>#include <dirent.h>#include <errno.h>#include <fcntl.h>#include <getopt.h>#include <limits.h>#include <netdb.h>#include <setjmp.h>#include <signal.h>#include <grp.h>#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__# include <stdarg.h>#else# include <varargs.h>#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <syslog.h>#ifdef TIME_WITH_SYS_TIME# include <sys/time.h># include <time.h>#else# ifdef HAVE_SYS_TIME_H# include <sys/time.h># else# include <time.h># endif#endif#include <unistd.h>#ifdef HAVE_MMAP#include <sys/mman.h>#endif/* Include glob.h last, because it may define "const" which breaks system headers on some platforms. */#include <glob.h>#include <libinetutils.h>#include "extern.h"#ifndef LINE_MAX# define LINE_MAX 2048#endif#ifndef LOG_FTP# define LOG_FTP LOG_DAEMON /* Use generic facility. */#endif#ifndef MAP_FAILED# define MAP_FAILED (void*)-1#endif#if !HAVE_DECL_FCLOSE/* Some systems don't declare fclose in <stdio.h>, so do it ourselves. */extern int fclose (FILE *);#endifchar *program_name;/* Exported to ftpcmd.h. */struct sockaddr_in data_dest; /* Data port. */struct sockaddr_in his_addr; /* Peer address. */int logging; /* Enable log to syslog. */int type = TYPE_A; /* Default TYPE_A. */int form = FORM_N; /* Default FORM_N. */int debug; /* Enable debug mode if 1. */int timeout = 900; /* Timeout after 15 minutes of inactivity. */int maxtimeout = 7200; /* Don't allow idle time to be set beyond 2 hours. */int pdata = -1; /* For passive mode. */char *hostname; /* Who we are. */int usedefault = 1; /* For data transfers. */char tmpline[7]; /* Temp buffer use in OOB. *//* Requester credentials. */struct credentials cred;static struct sockaddr_in ctrl_addr; /* Control address. */static struct sockaddr_in data_source; /* Port address. */static struct sockaddr_in pasv_addr; /* Pasv address. */static int data = -1; /* Port data connection socket. */static jmp_buf urgcatch;static int stru = STRU_F; /* Avoid C keyword. */static int stru_mode = MODE_S;/* Default STRU mode stru_mode = MODE_S. */static int anon_only; /* Allow only anonymous login. */static int no_version; /* Don't print version to client. */static int daemon_mode; /* Start in daemon mode. */static off_t file_size;static off_t byte_count;static sig_atomic_t transflag; /* Flag where in a middle of transfer. */static const char *pid_file = PATH_FTPDPID;#if !defined(CMASK) || CMASK == 0#undef CMASK#define CMASK 027#endifstatic int defumask = CMASK; /* Default umask value. */static int login_attempts; /* Number of failed login attempts. */static int askpasswd; /* Had user command, ask for passwd. */static char curname[10]; /* Current USER name. */static char ttyline[20]; /* Line to log in utmp. */#define NUM_SIMUL_OFF_TO_STRS 4/* Returns a string with the decimal representation of the off_t OFF, taking into account that off_t might be longer than a long. The return value is a pointer to a static buffer, but a return value will only be reused every NUM_SIMUL_OFF_TO_STRS calls, to allow multiple off_t's to be conveniently printed with a single printf statement. */static char *off_to_str (off_t off){ static char bufs[NUM_SIMUL_OFF_TO_STRS][80]; static char (*next_buf)[80] = bufs; if (next_buf >= (bufs+NUM_SIMUL_OFF_TO_STRS)) next_buf = bufs; if (sizeof (off) > sizeof (long)) sprintf (*next_buf, "%qd", off); else if (sizeof (off) == sizeof (long)) sprintf (*next_buf, "%ld", off); else sprintf (*next_buf, "%d", off); return *next_buf++;}/* * 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;#ifdef HAVE_SETPROCTITLEchar proctitle[LINE_MAX]; /* initial part of title */#endif /* SETPROCTITLE */#define LOGCMD(cmd, file) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file);#define LOGCMD2(cmd, file1, file2) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ *(file1) == '/' ? "" : curdir(), file1, \ *(file2) == '/' ? "" : curdir(), file2);#define LOGBYTES(cmd, file, cnt) \ if (logging > 1) { \ if (cnt == (off_t)-1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file); \ else \ syslog(LOG_INFO, "%s %s%s = %s bytes", \ cmd, (*(file) == '/') ? "" : curdir(), file, \ off_to_str (cnt)); \ }static void ack (const char *);static void authentication_setup (const char *);#ifdef HAVE_LIBWRAPstatic int check_host (struct sockaddr *sa);#endifstatic void complete_login (struct credentials *);static char *curdir (void);static FILE *dataconn (const char *, off_t, const char *);static void dolog (struct sockaddr_in *, struct credentials *);static void end_login (struct credentials *);static FILE *getdatasock (const char *);static char *gunique (const char *);static void lostconn (int);static void myoob (int);static int receive_data (FILE *, FILE *);static void send_data (FILE *, FILE *, off_t);static void sigquit (int);static void usage (int);static const char *short_options = "Aa:Ddlp:qt:T:u:";static struct option long_options[] ={ { "anonymous-only", no_argument, 0, 'A' }, { "auth", required_argument, 0, 'a' }, { "daemon", no_argument, 0, 'D' }, { "debug", no_argument, 0, 'd' }, { "help", no_argument, 0, '&' }, { "logging", no_argument, 0, 'l' }, { "pidfile", required_argument, 0, 'p' }, { "no-version", no_argument, 0, 'q' }, { "timeout", required_argument, 0, 't' }, { "max-timeout", required_argument, 0, 'T' }, { "umask", required_argument, 0, 'u' }, { "version", no_argument, 0, 'V' }, { 0, 0, 0, 0 }};static voidusage (int err){ if (err != 0) { fprintf (stderr, "Usage: %s [OPTION] ...\n", program_name); fprintf (stderr, "Try `%s --help' for more information.\n", program_name); } else { fprintf (stdout, "Usage: %s [OPTION] ...\n", program_name); puts ("Internet File Transfer Protocol server.\n\n\ -A, --anonymous-only Server configure for anonymous service only\n\ -D, --daemon Start the ftpd standalone\n\ -d, --debug Debug mode\n\ -l, --logging Increase verbosity of syslog messages\n\ -p, --pidfile=[PIDFILE] Change default location of pidfile\n\ -q, --no-version Do not display version in banner\n\ -t, --timeout=[TIMEOUT] Set default idle timeout\n\ -T, --max-timeout Reset maximum value of timeout allowed\n\ -u, --umask Set default umask(base 8)\n\ --help Print this message\n\ -V, --version Print version\n\ -a, --auth=[AUTH] Use AUTH for authentication, it can be:\n\ default passwd authentication.");#ifdef WITH_PAM puts ("\ pam using pam 'ftp' module.");#endif#ifdef WITH_KERBEROS puts ("\ kerberos");#endif#ifdef WITH_KERBEROS5 puts ("\ kderberos5");#endif#ifdef WITH_OPIE puts ("\ opie");#endif fprintf (stdout, "\nSubmit bug reports to %s.\n", PACKAGE_BUGREPORT); } exit (err);}intmain(int argc, char *argv[], char **envp){ int option; program_name = argv[0];#ifdef HAVE_TZSET tzset(); /* In case no timezone database in ~ftp. */#endif#ifdef HAVE_INITSETPROCTITLE /* Save start and extent of argv for setproctitle. */ initsetproctitle (argc, argv, envp);#endif /* HAVE_INITSETPROCTITLE */ while ((option = getopt_long (argc, argv, short_options, long_options, NULL)) != EOF) { switch (option) { case 'A': /* Anonymous ftp only. */ anon_only = 1; break; case 'a': /* Authentification method. */ if (strcasecmp (optarg, "default") == 0) cred.auth_type = AUTH_TYPE_PASSWD;#ifdef WITH_PAM else if (strcasecmp (optarg, "pam") == 0) cred.auth_type = AUTH_TYPE_PAM;#endif#ifdef WITH_KERBEROS else if (stracasecmp (optarg, "kerberos") == 0) cred.auth_type = AUTH_TYPE_KERBEROS;#endif#ifdef WITH_KERBEROS5 else if (stracasecmp (optarg, "kerberos5") == 0) cred.auth_type = AUTH_TYPE_KERBEROS5;#endif#ifdef WITH_OPIE else if (stracasecmp (optarg, "opie") == 0) cred.auth_type = AUTH_TYPE_OPIE;#endif break; case 'D': /* Run ftpd as daemon. */ daemon_mode = 1; break; case 'd': /* Enable debug mode. */ debug = 1; break; case 'l': /* Increase logging level. */ logging++; /* > 1 == Extra logging. */ break; case 'p': /* Override pid file */ pid_file = optarg; break; case 'q': /* Don't include version number in banner. */ no_version = 1; break; case 't': /* Set default timeout value. */ timeout = atoi (optarg); if (maxtimeout < timeout) maxtimeout = timeout; break; case 'T': /* Maximum timeout allowed. */ maxtimeout = atoi (optarg); if (timeout > maxtimeout) timeout = maxtimeout; break; case 'u': /* Set umask. */ { long val = 0; val = strtol (optarg, &optarg, 8); if (*optarg != '\0' || val < 0) fprintf (stderr, "%s: bad value for -u", argv[0]); else defumask = val; break; } case '&': /* Usage. */ usage (0); /* Not reached. */ case 'V': /* Version. */ printf ("ftpd (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); exit (0); case '?': default: usage (1); /* Not reached. */ } } /* Bail out, wrong usage */ argc -= optind; if (argc != 0) usage (1); /* 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_FTP); freopen (PATH_DEVNULL, "w", stderr); /* If not running via inetd, we detach and dup(fd, 0), dup(fd, 1) the fd = accept(). tcpd is check if compile with the support */ if (daemon_mode) { if (server_mode (pid_file, &his_addr) < 0) exit (1); } else { int addrlen = sizeof (his_addr); if (getpeername (STDIN_FILENO, (struct sockaddr *)&his_addr, &addrlen) < 0) { syslog (LOG_ERR, "getpeername (%s): %m", program_name); exit (1); } } signal (SIGHUP, sigquit); signal (SIGINT, sigquit); signal (SIGQUIT, sigquit); signal (SIGTERM, sigquit); signal (SIGPIPE, lostconn); signal (SIGCHLD, SIG_IGN); if (signal (SIGURG, myoob) == SIG_ERR) syslog (LOG_ERR, "signal: %m"); /* Get info on the ctrl connection. */ { int addrlen = sizeof (ctrl_addr); if (getsockname (STDIN_FILENO, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { syslog (LOG_ERR, "getsockname (%s): %m", program_name); exit (1); } }#if defined (IP_TOS) && defined (IPTOS_LOWDELAY) && defined (IPPROTO_IP) /* To minimize delays for interactive traffic. */ { int tos = IPTOS_LOWDELAY; if (setsockopt (STDIN_FILENO, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) syslog (LOG_WARNING, "setsockopt (IP_TOS): %m"); }#endif#ifdef SO_OOBINLINE /* Try to handle urgent data inline. */ { int on = 1; if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof (on)) < 0) syslog (LOG_ERR, "setsockopt: %m");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -