📄 ftpd.c
字号:
/* ftpd.c: BetaFTPD main Copyright (C) 1999-2000 Steinar H. Gunderson This program is is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2 if the License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//* * Special note: this file has been overwritten by another (0-byte) file, been * through the dead, and restored (with the help of dd, grep, gpm, vi and less) * with a sucess rate of 99.9%. Show it a little respect -- don't add junk * to it. :-) */#define _GNU_SOURCE#if HAVE_CONFIG_H#include <config.h>#endif#if HAVE_ERRNO_H#include <errno.h>#endif#if HAVE_STROPTS_H#include <stropts.h>#endif#if HAVE_SYS_CONF_H#include <sys/conf.h>#endif#if HAVE_FCNTL_H#include <fcntl.h>#endif#if HAVE_STDIO_H#include <stdio.h>#endif#if HAVE_ASSERT_H#include <assert.h>#endif#if HAVE_STRING_H#include <string.h>#endif#if HAVE_STRINGS_H#include <strings.h>#endif#if HAVE_STDARG_H#include <stdarg.h>#endif#if HAVE_STDLIB_H#include <stdlib.h>#endif#if HAVE_UNISTD_H#include <unistd.h>#endif#if HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#if HAVE_SYS_STAT_H#include <sys/stat.h>#endif#if HAVE_SYS_IOCTL_H#include <sys/ioctl.h>#endif#if HAVE_NETINET_IN_SYSTM_H#include <netinet/in_systm.h>#endif#if HAVE_NETINET_IP_H#include <netinet/ip.h>#endif#if HAVE_NETINET_TCP_H#include <netinet/tcp.h>#endif#if HAVE_LINUX_SOCKET_H#include <linux/socket.h>#endif#if HAVE_LINUX_TCP_H#include <linux/tcp.h>#endif#if HAVE_MMAP#include <sys/mman.h>#endif#if HAVE_TIME_H#include <time.h>#endif#if HAVE_SYS_TIME_H#include <sys/time.h>#endif#if HAVE_SYS_TIME_H#include <sys/time.h>#endif#if HAVE_SYS_FILIO_H#include <sys/filio.h>#endif#if HAVE_NETDB_H#include <netdb.h>#endif#if HAVE_SIGNAL_H#include <signal.h>#endif#if HAVE_GLOB_H#include <glob.h>#endif#if HAVE_SYS_SIGNAL_H#include <sys/signal.h>#endif#if HAVE_SYS_POLL_H#include <sys/poll.h>#endif#if HAVE_SYS_SENDFILE_H#include <sys/sendfile.h>#endif/* * <linux/socket.h> does not export this to glibc2 systems, and it isn't * always defined anywhere else. */#if !defined(TCP_CORK) && defined(__linux__)#define TCP_CORK 3#endif#include <ftpd.h>#include <cmds.h>#if WANT_ASCII#include <ascii.h>#endif#if WANT_DCACHE#include <dcache.h>#endif#ifndef MAP_FAILED#define MAP_FAILED -1#endifstruct conn *first_conn = NULL;struct ftran *first_ftran = NULL;#if WANT_DCACHEstruct dcache *first_dcache = NULL;#endif#if HAVE_POLLunsigned int highest_fds = 0;#define FD_MAX 1024#define fds_send fdsstruct pollfd fds[FD_MAX];#define MAXCLIENTS FD_MAX#elsefd_set master_fds, master_send_fds;#define MAXCLIENTS FD_SETSIZE#endif#if WANT_XFERLOGFILE *xferlog = NULL;#endif#if HAVE_LINUX_SENDFILEint sendfile_supported = 1;#endif/* * This variable specifies if it's soon time to check for timed out * clients, and timed out directory listing cache entries. It is * set to 1 by a signal handler every minute, and set to 0 when the * checking has been performed. */int time_to_check = 1;#ifndef HAVE_SPRINTF/* * snprintf(): snprintf() replacement for systems that miss it. Note * that this implementation does _not_ necessarily protect * against all buffer overflows. Get a real snprintf() in * your C library. That being said, the 8k limit is * substantially larger than any other string in BetaFTPD, * which should make such an attack harder. */int snprintf(char *str, size_t n, const char *format, ...){ char buf[8192]; va_list args; int err; va_start(args, format); err = vsprintf(buf, format, args); va_end(args); buf[(int)n] = 0; strcpy(str, buf); return err;}#endif#ifndef HAVE_VSNPRINTF/* * vsnprintf: vsnprintf() replacement for systems that miss it. Please * see snprintf (above) for more information. */int vsnprintf(char *str, size_t n, const char *format, va_list ap){ char buf[8192]; int err; err = vsprintf(buf, format, ap); buf[(int)n] = 0; strcpy(str, buf); return err;}#endif/* * add_fd(): Add an fd to the set we monitor. Return 0 on success. * This code is shared between poll() and select() versions. */int add_fd(const int fd, const int events){#if HAVE_POLL if (fd >= FD_MAX) { printf("add_fd(%d, %x): failed\n", fd, events); return E2BIG; } fds[fd].fd = fd; fds[fd].events = events; if (highest_fds < fd) highest_fds = fd;#else if (fd >= FD_SETSIZE) return E2BIG; if (events & POLLIN) FD_SET(fd, &master_fds); if (events & POLLOUT) FD_SET(fd, &master_send_fds);#endif return 0;}/* * del_fd(): Close and remove an fd from the set(s) we monitor. (See also add_fd().) */void del_fd(const int fd){#if HAVE_POLL if (fd >= FD_MAX) return; fds[fd].fd = -1; fds[fd].events = 0; /* Reduce poll()'s workload by not making it watch past end of array */ while ((highest_fds > 0) && (fds[highest_fds].fd == -1)) highest_fds--;#else if (fd >= FD_SETSIZE) return; FD_CLR(fd, &master_fds); FD_CLR(fd, &master_send_fds);#endif close(fd);}#if 0void list_clients(){ struct conn *c = first_conn; printf("list_clients:\n"); while (c && c->next_conn) { c = c->next_conn; printf("list_clients: fd %d\n", c->sock); }}#endif/* * add_to_linked_list(): * Inserts an element (conn, ftran or dcache) into its linked list. * The list is placed at the beginning, right after the (bogus) * first element of the list. */void add_to_linked_list(struct list_element * const first, struct list_element * const elem){ elem->prev = first; if (first) { elem->next = first->next; if (elem->next) elem->next->prev = elem; first->next = elem; } else { /* this is the bogus head of the list */ elem->next = NULL; }}/* * remove_from_linked_list(): * Removes an element (conn, ftran or dcache) from its linked list, * then frees it. */void remove_from_linked_list(struct list_element * const elem){ if (elem->prev != NULL) elem->prev->next = elem->next; if (elem->next != NULL) elem->next->prev = elem->prev; free(elem);}/* * alloc_new_conn(): * Allocates a new control connection (type `struct conn'), * initializes it, and adds it to the linked list. The connection * operates on the socket SOCK. */struct conn *alloc_new_conn(const int sock){ const unsigned int one = 1; struct conn *c = (struct conn *)(malloc(sizeof(struct conn))); if (c == NULL) return c; if (sock != -1) { ioctl(sock, FIONBIO, &one); if (add_fd(sock, POLLIN) != 0) { /* temp unavail */ send(sock, "230 Server too busy, please try again later.\r\n", 46, 0); close(sock); return NULL; } add_to_linked_list((struct list_element *)first_conn, (struct list_element *)c); } else { /* this is the bogus head of the list */ c->next_conn = NULL; c->prev_conn = NULL; } c->transfer = NULL; c->sock = sock; c->buf_len = c->auth = c->rest_pos = 0;#if WANT_ASCII c->ascii_mode = 0;#endif /* * equals: * strcpy(c->curr_dir, "/"); * strcpy(c->last_cmd, ""); * strcpy(c->rename_from, "") */ c->curr_dir[0] = '/';#if WANT_FULLSCREEN c->curr_dir[1] = c->last_cmd[0] = c->rename_from[0] = '\0';#else c->curr_dir[1] = c->rename_from[0] = '\0';#endif time(&(c->last_transfer)); /*list_clients();*/ return c;}/* * alloc_new_ftran(): * Allocates a new data connection (type `struct ftran'), and * adds it to the linked list. The connection operates on the * socket SOCK, and has the control connection C as its parent. */struct ftran *alloc_new_ftran(const int sock, const struct conn * const c){ struct ftran *f = (struct ftran *)(malloc(sizeof(struct ftran))); if (f == NULL) return f; if (c == NULL) { /* this is the bogus head of the list */ f->next_ftran = NULL; f->prev_ftran = NULL; } else { add_to_linked_list((struct list_element *)first_ftran, (struct list_element *)f); }#if HAVE_MMAP f->file_data = NULL;#endif f->owner = (struct conn * const)c; f->sock = sock; f->state = 0; f->local_file = -1;#if WANT_DCACHE f->dir_cache = NULL;#endif f->dir_listing = 0; return f;}/* * destroy_conn(): * Destroy a control connection, remove it from the linked * list, and clean up after it. */void destroy_conn(struct conn * const c){ if (c == NULL) return; del_fd(c->sock); destroy_ftran(c->transfer); remove_from_linked_list((struct list_element *)c);}/* * destroy_ftran(): * Destroy a data connection, remove it from the linked list, * and clean up after it. * * For some reason, TCP_CORK (Linux 2.2.x-only) doesn't flush * even _after_ the socket is closed, so we zero it just before * closing. We also zero just before sending the last packet, * as it seems to be needed on some systems. * * If you wonder why I check for `defined(SOL_TCP)' and don't * provide an alternative, see the comments on init_file_transfer(). */void destroy_ftran(struct ftran * const f){ const unsigned int zero = 0; if (f == NULL) return;#if defined(TCP_CORK) && defined(SOL_TCP) setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero));#endif del_fd(f->sock);#if WANT_DCACHE if (f->dir_cache) { time(&(f->dir_cache->last_used)); f->dir_cache->use_count--; f->dir_cache = NULL; } else#endif#if HAVE_MMAP if (f->file_data) { if (f->dir_listing) { free(f->file_data); f->file_data = NULL; } else { munmap(f->file_data, f->size); } } if (!f->dir_listing)#endif if (f->local_file != -1) close(f->local_file);#if !HAVE_MMAP if (f->dir_listing) unlink(f->filename);#endif f->owner->transfer = NULL;#if WANT_DCACHE if (f->dir_cache != NULL) f->dir_cache->use_count--;#endif remove_from_linked_list((struct list_element *)f);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -