?? ftpd_c.txt
字號:
/* $OpenBSD: ftpd.c,v 1.183 2008/06/30 12:03:51 ragge Exp $ */
/* $NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $ */
/*
* Copyright (C) 1997 and 1998 WIDE Project.
* 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. Neither the name of the project 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 PROJECT 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 PROJECT 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.
*/
/*
* Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
* 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.
* 3. 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.
*/
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
#if 0
static const char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94";
#else
static const char rcsid[] =
"$OpenBSD: ftpd.c,v 1.183 2008/06/30 12:03:51 ragge Exp $";
#endif
#endif /* not lint */
/*
* FTP server.
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#define FTP_NAMES
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <bsd_auth.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <limits.h>
#include <login_cap.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <vis.h>
#include <unistd.h>
#include <util.h>
#include <utmp.h>
#include <poll.h>
#if defined(TCPWRAPPERS)
#include <tcpd.h>
#endif /* TCPWRAPPERS */
#include "pathnames.h"
#include "monitor.h"
#include "extern.h"
extern off_t restart_point;
extern char cbuf[];
union sockunion ctrl_addr;
union sockunion data_source;
union sockunion data_dest;
union sockunion his_addr;
union sockunion pasv_addr;
sigset_t allsigs;
int daemon_mode = 0;
int data;
int logged_in;
struct passwd *pw;
int debug = 0;
int timeout = 900; /* timeout after 15 minutes of inactivity */
int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
int logging;
int anon_ok = 1;
int anon_only = 0;
int multihome = 0;
int guest;
int stats;
int statfd = -1;
int portcheck = 1;
int dochroot;
int type;
int form;
int stru; /* avoid C keyword */
int mode;
int doutmp = 0; /* update utmp file */
int usedefault = 1; /* for data transfers */
int pdata = -1; /* for passive mode */
int family = AF_UNSPEC;
volatile sig_atomic_t transflag;
off_t file_size;
off_t byte_count;
#if !defined(CMASK) || CMASK == 0
#undef CMASK
#define CMASK 022
#endif
mode_t defumask = CMASK; /* default umask value */
int umaskchange = 1; /* allow user to change umask value. */
char tmpline[7];
char hostname[MAXHOSTNAMELEN];
char remotehost[MAXHOSTNAMELEN];
char dhostname[MAXHOSTNAMELEN];
char *guestpw;
char ttyline[20];
#if 0
char *tty = ttyline; /* for klogin */
#endif
static struct utmp utmp; /* for utmp */
static login_cap_t *lc;
static auth_session_t *as;
static volatile sig_atomic_t recvurg;
#if defined(TCPWRAPPERS)
int allow_severity = LOG_INFO;
int deny_severity = LOG_NOTICE;
#endif /* TCPWRAPPERS */
char *ident = NULL;
int epsvall = 0;
/*
* 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 */
int swaitmax = SWAITMAX;
int swaitint = SWAITINT;
#ifdef HASSETPROCTITLE
char proctitle[BUFSIZ]; /* initial part of title */
#endif /* HASSETPROCTITLE */
#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 = %qd bytes", \
cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
}
static void ack(char *);
static void sigurg(int);
static void myoob(void);
static int checkuser(char *, char *);
static FILE *dataconn(char *, off_t, char *);
static void dolog(struct sockaddr *);
static char *copy_dir(char *, struct passwd *);
static char *curdir(void);
static void end_login(void);
static FILE *getdatasock(char *);
static int guniquefd(char *, char **);
static void lostconn(int);
static void sigquit(int);
static int receive_data(FILE *, FILE *);
static void replydirname(const char *, const char *);
static int send_data(FILE *, FILE *, off_t, off_t, int);
static struct passwd *
sgetpwnam(char *, struct passwd *);
static void reapchild(int);
#if defined(TCPWRAPPERS)
static int check_host(struct sockaddr *);
#endif /* TCPWRAPPERS */
static void usage(void);
void logxfer(char *, off_t, time_t);
void set_slave_signals(void);
static char *
curdir(void)
{
static char path[MAXPATHLEN+1]; /* path + '/' */
if (getcwd(path, sizeof(path)-1) == NULL)
return ("");
if (path[1] != '\0') /* special case for root dir. */
strlcat(path, "/", sizeof path);
/* For guest account, skip / since it's chrooted */
return (guest ? path+1 : path);
}
char *argstr = "AdDhnlMSt:T:u:UvP46";
static void
usage(void)
{
syslog(LOG_ERR,
"usage: ftpd [-46ADdlMnPSU] [-T maxtimeout] [-t timeout] [-u mask]");
exit(2);
}
int
main(int argc, char *argv[])
{
socklen_t addrlen;
int ch, on = 1, tos;
char line[LINE_MAX];
FILE *fp;
struct hostent *hp;
struct sigaction sa;
int error = 0;
const char *errstr;
tzset(); /* in case no timezone database in ~ftp */
sigfillset(&allsigs); /* used to block signals while root */
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
while ((ch = getopt(argc, argv, argstr)) != -1) {
switch (ch) {
case 'A':
anon_only = 1;
break;
case 'd':
case 'v': /* deprecated */
debug = 1;
break;
case 'D':
daemon_mode = 1;
break;
case 'P':
portcheck = 0;
break;
case 'h': /* deprecated */
break;
case 'l':
logging++; /* > 1 == extra logging */
break;
case 'M':
multihome = 1;
break;
case 'n':
anon_ok = 0;
break;
case 'S':
stats = 1;
break;
case 't':
timeout = strtonum(optarg, 0, INT_MAX, &errstr);
if (errstr) {
syslog(LOG_ERR,
"%s is a bad value for -t, aborting",
optarg);
exit(2);
}
if (maxtimeout < timeout)
maxtimeout = timeout;
break;
case 'T':
maxtimeout = strtonum(optarg, 0, INT_MAX,
&errstr);
if (errstr) {
syslog(LOG_ERR,
"%s is a bad value for -T, aborting",
optarg);
exit(2);
}
if (timeout > maxtimeout)
timeout = maxtimeout;
break;
case 'u':
{
long val = 0;
char *p;
umaskchange = 0;
val = strtol(optarg, &p, 8);
if (*optarg == '\0' || *p != '\0' || val < 0 ||
(val & ~ACCESSPERMS)) {
syslog(LOG_ERR,
"%s is a bad value for -u, aborting",
optarg);
exit(2);
}
defumask = val;
break;
}
case 'U':
doutmp = 1;
break;
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
default:
usage();
break;
}
}
(void) freopen(_PATH_DEVNULL, "w", stderr);
/*
* 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);
if (getpwnam(FTPD_PRIVSEP_USER) == NULL) {
syslog(LOG_ERR, "privilege separation user %s not found",
FTPD_PRIVSEP_USER);
exit(1);
}
endpwent();
if (daemon_mode) {
int *fds, i, fd;
struct pollfd *pfds;
struct addrinfo hints, *res, *res0;
nfds_t n;
/*
* Detach from parent.
*/
if (daemon(1, 1) < 0) {
syslog(LOG_ERR, "failed to become a daemon");
exit(1);
}
sa.sa_handler = reapchild;
(void) sigaction(SIGCHLD, &sa, NULL);
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
error = getaddrinfo(NULL, "ftp", &hints, &res0);
if (error) {
syslog(LOG_ERR, "%s", gai_strerror(error));
exit(1);
}
n = 0;
for (res = res0; res; res = res->ai_next)
n++;
fds = calloc(n, sizeof(int));
pfds = calloc(n, sizeof(struct pollfd));
if (!fds || !pfds) {
syslog(LOG_ERR, "%s", strerror(errno));
exit(1);
}
/*
* Open sockets, bind it to the FTP port, and start
* listening.
*/
n = 0;
for (res = res0; res; res = res->ai_next) {
fds[n] = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (fds[n] < 0)
continue;
if (setsockopt(fds[n], SOL_SOCKET, SO_REUSEADDR,
&on, sizeof(on)) < 0) {
close(fds[n]);
fds[n] = -1;
continue;
}
if (bind(fds[n], res->ai_addr, res->ai_addrlen) < 0) {
close(fds[n]);
fds[n] = -1;
continue;
}
if (listen(fds[n], 32) < 0) {
close(fds[n]);
fds[n] = -1;
continue;
}
pfds[n].fd = fds[n];
pfds[n].events = POLLIN;
n++;
}
freeaddrinfo(res0);
if (n == 0) {
syslog(LOG_ERR, "could not open control socket");
exit(1);
}
/* Stash pid in pidfile */
if (pidfile(NULL))
syslog(LOG_ERR, "can't open pidfile: %m");
/*
* Loop forever accepting connection requests and forking off
* children to handle them.
*/
while (1) {
if (poll(pfds, n, INFTIM) < 0) {
if (errno == EINTR)
continue;
syslog(LOG_ERR, "poll: %m");
exit(1);
}
for (i = 0; i < n; i++)
if (pfds[i].revents & POLLIN) {
addrlen = sizeof(his_addr);
fd = accept(pfds[i].fd,
(struct sockaddr *)&his_addr,
&addrlen);
if (fd != -1) {
if (fork() == 0)
goto child;
close(fd);
}
}
}
child:
/* child */
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
for (i = 0; i < n; i++)
close(fds[i]);
#if defined(TCPWRAPPERS)
/* ..in the child. */
if (!check_host((struct sockaddr *)&his_addr))
exit(1);
#endif /* TCPWRAPPERS */
} else {
addrlen = sizeof(his_addr);
if (getpeername(0, (struct sockaddr *)&his_addr,
&addrlen) < 0) {
/* syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); */
exit(1);
}
}
/* set this here so klogin can use it... */
(void)snprintf(ttyline, sizeof(ttyline), "ftp%ld", (long)getpid());
set_slave_signals();
addrlen = sizeof(ctrl_addr);
if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
syslog(LOG_ERR, "getsockname: %m");
exit(1);
}
if (his_addr.su_family == AF_INET6 &&
IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) {
#if 1
/*
* IPv4 control connection arrived to AF_INET6 socket.
* I hate to do this, but this is the easiest solution.
*/
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -