📄 ftpd.c
字号:
/* * 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. 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. *//* * From: @(#)ftpd.c 8.4 (Berkeley) 4/16/94 * From: NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp * From: OpenBSD: ftpd.c,v 1.26 1996/12/07 09:00:22 bitblt Exp * From: OpenBSD: ftpd.c,v 1.35 1997/05/01 14:45:37 deraadt Exp */char ftpd_rcsid[] = "$Id: ftpd.c,v 1.8 1997/06/09 01:04:59 dholland Exp $";char copyright[] = "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n" " The Regents of the University of California. All rights reserved.\n";/* * 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 <ctype.h>#include <dirent.h>#include <errno.h>#include <fcntl.h>#include <glob.h>#include <limits.h>#include <netdb.h>#include <pwd.h>#include <setjmp.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <syslog.h>#include <time.h>#include <vis.h>#include <unistd.h>#include <utmp.h>#ifndef __linux__#include <err.h>#else#include <grp.h> /* for initgroups() */#include <sys/file.h> /* for L_SET et al. */typedef int64_t quad_t;#endif#ifndef MAP_FAILED#define MAP_FAILED ((void *)-1)#endif#ifdef USE_SHADOW#include <shadow.h>#include "isexpired.h"#endif#include "pathnames.h"#include "extern.h"#if __STDC__#include <stdarg.h>#else#include <varargs.h>#endifstatic char version[] = "Version 6.2/OpenBSD/Linux-0.10";extern off_t restart_point;extern char cbuf[];struct sockaddr_in server_addr;struct sockaddr_in ctrl_addr;struct sockaddr_in data_source;struct sockaddr_in data_dest;struct sockaddr_in his_addr;struct sockaddr_in pasv_addr;int daemon_mode = 0;int data;jmp_buf errcatch, urgcatch;int logged_in;struct passwd *pw;#ifdef USE_SHADOWstruct spwd *spw = NULL;#endifint 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 high_data_ports = 0;int anon_only = 0;int multihome = 0;int guest;int stats;int statfd = -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 */sig_atomic_t transflag;off_t file_size;off_t byte_count;#if !defined(CMASK) || CMASK == 0#undef CMASK#define CMASK 027#endifint defumask = CMASK; /* default umask value */char tmpline[7];char hostname[MAXHOSTNAMELEN];char remotehost[MAXHOSTNAMELEN];char dhostname[MAXHOSTNAMELEN];char *guestpw;static char ttyline[20];char *tty = ttyline; /* for klogin */static struct utmp utmp; /* for utmp */#if defined(KERBEROS)int notickets = 1;char *krbtkfile_env = NULL;#endifchar *ident = NULL;/* * 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 HASSETPROCTITLEchar 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, (quad_t)(cnt)); \ }static void ack __P((const char *));static void myoob __P((int));static int checkuser __P((const char *, const char *));static FILE *dataconn __P((const char *, off_t, const char *));static void dolog __P((struct sockaddr_in *));static const char *curdir __P((void));static void end_login __P((void));static FILE *getdatasock __P((const char *));static int guniquefd __P((const char *, char **));static void lostconn __P((int));static void sigquit __P((int));static int receive_data __P((FILE *, FILE *));static void send_data __P((FILE *, FILE *, off_t, off_t, int));static struct passwd * sgetpwnam __P((const char *));static char *sgetsave __P((char *));static void reapchild __P((int));void logxfer __P((const char *, off_t, time_t));#ifdef __linux__static void warnx(const char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "ftpd: "); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); va_end(ap);}#endif /* __linux__ */static const char *curdir(void){ static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ if (getcwd(path, sizeof(path)-2) == NULL) return (""); if (path[1] != '\0') /* special case for root dir. */ strcat(path, "/"); /* For guest account, skip / since it's chrooted */ return (guest ? path+1 : path);}intmain(int argc, char *argv[], char **envp){ int addrlen, ch, on = 1, tos; char *cp, line[LINE_MAX]; FILE *fd; const char *argstr = "AdDhlMSt:T:u:Uv"; struct hostent *hp; void (*sigreturn)(int); initsetproctitle(argc, argv, envp); tzset(); /* in case no timezone database in ~ftp */ /* set this here so klogin can use it... */ (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); while ((ch = getopt(argc, argv, argstr)) != -1) { switch (ch) { case 'A': anon_only = 1; break; case 'd': debug = 1; break; case 'D': daemon_mode = 1; break; case 'h': high_data_ports = 1; break; case 'l': logging++; /* > 1 == extra logging */ break; case 'M': multihome = 1; break; case 'S': stats = 1; break; case 't': timeout = atoi(optarg); if (maxtimeout < timeout) maxtimeout = timeout; break; case 'T': maxtimeout = atoi(optarg); if (timeout > maxtimeout) timeout = maxtimeout; break; case 'u': { long val = 0; val = strtol(optarg, &optarg, 8); if (*optarg != '\0' || val < 0) warnx("bad value for -u"); else defumask = val; break; } case 'U': doutmp = 1; break; case 'v': debug = 1; break; default: warnx("unknown flag -%c ignored", optopt); 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. */#ifndef LOG_FTP#define LOG_FTP LOG_DAEMON#endif openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); if (daemon_mode) { int ctl_sock, fd2; struct servent *sv; /* * Detach from parent. */ if (daemon(1, 1) < 0) { syslog(LOG_ERR, "failed to become a daemon"); exit(1); } (void) signal(SIGCHLD, reapchild); /* * Get port number for ftp/tcp. */ sv = getservbyname("ftp", "tcp"); if (sv == NULL) { syslog(LOG_ERR, "getservbyname for ftp failed"); exit(1); } /* * Open a socket, bind it to the FTP port, and start * listening. */ ctl_sock = socket(AF_INET, SOCK_STREAM, 0); if (ctl_sock < 0) { syslog(LOG_ERR, "control socket: %m"); exit(1); } if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "control setsockopt: %m");; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = sv->s_port; if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) { syslog(LOG_ERR, "control bind: %m"); exit(1); } if (listen(ctl_sock, 32) < 0) { syslog(LOG_ERR, "control listen: %m"); exit(1); } /* * Loop forever accepting connection requests and forking off * children to handle them. */ while (1) { addrlen = sizeof(his_addr); fd2 = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); if (fork() == 0) { /* child */ (void) dup2(fd2, 0); (void) dup2(fd2, 1); close(ctl_sock); break; } close(fd2); } } else { addrlen = sizeof(his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); exit(1); } } (void) signal(SIGHUP, sigquit); (void) signal(SIGINT, sigquit); (void) signal(SIGQUIT, sigquit); (void) signal(SIGTERM, sigquit); (void) signal(SIGPIPE, lostconn); (void) signal(SIGCHLD, SIG_IGN); sigreturn = signal(SIGURG, myoob); if ((long)sigreturn < 0) syslog(LOG_ERR, "signal: %m"); 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); /* 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'; /* If logins are disabled, print out the message. */ if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(530, "%s", line); } (void) fflush(stdout); (void) fclose(fd); reply(530, "System not available."); exit(0); } if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(220, "%s", line); } (void) fflush(stdout); (void) fclose(fd); /* reply(220,) must follow */ } (void) gethostname(hostname, sizeof(hostname)); /* Make sure hostname is fully qualified. */ hp = gethostbyname(hostname); if (hp != NULL) strcpy (hostname, hp->h_name); if (multihome) { hp = gethostbyaddr((char *) &ctrl_addr.sin_addr, sizeof (struct in_addr), AF_INET); if (hp != NULL) { strcpy (dhostname, hp->h_name); } else { /* Default. */ strcpy (dhostname, inet_ntoa(ctrl_addr.sin_addr)); } } reply(220, "%s FTP server (%s) ready.", (multihome ? dhostname : hostname), version); (void) setjmp(errcatch); for (;;) (void) yyparse(); /* NOTREACHED */}/* * Signal handlers. */static voidlostconn(int signo){ (void)signo; if (debug) syslog(LOG_DEBUG, "lost connection"); dologout(-1);}static voidsigquit(signo) int signo;{ syslog(LOG_ERR, "got signal %s", strsignal(signo)); dologout(-1);}/* * Helper function for sgetpwnam(). */static char *sgetsave(s) char *s;{ char *new = malloc((unsigned) strlen(s) + 1); if (new == NULL) { perror_reply(421, "Local resource failure: malloc"); dologout(1); /* NOTREACHED */ } (void) strcpy(new, s);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -