├── debian ├── conffiles ├── docs ├── files ├── dirs ├── oftpd.substvars ├── prerm ├── oftpd.prerm.debhelper ├── oftpd.postrm.debhelper ├── postrm ├── oftpd.postinst.debhelper ├── copyright ├── control ├── rules ├── postinst ├── oftpd.config ├── changelog └── oftpd.templates ├── man ├── Makefile.am └── oftpd.8 ├── dist ├── oftpd.spec-petr ├── README └── oftpd.spec-mauro ├── Makefile.am ├── oftpd.conf ├── .gitignore ├── src ├── file_list.h ├── Makefile.am ├── error.h ├── daemon_assert.c ├── daemon_assert.h ├── oftpd.h ├── telnet_session.h ├── watchdog.h ├── ftp_command.h ├── af_portability.h ├── error.c ├── ftp_listener.h ├── ftp_session.h ├── watchdog.c ├── oftpd.c ├── file_list.c ├── telnet_session.c ├── ftp_command.c └── ftp_listener.c ├── AUTHORS ├── NEWS ├── BUGS ├── COPYING ├── FAQ ├── init └── oftpd.redhat7 ├── TODO ├── README ├── oftpd.startup ├── configure.ac ├── mkinstalldirs ├── INSTALL ├── install-sh ├── missing └── ChangeLog /debian/conffiles: -------------------------------------------------------------------------------- 1 | /etc/init.d/oftpd 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | BUGS 2 | README 3 | TODO 4 | -------------------------------------------------------------------------------- /debian/files: -------------------------------------------------------------------------------- 1 | oftpd_0.3.6-5_i386.deb net extra 2 | -------------------------------------------------------------------------------- /debian/dirs: -------------------------------------------------------------------------------- 1 | etc/default 2 | etc/init.d 3 | usr/bin 4 | usr/sbin 5 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = oftpd.8 2 | EXTRA_DIST = $(man_MANS) 3 | -------------------------------------------------------------------------------- /dist/oftpd.spec-petr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpg/oftpd/master/dist/oftpd.spec-petr -------------------------------------------------------------------------------- /debian/oftpd.substvars: -------------------------------------------------------------------------------- 1 | misc:Depends=debconf (>= 0.5) 2 | shlibs:Depends=libc6 (>= 2.2.4-4) 3 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | EXTRA_DIST = BUGS FAQ acconfig.h init/oftpd* dist/README dist/oftpd* 3 | SUBDIRS = src man 4 | -------------------------------------------------------------------------------- /debian/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | #DEBHELPER# 5 | 6 | 7 | if [ \( "$1" = "upgrade" -o "$1" = "remove" \) ]; then 8 | /etc/init.d/oftpd stop 9 | fi 10 | 11 | 12 | -------------------------------------------------------------------------------- /debian/oftpd.prerm.debhelper: -------------------------------------------------------------------------------- 1 | # Automatically added by dh_installdocs 2 | if [ \( "$1" = "upgrade" -o "$1" = "remove" \) -a -L /usr/doc/oftpd ]; then 3 | rm -f /usr/doc/oftpd 4 | fi 5 | # End automatically added section 6 | -------------------------------------------------------------------------------- /oftpd.conf: -------------------------------------------------------------------------------- 1 | # This file should be editted using 'dpkg-reconfigure oftpd' rather than 2 | # manually. 3 | ENABLE="no" 4 | USER=oftpd 5 | DIR=/home/oftpd/ 6 | PORT="" 7 | PASV_RANGE="" 8 | IFACE="0.0.0.0" 9 | MAXCON="250" 10 | -------------------------------------------------------------------------------- /debian/oftpd.postrm.debhelper: -------------------------------------------------------------------------------- 1 | # Automatically added by dh_installdebconf 2 | if [ "$1" = purge -a -e /usr/share/debconf/confmodule ]; then 3 | . /usr/share/debconf/confmodule 4 | db_purge 5 | fi 6 | # End automatically added section 7 | -------------------------------------------------------------------------------- /debian/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | #DEBHELPER# 5 | 6 | CONFFILE=/etc/default/oftpd 7 | 8 | if [ $1 == "purge" ]; then 9 | update-rc.d oftpd remove >/dev/null 2>/dev/null 10 | rm -f "$CONFFILE" 11 | fi 12 | 13 | exit 0; 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | aclocal.m4 2 | autom4te.cache/ 3 | configure 4 | config.h.in 5 | config.h 6 | config.log 7 | config.status 8 | stamp-h1 9 | Makefile.in 10 | Makefile 11 | .deps/ 12 | 13 | # Files created by make when not using a VPATH build 14 | *.o 15 | src/oftpd 16 | -------------------------------------------------------------------------------- /debian/oftpd.postinst.debhelper: -------------------------------------------------------------------------------- 1 | # Automatically added by dh_installdocs 2 | if [ "$1" = "configure" ]; then 3 | if [ -d /usr/doc -a ! -e /usr/doc/oftpd -a -d /usr/share/doc/oftpd ]; then 4 | ln -sf ../share/doc/oftpd /usr/doc/oftpd 5 | fi 6 | fi 7 | # End automatically added section 8 | -------------------------------------------------------------------------------- /src/file_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #ifndef FILE_LIST_H 6 | #define FILE_LIST_H 7 | 8 | int file_nlst(int out, const char *cur_dir, const char *filespec); 9 | int file_list(int out, const char *cur_dir, const char *filespec); 10 | 11 | #endif /* FILE_LIST_H */ 12 | 13 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = oftpd 2 | oftpd_SOURCES = file_list.c file_list.h ftp_command.c ftp_command.h ftp_listener.c ftp_listener.h ftp_session.c ftp_session.h oftpd.c oftpd.h telnet_session.c telnet_session.h watchdog.c watchdog.h error.c error.h af_portability.h daemon_assert.c daemon_assert.h 3 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by Matthew Danish on 2 | Thu, 22 Feb 2001 00:56:19 -0500. 3 | 4 | It was downloaded from http://www.time-travellers.org/oftpd/ 5 | 6 | Upstream Author(s): Shane Kerr 7 | 8 | Copyright: 9 | 10 | This program is distributed under the terms of the BSD license. 11 | The full text to this license may be found on Debian systems 12 | under /usr/share/common-licenses/BSD 13 | -------------------------------------------------------------------------------- /dist/README: -------------------------------------------------------------------------------- 1 | This directory contains various files used to build binary 2 | distributions. 3 | 4 | RPM specifications: 5 | 6 | oftpd.spec-petr 7 | 8 | Submitted by Petr Kristof, culled from unknown sources on the 9 | Internet. You can find this file and pre-built RPM's here: 10 | 11 | ftp://crash.fce.vutbr.cz/pub/redhat-cz/oftpd/ 12 | 13 | ---------------------------------------------------------------------- 14 | 15 | oftpd.spec-mauro 16 | 17 | Submitted by Mauro Tortonesi 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #ifndef ERROR_H 6 | #define ERROR_H 7 | 8 | typedef struct { 9 | int error_code; 10 | char desc[128]; 11 | } error_t; 12 | 13 | /* methods */ 14 | void error_init(error_t *err, int error_code, const char *desc_fmt, ...); 15 | int error_get_error_code(const error_t *err); 16 | const char *error_get_desc(const error_t *err); 17 | 18 | 19 | #ifndef HAVE_STRERROR_R 20 | char *strerror_r (int errnum, char *buf, size_t n); 21 | #endif 22 | 23 | #endif /* ERROR_H */ 24 | -------------------------------------------------------------------------------- /src/daemon_assert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "daemon_assert.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifndef NDEBUG 9 | void daemon_assert_fail(const char *assertion, 10 | const char *file, 11 | int line, 12 | const char *function) 13 | { 14 | syslog(LOG_CRIT, "%s:%d: %s: %s", file, line, function, assertion); 15 | fprintf(stderr, "%s:%d: %s: %s\n", file, line, function, assertion); 16 | exit(1); 17 | } 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Authors of oftpd: 2 | 3 | Shane Kerr 4 | 5 | 6 | Contributors: 7 | 8 | Beau Kuiper (Netscape timing bug fix) 9 | Mauro Tortonesi (IPv6 support) 10 | Matthew Danish (Debian compile help, 11 | command-line options, address parse consolidation, configure 12 | modifications to deal with ss_family variations) 13 | Eric Jensen (Red Hat 7.0 init script) 14 | Anders Nordby (FreeBSD port) 15 | 16 | For this this version: 17 | 18 | Werner Koch (new options, minor fixes) 19 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: oftpd 2 | Section: net 3 | Priority: extra 4 | Maintainer: Matthew Danish 5 | Build-Depends: debhelper (>> 2.0.0), debconf (>= 0.5.00) 6 | Standards-Version: 3.5.6.0 7 | 8 | Package: oftpd 9 | Architecture: any 10 | Depends: ${shlibs:Depends}, debconf (>= 0.5.00), sysklogd | system-log-daemon 11 | Provides: ftp-server 12 | Description: A secure anonymous FTP server 13 | Designed from the ground up to be as secure as an anonymous FTP server 14 | can be. It runs as non-root and uses chroot() to hide the rest of the 15 | system from the ftp daemon. It is designed to work efficiently with 16 | threads. 17 | -------------------------------------------------------------------------------- /src/daemon_assert.h: -------------------------------------------------------------------------------- 1 | #ifndef DAEMON_ASSERT_H 2 | #define DAEMON_ASSERT_H 3 | 4 | #ifdef NDEBUG 5 | 6 | #define daemon_assert(expr) 7 | 8 | #else 9 | 10 | void daemon_assert_fail(const char *assertion, 11 | const char *file, 12 | int line, 13 | const char *function); 14 | 15 | #define daemon_assert(expr) \ 16 | ((expr) ? 0 : \ 17 | (daemon_assert_fail(__STRING(expr), __FILE__, __LINE__, __func__))) 18 | 19 | #endif 20 | 21 | #endif /* DAEMON_ASSERT_H */ 22 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Unreleased 2 | 3 | New option --version. 4 | 5 | A couple of minor fixes. 6 | 7 | 8 | 2003-08-13 9 | 10 | New option -r, --pasv-range to limit the range for passive ports to 11 | specific firewall rules. 12 | 13 | Print a warning if the /dev directory does not exists below the 14 | chroot. It is required so that a syslogd restart won't harm. 15 | Ignore the /dev directory in the ftp session. 16 | 17 | Fixed some minor problems. 18 | 19 | 2000-12-13 20 | 21 | IPv6 code is maintained by Mauro Tortonesi . 22 | You can download the latest patches for oftpd from the Project6 23 | website: 24 | 25 | http://project6.ferrara.linux.it 26 | 27 | Thanks Mauro! 28 | -------------------------------------------------------------------------------- /src/oftpd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #ifndef OFTPD_H 6 | #define OFTPD_H 7 | 8 | /* address to listen on (use NULL to listen on all addresses) */ 9 | #define FTP_ADDRESS NULL 10 | 11 | /* default port FTP server listens on (use 0 to listen on default port) */ 12 | #define FTP_PORT 0 13 | 14 | /* ranges possible for command-line specified port numbers */ 15 | #define MIN_PORT 0 16 | #define MAX_PORT 65535 17 | 18 | /* default maximum number of clients */ 19 | #define MAX_CLIENTS 250 20 | 21 | /* bounds on command-line specified number of clients */ 22 | #define MIN_NUM_CLIENTS 1 23 | #define MAX_NUM_CLIENTS 300 24 | 25 | /* timeout (in seconds) before dropping inactive clients */ 26 | #define INACTIVITY_TIMEOUT (15 * 60) 27 | 28 | /* README file name (sent automatically as a response to users) */ 29 | #define README_FILE_NAME "README" 30 | 31 | /* The debug flags control the amount of logging. */ 32 | extern int debug_flags; 33 | #define DEBUG_NONE 0 34 | #define DEBUG_XFER (1 << 0) 35 | #define DEBUG_CMDS (1 << 1) 36 | 37 | #endif /* OFTPD_H */ 38 | 39 | -------------------------------------------------------------------------------- /src/telnet_session.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #ifndef TELNET_SESSION_H 6 | #define TELNET_SESSION_H 7 | 8 | /* size of buffer */ 9 | #define BUF_LEN 2048 10 | 11 | /* information on a telnet session */ 12 | typedef struct { 13 | int in_fd; 14 | int in_errno; 15 | int in_eof; 16 | int in_take; 17 | int in_add; 18 | char in_buf[BUF_LEN]; 19 | int in_buflen; 20 | 21 | int in_status; 22 | 23 | int out_fd; 24 | int out_errno; 25 | int out_eof; 26 | int out_take; 27 | int out_add; 28 | char out_buf[BUF_LEN]; 29 | int out_buflen; 30 | } telnet_session_t; 31 | 32 | /* functions */ 33 | void telnet_session_init(telnet_session_t *t, int in, int out); 34 | int telnet_session_print(telnet_session_t *t, const char *s); 35 | int telnet_session_print_with_prefix(telnet_session_t *t, 36 | const char *prefix, const char *s); 37 | int telnet_session_println(telnet_session_t *t, const char *s); 38 | int telnet_session_println_with_prefix(telnet_session_t *t, 39 | const char *prefix, const char *s); 40 | int telnet_session_readln(telnet_session_t *t, char *buf, int buflen); 41 | void telnet_session_destroy(telnet_session_t *t); 42 | 43 | #endif /* TELNET_SESSION_H */ 44 | 45 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | $Id$ 2 | 3 | - On Solaris, new POSIX threads cannot be created from a chroot() jail! 4 | pthread_create() uses mmap() on /dev/mem (or something like that) to 5 | create the stack for new threads. However, in a chroot() jail, no 6 | /dev files are visible (hopefully). It would still be possible to 7 | support Solaris by using malloc() and specifying the stack explicitly, 8 | but then there's the hassle of cleaning up this memory and so on. 9 | Yuck. So for now, no Solaris. In 0.5.x I expect to support Solaris 10 | again, as I'll only need a very small number of threads. 11 | 12 | - The pthread_cancel() does not actually meet POSIX compliance. It is 13 | forbidden to use PTHREAD_CANCEL_ASYNCHRONOUS on anything that makes 14 | system calls. This seems to work fine on Linux, probably due to its 15 | bizarre thread model. This will also be fixed on 0.3.x, as I'll pay 16 | careful attention to cancel points. 17 | 18 | - Probable memory leak when pthread_cancel() operates on a thread that 19 | has allocated memory (in glob() for instance). Need to disable cancel 20 | for appropriate regions of code. 21 | 22 | - Doesn't handle permissions properly. If a file is world-readable but 23 | not group-readable and someone in the group tries to read it they 24 | should not be allowed, apparently. :( 25 | 26 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2000, 2001 Shane Kerr. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | 27 | -------------------------------------------------------------------------------- /FAQ: -------------------------------------------------------------------------------- 1 | Q: When I try to upload I get an error. What am I doing wrong? 2 | 3 | A: Nothing. oftpd does not allow you to upload files, create 4 | directories, use any other FTP comands that modify the filesystem of 5 | the server. This is the way it is supposed to work. If the code 6 | doesn't write files, there's no way it can write them incorrectly! 7 | 8 | 9 | Q: Okay, can you add the STOR command, so I can upload files? I 10 | promise, I'll only use it on a secure network. 11 | 12 | A: This is possible, and I may add this some day. (Or, if you have some 13 | C programming background, you can add it yourself - the code's not 14 | that bad, I promise.) I don't have any dates in mind, however. ;) 15 | 16 | 17 | Q: Is there a Debian/Red Hat/whatever package for oftpd? 18 | 19 | A: Matthew Danish has a preliminary Debian package available at: 20 | 21 | ftp://emu.res.cmu.edu/pub/new-packages/ 22 | 23 | There are not any RPM's that I know of. The install should be fairly 24 | painless, but I'll probably put together an RPM at some point. 25 | 26 | 27 | Q: It doesn't compile on my system. What am I doing wrong? 28 | 29 | A: Probably nothing. All known compiler issues have been resolved, but 30 | there may still be problems. Send me an e-mail and I'll try to help 31 | you through any problems. 32 | 33 | 34 | Shane Kerr 35 | $Id$ 36 | -------------------------------------------------------------------------------- /init/oftpd.redhat7: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # RedHat 7.0 init style script 4 | # oftpd Start/Stop anon ftp server 5 | # Uses chkconfig tool to manage runlevel startup 6 | # To install do the following: 7 | # cp oftpd /etc/rc.d/init.d/.;chkconfig --add oftpd 8 | # 9 | # chkconfig: 345 50 50 10 | # description: Basic chroot jailed anon ftp server \ 11 | # see: http://www.time-travellers.com/oftpd/ 12 | # 13 | # processname: oftpd 14 | # pidfile: none 15 | # config: none, set ==> $OFTPD, $FTPROOT, $USER 16 | # 17 | 18 | #Change these three lines to suit your needs 19 | OFTPD=/opt/bin/oftpd 20 | FTPROOT=/var/oftp 21 | USER=ftp 22 | 23 | # Source function library. 24 | . /etc/init.d/functions 25 | 26 | [ -x $OFTPD ] || exit 0 27 | 28 | 29 | start() { 30 | echo -n "Starting oftpd: " 31 | daemon $OFTPD $USER $FTPROOT 32 | RETVAL=$? 33 | echo 34 | [ $RETVAL -eq 0 ] 35 | return $RETVAL 36 | } 37 | 38 | stop() { 39 | echo -n "Stopping oftpd services: " 40 | killproc oftpd 41 | RETVAL=$? 42 | echo 43 | [ $RETVAL -eq 0 ] 44 | return $RETVAL 45 | } 46 | 47 | # See how we were called. 48 | case "$1" in 49 | start) 50 | start 51 | ;; 52 | stop) 53 | stop 54 | ;; 55 | status) 56 | status oftpd 57 | ;; 58 | restart|reload) 59 | stop 60 | start 61 | ;; 62 | *) 63 | echo "Usage: oftpd {start|stop|status|restart|reload|}" 64 | exit 1 65 | esac 66 | 67 | exit $RETVAL 68 | -------------------------------------------------------------------------------- /src/watchdog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #ifndef WATCHDOG_H 6 | #define WATCHDOG_H 7 | 8 | #include 9 | 10 | #if TIME_WITH_SYS_TIME 11 | # include 12 | # include 13 | #else 14 | # if HAVE_SYS_TIME_H 15 | # include 16 | # else 17 | # include 18 | # endif 19 | #endif 20 | 21 | #include "error.h" 22 | 23 | /* each watched thread gets one of these structures */ 24 | typedef struct watched { 25 | /* thread to monitor */ 26 | pthread_t watched_thread; 27 | 28 | /* flag whether in a watchdog list */ 29 | int in_list; 30 | 31 | /* time when to cancel thread if no activity */ 32 | time_t alarm_time; 33 | 34 | /* for location in doubly-linked list */ 35 | struct watched *older; 36 | struct watched *newer; 37 | 38 | /* watchdog that this watched_t is in */ 39 | void *watchdog; 40 | } watched_t; 41 | 42 | /* the watchdog keeps track of all information */ 43 | typedef struct { 44 | pthread_mutex_t mutex; 45 | int inactivity_timeout; 46 | 47 | /* the head and tail of our list */ 48 | watched_t *oldest; 49 | watched_t *newest; 50 | } watchdog_t; 51 | 52 | int watchdog_init(watchdog_t *w, int inactivity_timeout, error_t *err); 53 | void watchdog_add_watched(watchdog_t *w, watched_t *watched); 54 | void watchdog_defer_watched(watched_t *watched); 55 | void watchdog_remove_watched(watched_t *watched); 56 | 57 | #endif /* WATCHDOG_H */ 58 | 59 | -------------------------------------------------------------------------------- /src/ftp_command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * The following comands are parsed: 5 | * 6 | * USER 7 | * PASS 8 | * CWD 9 | * CDUP 10 | * QUIT 11 | * PORT 12 | * LPRT 13 | * EPRT 14 | * PASV 15 | * LPSV 16 | * EPSV [ ] 17 | * TYPE 18 | * STRU 19 | * MODE 20 | * RETR 21 | * STOR 22 | * PWD 23 | * LIST [ ] 24 | * NLST [ ] 25 | * SYST 26 | * HELP [ ] 27 | * NOOP 28 | * REST 29 | */ 30 | 31 | #ifndef FTP_COMMAND_H 32 | #define FTP_COMMAND_H 33 | 34 | #include 35 | #include 36 | #include 37 | #include "af_portability.h" 38 | 39 | /* special macro for handling EPSV ALL requests */ 40 | #define EPSV_ALL (-1) 41 | 42 | /* maximum possible number of arguments */ 43 | #define MAX_ARG 2 44 | 45 | /* maximum string length */ 46 | #define MAX_STRING_LEN PATH_MAX 47 | 48 | typedef struct { 49 | char command[5]; 50 | int num_arg; 51 | union { 52 | char string[MAX_STRING_LEN+1]; 53 | sockaddr_storage_t host_port; 54 | int num; 55 | off_t offset; 56 | } arg[MAX_ARG]; 57 | } ftp_command_t; 58 | 59 | 60 | int ftp_command_parse(const char *input, ftp_command_t *cmd); 61 | 62 | #endif /* FTP_COMMAND_H */ 63 | 64 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | export DH_COMPAT=2 3 | 4 | configure: configure-stamp 5 | configure-stamp: 6 | dh_testdir 7 | ./configure --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info 8 | 9 | touch configure-stamp 10 | 11 | build: configure-stamp build-stamp 12 | build-stamp: 13 | dh_testdir 14 | 15 | $(MAKE) 16 | 17 | touch build-stamp 18 | 19 | clean: 20 | dh_testdir 21 | dh_testroot 22 | rm -f build-stamp configure-stamp 23 | 24 | -$(MAKE) distclean 25 | 26 | dh_clean 27 | 28 | install: build 29 | dh_testdir 30 | dh_testroot 31 | dh_clean -k 32 | dh_installdirs 33 | 34 | $(MAKE) install prefix=$(CURDIR)/debian/oftpd/usr 35 | mv $(CURDIR)/debian/oftpd/usr/bin/oftpd $(CURDIR)/debian/oftpd/usr/sbin 36 | #cp oftpd.conf $(CURDIR)/debian/oftpd/etc/default/oftpd 37 | cp oftpd.startup $(CURDIR)/debian/oftpd/etc/init.d/oftpd 38 | chmod 755 $(CURDIR)/debian/oftpd/etc/init.d/oftpd 39 | 40 | binary-indep: build install 41 | 42 | binary-arch: build install 43 | dh_testdir 44 | dh_testroot 45 | dh_installdebconf 46 | dh_installdocs 47 | dh_installexamples 48 | dh_installmenu 49 | dh_installcron 50 | dh_installmanpages 51 | dh_installinfo 52 | dh_installchangelogs ChangeLog 53 | dh_link 54 | dh_strip 55 | dh_compress 56 | dh_fixperms 57 | dh_installdeb 58 | dh_shlibdeps 59 | dh_gencontrol 60 | dh_md5sums 61 | dh_builddeb 62 | 63 | binary: binary-indep binary-arch 64 | .PHONY: build clean binary-indep binary-arch binary install configure 65 | -------------------------------------------------------------------------------- /src/af_portability.h: -------------------------------------------------------------------------------- 1 | #ifndef AF_PORTABILITY_H 2 | #define AF_PORTABILITY_H 3 | 4 | #include 5 | #include 6 | 7 | /* _x_ must be a pointer to a sockaddr structure */ 8 | 9 | #define SAFAM(_x_) (((struct sockaddr *)(_x_))->sa_family) 10 | 11 | #ifdef HAVE_NEW_SS_FAMILY 12 | #define SSFAM(_x_) (((struct sockaddr_storage *)(_x_))->ss_family) 13 | #else 14 | #define SSFAM(_x_) (((struct sockaddr_storage *)(_x_))->__ss_family) 15 | #endif 16 | 17 | #define SIN4ADDR(_x_) (((struct sockaddr_in *)(_x_))->sin_addr) 18 | #define SIN4PORT(_x_) (((struct sockaddr_in *)(_x_))->sin_port) 19 | #define SIN6ADDR(_x_) (((struct sockaddr_in6 *)(_x_))->sin6_addr) 20 | #define SIN6PORT(_x_) (((struct sockaddr_in6 *)(_x_))->sin6_port) 21 | 22 | #ifdef INET6 23 | #define SINADDR(_x_) ((SAFAM(_x_)==AF_INET6) ? SIN6ADDR(_x_) : SIN4ADDR(_x_)) 24 | #define SINPORT(_x_) ((SAFAM(_x_)==AF_INET6) ? SIN6PORT(_x_) : SIN4PORT(_x_)) 25 | #else 26 | #define SINADDR(_x_) SIN4ADDR(_x_) 27 | #define SINPORT(_x_) SIN4PORT(_x_) 28 | #endif 29 | 30 | #ifndef INET_ADDRSTRLEN 31 | #define INET_ADDRSTRLEN 16 32 | #endif 33 | 34 | #ifndef INET6_ADDRSTRLEN 35 | #define INET6_ADDRSTRLEN 46 36 | #endif 37 | 38 | #ifdef INET6 39 | #define IP6_ADDRSTRLEN INET6_ADDRSTRLEN 40 | #define IP4_ADDRSTRLEN INET_ADDRSTRLEN 41 | #define IP_ADDRSTRLEN INET6_ADDRSTRLEN 42 | #else 43 | #define IP_ADDRSTRLEN INET_ADDRSTRLEN 44 | #endif 45 | 46 | #ifdef INET6 47 | typedef struct sockaddr_storage sockaddr_storage_t; 48 | #else 49 | typedef struct sockaddr_in sockaddr_storage_t; 50 | #endif 51 | 52 | #endif /* AF_PORTABILITY_H */ 53 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * This data type allows modules to cleanly return error information in a 5 | * relatively clean fashion. It only includes an error number and a 6 | * description string right now. It could be modified to include a large 7 | * number of other data, e.g. module, file/line, timestamp. I don't 8 | * need that for my program right now, so I'm going to keep it simple. 9 | * 10 | * -Shane 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "daemon_assert.h" 18 | #include "error.h" 19 | 20 | static int invariant(const error_t *err); 21 | 22 | void error_init(error_t *err, int error_code, const char *desc_fmt, ...) 23 | { 24 | va_list args; 25 | 26 | daemon_assert(err != NULL); 27 | daemon_assert(error_code >= 0); 28 | daemon_assert(desc_fmt != NULL); 29 | 30 | err->error_code = error_code; 31 | va_start(args, desc_fmt); 32 | vsnprintf(err->desc, sizeof(err->desc), desc_fmt, args); 33 | va_end(args); 34 | 35 | daemon_assert(invariant(err)); 36 | } 37 | 38 | int error_get_error_code(const error_t *err) 39 | { 40 | daemon_assert(invariant(err)); 41 | return err->error_code; 42 | } 43 | 44 | const char *error_get_desc(const error_t *err) 45 | { 46 | daemon_assert(invariant(err)); 47 | return err->desc; 48 | } 49 | 50 | #ifndef NDEBUG 51 | static int invariant(const error_t *err) 52 | { 53 | if (err == NULL) { 54 | return 0; 55 | } 56 | if (err->error_code < 0) { 57 | return 0; 58 | } 59 | if (strlen(err->desc) >= sizeof(err->desc)) { 60 | return 0; 61 | } 62 | return 1; 63 | } 64 | #endif /* NDEBUG */ 65 | 66 | -------------------------------------------------------------------------------- /src/ftp_listener.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #ifndef FTP_LISTENER_H 6 | #define FTP_LISTENER_H 7 | 8 | #include 9 | #include 10 | #include "error.h" 11 | #include "watchdog.h" 12 | 13 | #define DEFAULT_FTP_PORT 21 14 | 15 | typedef struct { 16 | 17 | /* file descriptor incoming connections arrive on */ 18 | int fd; 19 | 20 | /* maximum number of connections */ 21 | int max_connections; 22 | 23 | /* current number of connections */ 24 | int num_connections; 25 | 26 | /* timeout (in seconds) for connections */ 27 | int inactivity_timeout; 28 | 29 | /* watchdog monitoring this listener's connections */ 30 | watchdog_t watchdog; 31 | 32 | /* mutext to lock changes to this structure */ 33 | pthread_mutex_t mutex; 34 | 35 | /* starting directory */ 36 | char dir[PATH_MAX+1]; 37 | 38 | /* boolean defining whether listener is running or not */ 39 | int listener_running; 40 | 41 | /* thread identifier for listener */ 42 | pthread_t listener_thread; 43 | 44 | /* end of pipe to wake up listening thread with */ 45 | int shutdown_request_send_fd; 46 | 47 | /* end of pipe listening thread waits on */ 48 | int shutdown_request_recv_fd; 49 | 50 | /* condition to signal thread requesting shutdown */ 51 | pthread_cond_t shutdown_cond; 52 | 53 | } ftp_listener_t; 54 | 55 | int ftp_listener_init(ftp_listener_t *f, 56 | char *address, 57 | int port, 58 | int max_connections, 59 | int inactivity_timeout, 60 | error_t *err); 61 | int ftp_listener_start(ftp_listener_t *f, error_t *err); 62 | void ftp_listener_stop(ftp_listener_t *f); 63 | 64 | #endif /* FTP_LISTENER_H */ 65 | 66 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | $Id$ 2 | 3 | 4 | User requested: 5 | 6 | - STOR 7 | 8 | - User login 9 | 10 | - Bandwidth limits 11 | 12 | From RIPE: 13 | 14 | - Limit maximum download time 15 | 16 | Externally visible: 17 | 18 | - Add support for SIZE command (RFC 959?) 19 | 20 | - Add support for STAT command (RFC 959?) 21 | 22 | - Add support for the RECORD structure in transfer. 23 | Surely someone must use it. :) 24 | 25 | - Allow server messages to be configurable. I'm not sure of the best way 26 | to do this. The problem is that this entails putting the messages in a 27 | single location, which means that you *don't* see what the message is 28 | when you are looking through the code. Hm. I may just make it a 29 | comment in the code to allow readability. 30 | 31 | - Better LIST and NLST support. 32 | 33 | - Drop browser connections before the 15-minute timeout. You can detect 34 | this by looking for users who logged in as "mozilla@" or "IEUser@". 35 | In Perl, this would be /^.+\@$/ 36 | 37 | 38 | Internal use only: 39 | 40 | - Handle glob() returns more elegantly. Right now if a glob() returns 0 files, 41 | it's okay. This should return an error unless a wildcard was specified. 42 | 43 | - Possibly write our own glob() to reduce memory fragmentation. 44 | 45 | - Perhaps buffer NLST and LIST output. 46 | 47 | - Use getrlimit()/setrlimit() to cap the amount of memory allowable, and 48 | possibly other resources as well. 49 | 50 | 51 | Auditing requirements: 52 | 53 | - Run through its4, and find other lint-like tools to pound code with. 54 | 55 | - Sprinkle assert() more fully. 56 | 57 | 58 | Testing: 59 | 60 | - Find more ftp clients 61 | 62 | - Create automated test 63 | 64 | - Run on multi-CPU box 65 | 66 | 67 | Porting: 68 | 69 | - Compile on FreeBSD 70 | 71 | - Port to Windows? 72 | 73 | 74 | Other stuff: 75 | 76 | - Document design 77 | 78 | - pth support? 79 | -------------------------------------------------------------------------------- /dist/oftpd.spec-mauro: -------------------------------------------------------------------------------- 1 | Summary: oftpd server for Linux 2 | Name: oftpd 3 | Version: 0.3.5 4 | Release: 1prj6 5 | Copyright: GPL 6 | Group: System/Servers 7 | Source: %{name}-%{version}.tar.gz 8 | BuildRoot: /tmp/%{name}-%{version}-root 9 | Provides: ftpserver 10 | 11 | %description 12 | oftpd is designed to be as secure as an anonymous FTP server can 13 | possibly be. It runs as non-root for most of the time, and uses the 14 | Unix chroot() command to hide most of the systems directories from 15 | external users - they cannot change into them even if the server is 16 | totally compromised! It contains its own directory change code, so that 17 | it can run efficiently as a threaded server, and its own directory 18 | listing code (many FTP servers execute the system "ls" command to list 19 | files). It is currently being code-reviewed for buffer overflows, and 20 | being load-tested. 21 | 22 | %prep 23 | %setup 24 | 25 | %build 26 | CFLAGS=${RPM_OPT_FLAGS} ./configure 27 | 28 | if [ -n `uname -v | grep SMP | sed "s/ /_/g"` ] 29 | then 30 | make -j 2 31 | else 32 | make 33 | fi 34 | 35 | 36 | %install 37 | rm -rf $RPM_BUILD_ROOT 38 | mkdir -p $RPM_BUILD_ROOT/usr/{sbin,man/man8} $RPM_BUILD_ROOT/etc/rc.d/init.d $RPM_BUILD_ROOT/var/oftp 39 | install -s src/oftpd $RPM_BUILD_ROOT/usr/sbin/oftpd 40 | install man/oftpd.8 $RPM_BUILD_ROOT/usr/man/man8/oftpd.8 41 | perl -pi -e "s/OFTPD=\/opt\/bin\/oftpd/OFTPD=\/usr\/sbin\/oftpd/g" init/oftpd.redhat7 42 | install init/oftpd.redhat7 $RPM_BUILD_ROOT/etc/rc.d/init.d/oftpd 43 | bzip2 -9 $RPM_BUILD_ROOT/usr/man/man8/oftpd.8 44 | 45 | 46 | %clean 47 | rm -rf $RPM_BUILD_ROOT 48 | 49 | 50 | %files 51 | %defattr(-,root,root,-) 52 | %doc AUTHORS BUGS FAQ README 53 | %attr(755,ftp,ftp) /var/oftp 54 | %attr(755,root,root) /usr/sbin/oftpd 55 | %attr(755,root,root) %config /etc/rc.d/init.d/oftpd 56 | %attr(664,root,man) %doc /usr/man/man8/oftpd.8.bz2 57 | 58 | 59 | %changelog 60 | 61 | * Tue May 1 2001 Mauro Tortonesi 62 | - first release 63 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | . /usr/share/debconf/confmodule 4 | 5 | CONFFILE=/etc/default/oftpd 6 | LOGFILE=/var/log/oftpd.log 7 | TMPFILE=$(tempfile) 8 | 9 | #DEBHELPER# 10 | 11 | update_value() { 12 | if [ -e "$CONFFILE" ]; then 13 | if [ $(grep -c "$1" "$CONFFILE") = "0" ]; then 14 | echo "$1=\"$2\"" >> $CONFFILE 15 | else 16 | TMPVAL=$(echo $2 | sed -e "s/\\//\\\\\\//g") 17 | sed -e "s/$1=.*/$1=\"$TMPVAL\"/" < $CONFFILE > $TMPFILE 18 | mv -f $TMPFILE $CONFFILE 19 | fi 20 | else 21 | echo "$1=\"$2\"" >> $TMPFILE 22 | fi 23 | } 24 | 25 | 26 | if [ "$1" = "configure" ]; then 27 | update-inetd --disable ftp || true 28 | 29 | update-rc.d oftpd defaults > /dev/null 2>/dev/null 30 | 31 | /etc/init.d/oftpd stop 32 | 33 | # . $CONFFILE 34 | 35 | db_get oftpd/enable_oftpd 36 | if [ "$RET" = "true" ]; then 37 | update_value "ENABLE" "yes" 38 | db_get oftpd/which_user 39 | if [ -z "$RET" -o "$RET" = "oftpd" ]; then 40 | USER_TEST=$(cut -d ':' -f 1 < /etc/passwd | grep -w oftpd) || true 41 | if [ -z "$USER_TEST" ]; then 42 | adduser --system oftpd 43 | fi 44 | update_value "USER" "oftpd" 45 | else 46 | update_value "USER" "$RET" 47 | fi 48 | db_get oftpd/which_dir 49 | if [ -z "$RET" ]; then 50 | update_value "DIR" "/home/oftpd" 51 | else 52 | update_value "DIR" "$RET" 53 | fi 54 | db_get oftpd/which_port 55 | if [ -n "$RET" ]; then 56 | update_value "PORT" "$RET" 57 | else 58 | update_value "PORT" "21" 59 | fi 60 | db_get oftpd/which_iface 61 | if [ -n "$RET" ]; then 62 | update_value "IFACE" "$RET" 63 | else 64 | update_value "IFACE" "0.0.0.0" 65 | fi 66 | db_get oftpd/max_conn 67 | if [ -n "$RET" ]; then 68 | update_value "MAXCON" "$RET" 69 | else 70 | update_value "MAXCON" "250" 71 | fi 72 | 73 | else 74 | update_value "ENABLE" "no" 75 | db_get oftpd/which_user 76 | update_value "USER" "$RET" 77 | db_get oftpd/which_dir 78 | update_value "DIR" "$RET" 79 | fi 80 | 81 | if [ ! -e "$CONFFILE" ]; then 82 | mv -f $TMPFILE $CONFFILE 83 | fi 84 | 85 | /etc/init.d/oftpd start 86 | 87 | fi 88 | 89 | exit 0; 90 | -------------------------------------------------------------------------------- /debian/oftpd.config: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . /usr/share/debconf/confmodule 4 | 5 | if [ -e /etc/default/oftpd ]; then 6 | USER="" 7 | ENABLE="no" 8 | DIR="" 9 | PORT="" 10 | IFACE="" 11 | MAXCON="" 12 | . /etc/default/oftpd || true 13 | if [ "$USER" ]; then 14 | db_set oftpd/which_user $USER 15 | fi 16 | if [ "$ENABLE" = "yes" ]; then 17 | db_set oftpd/enable_oftpd "true" 18 | else 19 | db_set oftpd/enable_oftpd "false" 20 | fi 21 | if [ "$DIR" ]; then 22 | db_set oftpd/which_dir $DIR 23 | fi 24 | if [ "$PORT" ]; then 25 | db_set oftpd/which_port $PORT 26 | fi 27 | if [ "$PASV_RANGE" ]; then 28 | db_set oftpd/which_pasv_range $PASV_RANGE 29 | fi 30 | if [ "$IFACE" ]; then 31 | db_set oftpd/which_iface $IFACE 32 | fi 33 | if [ "$MAXCON" ]; then 34 | db_set oftpd/max_conn $MAXCON 35 | fi 36 | fi 37 | 38 | db_input high oftpd/log_file_msg || true 39 | db_go 40 | 41 | db_input medium oftpd/enable_oftpd || true 42 | db_go 43 | 44 | db_get oftpd/enable_oftpd 45 | 46 | if [ "$RET" = "true" ]; then 47 | EXIT_LOOP="false" 48 | while [ "$EXIT_LOOP" != "true" ]; do 49 | db_input medium oftpd/option_menu || true 50 | db_go 51 | 52 | db_get oftpd/option_menu 53 | case "$RET" in 54 | "User to run the server as") 55 | USER_OKAY="false" 56 | while [ "$USER_OKAY" != "true" ]; do 57 | db_input medium oftpd/which_user || true 58 | db_go 59 | db_get oftpd/which_user 60 | [ -n "$RET" ] && USER_TEST=$(cut -d ':' -f 1 < /etc/passwd | grep -w "$RET") || true 61 | if [ -n "$USER_TEST" -o "$RET" = "oftpd" -o -z "$RET" ]; then 62 | USER_OKAY="true" 63 | else 64 | db_input medium oftpd/user_does_not_exist || true 65 | db_go 66 | db_fset oftpd/which_user seen false 67 | fi 68 | done 69 | RET="User to run the server as" 70 | ;; 71 | "Directory to serve") 72 | db_input medium oftpd/which_dir || true 73 | db_go 74 | ;; 75 | "Port to listen on") 76 | db_input medium oftpd/which_port || true 77 | db_go 78 | ;; 79 | "Passive port range") 80 | db_input medium oftpd/which_pasv_range || true 81 | db_go 82 | ;; 83 | "Interface to listen on") 84 | db_input medium oftpd/which_iface || true 85 | db_go 86 | ;; 87 | "Max number of connections") 88 | db_input medium oftpd/max_conn || true 89 | db_go 90 | ;; 91 | "Exit") 92 | EXIT_LOOP="true" 93 | ;; 94 | esac 95 | if [ "$EXIT_LOOP" != "true" ]; then 96 | db_fset oftpd/option_menu seen false 97 | fi 98 | done 99 | fi 100 | 101 | exit 0 102 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | oftpd - An anonymous FTP server by Shane Kerr 2 | 3 | Note: This is a forked version which is activley maintained and used. 4 | 5 | 6 | Installation 7 | 8 | See the INSTALL file for directions on compiling and installing the 9 | binary. Short version (as root): 10 | 11 | # ./configure 12 | # make 13 | # make install 14 | 15 | This will install the oftpd daemon itself. To run the server via the 16 | standard Unix startup mechanism, you'll need to add it to your startup 17 | files. In most Linux systems, this means putting a shell script in the 18 | /etc/rc.d/init.d directory and linking to it from the directories for 19 | your various run levels. If you have a Red Hat 7.0 (or similiar) 20 | system, you can use the oftpd.redhat7 script for this purpose: 21 | 22 | # cp init/oftpd.redhat7 /etc/rc.d/init.d/oftpd 23 | # chkconfig --add oftpd 24 | 25 | Be sure to read the FAQ if you have any questions! 26 | 27 | 28 | Introduction 29 | 30 | oftpd is designed to be as secure as an anonymous FTP server can 31 | possibly be. It runs as non-root for most of the time, and uses the 32 | Unix chroot() command to hide most of the systems directories from 33 | external users - they cannot change into them even if the server is 34 | totally compromised! It contains its own directory change code, so that 35 | it can run efficiently as a threaded server, and its own directory 36 | listing code (many FTP servers execute the system "ls" command to list 37 | files). It is currently being code-reviewed for buffer overflows, and 38 | being load-tested. 39 | 40 | 41 | History 42 | 43 | I wrote oftpd to fill a need we had at my company. Our public FTP site 44 | was a mess, and in addition to reorganizing organizing the hierarchy and 45 | file layout I wanted to get the latest version of our FTP server 46 | software. It turns out that the version we had had had a number of 47 | security issues. So I decided to find an anonymous-only, secure FTP 48 | server. None of the ones I found were fully baked. Time to write my 49 | own. :) 50 | 51 | 52 | Portability 53 | 54 | oftpd currently runs on modern Linux systems, including Red Hat-derived 55 | (Mandrake, Trustix, etc.) and Debian systems. oftpd has been ported to 56 | FreeBSD and is in the FreeBSD ports collection. 57 | 58 | 59 | While I have given up development of oftpd, it's small and reliable. Don't 60 | hesitate to e-mail if you have questions or suggestions. 61 | Good luck! 62 | 63 | 64 | Shane Kerr 65 | shane@time-travellers.org 66 | 67 | ====== 68 | 69 | Note: For my own purposes I maintain a separate GIT repository with a 70 | slightly changed version (well, the one you are currently reading). 71 | As of today the Debian version is based upon this one. 72 | 73 | Werner Koch 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /oftpd.startup: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # oftpd init script for oftpd 4 | # written by Matthew Danish 5 | # based on skeleton 6 | # 7 | # 8 | # skeleton example file to build /etc/init.d/ scripts. 9 | # This file should be used to construct scripts for /etc/init.d. 10 | # 11 | # Written by Miquel van Smoorenburg . 12 | # Modified for Debian GNU/Linux 13 | # by Ian Murdock . 14 | # 15 | # Version: @(#)skeleton 1.8 03-Mar-1998 miquels@cistron.nl 16 | # 17 | 18 | CONFFILE=/etc/default/oftpd 19 | 20 | ENABLE="no" 21 | if [ -f "$CONFFILE" ]; then 22 | source "$CONFFILE" 23 | else 24 | echo "$CONFFILE not present, skipping oftpd" 25 | echo "Use 'dpkg-reconfigure oftpd' to create a $CONFFILE" 26 | exit 0; 27 | fi 28 | 29 | if [ "$ENABLE" == "no" ]; then 30 | echo "oftpd not enabled, continuing." 31 | exit 0; 32 | fi 33 | 34 | ARGS="$USER $DIR" 35 | 36 | if [ -n "$PORT" ]; then 37 | ARGS="-p $PORT $ARGS" 38 | fi 39 | 40 | if [ -n "$IFACE" ]; then 41 | ARGS="-i $IFACE $ARGS" 42 | fi 43 | 44 | if [ -n "$MAXCON" ]; then 45 | ARGS="-m $MAXCON $ARGS" 46 | fi 47 | 48 | ARGS="$OPTIONS $ARGS" 49 | 50 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 51 | DAEMON=/usr/sbin/oftpd 52 | NAME=oftpd 53 | DESC="oftpd" 54 | 55 | test -f $DAEMON || exit 0 56 | 57 | #set -e 58 | 59 | case "$1" in 60 | start) 61 | echo -n "Starting $DESC: " 62 | start-stop-daemon --start --quiet --name $NAME \ 63 | --exec $DAEMON -- $ARGS 64 | echo "$NAME." 65 | ;; 66 | stop) 67 | echo -n "Stopping $DESC: " 68 | start-stop-daemon --stop --quiet --name $NAME \ 69 | --exec $DAEMON -- $ARGS > /dev/null 2>&1 70 | echo "$NAME." 71 | ;; 72 | #reload) 73 | # 74 | # If the daemon can reload its config files on the fly 75 | # for example by sending it SIGHUP, do it here. 76 | # 77 | # If the daemon responds to changes in its config file 78 | # directly anyway, make this a do-nothing entry. 79 | # 80 | # echo "Reloading $DESC configuration files." 81 | # start-stop-daemon --stop --signal 1 --quiet --pidfile \ 82 | # /var/run/$NAME.pid --exec $DAEMON 83 | #;; 84 | restart|force-reload) 85 | # 86 | # If the "reload" option is implemented, move the "force-reload" 87 | # option to the "reload" entry above. If not, "force-reload" is 88 | # just the same as "restart". 89 | # 90 | echo -n "Restarting $DESC: " 91 | start-stop-daemon --stop --quiet --name $NAME \ 92 | --exec $DAEMON -- $ARGS 93 | sleep 1 94 | start-stop-daemon --start --quiet --name $NAME \ 95 | --exec $DAEMON -- $ARGS 96 | echo "$NAME." 97 | ;; 98 | *) 99 | N=/etc/init.d/$NAME 100 | # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 101 | echo "Usage: $N {start|stop|restart|force-reload}" >&2 102 | exit 1 103 | ;; 104 | esac 105 | 106 | exit 0 107 | -------------------------------------------------------------------------------- /src/ftp_session.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Restrictions: 5 | * - Only stream MODE is supported. 6 | * - Only "ftp" or "anonymous" accepted as a user. 7 | */ 8 | 9 | #ifndef FTP_SESSION_H 10 | #define FTP_SESSION_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include "af_portability.h" 16 | #include "watchdog.h" 17 | #include "error.h" 18 | 19 | /* data representation types supported */ 20 | #define TYPE_ASCII 0 21 | #define TYPE_IMAGE 1 22 | 23 | /* file structure types supported */ 24 | #define STRU_FILE 0 25 | #define STRU_RECORD 1 26 | 27 | /* data path chosen */ 28 | #define DATA_PORT 0 29 | #define DATA_PASSIVE 1 30 | 31 | /* space required for text representation of address and port, 32 | e.g. "192.168.0.1 port 1024" or 33 | "2001:3333:DEAD:BEEF:0666:0013:0069:0042 port 65535" */ 34 | #define ADDRPORT_STRLEN 58 35 | 36 | /* structure encapsulating an FTP session's information */ 37 | typedef struct { 38 | /* flag whether session is active */ 39 | int session_active; 40 | 41 | /* incremented for each command */ 42 | unsigned long command_number; 43 | 44 | /* options about transfer set by user */ 45 | int data_type; 46 | int file_structure; 47 | 48 | /* offset to begin sending file from */ 49 | off_t file_offset; 50 | unsigned long file_offset_command_number; 51 | 52 | /* flag set if client requests ESPV ALL - this prevents subsequent 53 | use of PORT, PASV, LPRT, LPSV, or EPRT */ 54 | int epsv_all_set; 55 | 56 | /* address of client */ 57 | sockaddr_storage_t client_addr; 58 | char client_addr_str[ADDRPORT_STRLEN]; 59 | 60 | /* address of server (including IPv4 version) */ 61 | sockaddr_storage_t server_addr; 62 | struct sockaddr_in server_ipv4_addr; 63 | 64 | /* telnet session to encapsulate control channel logic */ 65 | telnet_session_t *telnet_session; 66 | 67 | /* current working directory of this connection */ 68 | char dir[PATH_MAX+1]; 69 | 70 | /* data channel information, including type, 71 | and client address or server port depending on type */ 72 | int data_channel; 73 | sockaddr_storage_t data_port; 74 | int server_fd; 75 | 76 | /* watchdog to handle timeout */ 77 | watched_t *watched; 78 | } ftp_session_t; 79 | 80 | 81 | /* Range of passive ports to use. Defined by oftpd.c */ 82 | extern int pasv_port_low; 83 | extern int pasv_port_high; 84 | 85 | 86 | int ftp_session_init(ftp_session_t *f, 87 | const sockaddr_storage_t *client_addr, 88 | const sockaddr_storage_t *server_addr, 89 | telnet_session_t *t, 90 | const char *dir, 91 | error_t *err); 92 | void ftp_session_drop(ftp_session_t *f, const char *reason); 93 | void ftp_session_run(ftp_session_t *f, watched_t *watched); 94 | void ftp_session_destroy(ftp_session_t *f); 95 | 96 | #endif /* FTP_SESSION_H */ 97 | 98 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce a configure script. 2 | 3 | 4 | AC_INIT([oftpd], [0.3.7-wk5], [bug-oftpd@g10code.com]) 5 | AC_CONFIG_SRCDIR(src/file_list.c) 6 | AM_CONFIG_HEADER(src/config.h) 7 | AM_INIT_AUTOMAKE 8 | 9 | dnl Checks for programs. 10 | AC_PROG_AWK 11 | AC_PROG_CC 12 | AC_PROG_INSTALL 13 | AC_PROG_LN_S 14 | 15 | dnl Checks for header files. 16 | AC_HEADER_STDC 17 | AC_CHECK_HEADERS(fcntl.h limits.h sys/time.h syslog.h unistd.h sys/types.h sys/sendfile.h) 18 | 19 | dnl Check for ss_family 20 | AC_EGREP_CPP(__ss_family, 21 | [#include 22 | ],,AC_DEFINE([HAVE_NEW_SS_FAMILY], 1, 23 | [new ss_family or old (meaning __ss_family) in sockaddr structure])) 24 | 25 | dnl Checks for typedefs, structures, and compiler characteristics. 26 | AC_C_CONST 27 | AC_TYPE_MODE_T 28 | AC_TYPE_OFF_T 29 | AC_TYPE_SIZE_T 30 | AC_STRUCT_ST_BLOCKS 31 | AC_STRUCT_ST_RDEV 32 | AC_HEADER_STAT 33 | AC_HEADER_TIME 34 | AC_STRUCT_ST_BLOCKS 35 | AC_STRUCT_ST_RDEV 36 | AC_STRUCT_TM 37 | 38 | # We need the size of some types to be able to detect overflows 39 | # in parsing numeric values. 40 | AC_CHECK_SIZEOF(off_t) 41 | AC_CHECK_SIZEOF(unsigned long) 42 | AC_CHECK_SIZEOF(unsigned long long) 43 | 44 | dnl Checks for library functions. 45 | AC_FUNC_ALLOCA 46 | AC_FUNC_MEMCMP 47 | AC_TYPE_SIGNAL 48 | AC_FUNC_STRFTIME 49 | AC_CHECK_FUNCS(getcwd gettimeofday select socket strerror localtime_r gmtime_r) 50 | AC_CHECK_FUNCS(strerror_r) 51 | dnl AC_CHECK_LIB(pthread, pthread_create) 52 | dnl AC_SEARCH_LIBS(pthread_create, [ pthread pthreads thread threads ]) 53 | AC_SEARCH_LIBS(socket, socket) 54 | AC_SEARCH_LIBS(inet_ntoa, nsl) 55 | AC_CHECK_FUNCS(inet_aton) 56 | 57 | 58 | dnl check for efficient file transfer mechanisms (i.e. sendfile()) 59 | have_sendfile=no 60 | AC_TRY_LINK([ 61 | #include 62 | #include 63 | #include 64 | ], [ 65 | off_t offset; 66 | sendfile(0, 1, &offset); 67 | ], 68 | AC_DEFINE([HAVE_LINUX_SENDFILE], 1, [Linux-style sendfile() supported]) 69 | AC_DEFINE([HAVE_SENDFILE], 1, [sendfile() supported]) 70 | have_sendfile=yes 71 | ) 72 | if test $have_sendfile = no; then 73 | AC_TRY_LINK([ 74 | #include 75 | #include 76 | #include 77 | ], [ 78 | off_t offset; 79 | off_t sbytes; 80 | sendfile(0, 1, &offset, 0, NULL, &sbytes, 0); 81 | ], 82 | dnl sendfile() seems broken on FreeBSD, so for now we'll just omit it 83 | dnl AC_DEFINE(HAVE_FREEBSD_SENDFILE) AC_DEFINE(HAVE_SENDFILE) have_sendfile=yes 84 | ) 85 | fi 86 | 87 | 88 | 89 | dnl decipher pthread compilation model 90 | 91 | dnl first, try linking a threaded application with "-pthread", 92 | dnl which will work on FreeBSD (and OpenBSD) systems 93 | save_LDFLAGS="$LDFLAGS" 94 | if test "$GCC" = yes; then 95 | LDFLAGS="-pthread -lgcc_s $LDFLAGS" 96 | else 97 | LDFLAGS="-pthread $LDFLAGS" 98 | fi 99 | AC_TRY_LINK([ 100 | #include 101 | ], [ 102 | pthread_self(); 103 | ], 104 | , 105 | LDFLAGS="$save_LDFLAGS" 106 | ) 107 | 108 | dnl then, try looking in various libraries, which will work on other systems 109 | AC_SEARCH_LIBS(pthread_self, [ pthread pthreads thread threads ]) 110 | 111 | dnl add reentrant flags 112 | CFLAGS="$CFLAGS -D_REENTRANT -D_THREAD_SAFE" 113 | 114 | dnl Check whether to enable IPv6 support 115 | AC_ARG_ENABLE([ipv6],[ --enable-ipv6 Enable IPv6 support (disabled by default)], 116 | CFLAGS="$CFLAGS -DINET6",) 117 | 118 | 119 | AH_BOTTOM([ 120 | /* Size of the buffer used for strerror_r. */ 121 | #define ERRBUF_SIZE 256 122 | ]) 123 | 124 | 125 | AC_SUBST(HAVE_NEW_SS_FAMILY) 126 | 127 | AC_CONFIG_FILES([Makefile src/Makefile man/Makefile]) 128 | AC_OUTPUT 129 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | oftpd (0.3.6-5) unstable; urgency=low 2 | 3 | * Priority overridden to extra; changed control file to concur. 4 | * Fixed lintian error: depends on debconf >= 0.5.00 now 5 | * /etc/default/oftpd no longer distributed with .deb 6 | * Extra special care now taken to ensure that the sysadmin can modify 7 | the file, without debconf, and have changes preserved. (Closes: #132169) 8 | 9 | -- Matthew Danish Sun, 3 Feb 2002 16:48:05 -0500 10 | 11 | oftpd (0.3.6-4) unstable; urgency=low 12 | 13 | * Fixed error in templates, the default directory should now be /home/oftpd 14 | 15 | -- Matthew Danish Fri, 21 Sep 2001 19:44:29 -0400 16 | 17 | oftpd (0.3.6-3) unstable; urgency=low 18 | 19 | * Removed /etc/default/oftpd from conffiles, because it is really isn't one. 20 | * Now disables FTP service from inetd, in the postinst. (Closes: #107508) 21 | * Updated Standards-Version to 3.5.6.0 22 | * Uses 'oftpd' user by default, will create it if it does not exist. 23 | Will not allow you to choose a user that does not exist. 24 | (Closes: #107507) 25 | 26 | -- Matthew Danish Fri, 3 Aug 2001 22:21:38 -0400 27 | 28 | oftpd (0.3.6-2) unstable; urgency=low 29 | 30 | * Removed Build-Dependency on libc6-dev 31 | * Modified logging template message to mention other sysloggers. 32 | * Updated Standards-Version 33 | 34 | -- Matthew Danish Tue, 10 Jul 2001 09:44:20 -0400 35 | 36 | oftpd (0.3.6-1) unstable; urgency=low 37 | 38 | * New upstream release 39 | * Updated Standards Version 40 | * Added debconf note about setting up FTP syslog facility 41 | 42 | -- Matthew Danish Thu, 31 May 2001 23:51:12 -0400 43 | 44 | oftpd (0.3.5-1) unstable; urgency=low 45 | 46 | * New upstream release 47 | * Upload sponsored by Lenart Janos . 48 | 49 | -- Matthew Danish Wed, 18 Apr 2001 20:29:04 -0400 50 | 51 | oftpd (0.3.4-2) unstable; urgency=low 52 | 53 | * Moved oftpd.conf to /etc/default 54 | * Added dependency on sysklogd | system-log-daemon 55 | * Upload sponsored by Lenart Janos . 56 | 57 | -- Matthew Danish Tue, 17 Apr 2001 19:36:16 -0400 58 | 59 | oftpd (0.3.4-1) unstable; urgency=low 60 | 61 | * New upstream release 62 | * Included new upstream man page 63 | * Upload sponsored by Lenart Janos . 64 | 65 | -- Matthew Danish Mon, 9 Apr 2001 10:07:05 -0400 66 | 67 | oftpd (0.3.3-1) unstable; urgency=low 68 | 69 | * New upstream release 70 | * Moved oftpd to /usr/sbin 71 | 72 | -- Matthew Danish Thu, 29 Mar 2001 22:18:18 -0500 73 | 74 | oftpd (0.3.2-1) unstable; urgency=low 75 | 76 | * New upstream release 77 | * Patched against ss_family bug 78 | 79 | -- Matthew Danish Tue, 27 Mar 2001 14:21:05 -0500 80 | 81 | oftpd (0.3.1-3) unstable; urgency=low 82 | 83 | * Updated Standards-Version 84 | 85 | -- Matthew Danish Sun, 25 Mar 2001 23:07:00 -0500 86 | 87 | oftpd (0.3.1-2) unstable; urgency=low 88 | 89 | * Added new options to debconf script 90 | 91 | -- Matthew Danish Sun, 25 Mar 2001 21:29:21 -0500 92 | 93 | oftpd (0.3.1-1) unstable; urgency=low 94 | 95 | * New upstream release 96 | 97 | -- Matthew Danish Fri, 23 Mar 2001 00:28:29 -0500 98 | 99 | oftpd (0.2.0-2) unstable; urgency=low 100 | 101 | * Uses debconf now 102 | 103 | -- Matthew Danish Sun, 18 Mar 2001 13:18:34 -0500 104 | 105 | oftpd (0.2.0-1) unstable; urgency=low 106 | 107 | * Initial Release. 108 | 109 | -- Matthew Danish Thu, 22 Feb 2001 00:56:19 -0500 110 | 111 | Local variables: 112 | mode: debian-changelog 113 | End: 114 | -------------------------------------------------------------------------------- /mkinstalldirs: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # mkinstalldirs --- make directory hierarchy 3 | 4 | scriptversion=2005-06-29.22 5 | 6 | # Original author: Noah Friedman 7 | # Created: 1993-05-16 8 | # Public domain. 9 | # 10 | # This file is maintained in Automake, please report 11 | # bugs to or send patches to 12 | # . 13 | 14 | errstatus=0 15 | dirmode= 16 | 17 | usage="\ 18 | Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... 19 | 20 | Create each directory DIR (with mode MODE, if specified), including all 21 | leading file name components. 22 | 23 | Report bugs to ." 24 | 25 | # process command line arguments 26 | while test $# -gt 0 ; do 27 | case $1 in 28 | -h | --help | --h*) # -h for help 29 | echo "$usage" 30 | exit $? 31 | ;; 32 | -m) # -m PERM arg 33 | shift 34 | test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } 35 | dirmode=$1 36 | shift 37 | ;; 38 | --version) 39 | echo "$0 $scriptversion" 40 | exit $? 41 | ;; 42 | --) # stop option processing 43 | shift 44 | break 45 | ;; 46 | -*) # unknown option 47 | echo "$usage" 1>&2 48 | exit 1 49 | ;; 50 | *) # first non-opt arg 51 | break 52 | ;; 53 | esac 54 | done 55 | 56 | for file 57 | do 58 | if test -d "$file"; then 59 | shift 60 | else 61 | break 62 | fi 63 | done 64 | 65 | case $# in 66 | 0) exit 0 ;; 67 | esac 68 | 69 | # Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and 70 | # mkdir -p a/c at the same time, both will detect that a is missing, 71 | # one will create a, then the other will try to create a and die with 72 | # a "File exists" error. This is a problem when calling mkinstalldirs 73 | # from a parallel make. We use --version in the probe to restrict 74 | # ourselves to GNU mkdir, which is thread-safe. 75 | case $dirmode in 76 | '') 77 | if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then 78 | echo "mkdir -p -- $*" 79 | exec mkdir -p -- "$@" 80 | else 81 | # On NextStep and OpenStep, the `mkdir' command does not 82 | # recognize any option. It will interpret all options as 83 | # directories to create, and then abort because `.' already 84 | # exists. 85 | test -d ./-p && rmdir ./-p 86 | test -d ./--version && rmdir ./--version 87 | fi 88 | ;; 89 | *) 90 | if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && 91 | test ! -d ./--version; then 92 | echo "mkdir -m $dirmode -p -- $*" 93 | exec mkdir -m "$dirmode" -p -- "$@" 94 | else 95 | # Clean up after NextStep and OpenStep mkdir. 96 | for d in ./-m ./-p ./--version "./$dirmode"; 97 | do 98 | test -d $d && rmdir $d 99 | done 100 | fi 101 | ;; 102 | esac 103 | 104 | for file 105 | do 106 | case $file in 107 | /*) pathcomp=/ ;; 108 | *) pathcomp= ;; 109 | esac 110 | oIFS=$IFS 111 | IFS=/ 112 | set fnord $file 113 | shift 114 | IFS=$oIFS 115 | 116 | for d 117 | do 118 | test "x$d" = x && continue 119 | 120 | pathcomp=$pathcomp$d 121 | case $pathcomp in 122 | -*) pathcomp=./$pathcomp ;; 123 | esac 124 | 125 | if test ! -d "$pathcomp"; then 126 | echo "mkdir $pathcomp" 127 | 128 | mkdir "$pathcomp" || lasterr=$? 129 | 130 | if test ! -d "$pathcomp"; then 131 | errstatus=$lasterr 132 | else 133 | if test ! -z "$dirmode"; then 134 | echo "chmod $dirmode $pathcomp" 135 | lasterr= 136 | chmod "$dirmode" "$pathcomp" || lasterr=$? 137 | 138 | if test ! -z "$lasterr"; then 139 | errstatus=$lasterr 140 | fi 141 | fi 142 | fi 143 | fi 144 | 145 | pathcomp=$pathcomp/ 146 | done 147 | done 148 | 149 | exit $errstatus 150 | 151 | # Local Variables: 152 | # mode: shell-script 153 | # sh-indentation: 2 154 | # eval: (add-hook 'write-file-hooks 'time-stamp) 155 | # time-stamp-start: "scriptversion=" 156 | # time-stamp-format: "%:y-%02m-%02d.%02H" 157 | # time-stamp-end: "$" 158 | # End: 159 | -------------------------------------------------------------------------------- /debian/oftpd.templates: -------------------------------------------------------------------------------- 1 | Template: oftpd/enable_oftpd 2 | Type: boolean 3 | Default: false 4 | Description: Do you want oftpd set up to start at boot-time? 5 | This option lets you decide whether you want oftpd to be configured to 6 | start automatically whenever you reboot. 7 | 8 | Template: oftpd/option_menu 9 | Type: select 10 | Choices: User to run the server as, Directory to serve, Port to listen on, Passive port range, Interface to listen on, Max number of connections, Exit 11 | Default: Exit 12 | Description: Choose a menu item to configure 13 | This menu lets you configure various parameters to pass to oftpd. Each 14 | one contains a better description of its purpose, so select each one to 15 | find out more. If you don't want to set it, leave the default value. 16 | 17 | Template: oftpd/which_user 18 | Type: string 19 | Default: oftpd 20 | Description: What user would you like oftpd to run as? 21 | oftpd is capable of running as a normal system user, this decreases 22 | the security risk from any possible security flaws in the program. 23 | 24 | Template: oftpd/user_does_not_exist 25 | Type: note 26 | Description: That user does not exist. 27 | You have selected a user which does not exist. Please enter in another 28 | username, or 'oftpd' for the default. 29 | 30 | Template: oftpd/which_dir 31 | Type: string 32 | Default: /home/oftpd 33 | Description: From what directory will oftpd serve files? 34 | oftpd is a file server, and it needs to know from what directory in the 35 | file system it will serve files from. To all users of the FTP service, 36 | it will appear as if that directory and its contents were the only 37 | available files and directories on your system. 38 | . 39 | Note that oftpd runs chroot'd to this directory as well, this helps 40 | decrease security risks in the possibility of a breach. 41 | 42 | Template: oftpd/which_port 43 | Type: string 44 | Default: 21 45 | Description: Which TCP port will oftpd listen on? 46 | oftpd is a TCP/IP network server, which means that it needs to know what 47 | TCP port to listen for connections on. Port 21 is the standard FTP 48 | server port, and is highly recommended if oftpd is to be your main FTP 49 | server. Any other port may be chosen as well, perhaps for personal 50 | use or other reason. 51 | 52 | Template: oftpd/which_pasv_range 53 | Type: string 54 | Default: 1024 65535 55 | Description: Which TCP port range will oftpd use in passive mode? 56 | Most modern FTP clients use the passive mode to contact an FTP 57 | server. The server therefore tells the client a port to connect to. 58 | The port is a random one chosen from the range given here. To adjust 59 | firewall rules on the server some administrators like to limit the 60 | ports a client may connect to. Use this setting here to match the 61 | firewall rules. Most users keep the default. 62 | 63 | Template: oftpd/which_iface 64 | Type: string 65 | Default: 0.0.0.0 66 | Description: Which interface will oftpd listen on? 67 | A machine may have one or multiple network interfaces. Interfaces include 68 | loopback (127.0.0.1), ethernet, PPP connections over modems, wireless 69 | networking, and many more. To have oftpd only listen on one of those 70 | interfaces, enter the IP address of that interface (for example, to 71 | have oftpd only be available to local users, make it listen to the 72 | interface '127.0.0.1'). The default value is '0.0.0.0' which means to 73 | listen to all interfaces. 74 | 75 | Template: oftpd/max_conn 76 | Type: string 77 | Default: 250 78 | Description: What shall be the maximum number of users allowed at once? 79 | oftpd can limit the maximum number of users logged into your FTP server, 80 | from 1 to 300. The default is 250. 81 | 82 | Template: oftpd/log_file_msg 83 | Type: note 84 | Description: oftpd will be logging to the FTP syslog facility 85 | This means that you should make sure that there is a line that looks like: 86 | . 87 | ftp.* -/var/log/ftp.log 88 | . 89 | In your /etc/syslog.conf file, if you are running sysklogd. Consult 90 | your documentation if using syslog-ng, msyslog, or others. 91 | Then all the logging from oftpd will be sent to the file /var/log/ftp.log. 92 | If you do not do this, then the output will probably end up scattered 93 | throughout /var/log/debug. If this has already been set in your 94 | syslog configuration, then modify it to suit your tastes. 95 | -------------------------------------------------------------------------------- /man/oftpd.8: -------------------------------------------------------------------------------- 1 | .TH OFTPD 8 "2001-04-03" Linux "Linux System Administration" 2 | .SH NAME 3 | oftpd \- anonymous, read-only FTP server 4 | .SH SYNOPSIS 5 | .B oftpd [-p|--port 6 | .I port 7 | .B ] [-r|--pasv-range 8 | .I low high 9 | .B ] [-i|--interface 10 | .I ip-address 11 | .B ] [-m|--max-clients 12 | .I num 13 | .B ] [-l|--local 14 | .I local-logging 15 | .B ] [-N|--nodetach] 16 | .I user-name root-directory 17 | .SH DESCRIPTION 18 | .B oftpd 19 | is an anonymous-only FTP server. 20 | 21 | Although it must be started by the root user, after initialization it 22 | runs as the user specified by 23 | .I user-name 24 | parameter. This should be a user with minimal permissions, preferably 25 | from an account set up for this purpose only. 26 | 27 | The server uses 28 | .BR chroot (2) 29 | to change the root directory of the server to the 30 | .I root-directory 31 | directory. When a user connects, this is the directory that they will 32 | start in, and is the top of their directory tree. 33 | 34 | When FTP clients connect, they may log in as "ftp" or "anonymous". 35 | Typically they will then send their e-mail address as password (most web 36 | browsers send a browser identifier rather than the user's e-mail 37 | address). Standard Unix file permissions are enforced for downloading, 38 | meaning clients may download any file 39 | .I user-name 40 | has permission to read. No uploads are permitted. All client activity 41 | is logged (see 42 | .B DIAGNOSTICS 43 | below). 44 | .SH OPTIONS 45 | .IP "-p|--port port" 46 | Use the specified 47 | .I port 48 | to listen for client connections. If not specified, the default FTP 49 | port (number 21) is used. 50 | .IP "-r|--pasv-range low high" 51 | Limit the range for ports used in passive mode from 52 | .I low 53 | to 54 | .I high 55 | . If not specified 1024 and 65535 are used. 56 | .IP "-i|--interface ip-address" 57 | Use the interface connected to the IP address 58 | .I ip-address 59 | to accept connections. If not specified, the server listens on all 60 | interfaces. 61 | .IP "-m|--max-clients num" 62 | Accept at most 63 | .I num 64 | simultaneous clients. If not specified, 250 will be the limit. 65 | .IP "-l|--local local-logging" 66 | Normally oftpd logs messages to syslog as the FTP daemon. With this 67 | option, the specified 68 | .I local-logging 69 | level will be used instead. Valid numbers are 0-8. 70 | .IP "-N|--nodetach" 71 | Do not run in the background; for running from 72 | .BR init (8) 73 | or for testing. 74 | .SH DIAGNOSTICS 75 | Syntax errors will result in a help message being displayed, and a 76 | non-zero exit code returned. Otherwise the server will exit without 77 | output, and return zero. 78 | 79 | After the server has started, you should check the appropriate log 80 | produced by 81 | .BR sysklogd (8) 82 | for the FTP daemon to insure that startup completed correctly. Certain 83 | errors occur only after the server has disconnected from the TTY, so 84 | can only be recorded via the log mechanism. 85 | 86 | If the server must terminate for any reason, it will also be logged, as 87 | will other non-fatal internal errors. They are logged with attention to 88 | how serious the condition is believed to be, as documented in the 89 | .BR syslog (3) 90 | library call. 91 | 92 | Client activity will also be logged through this mechanism. This 93 | includes connect and disconnect (or rejection due to too many 94 | simultaneous users), the e-mail address reported as password, and file 95 | transfers. All client commands are logged as sent at the 96 | .B DEBUG 97 | level. To fully monitor client activity you may configure 98 | .BR sysklogd (8) 99 | to record these. 100 | .SH NOTES 101 | .B oftpd 102 | does not use 103 | .BR inetd (8) 104 | to run. It is a stand-alone server. There is no need to configure the 105 | .I /etc/inetd.conf 106 | file to run it. In fact, there should be no entry for FTP there at all. 107 | 108 | You can use the 109 | .BR kill (1) 110 | command to stop 111 | .B oftpd. 112 | When the server receives SIGHUP or SIGINT, it will stop listening for 113 | new FTP connections, and a new FTP server may be started. Any existing 114 | connections to the old server will continue to function normally until 115 | the client disconnects or times out. After all clients connections have 116 | closed, the server will exit. 117 | 118 | To shutdown the server and close all client connections immediately, use 119 | SIGKILL. 120 | .SH AUTHOR 121 | Shane Kerr 122 | .SH "SEE ALSO" 123 | .BR ftp (1) 124 | 125 | -------------------------------------------------------------------------------- /src/watchdog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #include 6 | #include 7 | #include "daemon_assert.h" 8 | #include "watchdog.h" 9 | 10 | static int invariant(watchdog_t *w); 11 | static void insert(watchdog_t *w, watched_t *watched); 12 | static void delete(watchdog_t *w, watched_t *watched); 13 | static void *watcher(void *void_w); 14 | 15 | int watchdog_init(watchdog_t *w, int inactivity_timeout, error_t *err) 16 | { 17 | pthread_t thread_id; 18 | int error_code; 19 | 20 | daemon_assert(w != NULL); 21 | daemon_assert(inactivity_timeout > 0); 22 | daemon_assert(err != NULL); 23 | 24 | pthread_mutex_init(&w->mutex, NULL); 25 | w->inactivity_timeout = inactivity_timeout; 26 | w->oldest = NULL; 27 | w->newest = NULL; 28 | 29 | error_code = pthread_create(&thread_id, NULL, watcher, w); 30 | if (error_code != 0) { 31 | error_init(err, error_code, "error %d from pthread_create()", 32 | error_code); 33 | return 0; 34 | } 35 | pthread_detach(thread_id); 36 | 37 | daemon_assert(invariant(w)); 38 | 39 | return 1; 40 | } 41 | 42 | void watchdog_add_watched(watchdog_t *w, watched_t *watched) 43 | { 44 | daemon_assert(invariant(w)); 45 | 46 | pthread_mutex_lock(&w->mutex); 47 | 48 | watched->watched_thread = pthread_self(); 49 | watched->watchdog = w; 50 | insert(w, watched); 51 | 52 | pthread_mutex_unlock(&w->mutex); 53 | 54 | daemon_assert(invariant(w)); 55 | } 56 | 57 | void watchdog_defer_watched(watched_t *watched) 58 | { 59 | watchdog_t *w; 60 | 61 | daemon_assert(invariant(watched->watchdog)); 62 | 63 | w = watched->watchdog; 64 | pthread_mutex_lock(&w->mutex); 65 | 66 | delete(w, watched); 67 | insert(w, watched); 68 | 69 | pthread_mutex_unlock(&w->mutex); 70 | daemon_assert(invariant(w)); 71 | } 72 | 73 | void watchdog_remove_watched(watched_t *watched) 74 | { 75 | watchdog_t *w; 76 | 77 | daemon_assert(invariant(watched->watchdog)); 78 | 79 | w = watched->watchdog; 80 | pthread_mutex_lock(&w->mutex); 81 | 82 | delete(w, watched); 83 | 84 | pthread_mutex_unlock(&w->mutex); 85 | daemon_assert(invariant(w)); 86 | } 87 | 88 | static void insert(watchdog_t *w, watched_t *watched) 89 | { 90 | /********************************************************************* 91 | Set alarm to current time + timeout duration. Note that this is not 92 | strictly legal, since time_t is an abstract data type. 93 | *********************************************************************/ 94 | watched->alarm_time = time(NULL) + w->inactivity_timeout; 95 | 96 | /********************************************************************* 97 | If the system clock got set backwards, we really should search for 98 | the correct location, instead of just inserting at the end. However, 99 | this happens very rarely (ntp and other synchronization protocols 100 | speed up or slow down the clock to adjust the time), so we'll just 101 | set our alarm to the time of the newest alarm - giving any watched 102 | processes added some extra time. 103 | *********************************************************************/ 104 | if (w->newest != NULL) { 105 | if (w->newest->alarm_time > watched->alarm_time) { 106 | watched->alarm_time = w->newest->alarm_time; 107 | } 108 | } 109 | 110 | /* set our pointers */ 111 | watched->older = w->newest; 112 | watched->newer = NULL; 113 | 114 | /* add to list */ 115 | if (w->oldest == NULL) { 116 | w->oldest = watched; 117 | } else { 118 | w->newest->newer = watched; 119 | } 120 | w->newest = watched; 121 | watched->in_list = 1; 122 | } 123 | 124 | static void delete(watchdog_t *w, watched_t *watched) 125 | { 126 | if (!watched->in_list) { 127 | return; 128 | } 129 | 130 | if (watched->newer == NULL) { 131 | daemon_assert(w->newest == watched); 132 | w->newest = w->newest->older; 133 | if (w->newest != NULL) { 134 | w->newest->newer = NULL; 135 | } 136 | } else { 137 | daemon_assert(w->newest != watched); 138 | watched->newer->older = watched->older; 139 | } 140 | 141 | if (watched->older == NULL) { 142 | daemon_assert(w->oldest == watched); 143 | w->oldest = w->oldest->newer; 144 | if (w->oldest != NULL) { 145 | w->oldest->older = NULL; 146 | } 147 | } else { 148 | daemon_assert(w->oldest != watched); 149 | watched->older->newer = watched->newer; 150 | } 151 | 152 | watched->older = NULL; 153 | watched->newer = NULL; 154 | watched->in_list = 0; 155 | } 156 | 157 | static void *watcher(void *void_w) 158 | { 159 | watchdog_t *w; 160 | struct timeval tv; 161 | time_t now; 162 | watched_t *watched; 163 | 164 | w = (watchdog_t *)void_w; 165 | for (;;) { 166 | tv.tv_sec = 1; 167 | tv.tv_usec = 0; 168 | select(0, NULL, NULL, NULL, &tv); 169 | 170 | time(&now); 171 | 172 | pthread_mutex_lock(&w->mutex); 173 | while ((w->oldest != NULL) && 174 | (difftime(now, w->oldest->alarm_time) > 0)) 175 | { 176 | watched = w->oldest; 177 | 178 | /******************************************************* 179 | This might seem like a memory leak, but in oftpd the 180 | watched_t structure is held in the thread itself, so 181 | canceling the thread effectively frees the memory. I'm 182 | not sure whether this is elegant or a hack. :) 183 | *******************************************************/ 184 | delete(w, watched); 185 | 186 | pthread_cancel(watched->watched_thread); 187 | } 188 | pthread_mutex_unlock(&w->mutex); 189 | } 190 | } 191 | 192 | #ifndef NDEBUG 193 | static int invariant(watchdog_t *w) 194 | { 195 | int ret_val; 196 | 197 | watched_t *ptr; 198 | int old_to_new_count; 199 | int new_to_old_count; 200 | 201 | 202 | if (w == NULL) { 203 | return 0; 204 | } 205 | 206 | ret_val = 0; 207 | pthread_mutex_lock(&w->mutex); 208 | 209 | if (w->inactivity_timeout <= 0) { 210 | goto exit_invariant; 211 | } 212 | 213 | /* either oldest and newest are both NULL, or neither is */ 214 | if (w->oldest != NULL) { 215 | if (w->newest == NULL) { 216 | goto exit_invariant; 217 | } 218 | 219 | /* check list from oldest to newest */ 220 | old_to_new_count = 0; 221 | ptr = w->oldest; 222 | while (ptr != NULL) { 223 | old_to_new_count++; 224 | if (ptr->older != NULL) { 225 | if (ptr->alarm_time < ptr->older->alarm_time) { 226 | goto exit_invariant; 227 | } 228 | } 229 | if (ptr->newer != NULL) { 230 | if (ptr->alarm_time > ptr->newer->alarm_time) { 231 | goto exit_invariant; 232 | } 233 | } 234 | ptr = ptr->newer; 235 | } 236 | 237 | /* check list from newest to oldest */ 238 | new_to_old_count = 0; 239 | ptr = w->newest; 240 | while (ptr != NULL) { 241 | new_to_old_count++; 242 | if (ptr->older != NULL) { 243 | if (ptr->alarm_time < ptr->older->alarm_time) { 244 | goto exit_invariant; 245 | } 246 | } 247 | if (ptr->newer != NULL) { 248 | if (ptr->alarm_time > ptr->newer->alarm_time) { 249 | goto exit_invariant; 250 | } 251 | } 252 | ptr = ptr->older; 253 | } 254 | 255 | /* verify forward and backward lists at least have the same count */ 256 | if (old_to_new_count != new_to_old_count) { 257 | goto exit_invariant; 258 | } 259 | 260 | } else { 261 | if (w->newest != NULL) { 262 | goto exit_invariant; 263 | } 264 | } 265 | 266 | /* at this point, we're probably okay */ 267 | ret_val = 1; 268 | 269 | exit_invariant: 270 | pthread_mutex_unlock(&w->mutex); 271 | return ret_val; 272 | } 273 | #endif /* NDEBUG */ 274 | 275 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free 5 | Software Foundation, Inc. 6 | 7 | This file is free documentation; the Free Software Foundation gives 8 | unlimited permission to copy, distribute and modify it. 9 | 10 | Basic Installation 11 | ================== 12 | 13 | These are generic installation instructions. 14 | 15 | The `configure' shell script attempts to guess correct values for 16 | various system-dependent variables used during compilation. It uses 17 | those values to create a `Makefile' in each directory of the package. 18 | It may also create one or more `.h' files containing system-dependent 19 | definitions. Finally, it creates a shell script `config.status' that 20 | you can run in the future to recreate the current configuration, and a 21 | file `config.log' containing compiler output (useful mainly for 22 | debugging `configure'). 23 | 24 | It can also use an optional file (typically called `config.cache' 25 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 26 | the results of its tests to speed up reconfiguring. (Caching is 27 | disabled by default to prevent problems with accidental use of stale 28 | cache files.) 29 | 30 | If you need to do unusual things to compile the package, please try 31 | to figure out how `configure' could check whether to do them, and mail 32 | diffs or instructions to the address given in the `README' so they can 33 | be considered for the next release. If you are using the cache, and at 34 | some point `config.cache' contains results you don't want to keep, you 35 | may remove or edit it. 36 | 37 | The file `configure.ac' (or `configure.in') is used to create 38 | `configure' by a program called `autoconf'. You only need 39 | `configure.ac' if you want to change it or regenerate `configure' using 40 | a newer version of `autoconf'. 41 | 42 | The simplest way to compile this package is: 43 | 44 | 1. `cd' to the directory containing the package's source code and type 45 | `./configure' to configure the package for your system. If you're 46 | using `csh' on an old version of System V, you might need to type 47 | `sh ./configure' instead to prevent `csh' from trying to execute 48 | `configure' itself. 49 | 50 | Running `configure' takes awhile. While running, it prints some 51 | messages telling which features it is checking for. 52 | 53 | 2. Type `make' to compile the package. 54 | 55 | 3. Optionally, type `make check' to run any self-tests that come with 56 | the package. 57 | 58 | 4. Type `make install' to install the programs and any data files and 59 | documentation. 60 | 61 | 5. You can remove the program binaries and object files from the 62 | source code directory by typing `make clean'. To also remove the 63 | files that `configure' created (so you can compile the package for 64 | a different kind of computer), type `make distclean'. There is 65 | also a `make maintainer-clean' target, but that is intended mainly 66 | for the package's developers. If you use it, you may have to get 67 | all sorts of other programs in order to regenerate files that came 68 | with the distribution. 69 | 70 | Compilers and Options 71 | ===================== 72 | 73 | Some systems require unusual options for compilation or linking that the 74 | `configure' script does not know about. Run `./configure --help' for 75 | details on some of the pertinent environment variables. 76 | 77 | You can give `configure' initial values for configuration parameters 78 | by setting variables in the command line or in the environment. Here 79 | is an example: 80 | 81 | ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix 82 | 83 | *Note Defining Variables::, for more details. 84 | 85 | Compiling For Multiple Architectures 86 | ==================================== 87 | 88 | You can compile the package for more than one kind of computer at the 89 | same time, by placing the object files for each architecture in their 90 | own directory. To do this, you must use a version of `make' that 91 | supports the `VPATH' variable, such as GNU `make'. `cd' to the 92 | directory where you want the object files and executables to go and run 93 | the `configure' script. `configure' automatically checks for the 94 | source code in the directory that `configure' is in and in `..'. 95 | 96 | If you have to use a `make' that does not support the `VPATH' 97 | variable, you have to compile the package for one architecture at a 98 | time in the source code directory. After you have installed the 99 | package for one architecture, use `make distclean' before reconfiguring 100 | for another architecture. 101 | 102 | Installation Names 103 | ================== 104 | 105 | By default, `make install' installs the package's commands under 106 | `/usr/local/bin', include files under `/usr/local/include', etc. You 107 | can specify an installation prefix other than `/usr/local' by giving 108 | `configure' the option `--prefix=PREFIX'. 109 | 110 | You can specify separate installation prefixes for 111 | architecture-specific files and architecture-independent files. If you 112 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 113 | PREFIX as the prefix for installing programs and libraries. 114 | Documentation and other data files still use the regular prefix. 115 | 116 | In addition, if you use an unusual directory layout you can give 117 | options like `--bindir=DIR' to specify different values for particular 118 | kinds of files. Run `configure --help' for a list of the directories 119 | you can set and what kinds of files go in them. 120 | 121 | If the package supports it, you can cause programs to be installed 122 | with an extra prefix or suffix on their names by giving `configure' the 123 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 124 | 125 | Optional Features 126 | ================= 127 | 128 | Some packages pay attention to `--enable-FEATURE' options to 129 | `configure', where FEATURE indicates an optional part of the package. 130 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 131 | is something like `gnu-as' or `x' (for the X Window System). The 132 | `README' should mention any `--enable-' and `--with-' options that the 133 | package recognizes. 134 | 135 | For packages that use the X Window System, `configure' can usually 136 | find the X include and library files automatically, but if it doesn't, 137 | you can use the `configure' options `--x-includes=DIR' and 138 | `--x-libraries=DIR' to specify their locations. 139 | 140 | Specifying the System Type 141 | ========================== 142 | 143 | There may be some features `configure' cannot figure out automatically, 144 | but needs to determine by the type of machine the package will run on. 145 | Usually, assuming the package is built to be run on the _same_ 146 | architectures, `configure' can figure that out, but if it prints a 147 | message saying it cannot guess the machine type, give it the 148 | `--build=TYPE' option. TYPE can either be a short name for the system 149 | type, such as `sun4', or a canonical name which has the form: 150 | 151 | CPU-COMPANY-SYSTEM 152 | 153 | where SYSTEM can have one of these forms: 154 | 155 | OS KERNEL-OS 156 | 157 | See the file `config.sub' for the possible values of each field. If 158 | `config.sub' isn't included in this package, then this package doesn't 159 | need to know the machine type. 160 | 161 | If you are _building_ compiler tools for cross-compiling, you should 162 | use the option `--target=TYPE' to select the type of system they will 163 | produce code for. 164 | 165 | If you want to _use_ a cross compiler, that generates code for a 166 | platform different from the build platform, you should specify the 167 | "host" platform (i.e., that on which the generated programs will 168 | eventually be run) with `--host=TYPE'. 169 | 170 | Sharing Defaults 171 | ================ 172 | 173 | If you want to set default values for `configure' scripts to share, you 174 | can create a site shell script called `config.site' that gives default 175 | values for variables like `CC', `cache_file', and `prefix'. 176 | `configure' looks for `PREFIX/share/config.site' if it exists, then 177 | `PREFIX/etc/config.site' if it exists. Or, you can set the 178 | `CONFIG_SITE' environment variable to the location of the site script. 179 | A warning: not all `configure' scripts look for a site script. 180 | 181 | Defining Variables 182 | ================== 183 | 184 | Variables not defined in a site shell script can be set in the 185 | environment passed to `configure'. However, some packages may run 186 | configure again during the build, and the customized values of these 187 | variables may be lost. In order to avoid this problem, you should set 188 | them in the `configure' command line, using `VAR=value'. For example: 189 | 190 | ./configure CC=/usr/local2/bin/gcc 191 | 192 | causes the specified `gcc' to be used as the C compiler (unless it is 193 | overridden in the site shell script). Here is a another example: 194 | 195 | /bin/bash ./configure CONFIG_SHELL=/bin/bash 196 | 197 | Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent 198 | configuration-related scripts to be executed by `/bin/bash'. 199 | 200 | `configure' Invocation 201 | ====================== 202 | 203 | `configure' recognizes the following options to control how it operates. 204 | 205 | `--help' 206 | `-h' 207 | Print a summary of the options to `configure', and exit. 208 | 209 | `--version' 210 | `-V' 211 | Print the version of Autoconf used to generate the `configure' 212 | script, and exit. 213 | 214 | `--cache-file=FILE' 215 | Enable the cache: use and save the results of the tests in FILE, 216 | traditionally `config.cache'. FILE defaults to `/dev/null' to 217 | disable caching. 218 | 219 | `--config-cache' 220 | `-C' 221 | Alias for `--cache-file=config.cache'. 222 | 223 | `--quiet' 224 | `--silent' 225 | `-q' 226 | Do not print messages saying which checks are being made. To 227 | suppress all normal output, redirect it to `/dev/null' (any error 228 | messages will still be shown). 229 | 230 | `--srcdir=DIR' 231 | Look for the package's source code in directory DIR. Usually 232 | `configure' can determine that directory automatically. 233 | 234 | `configure' also accepts some other, not widely useful, options. Run 235 | `configure --help' for more details. 236 | 237 | -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # install - install a program, script, or datafile 3 | 4 | scriptversion=2005-05-14.22 5 | 6 | # This originates from X11R5 (mit/util/scripts/install.sh), which was 7 | # later released in X11R6 (xc/config/util/install.sh) with the 8 | # following copyright and license. 9 | # 10 | # Copyright (C) 1994 X Consortium 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documentation files (the "Software"), to 14 | # deal in the Software without restriction, including without limitation the 15 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | # sell copies of the Software, and to permit persons to whom the Software is 17 | # furnished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26 | # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- 27 | # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # Except as contained in this notice, the name of the X Consortium shall not 30 | # be used in advertising or otherwise to promote the sale, use or other deal- 31 | # ings in this Software without prior written authorization from the X Consor- 32 | # tium. 33 | # 34 | # 35 | # FSF changes to this file are in the public domain. 36 | # 37 | # Calling this script install-sh is preferred over install.sh, to prevent 38 | # `make' implicit rules from creating a file called install from it 39 | # when there is no Makefile. 40 | # 41 | # This script is compatible with the BSD install script, but was written 42 | # from scratch. It can only install one file at a time, a restriction 43 | # shared with many OS's install programs. 44 | 45 | # set DOITPROG to echo to test this script 46 | 47 | # Don't use :- since 4.3BSD and earlier shells don't like it. 48 | doit="${DOITPROG-}" 49 | 50 | # put in absolute paths if you don't have them in your path; or use env. vars. 51 | 52 | mvprog="${MVPROG-mv}" 53 | cpprog="${CPPROG-cp}" 54 | chmodprog="${CHMODPROG-chmod}" 55 | chownprog="${CHOWNPROG-chown}" 56 | chgrpprog="${CHGRPPROG-chgrp}" 57 | stripprog="${STRIPPROG-strip}" 58 | rmprog="${RMPROG-rm}" 59 | mkdirprog="${MKDIRPROG-mkdir}" 60 | 61 | chmodcmd="$chmodprog 0755" 62 | chowncmd= 63 | chgrpcmd= 64 | stripcmd= 65 | rmcmd="$rmprog -f" 66 | mvcmd="$mvprog" 67 | src= 68 | dst= 69 | dir_arg= 70 | dstarg= 71 | no_target_directory= 72 | 73 | usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE 74 | or: $0 [OPTION]... SRCFILES... DIRECTORY 75 | or: $0 [OPTION]... -t DIRECTORY SRCFILES... 76 | or: $0 [OPTION]... -d DIRECTORIES... 77 | 78 | In the 1st form, copy SRCFILE to DSTFILE. 79 | In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. 80 | In the 4th, create DIRECTORIES. 81 | 82 | Options: 83 | -c (ignored) 84 | -d create directories instead of installing files. 85 | -g GROUP $chgrpprog installed files to GROUP. 86 | -m MODE $chmodprog installed files to MODE. 87 | -o USER $chownprog installed files to USER. 88 | -s $stripprog installed files. 89 | -t DIRECTORY install into DIRECTORY. 90 | -T report an error if DSTFILE is a directory. 91 | --help display this help and exit. 92 | --version display version info and exit. 93 | 94 | Environment variables override the default commands: 95 | CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG 96 | " 97 | 98 | while test -n "$1"; do 99 | case $1 in 100 | -c) shift 101 | continue;; 102 | 103 | -d) dir_arg=true 104 | shift 105 | continue;; 106 | 107 | -g) chgrpcmd="$chgrpprog $2" 108 | shift 109 | shift 110 | continue;; 111 | 112 | --help) echo "$usage"; exit $?;; 113 | 114 | -m) chmodcmd="$chmodprog $2" 115 | shift 116 | shift 117 | continue;; 118 | 119 | -o) chowncmd="$chownprog $2" 120 | shift 121 | shift 122 | continue;; 123 | 124 | -s) stripcmd=$stripprog 125 | shift 126 | continue;; 127 | 128 | -t) dstarg=$2 129 | shift 130 | shift 131 | continue;; 132 | 133 | -T) no_target_directory=true 134 | shift 135 | continue;; 136 | 137 | --version) echo "$0 $scriptversion"; exit $?;; 138 | 139 | *) # When -d is used, all remaining arguments are directories to create. 140 | # When -t is used, the destination is already specified. 141 | test -n "$dir_arg$dstarg" && break 142 | # Otherwise, the last argument is the destination. Remove it from $@. 143 | for arg 144 | do 145 | if test -n "$dstarg"; then 146 | # $@ is not empty: it contains at least $arg. 147 | set fnord "$@" "$dstarg" 148 | shift # fnord 149 | fi 150 | shift # arg 151 | dstarg=$arg 152 | done 153 | break;; 154 | esac 155 | done 156 | 157 | if test -z "$1"; then 158 | if test -z "$dir_arg"; then 159 | echo "$0: no input file specified." >&2 160 | exit 1 161 | fi 162 | # It's OK to call `install-sh -d' without argument. 163 | # This can happen when creating conditional directories. 164 | exit 0 165 | fi 166 | 167 | for src 168 | do 169 | # Protect names starting with `-'. 170 | case $src in 171 | -*) src=./$src ;; 172 | esac 173 | 174 | if test -n "$dir_arg"; then 175 | dst=$src 176 | src= 177 | 178 | if test -d "$dst"; then 179 | mkdircmd=: 180 | chmodcmd= 181 | else 182 | mkdircmd=$mkdirprog 183 | fi 184 | else 185 | # Waiting for this to be detected by the "$cpprog $src $dsttmp" command 186 | # might cause directories to be created, which would be especially bad 187 | # if $src (and thus $dsttmp) contains '*'. 188 | if test ! -f "$src" && test ! -d "$src"; then 189 | echo "$0: $src does not exist." >&2 190 | exit 1 191 | fi 192 | 193 | if test -z "$dstarg"; then 194 | echo "$0: no destination specified." >&2 195 | exit 1 196 | fi 197 | 198 | dst=$dstarg 199 | # Protect names starting with `-'. 200 | case $dst in 201 | -*) dst=./$dst ;; 202 | esac 203 | 204 | # If destination is a directory, append the input filename; won't work 205 | # if double slashes aren't ignored. 206 | if test -d "$dst"; then 207 | if test -n "$no_target_directory"; then 208 | echo "$0: $dstarg: Is a directory" >&2 209 | exit 1 210 | fi 211 | dst=$dst/`basename "$src"` 212 | fi 213 | fi 214 | 215 | # This sed command emulates the dirname command. 216 | dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` 217 | 218 | # Make sure that the destination directory exists. 219 | 220 | # Skip lots of stat calls in the usual case. 221 | if test ! -d "$dstdir"; then 222 | defaultIFS=' 223 | ' 224 | IFS="${IFS-$defaultIFS}" 225 | 226 | oIFS=$IFS 227 | # Some sh's can't handle IFS=/ for some reason. 228 | IFS='%' 229 | set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` 230 | shift 231 | IFS=$oIFS 232 | 233 | pathcomp= 234 | 235 | while test $# -ne 0 ; do 236 | pathcomp=$pathcomp$1 237 | shift 238 | if test ! -d "$pathcomp"; then 239 | $mkdirprog "$pathcomp" 240 | # mkdir can fail with a `File exist' error in case several 241 | # install-sh are creating the directory concurrently. This 242 | # is OK. 243 | test -d "$pathcomp" || exit 244 | fi 245 | pathcomp=$pathcomp/ 246 | done 247 | fi 248 | 249 | if test -n "$dir_arg"; then 250 | $doit $mkdircmd "$dst" \ 251 | && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ 252 | && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ 253 | && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ 254 | && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } 255 | 256 | else 257 | dstfile=`basename "$dst"` 258 | 259 | # Make a couple of temp file names in the proper directory. 260 | dsttmp=$dstdir/_inst.$$_ 261 | rmtmp=$dstdir/_rm.$$_ 262 | 263 | # Trap to clean up those temp files at exit. 264 | trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 265 | trap '(exit $?); exit' 1 2 13 15 266 | 267 | # Copy the file name to the temp name. 268 | $doit $cpprog "$src" "$dsttmp" && 269 | 270 | # and set any options; do chmod last to preserve setuid bits. 271 | # 272 | # If any of these fail, we abort the whole thing. If we want to 273 | # ignore errors from any of these, just make sure not to ignore 274 | # errors from the above "$doit $cpprog $src $dsttmp" command. 275 | # 276 | { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ 277 | && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ 278 | && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ 279 | && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && 280 | 281 | # Now rename the file to the real destination. 282 | { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ 283 | || { 284 | # The rename failed, perhaps because mv can't rename something else 285 | # to itself, or perhaps because mv is so ancient that it does not 286 | # support -f. 287 | 288 | # Now remove or move aside any old file at destination location. 289 | # We try this two ways since rm can't unlink itself on some 290 | # systems and the destination file might be busy for other 291 | # reasons. In this case, the final cleanup might fail but the new 292 | # file should still install successfully. 293 | { 294 | if test -f "$dstdir/$dstfile"; then 295 | $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ 296 | || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ 297 | || { 298 | echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 299 | (exit 1); exit 1 300 | } 301 | else 302 | : 303 | fi 304 | } && 305 | 306 | # Now rename the file to the real destination. 307 | $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" 308 | } 309 | } 310 | fi || { (exit 1); exit 1; } 311 | done 312 | 313 | # The final little trick to "correctly" pass the exit status to the exit trap. 314 | { 315 | (exit 0); exit 0 316 | } 317 | 318 | # Local variables: 319 | # eval: (add-hook 'write-file-hooks 'time-stamp) 320 | # time-stamp-start: "scriptversion=" 321 | # time-stamp-format: "%:y-%02m-%02d.%02H" 322 | # time-stamp-end: "$" 323 | # End: 324 | -------------------------------------------------------------------------------- /missing: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Common stub for a few missing GNU programs while installing. 3 | 4 | scriptversion=2005-06-08.21 5 | 6 | # Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 7 | # Free Software Foundation, Inc. 8 | # Originally by Fran,cois Pinard , 1996. 9 | 10 | # This program is free software; you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation; either version 2, or (at your option) 13 | # any later version. 14 | 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program; if not, write to the Free Software 22 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 23 | # 02110-1301, USA. 24 | 25 | # As a special exception to the GNU General Public License, if you 26 | # distribute this file as part of a program that contains a 27 | # configuration script generated by Autoconf, you may include it under 28 | # the same distribution terms that you use for the rest of that program. 29 | 30 | if test $# -eq 0; then 31 | echo 1>&2 "Try \`$0 --help' for more information" 32 | exit 1 33 | fi 34 | 35 | run=: 36 | 37 | # In the cases where this matters, `missing' is being run in the 38 | # srcdir already. 39 | if test -f configure.ac; then 40 | configure_ac=configure.ac 41 | else 42 | configure_ac=configure.in 43 | fi 44 | 45 | msg="missing on your system" 46 | 47 | case "$1" in 48 | --run) 49 | # Try to run requested program, and just exit if it succeeds. 50 | run= 51 | shift 52 | "$@" && exit 0 53 | # Exit code 63 means version mismatch. This often happens 54 | # when the user try to use an ancient version of a tool on 55 | # a file that requires a minimum version. In this case we 56 | # we should proceed has if the program had been absent, or 57 | # if --run hadn't been passed. 58 | if test $? = 63; then 59 | run=: 60 | msg="probably too old" 61 | fi 62 | ;; 63 | 64 | -h|--h|--he|--hel|--help) 65 | echo "\ 66 | $0 [OPTION]... PROGRAM [ARGUMENT]... 67 | 68 | Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an 69 | error status if there is no known handling for PROGRAM. 70 | 71 | Options: 72 | -h, --help display this help and exit 73 | -v, --version output version information and exit 74 | --run try to run the given command, and emulate it if it fails 75 | 76 | Supported PROGRAM values: 77 | aclocal touch file \`aclocal.m4' 78 | autoconf touch file \`configure' 79 | autoheader touch file \`config.h.in' 80 | automake touch all \`Makefile.in' files 81 | bison create \`y.tab.[ch]', if possible, from existing .[ch] 82 | flex create \`lex.yy.c', if possible, from existing .c 83 | help2man touch the output file 84 | lex create \`lex.yy.c', if possible, from existing .c 85 | makeinfo touch the output file 86 | tar try tar, gnutar, gtar, then tar without non-portable flags 87 | yacc create \`y.tab.[ch]', if possible, from existing .[ch] 88 | 89 | Send bug reports to ." 90 | exit $? 91 | ;; 92 | 93 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 94 | echo "missing $scriptversion (GNU Automake)" 95 | exit $? 96 | ;; 97 | 98 | -*) 99 | echo 1>&2 "$0: Unknown \`$1' option" 100 | echo 1>&2 "Try \`$0 --help' for more information" 101 | exit 1 102 | ;; 103 | 104 | esac 105 | 106 | # Now exit if we have it, but it failed. Also exit now if we 107 | # don't have it and --version was passed (most likely to detect 108 | # the program). 109 | case "$1" in 110 | lex|yacc) 111 | # Not GNU programs, they don't have --version. 112 | ;; 113 | 114 | tar) 115 | if test -n "$run"; then 116 | echo 1>&2 "ERROR: \`tar' requires --run" 117 | exit 1 118 | elif test "x$2" = "x--version" || test "x$2" = "x--help"; then 119 | exit 1 120 | fi 121 | ;; 122 | 123 | *) 124 | if test -z "$run" && ($1 --version) > /dev/null 2>&1; then 125 | # We have it, but it failed. 126 | exit 1 127 | elif test "x$2" = "x--version" || test "x$2" = "x--help"; then 128 | # Could not run --version or --help. This is probably someone 129 | # running `$TOOL --version' or `$TOOL --help' to check whether 130 | # $TOOL exists and not knowing $TOOL uses missing. 131 | exit 1 132 | fi 133 | ;; 134 | esac 135 | 136 | # If it does not exist, or fails to run (possibly an outdated version), 137 | # try to emulate it. 138 | case "$1" in 139 | aclocal*) 140 | echo 1>&2 "\ 141 | WARNING: \`$1' is $msg. You should only need it if 142 | you modified \`acinclude.m4' or \`${configure_ac}'. You might want 143 | to install the \`Automake' and \`Perl' packages. Grab them from 144 | any GNU archive site." 145 | touch aclocal.m4 146 | ;; 147 | 148 | autoconf) 149 | echo 1>&2 "\ 150 | WARNING: \`$1' is $msg. You should only need it if 151 | you modified \`${configure_ac}'. You might want to install the 152 | \`Autoconf' and \`GNU m4' packages. Grab them from any GNU 153 | archive site." 154 | touch configure 155 | ;; 156 | 157 | autoheader) 158 | echo 1>&2 "\ 159 | WARNING: \`$1' is $msg. You should only need it if 160 | you modified \`acconfig.h' or \`${configure_ac}'. You might want 161 | to install the \`Autoconf' and \`GNU m4' packages. Grab them 162 | from any GNU archive site." 163 | files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` 164 | test -z "$files" && files="config.h" 165 | touch_files= 166 | for f in $files; do 167 | case "$f" in 168 | *:*) touch_files="$touch_files "`echo "$f" | 169 | sed -e 's/^[^:]*://' -e 's/:.*//'`;; 170 | *) touch_files="$touch_files $f.in";; 171 | esac 172 | done 173 | touch $touch_files 174 | ;; 175 | 176 | automake*) 177 | echo 1>&2 "\ 178 | WARNING: \`$1' is $msg. You should only need it if 179 | you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. 180 | You might want to install the \`Automake' and \`Perl' packages. 181 | Grab them from any GNU archive site." 182 | find . -type f -name Makefile.am -print | 183 | sed 's/\.am$/.in/' | 184 | while read f; do touch "$f"; done 185 | ;; 186 | 187 | autom4te) 188 | echo 1>&2 "\ 189 | WARNING: \`$1' is needed, but is $msg. 190 | You might have modified some files without having the 191 | proper tools for further handling them. 192 | You can get \`$1' as part of \`Autoconf' from any GNU 193 | archive site." 194 | 195 | file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` 196 | test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` 197 | if test -f "$file"; then 198 | touch $file 199 | else 200 | test -z "$file" || exec >$file 201 | echo "#! /bin/sh" 202 | echo "# Created by GNU Automake missing as a replacement of" 203 | echo "# $ $@" 204 | echo "exit 0" 205 | chmod +x $file 206 | exit 1 207 | fi 208 | ;; 209 | 210 | bison|yacc) 211 | echo 1>&2 "\ 212 | WARNING: \`$1' $msg. You should only need it if 213 | you modified a \`.y' file. You may need the \`Bison' package 214 | in order for those modifications to take effect. You can get 215 | \`Bison' from any GNU archive site." 216 | rm -f y.tab.c y.tab.h 217 | if [ $# -ne 1 ]; then 218 | eval LASTARG="\${$#}" 219 | case "$LASTARG" in 220 | *.y) 221 | SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` 222 | if [ -f "$SRCFILE" ]; then 223 | cp "$SRCFILE" y.tab.c 224 | fi 225 | SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` 226 | if [ -f "$SRCFILE" ]; then 227 | cp "$SRCFILE" y.tab.h 228 | fi 229 | ;; 230 | esac 231 | fi 232 | if [ ! -f y.tab.h ]; then 233 | echo >y.tab.h 234 | fi 235 | if [ ! -f y.tab.c ]; then 236 | echo 'main() { return 0; }' >y.tab.c 237 | fi 238 | ;; 239 | 240 | lex|flex) 241 | echo 1>&2 "\ 242 | WARNING: \`$1' is $msg. You should only need it if 243 | you modified a \`.l' file. You may need the \`Flex' package 244 | in order for those modifications to take effect. You can get 245 | \`Flex' from any GNU archive site." 246 | rm -f lex.yy.c 247 | if [ $# -ne 1 ]; then 248 | eval LASTARG="\${$#}" 249 | case "$LASTARG" in 250 | *.l) 251 | SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` 252 | if [ -f "$SRCFILE" ]; then 253 | cp "$SRCFILE" lex.yy.c 254 | fi 255 | ;; 256 | esac 257 | fi 258 | if [ ! -f lex.yy.c ]; then 259 | echo 'main() { return 0; }' >lex.yy.c 260 | fi 261 | ;; 262 | 263 | help2man) 264 | echo 1>&2 "\ 265 | WARNING: \`$1' is $msg. You should only need it if 266 | you modified a dependency of a manual page. You may need the 267 | \`Help2man' package in order for those modifications to take 268 | effect. You can get \`Help2man' from any GNU archive site." 269 | 270 | file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` 271 | if test -z "$file"; then 272 | file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` 273 | fi 274 | if [ -f "$file" ]; then 275 | touch $file 276 | else 277 | test -z "$file" || exec >$file 278 | echo ".ab help2man is required to generate this page" 279 | exit 1 280 | fi 281 | ;; 282 | 283 | makeinfo) 284 | echo 1>&2 "\ 285 | WARNING: \`$1' is $msg. You should only need it if 286 | you modified a \`.texi' or \`.texinfo' file, or any other file 287 | indirectly affecting the aspect of the manual. The spurious 288 | call might also be the consequence of using a buggy \`make' (AIX, 289 | DU, IRIX). You might want to install the \`Texinfo' package or 290 | the \`GNU make' package. Grab either from any GNU archive site." 291 | # The file to touch is that specified with -o ... 292 | file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` 293 | if test -z "$file"; then 294 | # ... or it is the one specified with @setfilename ... 295 | infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` 296 | file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` 297 | # ... or it is derived from the source name (dir/f.texi becomes f.info) 298 | test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info 299 | fi 300 | # If the file does not exist, the user really needs makeinfo; 301 | # let's fail without touching anything. 302 | test -f $file || exit 1 303 | touch $file 304 | ;; 305 | 306 | tar) 307 | shift 308 | 309 | # We have already tried tar in the generic part. 310 | # Look for gnutar/gtar before invocation to avoid ugly error 311 | # messages. 312 | if (gnutar --version > /dev/null 2>&1); then 313 | gnutar "$@" && exit 0 314 | fi 315 | if (gtar --version > /dev/null 2>&1); then 316 | gtar "$@" && exit 0 317 | fi 318 | firstarg="$1" 319 | if shift; then 320 | case "$firstarg" in 321 | *o*) 322 | firstarg=`echo "$firstarg" | sed s/o//` 323 | tar "$firstarg" "$@" && exit 0 324 | ;; 325 | esac 326 | case "$firstarg" in 327 | *h*) 328 | firstarg=`echo "$firstarg" | sed s/h//` 329 | tar "$firstarg" "$@" && exit 0 330 | ;; 331 | esac 332 | fi 333 | 334 | echo 1>&2 "\ 335 | WARNING: I can't seem to be able to run \`tar' with the given arguments. 336 | You may want to install GNU tar or Free paxutils, or check the 337 | command line arguments." 338 | exit 1 339 | ;; 340 | 341 | *) 342 | echo 1>&2 "\ 343 | WARNING: \`$1' is needed, and is $msg. 344 | You might have modified some files without having the 345 | proper tools for further handling them. Check the \`README' file, 346 | it often tells you about the needed prerequisites for installing 347 | this package. You may also peek at any GNU archive site, in case 348 | some other package would contain this missing \`$1' program." 349 | exit 1 350 | ;; 351 | esac 352 | 353 | exit 0 354 | 355 | # Local variables: 356 | # eval: (add-hook 'write-file-hooks 'time-stamp) 357 | # time-stamp-start: "scriptversion=" 358 | # time-stamp-format: "%:y-%02m-%02d.%02H" 359 | # time-stamp-end: "$" 360 | # End: 361 | -------------------------------------------------------------------------------- /src/oftpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "oftpd.h" 22 | #include "ftp_listener.h" 23 | #include "error.h" 24 | 25 | /* put our executable name here where everybody can see it */ 26 | static const char *exe_name = "oftpd"; 27 | 28 | int pasv_port_low = 1024; 29 | int pasv_port_high = MAX_PORT; 30 | 31 | /* The debug flags. */ 32 | int debug_flags; 33 | 34 | static void daemonize(); 35 | static void print_usage(const char *error); 36 | 37 | int main(int argc, char *argv[]) 38 | { 39 | int i; 40 | 41 | long num; 42 | char *endptr; 43 | int port; 44 | int max_clients; 45 | 46 | char *user_ptr; 47 | char *dir_ptr; 48 | char *address; 49 | 50 | char temp_buf[256]; 51 | char errbuf[ERRBUF_SIZE]; 52 | 53 | struct passwd *user_info; 54 | error_t err; 55 | 56 | ftp_listener_t ftp_listener; 57 | 58 | int detach; 59 | 60 | int log_facility; 61 | 62 | sigset_t term_signal; 63 | int sig; 64 | 65 | /* grab our executable name */ 66 | if (argc > 0) { 67 | exe_name = argv[0]; 68 | } 69 | 70 | /* verify we're running as root */ 71 | if (geteuid() != 0) { 72 | if ( argc > 1 && !strcmp (argv[1], "--version")) { 73 | printf ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); 74 | exit (0); 75 | } 76 | fprintf(stderr, "%s: program needs root permission to run\n", exe_name); 77 | exit(1); 78 | 79 | } 80 | 81 | /* default command-line arguments */ 82 | port = FTP_PORT; 83 | user_ptr = NULL; 84 | dir_ptr = NULL; 85 | address = FTP_ADDRESS; 86 | max_clients = MAX_CLIENTS; 87 | detach = 1; 88 | log_facility = LOG_FTP; 89 | 90 | /* check our command-line arguments */ 91 | /* we're stubbornly refusing to use getopt(), because we can */ 92 | /* :) */ 93 | for (i=1; i= argc) { 100 | print_usage("missing port number"); 101 | exit(1); 102 | } 103 | num = strtol(argv[i], &endptr, 0); 104 | if ((num < MIN_PORT) || (num > MAX_PORT) || (*endptr != '\0')) { 105 | 106 | snprintf(temp_buf, sizeof(temp_buf), 107 | "port must be a number between %d and %d", 108 | MIN_PORT, MAX_PORT); 109 | print_usage(temp_buf); 110 | 111 | exit(1); 112 | } 113 | port = num; 114 | } 115 | else if (strcmp(argv[i], "-r") == 0 116 | || strcmp(argv[i], "--pasv-range") == 0) { 117 | i += 2; 118 | if (i >= argc) { 119 | print_usage("invalid passive port range"); 120 | exit(1); 121 | } 122 | num = strtol(argv[i-1], &endptr, 0); 123 | if ((num < 1024) || (num > MAX_PORT) || (*endptr != '\0')) { 124 | 125 | snprintf(temp_buf, sizeof(temp_buf), 126 | "low passive port must be a number between 1024 and %d", 127 | MAX_PORT); 128 | print_usage(temp_buf); 129 | 130 | exit(1); 131 | } 132 | pasv_port_low = num; 133 | num = strtol(argv[i], &endptr, 0); 134 | if ((num < pasv_port_low) || (num > MAX_PORT) || (*endptr != '\0')) { 135 | 136 | snprintf(temp_buf, sizeof(temp_buf), 137 | "high passive port must be a number between %d and %d", 138 | pasv_port_low, MAX_PORT); 139 | print_usage(temp_buf); 140 | 141 | exit(1); 142 | } 143 | pasv_port_high = num; 144 | } else if (strcmp(argv[i], "-h") == 0 145 | || strcmp(argv[i], "--help") == 0) { 146 | print_usage(NULL); 147 | exit(0); 148 | } else if (strcmp(argv[i], "-i") == 0 149 | || strcmp(argv[i], "--interface") == 0) { 150 | if (++i >= argc) { 151 | print_usage("missing interface"); 152 | exit(1); 153 | } 154 | address = argv[i]; 155 | } else if (strcmp(argv[i], "-m") == 0 156 | || strcmp(argv[i], "--max-clients") == 0) { 157 | if (++i >= argc) { 158 | print_usage("missing number of max clients"); 159 | exit(1); 160 | } 161 | num = strtol(argv[i], &endptr, 0); 162 | if ((num < MIN_NUM_CLIENTS) || (num > MAX_NUM_CLIENTS) 163 | || (*endptr != '\0')) { 164 | 165 | snprintf(temp_buf, sizeof(temp_buf), 166 | "max clients must be a number between %d and %d", 167 | MIN_NUM_CLIENTS, MAX_NUM_CLIENTS); 168 | print_usage(temp_buf); 169 | 170 | exit(1); 171 | } 172 | max_clients = num; 173 | } else if (strcmp(argv[i], "-N") == 0 174 | || strcmp(argv[i], "--nodetach") == 0) { 175 | detach = 0; 176 | } else if (strcmp(argv[i], "-d") == 0 177 | || strcmp(argv[i], "--debug") == 0) { 178 | /* We just add another flag for each -d option. */ 179 | debug_flags = (debug_flags << 1) | 1; 180 | } else if (strcmp(argv[i], "-l") == 0 181 | || strcmp(argv[i], "--local") == 0) { 182 | if (++i >= argc) { 183 | print_usage("missing number for local facility logging"); 184 | exit(1); 185 | } 186 | switch (argv[i][0]) { 187 | case '0': 188 | log_facility = LOG_LOCAL0; 189 | break; 190 | case '1': 191 | log_facility = LOG_LOCAL1; 192 | break; 193 | case '2': 194 | log_facility = LOG_LOCAL2; 195 | break; 196 | case '3': 197 | log_facility = LOG_LOCAL3; 198 | break; 199 | case '4': 200 | log_facility = LOG_LOCAL4; 201 | break; 202 | case '5': 203 | log_facility = LOG_LOCAL5; 204 | break; 205 | case '6': 206 | log_facility = LOG_LOCAL6; 207 | break; 208 | case '7': 209 | log_facility = LOG_LOCAL7; 210 | break; 211 | } 212 | } else if (strcmp(argv[i], "--version") == 0) { 213 | printf ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); 214 | exit (0); 215 | } else { 216 | print_usage("unknown option"); 217 | exit(1); 218 | } 219 | 220 | /* required parameters */ 221 | } else { 222 | if (user_ptr == NULL) { 223 | user_ptr = argv[i]; 224 | } else if (dir_ptr == NULL) { 225 | dir_ptr = argv[i]; 226 | } else { 227 | print_usage("too many arguments on the command line"); 228 | exit(1); 229 | } 230 | } 231 | } 232 | if ((user_ptr == NULL) || (dir_ptr == NULL)) { 233 | print_usage("missing user and/or directory name"); 234 | exit(1); 235 | } 236 | 237 | user_info = getpwnam(user_ptr); 238 | if (user_info == NULL) { 239 | fprintf(stderr, "%s: invalid user name\n", exe_name); 240 | exit(1); 241 | } 242 | 243 | /* become a daemon */ 244 | if (detach) { 245 | daemonize(); 246 | } 247 | 248 | /* avoid SIGPIPE on socket activity */ 249 | signal(SIGPIPE, SIG_IGN); 250 | 251 | /* log the start time */ 252 | openlog(NULL, LOG_NDELAY, log_facility); 253 | syslog(LOG_INFO,"Starting, version %s, as PID %d", 254 | PACKAGE_VERSION, getpid()); 255 | 256 | /* change to root directory */ 257 | if (chroot(dir_ptr) != 0) { 258 | syslog(LOG_ERR, "error with root directory; %s\n", 259 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 260 | exit(1); 261 | } 262 | if (chdir("/") != 0) { 263 | syslog(LOG_ERR, "error changing directory; %s\n", 264 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 265 | exit(1); 266 | } 267 | 268 | /* Check that a /dev directory exists, so that syslog works 269 | properly even after a syslogd restart. */ 270 | { 271 | struct stat stat_buf; 272 | 273 | if (stat( "/dev", &stat_buf)) { 274 | syslog (LOG_ERR, "required `%s/dev' directory is missing: %s\n", 275 | dir_ptr, strerror_r (errno, errbuf, ERRBUF_SIZE)); 276 | } 277 | #ifndef STATS_MACRO_BROKEN 278 | if (!S_ISDIR(stat_buf.st_mode)) { 279 | #else 280 | if (S_ISDIR(stat_buf.st_mode)) { 281 | #endif 282 | syslog (LOG_ERR, "`%s/dev' is not a directory\n", dir_ptr); 283 | } 284 | } 285 | 286 | 287 | /* create our main listener */ 288 | if (!ftp_listener_init(&ftp_listener, 289 | address, 290 | port, 291 | max_clients, 292 | INACTIVITY_TIMEOUT, 293 | &err)) 294 | { 295 | syslog(LOG_ERR, "error initializing FTP listener; %s", 296 | error_get_desc(&err)); 297 | exit(1); 298 | } 299 | 300 | /* set user to be as inoffensive as possible */ 301 | if (setgid(user_info->pw_gid) != 0) { 302 | syslog(LOG_ERR, "error changing group; %s", 303 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 304 | exit(1); 305 | } 306 | if (setuid(user_info->pw_uid) != 0) { 307 | syslog(LOG_ERR, "error changing group; %s", 308 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 309 | exit(1); 310 | } 311 | 312 | /* start our listener */ 313 | if (ftp_listener_start(&ftp_listener, &err) == 0) { 314 | syslog(LOG_ERR, "error starting FTP service; %s", error_get_desc(&err)); 315 | exit(1); 316 | } 317 | 318 | /* wait for a SIGTERM and exit gracefully */ 319 | sigemptyset(&term_signal); 320 | sigaddset(&term_signal, SIGTERM); 321 | sigaddset(&term_signal, SIGINT); 322 | pthread_sigmask(SIG_BLOCK, &term_signal, NULL); 323 | sigwait(&term_signal, &sig); 324 | if (sig == SIGTERM) { 325 | syslog(LOG_INFO, "SIGTERM received, shutting down"); 326 | } else { 327 | syslog(LOG_INFO, "SIGINT received, shutting down"); 328 | } 329 | ftp_listener_stop(&ftp_listener); 330 | syslog(LOG_INFO, "all connections finished, FTP server exiting"); 331 | exit(0); 332 | } 333 | 334 | static void print_usage(const char *error) 335 | { 336 | if (error != NULL) { 337 | fprintf(stderr, "%s: %s\n", exe_name, error); 338 | } 339 | fprintf(stderr, 340 | " Syntax: %s [ options... ] user_name root_directory\n", exe_name); 341 | fprintf(stderr, 342 | " Options:\n" 343 | " -p, --port \n" 344 | " Set the port to listen on (Default: %d)\n" 345 | " -i, --interface \n" 346 | " Set the interface to listen on (Default: all)\n" 347 | " -m, --max-clients \n" 348 | " Set the number of clients allowed at one time (Default: %d)\n" 349 | "-l, --local \n" 350 | " Use LOCAL facility for syslog, local-logging is 0 to 7\n" 351 | " -N, --nodetach\n" 352 | " Do not detach from TTY and become a daemon\n", 353 | DEFAULT_FTP_PORT, MAX_CLIENTS); 354 | } 355 | 356 | static void daemonize() 357 | { 358 | int fork_ret; 359 | int max_fd; 360 | int null_fd; 361 | int fd; 362 | char errbuf[ERRBUF_SIZE]; 363 | 364 | null_fd = open("/dev/null", O_RDWR); 365 | if (null_fd == -1) { 366 | fprintf(stderr, "%s: error opening null output device; %s\n", exe_name, 367 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 368 | exit(1); 369 | } 370 | 371 | max_fd = sysconf(_SC_OPEN_MAX); 372 | if (max_fd == -1) { 373 | fprintf(stderr, "%s: error getting maximum open file; %s\n", exe_name, 374 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 375 | exit(1); 376 | } 377 | 378 | 379 | fork_ret = fork(); 380 | if (fork_ret == -1) { 381 | fprintf(stderr, "%s: error forking; %s\n", 382 | exe_name, strerror_r(errno, errbuf, ERRBUF_SIZE)); 383 | exit(1); 384 | } 385 | if (fork_ret != 0) { 386 | exit(0); 387 | } 388 | if (setsid() == -1) { 389 | fprintf(stderr, "%s: error creating process group; %s\n", exe_name, 390 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 391 | exit(1); 392 | } 393 | fork_ret = fork(); 394 | if (fork_ret == -1) { 395 | fprintf(stderr, "%s: error forking; %s\n", 396 | exe_name, strerror_r(errno, errbuf, ERRBUF_SIZE)); 397 | exit(1); 398 | } 399 | if (fork_ret != 0) { 400 | exit(0); 401 | } 402 | if (dup2(null_fd, 0) == -1) { 403 | syslog(LOG_ERR, "error setting input to null; %s", 404 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 405 | exit(1); 406 | } 407 | if (dup2(null_fd, 1) == -1) { 408 | syslog(LOG_ERR, "error setting output to null; %s", 409 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 410 | exit(1); 411 | } 412 | if (dup2(null_fd, 2) == -1) { 413 | syslog(LOG_ERR, "error setting error output to null; %s", 414 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 415 | exit(1); 416 | } 417 | for (fd=3; fd 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if TIME_WITH_SYS_TIME 20 | # include 21 | # include 22 | #else 23 | # if HAVE_SYS_TIME_H 24 | # include 25 | # else 26 | # include 27 | # endif 28 | #endif 29 | 30 | #include "file_list.h" 31 | #include "daemon_assert.h" 32 | 33 | /* AIX requires this to be the first thing in the file. */ 34 | #ifndef __GNUC__ 35 | # if HAVE_ALLOCA_H 36 | # include 37 | # else 38 | # ifdef _AIX 39 | #pragma alloca 40 | # else 41 | # ifndef alloca /* predefined by HP cc +Olibcalls */ 42 | char *alloca (); 43 | # endif 44 | # endif 45 | # endif 46 | #endif 47 | 48 | /* GLOB_PERIOD is defined in Linux but not Solaris */ 49 | #ifndef GLOB_PERIOD 50 | #define GLOB_PERIOD 0 51 | #endif /* GLOB_PERIOD */ 52 | 53 | /* GLOB_ABORTED is defined in Linux but not on FreeBSD */ 54 | #ifndef GLOB_ABORTED 55 | #ifdef GLOB_ABEND 56 | #define GLOB_ABORTED GLOB_ABEND 57 | #endif 58 | #endif 59 | 60 | static int is_valid_dir(const char *dir); 61 | static void fdprintf(int fd, const char *fmt, ...); 62 | static const char *skip_ls_options(const char *filespec); 63 | 64 | /* if no localtime_r() is available, provide one */ 65 | #ifndef HAVE_LOCALTIME_R 66 | 67 | struct tm *localtime_r(const time_t *timep, struct tm *timeptr) 68 | { 69 | static pthread_mutex_t time_lock = PTHREAD_MUTEX_INITIALIZER; 70 | 71 | pthread_mutex_lock(&time_lock); 72 | *timeptr = *(localtime(timep)); 73 | pthread_mutex_unlock(&time_lock); 74 | return timeptr; 75 | } 76 | #endif /* HAVE_LOCALTIME_R */ 77 | 78 | static void file_nlst_cleanup(glob_t *glob_buf) 79 | { 80 | globfree (glob_buf); 81 | } 82 | 83 | int file_nlst(int out, const char *cur_dir, const char *filespec) 84 | { 85 | int dir_len; 86 | char pattern[PATH_MAX+1]; 87 | int glob_ret; 88 | glob_t glob_buf; 89 | int i; 90 | char *file_name; 91 | 92 | daemon_assert(out >= 0); 93 | daemon_assert(is_valid_dir(cur_dir)); 94 | daemon_assert(filespec != NULL); 95 | 96 | if (filespec[0] == '/') { 97 | cur_dir = ""; 98 | dir_len = 0; 99 | } else { 100 | dir_len = strlen(cur_dir); 101 | if ((dir_len + 1) > PATH_MAX) { 102 | fdprintf(out, "Error; Directory name too long\r\n"); 103 | return 0; 104 | } 105 | strcpy(pattern, cur_dir); 106 | if ((cur_dir[0] != '/') || (cur_dir[1] != '\0')) { 107 | strcat(pattern, "/"); 108 | dir_len++; 109 | } 110 | } 111 | 112 | /* FIXME: Stat the directory to see whether something is hidden. */ 113 | 114 | /* make sure we have enough space */ 115 | if ((dir_len + 1 + strlen(filespec)) > PATH_MAX) { 116 | fdprintf(out, "Error; Path name too long\r\n"); 117 | return 0; 118 | } 119 | strcat(pattern, filespec); 120 | if ( !strncmp (pattern, "/dev", 4) 121 | && (!pattern[4] || pattern[4] == '/')) 122 | return 1; /* ignore /dev */ 123 | 124 | /* do a glob() */ 125 | memset(&glob_buf, 0, sizeof(glob_buf)); 126 | glob_ret = glob(pattern, 127 | GLOB_ERR | GLOB_NOSORT | GLOB_PERIOD, 128 | NULL, 129 | &glob_buf); 130 | if (glob_ret == GLOB_NOSPACE) { 131 | fdprintf(out, "Error; Out of memory\r\n"); 132 | return 0; 133 | #ifdef GLOB_NOMATCH /* not present in FreeBSD */ 134 | } else if (glob_ret == GLOB_NOMATCH) { 135 | return 1; 136 | #endif /* GLOB_NOMATCH */ 137 | #ifdef GLOB_ABORTED /* not present in older gcc */ 138 | } else if (glob_ret == GLOB_ABORTED) { 139 | fdprintf(out, "Error; Read error\r\n"); 140 | return 0; 141 | #endif /*GLOB_ABORTED*/ 142 | } else if (glob_ret != 0) { 143 | fdprintf(out, "Error; Unknown glob() error %d\r\n", glob_ret); 144 | return 0; 145 | } 146 | 147 | pthread_cleanup_push ((void (*)())file_nlst_cleanup, &glob_buf); 148 | 149 | /* print our results */ 150 | for (i=0; ifile_info) 182 | free (info->file_info); 183 | if (info->glob_buf) 184 | globfree (info->glob_buf); 185 | } 186 | 187 | int file_list(int out, const char *cur_dir, const char *filespec) 188 | { 189 | int dir_len; 190 | char pattern[PATH_MAX+1]; 191 | int glob_ret; 192 | glob_t glob_buf; 193 | int i; 194 | file_info_t *file_info; 195 | int num_files; 196 | unsigned long total_blocks; 197 | char *file_name; 198 | 199 | mode_t mode; 200 | time_t now; 201 | struct tm tm_now; 202 | double age; 203 | char date_buf[13]; 204 | char link[PATH_MAX+1]; 205 | int link_len; 206 | 207 | struct file_list_cleanup_s cleanup_info; 208 | 209 | cleanup_info.file_info = NULL; 210 | cleanup_info.glob_buf = NULL; 211 | 212 | daemon_assert(out >= 0); 213 | daemon_assert(is_valid_dir(cur_dir)); 214 | daemon_assert(filespec != NULL); 215 | 216 | filespec = skip_ls_options(filespec); 217 | 218 | if (filespec[0] == '/') { 219 | cur_dir = ""; 220 | dir_len = 0; 221 | } else { 222 | dir_len = strlen(cur_dir); 223 | if ((dir_len + 1) > PATH_MAX) { 224 | fdprintf(out, "Error; Directory name too long\r\n"); 225 | return 0; 226 | } 227 | strcpy(pattern, cur_dir); 228 | if ((cur_dir[0] != '/') || (cur_dir[1] != '\0')) { 229 | strcat(pattern, "/"); 230 | dir_len++; 231 | } 232 | } 233 | 234 | /* FIXME: Stat the directory to see whether something is hidden. */ 235 | 236 | /* make sure we have enough space */ 237 | if ((dir_len + 1 + strlen(filespec)) > PATH_MAX) { 238 | fdprintf(out, "Error; Path name too long\r\n"); 239 | return 0; 240 | } 241 | strcat(pattern, filespec); 242 | if ( !strncmp (pattern, "/dev", 4) 243 | && (!pattern[4] || pattern[4] == '/')) 244 | return 1; /* ignore /dev */ 245 | 246 | 247 | /* do a glob() */ 248 | memset(&glob_buf, 0, sizeof(glob_buf)); 249 | glob_ret = glob(pattern, GLOB_ERR, NULL, &glob_buf); 250 | #ifndef GLOB_NOMATCH /* FreeBSD */ 251 | if (glob_ret == GLOB_NOCHECK) { 252 | #else 253 | if (glob_ret == GLOB_NOMATCH) { 254 | #endif 255 | fdprintf(out, "total 0\r\n"); 256 | return 1; 257 | } else if (glob_ret == GLOB_NOSPACE) { 258 | fdprintf(out, "Error; Out of memory\r\n"); 259 | return 0; 260 | #ifdef GLOB_ABORTED /* Not present in older gcc. */ 261 | } else if (glob_ret == GLOB_ABORTED) { 262 | fdprintf(out, "Error; Read error\r\n"); 263 | return 0; 264 | #endif /*GLOB_ABORTED*/ 265 | } else if (glob_ret != 0) { 266 | fdprintf(out, "Error; Unknown glob() error %d\r\n", glob_ret); 267 | return 0; 268 | } 269 | 270 | 271 | /* make a buffer to store our information */ 272 | #ifdef HAVE_ALLOCA 273 | file_info = (file_info_t *)alloca(sizeof(file_info_t) * glob_buf.gl_pathc); 274 | #else 275 | file_info = (file_info_t *)malloc(sizeof(file_info_t) * glob_buf.gl_pathc); 276 | cleanup_info.file_info = file_info; 277 | #endif 278 | if (file_info == NULL) { 279 | /* Note that fdprintf is a cancellation point. */ 280 | globfree(&glob_buf); 281 | fdprintf(out, "Error; Out of memory\r\n"); 282 | return 0; 283 | } 284 | 285 | cleanup_info.glob_buf = &glob_buf; 286 | pthread_cleanup_push ((void (*)())file_list_cleanup, &cleanup_info); 287 | 288 | /* collect information */ 289 | num_files = 0; 290 | total_blocks = 0; 291 | for (i=0; i= 7 && !strcmp (file_name+n-7, ".hidden")) { 297 | num_files = 0; 298 | total_blocks = 0; 299 | break; 300 | } 301 | if ( !strncmp (file_name, "/dev", 4) 302 | && (!file_name[4] || file_name[4] == '/')) 303 | continue; 304 | if (memcmp(file_name, pattern, dir_len) == 0) { 305 | file_name += dir_len; 306 | } 307 | if (lstat(glob_buf.gl_pathv[i], &file_info[num_files].stat) == 0) { 308 | #ifdef AC_STRUCT_ST_BLKSIZE 309 | total_blocks += file_info[num_files].stat.st_blocks; 310 | #endif 311 | file_info[num_files].name = file_name; 312 | file_info[num_files].full_path = glob_buf.gl_pathv[i]; 313 | num_files++; 314 | } 315 | } 316 | 317 | /* okay, we have information, now display it */ 318 | fdprintf(out, "total %lu\r\n", total_blocks); 319 | time(&now); 320 | for (i=0; i> 8) & 0xff), 369 | (int)(file_info[i].stat.st_rdev & 0xff)); 370 | } else { 371 | fdprintf(out, "%8lu ", 372 | (unsigned long)file_info[i].stat.st_size); 373 | } 374 | #else 375 | fdprintf(out, "%8lu ", (unsigned long)file_info[i].stat.st_size); 376 | #endif 377 | 378 | /* output date */ 379 | localtime_r(&file_info[i].stat.st_mtime, &tm_now); 380 | age = difftime(now, file_info[i].stat.st_mtime); 381 | if ((age > 60 * 60 * 24 * 30 * 6) || (age < -(60 * 60 * 24 * 30 * 6))) { 382 | strftime(date_buf, sizeof(date_buf), "%b %e %Y", &tm_now); 383 | } else { 384 | strftime(date_buf, sizeof(date_buf), "%b %e %H:%M", &tm_now); 385 | } 386 | fdprintf(out, "%s ", date_buf); 387 | 388 | /* output filename */ 389 | fdprintf(out, "%s", file_info[i].name); 390 | 391 | /* display symbolic link information */ 392 | if ((mode & S_IFMT) == S_IFLNK) { 393 | link_len = readlink(file_info[i].full_path, link, sizeof(link)); 394 | if (link_len > 0) { 395 | fdprintf(out, " -> "); 396 | link[link_len] = '\0'; 397 | fdprintf(out, "%s", link); 398 | } 399 | } 400 | 401 | /* advance to next line */ 402 | fdprintf(out, "\r\n"); 403 | } 404 | 405 | /* This frees file_info (if necessary) and glob_buf. */ 406 | pthread_cleanup_pop (1); 407 | 408 | return 1; 409 | } 410 | 411 | static int is_valid_dir(const char *dir) 412 | { 413 | /* directory can not be NULL (of course) */ 414 | if (dir == NULL) { 415 | return 0; 416 | } 417 | 418 | /* directory must be absolute (i.e. start with '/') */ 419 | if (dir[0] != '/') { 420 | return 0; 421 | } 422 | 423 | /* length cannot be greater than PATH_MAX */ 424 | if (strlen(dir) > PATH_MAX) { 425 | return 0; 426 | } 427 | 428 | /* assume okay */ 429 | return 1; 430 | } 431 | 432 | static void fdprintf(int fd, const char *fmt, ...) 433 | { 434 | char buf[PATH_MAX+3]; 435 | int buflen; 436 | va_list ap; 437 | int amt_written; 438 | int write_ret; 439 | int state; 440 | 441 | daemon_assert(fd >= 0); 442 | daemon_assert(fmt != NULL); 443 | 444 | va_start(ap, fmt); 445 | buflen = vsnprintf(buf, sizeof(buf)-1, fmt, ap); 446 | va_end(ap); 447 | if (buflen <= 0) { 448 | return; 449 | } 450 | if (buflen >= sizeof(buf)) { 451 | buflen = sizeof(buf)-1; 452 | } 453 | buf[buflen] = 0; /* Make extra sure the string is terminated. */ 454 | 455 | pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &state); 456 | amt_written = 0; 457 | while (amt_written < buflen) { 458 | write_ret = write(fd, buf+amt_written, buflen-amt_written); 459 | if (write_ret <= 0) { 460 | break; 461 | } 462 | amt_written += write_ret; 463 | } 464 | pthread_setcancelstate (state, NULL); 465 | } 466 | 467 | 468 | /* 469 | hack workaround clients like Midnight Commander that send: 470 | LIST -al /dirname 471 | */ 472 | static const char * 473 | skip_ls_options(const char *filespec) 474 | { 475 | daemon_assert(filespec != NULL); 476 | 477 | for (;;) { 478 | /* end when we've passed all options */ 479 | if (*filespec != '-') { 480 | break; 481 | } 482 | filespec++; 483 | 484 | /* if we find "--", eat it and any following whitespace then return */ 485 | if ((filespec[0] == '-') && (filespec[1] == ' ')) { 486 | filespec += 2; 487 | while (isspace(*filespec)) { 488 | filespec++; 489 | } 490 | break; 491 | } 492 | 493 | /* otherwise, skip this option */ 494 | while ((*filespec != '\0') && !isspace(*filespec)) { 495 | filespec++; 496 | } 497 | 498 | /* and skip any whitespace */ 499 | while (isspace(*filespec)) { 500 | filespec++; 501 | } 502 | } 503 | 504 | daemon_assert(filespec != NULL); 505 | 506 | return filespec; 507 | } 508 | 509 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2010-06-25 Werner Koch 2 | 3 | * configure.ac (LDFLAGS): Add -lgcc_s. 4 | 5 | 2007-04-05 Marcus Brinkmann 6 | 7 | * src/ftp_session.c (do_retr, open_connection, send_readme): 8 | Add dummy block between jump label and pthread_cleanup_pop() macro. 9 | 10 | * oftpd.startup (ARGS): Add $OPTIONS. 11 | * src/oftpd.h (debug_flags): Declare global variable. 12 | (DEBUG_NONE, DEBUG_XFER, DEBUG_CMDS): New macros. 13 | * src/oftpd.c (debug_flags): New global variable. 14 | (main): Support new -d option. 15 | * src/ftp_session.c (ftp_session_run): Only log if DEBUG_CMDS flag 16 | is set. 17 | (reply): Likewise. 18 | (do_retr): Log xfer in WU-FTPD format. 19 | 20 | 2007-03-26 Werner Koch 21 | 22 | * src/telnet_session.c (do_telnet_session_print): Add argument S0 23 | to act as a prefix. 24 | (telnet_session_println_with_prefix): New. 25 | (telnet_session_print_with_prefix): New. 26 | * src/ftp_session.c: Use the new function. Allow for CRLF 27 | terminated readme files. Again for some ill behaving firewalls. 28 | 29 | 2007-03-23 Werner Koch 30 | 31 | * configure.ac: Set version to 0.3.7-wk4. 32 | 33 | * src/telnet_session.c (telnet_session_print): Factor code out 34 | to .. 35 | (do_telnet_session_print): .. new. Add additional argument S2. 36 | (telnet_session_println): Change to use the new fucntion. This is 37 | required so that we use just one write for the common case that 38 | the CRLF fits into the same buffer. Some firewalls have problems 39 | with receiving an FTP command without the CRLF in one [acket and 40 | the CRLF in the next one. 41 | 42 | 2007-02-08 Werner Koch 43 | 44 | * src/oftpd.c (main): s/PACKAGE/PACKAGE_NAME/ 45 | s/VERSION/PACKAGE_VERSION/. 46 | 47 | * configure.in: Renamed to configure.ac and converted to modern 48 | style. 49 | * acconfig.h: Removed. 50 | 51 | 2007-02-08 Marcus Brinkmann 52 | 53 | * src/telnet_session.c: Include . 54 | (process_data): Enable cancellation. 55 | * src/ftp_session.c (netscape_hack): Likewise. 56 | (send_readme, do_list, do_nlst): Use cancellation cleanup handler. 57 | (set_pasv): Release socket_fd before invoking reply() in case of 58 | failure. 59 | (open_connection): Likewise. 60 | (do_pasv): Close outstanding passive port before invoking reply(). 61 | (do_lpsv): Likewise. 62 | (do_epsv): Likewise. 63 | * src/file_list.c: Always include . 64 | (fdprintf): Bump buffer size to PATH_MAX + 3. Enable cancellation 65 | while writing. 66 | * src/ftp_listener.c (connection_handler): Do not use asynchronous 67 | cancellation. Disable cancellation until further notice. 68 | 69 | 2007-02-06 Marcus Brinkmann 70 | 71 | * configure.in (AC_CHECK_HEADERS): Check for sys/sendfile.h. 72 | * src/file_list.c (file_nlst): Remove unused variable HIDDEN. 73 | * src/ftp_session.c [HAVE_SYS_SENDFILE_H]: Include 74 | . 75 | (do_user, do_pass): Remove unused variable addr_port. 76 | (do_eprt): Remove unused variable HOST_PORT. 77 | (do_retr): Remove unused variable host_port. Adjust format output 78 | parameter for timespec members, cast these to the format type. 79 | [!HAVE_FREEBSD_SENDFILE]: Do not define otherwise unused variable 80 | AMT_SENT. 81 | * src/ftp_listener.c (addr2string) [!INET6]: Do not define 82 | variables ERROR and ADDR to suppress compiler warning. 83 | (addr_to_string): Replace with prototype for addr2string. 84 | * src/oftpd.c: Remove extra argument EXE_NAME in syslog 85 | invocation. 86 | (my_syslog_fd): Remove unused global variable. 87 | * src/daemon_assert.c: Include . 88 | 89 | 2006-03-09 Werner Koch 90 | 91 | * configure.in: Define ERRBUF_SIZE. 92 | 93 | * src/oftpd.c: Add option --version. 94 | 95 | 2005-04-30 Werner Koch 96 | 97 | Bumped version to 0.3.7-wk1. 98 | 99 | * src/file_list.c (file_nlst, file_list): Fixed the paranoia test for 100 | overlong directories. 101 | 102 | 2005-04-30 Werner Koch 103 | 104 | Ported patches from Shane Kerr's 0.3.7 release: 105 | 106 | * configure.in: Disabled support for sendfile() on FreeBSD. It 107 | appears to me that there's a bug in the sendfile() user-level 108 | library. I would welcome advice from any FreeBSD developer 109 | wishing to set me straight. 110 | 111 | * src/ftp_session.c (do_size): Changed SIZE command to return 112 | error for directories. 113 | * src/file_list.c: #ifdef wrap GLOB_ABORTED for really old gcc 114 | compilers (Slackware or older installations). 115 | (skip_ls_options): Add code to skip command line options that are 116 | intended for "ls". 117 | 118 | 2004-01-12 Werner Koch 119 | 120 | * src/ftp_listener.c (connection_acceptor): Don't increase the 121 | error counter for EMFILE conditions but sleep a few seconds. 122 | 123 | 2003-08-13 Werner Koch 124 | 125 | * src/oftpd.c (reopen_syslog_hack): Removed. 126 | (init_syslog_hack): Removed. That was a whole stupid thing by me 127 | as I didn'd realized that we are running chroot and so the 128 | /dev/log socket could not be reopened by libc. 129 | (main): Print a warning if there is no /dev directory below the 130 | chroot. 131 | 132 | * src/file_list.c (file_nlst,file_list): Don't list "/dev". 133 | * src/ftp_session.c (change_dir, do_retr) 134 | (do_size, do_mdtm): Likewise. 135 | 136 | 2003-08-12 Werner Koch 137 | 138 | * src/oftpd.c (reopen_syslog_hack): Simply comparing the fd is not 139 | enough due to race conditions. So always check whether the syslog 140 | fd is still bound to a unix domain socket. 141 | 142 | 2003-08-09 Werner Koch 143 | 144 | * src/oftpd.c (main): Give LOG_FACILITY file scope. 145 | (init_syslog_hack): New 146 | (reopen_syslog_hack): New. 147 | * src/ftp_session.c (set_pasv, open_connection): Insert 148 | reopen_syslog_hack calls. 149 | * src/ftp_listener.c (connection_acceptor, ftp_listener_init): Ditto. 150 | * src/ftp_session.c (open_connection): Ditto. 151 | 152 | * src/file_list.c: As an extra safeguard check the provided 153 | dir_len. 154 | (file_nlst): Ditto. 155 | (fdprintf): Extra paranoid safeguard for buggy vsnprintfs. 156 | 157 | 2003-05-22 Werner Koch 158 | 159 | * src/ftp_command.c (parse_offset): Fixed offset checking. 160 | 161 | * configure.in (AC_CHECK_SIZEOF): Add tests for off_t, 162 | unsigned long and unsigned long long. 163 | 164 | 2003-05-06 Werner Koch 165 | 166 | * src/ftp_session.c (do_lprt): Do not set the port after emitting 167 | an error. Fixes an assertion failure in ip_equal. 168 | 169 | 2003-05-05 Werner Koch 170 | 171 | - New option -r, --pasv-range to limit the range for passive ports to 172 | specific firewall rules. 173 | 174 | 2001-05-28, Shane Kerr 175 | - Version 0.3.6 released. 176 | - Beat FreeBSD into submission. Now compiles (and runs!) on FreeBSD 177 | out of the box. 178 | 179 | 2001-05-11, Shane Kerr 180 | - Changed REST to only work for IMAGE (i.e. binary) mode. 181 | - Added the SIZE command. 182 | - Added the MDTM (modification time) command. 183 | 184 | 2001-04-20, Shane Kerr 185 | - Moved SIGPIPE ignore to run even in --nodetach mode. Needed to avoid 186 | unwanted signals on TCP disconnects. 187 | 188 | 2001-04-19, Shane Kerr 189 | - Added more logging: all client messages are logged with address and 190 | port, and all server responses are also logged. This occurs at DEBUG 191 | level, as it is probably not normally of interest. 192 | 193 | 2001-04-18, Shane Kerr 194 | - Version 0.3.5 released. 195 | - Changed resume code to reset offset to 0 after each file transfer. 196 | - Set accept() socket to NONBLOCK to insure the listener doesn't get 197 | stuck waiting for a connection. This is documented in the NOTES 198 | section for the accept() call in Linux. 199 | - Added sendfile() support on Linux systems. 200 | 201 | 2001-04-14, Shane Kerr 202 | - Fixed a bug caused by parsing EPRT commands. The server doesn't 203 | actually allow these commands, but it bravely attempts to parse them 204 | anyway. A special shout out to Anders Nordby for finding this. The 205 | error caused the server to terminate on an assert() fail, which is 206 | good because no server corruption happened, but it's bad because the 207 | server terminated without any indication. Therefore.... 208 | - Changed most assert() calls to daemon_assert() calls. These terminate 209 | the application as before, but log to syslog and STDERR. 210 | - Added ability to run as a non-daemon process. This will allow the 211 | server to be started from init, for instance. 212 | - Added support for SIGTERM and SIGINT. If one of these is received, 213 | then the server closes the port 21 socket - meaning that it will 214 | accept no further connections. When all existing connections have 215 | closed, then the server exits. A new server may be started before 216 | this occurs, to handle new connections. This allows very short 217 | downtimes: 218 | # killall oftpd; sleep 1; /etc/init.d/oftpd start 219 | This will result in a 1 second period of time where no new connections 220 | are accepted, with no existing connections closed. It's probably best 221 | to wait for this second, to allow the signal time to arrive. :) 222 | 223 | 2001-04-08, Shane Kerr 224 | - Changed check for '/' in path - was improperly allowing escaped '/' 225 | characters through the check! 226 | - Set TCP_NODELAY flag on socket - much reduced latency on control 227 | channel for high-speed connections. 228 | - New and improved init script from Eric Jensen. 229 | 230 | 2001-04-07, Anders Nordby 231 | - FreeBSD port. 232 | 233 | 2001-04-03, Shane Kerr 234 | - Version 0.3.4 released. 235 | - Changed FTP listener code to attempt to continue processing when 236 | formerly fatal errors occur. 237 | - Added man page. 238 | - Added Red Hat init script donated by Eric Jensen to release. 239 | 240 | 2001-03-28, Shane Kerr 241 | - Version 0.3.3 released. 242 | - Use IP address and port of the control connection port for default 243 | Data Transfer Port, as defined by RFC 959. Note that the default 244 | server process DTP is never used in oftpd, but that's not a problem as 245 | I read the RFC. 246 | - Use config.h 247 | 248 | 2001-03-27, Matthew Danish 249 | - configure.in and af_portability.h fixes to allow compilation on 250 | systems with both RFC 2553 and XNET formats 251 | 252 | 2001-03-27, Shane Kerr 253 | - Fixed bug where wrong server address was used on initalization of 254 | ftp_server class. Passive probably didn't work in the 0.3.x series 255 | for IPv4! 256 | 257 | 2001-03-26, Shane Kerr 258 | - Version 0.3.2 released. 259 | - Minor additions for better error reporting on error creating threads 260 | in listener, as well as checking success on setgid() and setuid(). I 261 | can't believe I wasn't!!! 262 | - Set syslog() to use FTP_DAEMON 263 | 264 | 2001-03-23, Shane Kerr 265 | - Fixed configure bug on Debian systems. Special thanks to Matthew 266 | Danish for helping me on this one. 267 | 268 | 2001-03-20, Matthew Danish 269 | - Clean up parsing of FTP address in ftp_listener.c 270 | - Added options to specify interface and maximum number of client 271 | connections at startup. 272 | - Added --long-style command-line options. 273 | 274 | 2001-03-21, Shane Kerr 275 | - The "Spring Fever" edition, version 0.3.1 released. 276 | 277 | Added workaround for the evil glob() denial-of-service attack. I 278 | chose the simple method of preventing file listing with both path 279 | separator and wildcards. Either is okay, but not both. I don't 280 | expect this to cause problems with legitimate use, but I could be 281 | wrong. It was much simpler than my other thought. :) 282 | 283 | Changed code so that ECONNABORTED and ECONNRESET errors don't increase 284 | the error count. This is to prevent a malicious client from taking a 285 | server down by sending a large number of connection requests that it 286 | then aborts. 287 | 288 | Integrated code from Matthew Danish that 289 | allows the user to specify the port to use from the command line. 290 | Thanks! 291 | 292 | Warnings from 0.3.0 still apply. Use with caution. Spring is in the 293 | air, after all. 294 | 295 | 2001-03-20, Shane Kerr 296 | - Version 0.3.0 released. This is almost guaranteed not to work, as 297 | I've not tested it with clients that support the new features. But I 298 | decided that after a year, I wasn't living up to the "release early, 299 | release often" motto of open source. 300 | 301 | In addition to working in the changes since the last release, I've 302 | done: 303 | 304 | o removed free list from ftp_listener; simple is better 305 | o use a random port rather than incrememting sequence; useful for 306 | preventing classes of data hijacking attacks 307 | 308 | There is a potential DOS attack against all versions, see the BUGS 309 | file. This will be fixed ASAP (hopefully tomorrow). 310 | 311 | 2001-02-22, Matthew Danish 312 | - Fix for missing check in configure.in 313 | 314 | 2000-12-13, Mauro Tortonesi 315 | - Added IPv6 support (EPRT & EPSV - RFC2428) 316 | - Added LPRT and LPSV support (RFC1639) 317 | 318 | 2000-08-25, Shane Kerr 319 | - Fixed telnet module to properly handle telnet escape sequences (since 320 | no clients actually use this, it wasn't really a problem) 321 | 322 | 2000-06-01, Shane Kerr 323 | - Beau Kuiper, author of muddleftpd, sent me a fix for the Netscape 324 | timing bug. Thanks!!! 325 | 326 | 2000-04-03, Shane Kerr 327 | - Version 0.2.0 released 328 | 329 | 2000-03-30, Shane Kerr 330 | - Changed README send to only send on directory change if you actually 331 | change to a different directory. That is, "CWD ." does not send a 332 | README file. The reason for this is that many clients perform a 333 | "CWD /" as soon as it connects, which cause the README to be sent 334 | twice - yuck! 335 | - Added missing check for closed file descriptor in write_fully(). 336 | - Converted use of FILE pointers to file descriptors. This wasn't done 337 | in the most efficient possible method (i.e. with buffers), but it does 338 | remove the maximum FILE limitation from the server. 339 | 340 | 2000-03-29, Shane Kerr 341 | - Added -D_REENTRANT flag to compile options. 342 | - Added an error data type to return details about errors that occur 343 | in module initialization. 344 | - Changed telnet module to never drop characters on outbound, even if 345 | we have a ton of DO and WILL commands from the other end. 346 | 347 | 2000-03-27, Shane Kerr 348 | - Fixed bug in telnet code when sending "" string. 349 | - Now sends README to client if file is in directory on connect 350 | or directory change. 351 | - Simplified watchdog a bit by adding pointer to watchdog in the 352 | watched structure. 353 | - Wrapped invariant() methods with #ifndef NDEBUG so they won't 354 | get compiled in non-debug code (in case there's ever a 355 | non-debug version) 356 | 357 | 2000-03-22, Shane Kerr 358 | - Changed NLST and LIST to silently drop a file specification that 359 | starts with a '-'. This will ignore attempts to pass an argument 360 | to "ls" that some clients try - it probably won't do what they 361 | expect, but at least they'll get a list of files. 362 | 363 | 2000-03-19, Shane Kerr 364 | - Set file descriptors 0, 1, and 2 to go to "/dev/null", so that 365 | any error messages sent by, say, the kernel don't accidentally 366 | go to a user. 367 | - Changed watchdog to have a single thread to watch all connections, 368 | rather than a thread per connection. This is considerably more 369 | complex, but effectively doubles the number of connections that 370 | can be supported (due to thread limits). 371 | 372 | 2000-03-16, Shane Kerr 373 | - Added support for REST command. (Note that this is *not* the REST 374 | exactly as described by RFC. The REST in the RFC only applies to 375 | block or compressed mode transfer, which oftpd does not currently 376 | support. However, it appears that Unix systems interpret the 377 | parameter to the REST command as the offset into the file to resume 378 | from. 379 | 380 | 2000-03-13, Shane Kerr 381 | - Version 0.1.3 released 382 | 383 | 2000-03-12, Shane Kerr 384 | - Fixed bug when connection limit reached 385 | - Fixed bug when attempt to bind() an already bound port 386 | 387 | 2000-03-12, Shane Kerr 388 | - Version 0.1.2 released 389 | 390 | 2000-03-11, Shane Kerr 391 | - Move configuration values into oftpd.h 392 | - Wrap source to 80 columns 393 | - Fix pthread_create() error check (code had incorrectly used errno 394 | rather than the return of pthread_create() to determine error) 395 | - Fix threads to run in detached mode (when appropriate) 396 | - Solaris port completed 397 | - Support for STOR added (reply with 553 error) 398 | - Added free list for per-thread information structures. 399 | 400 | -------------------------------------------------------------------------------- /src/telnet_session.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #include 6 | 7 | #if TIME_WITH_SYS_TIME 8 | # include 9 | # include 10 | #else 11 | # if HAVE_SYS_TIME_H 12 | # include 13 | # else 14 | # include 15 | # endif 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "daemon_assert.h" 24 | #include "telnet_session.h" 25 | 26 | /* characters to process */ 27 | #define SE 240 28 | #define NOP 241 29 | #define DATA_MARK 242 30 | #define BRK 243 31 | #define IP 244 32 | #define AO 245 33 | #define AYT 246 34 | #define EC 247 35 | #define EL 248 36 | #define GA 249 37 | #define SB 250 38 | #define WILL 251 39 | #define WONT 252 40 | #define DO 253 41 | #define DONT 254 42 | #define IAC 255 43 | 44 | /* input states */ 45 | #define NORMAL 0 46 | #define GOT_IAC 1 47 | #define GOT_WILL 2 48 | #define GOT_WONT 3 49 | #define GOT_DO 4 50 | #define GOT_DONT 5 51 | #define GOT_CR 6 52 | 53 | /* prototypes */ 54 | static int invariant(const telnet_session_t *t); 55 | static void process_data(telnet_session_t *t, int wait_flag); 56 | static void read_incoming_data(telnet_session_t *t); 57 | static void process_incoming_char(telnet_session_t *t, int c); 58 | static void add_incoming_char(telnet_session_t *t, int c); 59 | static int use_incoming_char(telnet_session_t *t); 60 | static void write_outgoing_data(telnet_session_t *t); 61 | static void add_outgoing_char(telnet_session_t *t, int c); 62 | static int max_input_read(telnet_session_t *t); 63 | 64 | /* initialize a telnet session */ 65 | void telnet_session_init(telnet_session_t *t, int in, int out) 66 | { 67 | daemon_assert(t != NULL); 68 | daemon_assert(in >= 0); 69 | daemon_assert(out >= 0); 70 | 71 | t->in_fd = in; 72 | t->in_errno = 0; 73 | t->in_eof = 0; 74 | t->in_take = t->in_add = 0; 75 | t->in_buflen = 0; 76 | 77 | t->in_status = NORMAL; 78 | 79 | t->out_fd = out; 80 | t->out_errno = 0; 81 | t->out_eof = 0; 82 | t->out_take = t->out_add = 0; 83 | t->out_buflen = 0; 84 | 85 | process_data(t, 0); 86 | 87 | daemon_assert(invariant(t)); 88 | } 89 | 90 | /* print output */ 91 | static int do_telnet_session_print(telnet_session_t *t, 92 | const char *s0, 93 | const char *s, 94 | const char *s2) 95 | { 96 | int len, len0, len2; 97 | int amt_printed, amt_printed0, amt_printed2; 98 | 99 | daemon_assert(invariant(t)); 100 | 101 | len = strlen(s); 102 | if (len == 0) { 103 | daemon_assert(invariant(t)); 104 | return 1; 105 | } 106 | len0 = strlen(s0); 107 | len2 = strlen(s2); 108 | 109 | amt_printed = amt_printed0 = amt_printed2 = 0; 110 | do { 111 | if ((t->out_errno != 0) || (t->out_eof != 0)) { 112 | daemon_assert(invariant(t)); 113 | return 0; 114 | } 115 | while ((amt_printed0 < len0) && (t->out_buflen < BUF_LEN)) 116 | { 117 | daemon_assert(s0[amt_printed0] != '\0'); 118 | add_outgoing_char(t, s0[amt_printed0]); 119 | amt_printed0++; 120 | } 121 | if (amt_printed0 == len0) 122 | { 123 | while ((amt_printed < len) && (t->out_buflen < BUF_LEN)) 124 | { 125 | daemon_assert(s[amt_printed] != '\0'); 126 | add_outgoing_char(t, s[amt_printed]); 127 | amt_printed++; 128 | } 129 | if (amt_printed == len) 130 | { 131 | while ((amt_printed2 < len2) && (t->out_buflen < BUF_LEN)) 132 | { 133 | daemon_assert(s2[amt_printed2] != '\0'); 134 | add_outgoing_char(t, s2[amt_printed2]); 135 | amt_printed2++; 136 | } 137 | } 138 | } 139 | process_data(t, 1); 140 | } while (amt_printed < len && amt_printed2 < len2); 141 | 142 | while (t->out_buflen > 0) { 143 | if ((t->out_errno != 0) || (t->out_eof != 0)) { 144 | daemon_assert(invariant(t)); 145 | return 0; 146 | } 147 | process_data(t, 1); 148 | } 149 | 150 | daemon_assert(invariant(t)); 151 | return 1; 152 | } 153 | 154 | 155 | /* print output */ 156 | int telnet_session_print(telnet_session_t *t, const char *s) 157 | { 158 | return do_telnet_session_print (t, "", s, ""); 159 | } 160 | 161 | int telnet_session_print_with_prefix(telnet_session_t *t, 162 | const char *prefix, const char *s) 163 | { 164 | return do_telnet_session_print (t, prefix, s, ""); 165 | } 166 | 167 | 168 | /* print a line output */ 169 | int telnet_session_println(telnet_session_t *t, const char *s) 170 | { 171 | daemon_assert(invariant(t)); 172 | if (!do_telnet_session_print(t, "", s, "\015\012")) { 173 | daemon_assert(invariant(t)); 174 | return 0; 175 | } 176 | daemon_assert(invariant(t)); 177 | return 1; 178 | } 179 | 180 | /* print a line output */ 181 | int telnet_session_println_with_prefix(telnet_session_t *t, 182 | const char *prefix, const char *s) 183 | { 184 | daemon_assert(invariant(t)); 185 | if (!do_telnet_session_print(t, prefix, s, "\015\012")) { 186 | daemon_assert(invariant(t)); 187 | return 0; 188 | } 189 | daemon_assert(invariant(t)); 190 | return 1; 191 | } 192 | 193 | /* read a line */ 194 | int telnet_session_readln(telnet_session_t *t, char *buf, int buflen) 195 | { 196 | int amt_read; 197 | 198 | daemon_assert(invariant(t)); 199 | amt_read = 0; 200 | for (;;) { 201 | if ((t->in_errno != 0) || (t->in_eof != 0)) { 202 | daemon_assert(invariant(t)); 203 | return 0; 204 | } 205 | while (t->in_buflen > 0) { 206 | if (amt_read == buflen-1) { 207 | buf[amt_read] = '\0'; 208 | daemon_assert(invariant(t)); 209 | return 1; 210 | } 211 | daemon_assert(amt_read >= 0); 212 | daemon_assert(amt_read < buflen); 213 | buf[amt_read] = use_incoming_char(t); 214 | if (buf[amt_read] == '\012') { 215 | daemon_assert(amt_read+1 >= 0); 216 | daemon_assert(amt_read+1 < buflen); 217 | buf[amt_read+1] = '\0'; 218 | daemon_assert(invariant(t)); 219 | return 1; 220 | } 221 | amt_read++; 222 | } 223 | process_data(t, 1); 224 | } 225 | } 226 | 227 | void telnet_session_destroy(telnet_session_t *t) 228 | { 229 | daemon_assert(invariant(t)); 230 | 231 | close(t->in_fd); 232 | if (t->out_fd != t->in_fd) { 233 | close(t->out_fd); 234 | } 235 | t->in_fd = -1; 236 | t->out_fd = -1; 237 | } 238 | 239 | /* receive any incoming data, send any pending data */ 240 | static void process_data(telnet_session_t *t, int wait_flag) 241 | { 242 | fd_set readfds; 243 | fd_set writefds; 244 | fd_set exceptfds; 245 | struct timeval tv_zero; 246 | struct timeval *tv; 247 | int state; 248 | 249 | /* set up our select() variables */ 250 | FD_ZERO(&readfds); 251 | FD_ZERO(&writefds); 252 | FD_ZERO(&exceptfds); 253 | if (wait_flag) { 254 | tv = NULL; 255 | } else { 256 | tv_zero.tv_sec = 0; 257 | tv_zero.tv_usec = 0; 258 | tv = &tv_zero; 259 | } 260 | 261 | /* only check for input if we can accept input */ 262 | if ((t->in_errno == 0) && 263 | (t->in_eof == 0) && 264 | (max_input_read(t) > 0)) 265 | { 266 | FD_SET(t->in_fd, &readfds); 267 | FD_SET(t->in_fd, &exceptfds); 268 | } 269 | 270 | /* only check for output if we have pending output */ 271 | if ((t->out_errno == 0) && (t->out_eof == 0) && (t->out_buflen > 0)) { 272 | FD_SET(t->out_fd, &writefds); 273 | FD_SET(t->out_fd, &exceptfds); 274 | } 275 | 276 | pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &state); 277 | 278 | /* see if there's anything to do */ 279 | if (select(FD_SETSIZE, &readfds, &writefds, &exceptfds, tv) > 0) { 280 | 281 | if (FD_ISSET(t->in_fd, &exceptfds)) { 282 | t->in_eof = 1; 283 | } else if (FD_ISSET(t->in_fd, &readfds)) { 284 | read_incoming_data(t); 285 | } 286 | 287 | if (FD_ISSET(t->out_fd, &exceptfds)) { 288 | t->out_eof = 1; 289 | } else if (FD_ISSET(t->out_fd, &writefds)) { 290 | write_outgoing_data(t); 291 | } 292 | } 293 | 294 | pthread_setcancelstate (state, NULL); 295 | } 296 | 297 | static void read_incoming_data(telnet_session_t *t) 298 | { 299 | size_t read_ret; 300 | char buf[BUF_LEN]; 301 | size_t i; 302 | 303 | /* read as much data as we have buffer space for */ 304 | daemon_assert(max_input_read(t) <= BUF_LEN); 305 | read_ret = read(t->in_fd, buf, max_input_read(t)); 306 | 307 | /* handle three possible read results */ 308 | if (read_ret == -1) { 309 | t->in_errno = errno; 310 | } else if (read_ret == 0) { 311 | t->in_eof = 1; 312 | } else { 313 | for (i=0; iin_status) { 324 | case GOT_IAC: 325 | switch (c) { 326 | case WILL: 327 | t->in_status = GOT_WILL; 328 | break; 329 | case WONT: 330 | t->in_status = GOT_WONT; 331 | break; 332 | case DO: 333 | t->in_status = GOT_DO; 334 | break; 335 | case DONT: 336 | t->in_status = GOT_DONT; 337 | break; 338 | case IAC: 339 | add_incoming_char(t, IAC); 340 | t->in_status = NORMAL; 341 | break; 342 | default: 343 | t->in_status = NORMAL; 344 | } 345 | break; 346 | case GOT_WILL: 347 | add_outgoing_char(t, IAC); 348 | add_outgoing_char(t, DONT); 349 | add_outgoing_char(t, c); 350 | t->in_status = NORMAL; 351 | break; 352 | case GOT_WONT: 353 | t->in_status = NORMAL; 354 | break; 355 | case GOT_DO: 356 | add_outgoing_char(t, IAC); 357 | add_outgoing_char(t, WONT); 358 | add_outgoing_char(t, c); 359 | t->in_status = NORMAL; 360 | break; 361 | case GOT_DONT: 362 | t->in_status = NORMAL; 363 | break; 364 | case GOT_CR: 365 | add_incoming_char(t, '\012'); 366 | if (c != '\012') { 367 | add_incoming_char(t, c); 368 | } 369 | t->in_status = NORMAL; 370 | break; 371 | case NORMAL: 372 | if (c == IAC) { 373 | t->in_status = GOT_IAC; 374 | } else if (c == '\015') { 375 | t->in_status = GOT_CR; 376 | } else { 377 | add_incoming_char(t, c); 378 | } 379 | } 380 | } 381 | 382 | /* add a single character, wrapping buffer if necessary (should never occur) */ 383 | static void add_incoming_char(telnet_session_t *t, int c) 384 | { 385 | daemon_assert(t->in_add >= 0); 386 | daemon_assert(t->in_add < BUF_LEN); 387 | t->in_buf[t->in_add] = c; 388 | t->in_add++; 389 | if (t->in_add == BUF_LEN) { 390 | t->in_add = 0; 391 | } 392 | if (t->in_buflen == BUF_LEN) { 393 | t->in_take++; 394 | if (t->in_take == BUF_LEN) { 395 | t->in_take = 0; 396 | } 397 | } else { 398 | t->in_buflen++; 399 | } 400 | } 401 | 402 | /* remove a single character */ 403 | static int use_incoming_char(telnet_session_t *t) 404 | { 405 | int c; 406 | 407 | daemon_assert(t->in_take >= 0); 408 | daemon_assert(t->in_take < BUF_LEN); 409 | c = t->in_buf[t->in_take]; 410 | t->in_take++; 411 | if (t->in_take == BUF_LEN) { 412 | t->in_take = 0; 413 | } 414 | t->in_buflen--; 415 | 416 | return c; 417 | } 418 | 419 | /* add a single character, hopefully will never happen :) */ 420 | static void add_outgoing_char(telnet_session_t *t, int c) 421 | { 422 | daemon_assert(t->out_add >= 0); 423 | daemon_assert(t->out_add < BUF_LEN); 424 | t->out_buf[t->out_add] = c; 425 | t->out_add++; 426 | if (t->out_add == BUF_LEN) { 427 | t->out_add = 0; 428 | } 429 | if (t->out_buflen == BUF_LEN) { 430 | t->out_take++; 431 | if (t->out_take == BUF_LEN) { 432 | t->out_take = 0; 433 | } 434 | } else { 435 | t->out_buflen++; 436 | } 437 | } 438 | 439 | 440 | static void write_outgoing_data(telnet_session_t *t) 441 | { 442 | size_t write_ret; 443 | 444 | if (t->out_take < t->out_add) { 445 | /* handle a buffer that looks like this: */ 446 | /* |-- empty --|-- data --|-- empty --| */ 447 | daemon_assert(t->out_take >= 0); 448 | daemon_assert(t->out_take < BUF_LEN); 449 | daemon_assert(t->out_buflen > 0); 450 | daemon_assert(t->out_buflen + t->out_take <= BUF_LEN); 451 | write_ret = write(t->out_fd, t->out_buf + t->out_take, t->out_buflen); 452 | } else { 453 | /* handle a buffer that looks like this: */ 454 | /* |-- data --|-- empty --|-- data --| */ 455 | daemon_assert(t->out_take >= 0); 456 | daemon_assert(t->out_take < BUF_LEN); 457 | daemon_assert((BUF_LEN - t->out_take) > 0); 458 | write_ret = write(t->out_fd, 459 | t->out_buf + t->out_take, 460 | BUF_LEN - t->out_take); 461 | } 462 | 463 | /* handle three possible write results */ 464 | if (write_ret == -1) { 465 | t->out_errno = errno; 466 | } else if (write_ret == 0) { 467 | t->out_eof = 1; 468 | } else { 469 | t->out_buflen -= write_ret; 470 | t->out_take += write_ret; 471 | if (t->out_take >= BUF_LEN) { 472 | t->out_take -= BUF_LEN; 473 | } 474 | } 475 | } 476 | 477 | /* return the amount of a read */ 478 | static int max_input_read(telnet_session_t *t) 479 | { 480 | int max_in; 481 | int max_out; 482 | 483 | daemon_assert(invariant(t)); 484 | 485 | /* figure out how much space is available in the input buffer */ 486 | if (t->in_buflen < BUF_LEN) { 487 | max_in = BUF_LEN - t->in_buflen; 488 | } else { 489 | max_in = 0; 490 | } 491 | 492 | /* worry about space in the output buffer (for DONT/WONT replies) */ 493 | if (t->out_buflen < BUF_LEN) { 494 | max_out = BUF_LEN - t->out_buflen; 495 | } else { 496 | max_out = 0; 497 | } 498 | 499 | daemon_assert(invariant(t)); 500 | 501 | /* return the minimum of the two values */ 502 | return (max_in < max_out) ? max_in : max_out; 503 | } 504 | 505 | #ifndef NDEBUG 506 | static int invariant(const telnet_session_t *t) 507 | { 508 | if (t == NULL) { 509 | return 0; 510 | } 511 | if (t->in_fd < 0) { 512 | return 0; 513 | } 514 | if ((t->in_take < 0) || (t->in_take >= BUF_LEN)) { 515 | return 0; 516 | } 517 | if ((t->in_add < 0) || (t->in_add >= BUF_LEN)) { 518 | return 0; 519 | } 520 | if ((t->in_buflen < 0) || (t->in_buflen > BUF_LEN)) { 521 | return 0; 522 | } 523 | 524 | switch (t->in_status) { 525 | case NORMAL: 526 | break; 527 | case GOT_IAC: 528 | break; 529 | case GOT_WILL: 530 | break; 531 | case GOT_WONT: 532 | break; 533 | case GOT_DO: 534 | break; 535 | case GOT_DONT: 536 | break; 537 | case GOT_CR: 538 | break; 539 | default: 540 | return 0; 541 | } 542 | 543 | if (t->out_fd < 0) { 544 | return 0; 545 | } 546 | if ((t->out_take < 0) || (t->out_take >= BUF_LEN)) { 547 | return 0; 548 | } 549 | if ((t->out_add < 0) || (t->out_add >= BUF_LEN)) { 550 | return 0; 551 | } 552 | if ((t->out_buflen < 0) || (t->out_buflen > BUF_LEN)) { 553 | return 0; 554 | } 555 | 556 | return 1; 557 | } 558 | #endif /* NDEBUG */ 559 | 560 | #ifdef STUB_TEST 561 | #include 562 | #include 563 | #include 564 | #include 565 | 566 | int main() 567 | { 568 | telnet_session_t t; 569 | char buf[64]; 570 | int fd; 571 | int val; 572 | struct sockaddr_in addr; 573 | int newfd; 574 | struct sockaddr_in newaddr; 575 | unsigned newaddrlen; 576 | int i; 577 | 578 | daemon_assert((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1); 579 | val = 1; 580 | daemon_assert(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) 581 | == 0); 582 | addr.sin_port = htons(23); 583 | addr.sin_addr.s_addr = INADDR_ANY; 584 | daemon_assert(bind(fd, &addr, sizeof(addr)) == 0); 585 | daemon_assert(listen(fd, SOMAXCONN) == 0); 586 | 587 | signal(SIGPIPE, SIG_IGN); 588 | while ((newfd = accept(fd, &newaddr, &newaddrlen)) >= 0) { 589 | telnet_session_init(&t, newfd, newfd); 590 | telnet_session_print(&t, "Password:"); 591 | telnet_session_readln(&t, buf, sizeof(buf)); 592 | for (i=0; buf[i] != '\0'; i++) { 593 | printf("[%02d]: 0x%02X ", i, buf[i] & 0xFF); 594 | if (isprint(buf[i])) { 595 | printf("'%c'\n", buf[i]); 596 | } else { 597 | printf("'\\x%02X'\n", buf[i] & 0xFF); 598 | } 599 | } 600 | telnet_session_println(&t, "Hello, world."); 601 | telnet_session_println(&t, "Ipso, facto"); 602 | telnet_session_println(&t, "quid pro quo"); 603 | sleep(1); 604 | close(newfd); 605 | } 606 | return 0; 607 | } 608 | #endif /* STUB_TEST */ 609 | 610 | -------------------------------------------------------------------------------- /src/ftp_command.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "ftp_command.h" 15 | #include "af_portability.h" 16 | #include "daemon_assert.h" 17 | 18 | /* argument types */ 19 | #define ARG_NONE 0 20 | #define ARG_STRING 1 21 | #define ARG_OPTIONAL_STRING 2 22 | #define ARG_HOST_PORT 3 23 | #define ARG_TYPE 4 24 | #define ARG_STRUCTURE 5 25 | #define ARG_MODE 6 26 | #define ARG_OFFSET 7 27 | #define ARG_HOST_PORT_LONG 8 28 | #define ARG_HOST_PORT_EXT 9 29 | #define ARG_OPTIONAL_NUMBER 10 30 | 31 | /* our FTP commands */ 32 | struct { 33 | char *name; 34 | int arg_type; 35 | } command_def[] = { 36 | { "USER", ARG_STRING }, 37 | { "PASS", ARG_STRING }, 38 | { "CWD", ARG_STRING }, 39 | { "CDUP", ARG_NONE }, 40 | { "QUIT", ARG_NONE }, 41 | { "PORT", ARG_HOST_PORT }, 42 | { "LPRT", ARG_HOST_PORT_LONG }, 43 | { "EPRT", ARG_HOST_PORT_EXT }, 44 | { "PASV", ARG_NONE }, 45 | { "LPSV", ARG_NONE }, 46 | { "EPSV", ARG_OPTIONAL_NUMBER }, 47 | { "TYPE", ARG_TYPE }, 48 | { "STRU", ARG_STRUCTURE }, 49 | { "MODE", ARG_MODE }, 50 | { "RETR", ARG_STRING }, 51 | { "STOR", ARG_STRING }, 52 | { "PWD", ARG_NONE }, 53 | { "LIST", ARG_OPTIONAL_STRING }, 54 | { "NLST", ARG_OPTIONAL_STRING }, 55 | { "SYST", ARG_NONE }, 56 | { "HELP", ARG_OPTIONAL_STRING }, 57 | { "NOOP", ARG_NONE }, 58 | { "REST", ARG_OFFSET }, 59 | { "SIZE", ARG_STRING }, 60 | { "MDTM", ARG_STRING } 61 | }; 62 | 63 | #define NUM_COMMAND (sizeof(command_def) / sizeof(command_def[0])) 64 | 65 | /* prototypes */ 66 | static const char *copy_string(char *dst, const char *src); 67 | static const char *parse_host_port(struct sockaddr_in *addr, const char *s); 68 | static const char *parse_number(int *num, const char *s, int max_num); 69 | static const char *parse_offset(off_t *ofs, const char *s); 70 | static const char *parse_host_port_long(sockaddr_storage_t *sa, const char *s); 71 | static const char *parse_host_port_ext(sockaddr_storage_t *sa, const char *s); 72 | 73 | int ftp_command_parse(const char *input, ftp_command_t *cmd) 74 | { 75 | int i; 76 | int len; 77 | int match; 78 | ftp_command_t tmp; 79 | int c; 80 | const char *optional_number; 81 | 82 | daemon_assert(input != NULL); 83 | daemon_assert(cmd != NULL); 84 | 85 | /* see if our input starts with a valid command */ 86 | match = -1; 87 | for (i=0; (i= 0); 99 | daemon_assert(match < NUM_COMMAND); 100 | 101 | /* copy our command */ 102 | strcpy(tmp.command, command_def[match].name); 103 | 104 | /* advance input past the command */ 105 | input += strlen(command_def[match].name); 106 | 107 | /* now act based on the command */ 108 | switch (command_def[match].arg_type) { 109 | 110 | case ARG_NONE: 111 | tmp.num_arg = 0; 112 | break; 113 | 114 | case ARG_STRING: 115 | if (*input != ' ') { 116 | return 0; 117 | } 118 | ++input; 119 | input = copy_string(tmp.arg[0].string, input); 120 | tmp.num_arg = 1; 121 | break; 122 | 123 | case ARG_OPTIONAL_STRING: 124 | if (*input == ' ') { 125 | ++input; 126 | input = copy_string(tmp.arg[0].string, input); 127 | tmp.num_arg = 1; 128 | } else { 129 | tmp.num_arg = 0; 130 | } 131 | break; 132 | 133 | case ARG_HOST_PORT: 134 | if (*input != ' ') { 135 | return 0; 136 | } 137 | input++; 138 | 139 | /* parse the host & port information (if any) */ 140 | input = parse_host_port(&tmp.arg[0].host_port, input); 141 | if (input == NULL) { 142 | return 0; 143 | } 144 | tmp.num_arg = 1; 145 | break; 146 | 147 | case ARG_HOST_PORT_LONG: 148 | if (*input != ' ') { 149 | return 0; 150 | } 151 | input++; 152 | 153 | /* parse the host & port information (if any) */ 154 | input = parse_host_port_long(&tmp.arg[0].host_port, input); 155 | if (input == NULL) { 156 | return 0; 157 | } 158 | tmp.num_arg = 1; 159 | break; 160 | 161 | case ARG_HOST_PORT_EXT: 162 | if (*input != ' ') { 163 | return 0; 164 | } 165 | input++; 166 | 167 | /* parse the host & port information (if any) */ 168 | input = parse_host_port_ext(&tmp.arg[0].host_port, input); 169 | if (input == NULL) { 170 | return 0; 171 | } 172 | tmp.num_arg = 1; 173 | break; 174 | 175 | /* the optional number may also be "ALL" */ 176 | case ARG_OPTIONAL_NUMBER: 177 | if (*input == ' ') { 178 | ++input; 179 | optional_number = parse_number(&tmp.arg[0].num, input, 255); 180 | if (optional_number != NULL) { 181 | input = optional_number; 182 | } else { 183 | if ((tolower(input[0]) == 'a') && 184 | (tolower(input[1]) == 'l') && 185 | (tolower(input[2]) == 'l')) 186 | { 187 | tmp.arg[0].num = EPSV_ALL; 188 | input += 3; 189 | } else { 190 | return 0; 191 | } 192 | } 193 | tmp.num_arg = 1; 194 | } else { 195 | tmp.num_arg = 0; 196 | } 197 | break; 198 | 199 | case ARG_TYPE: 200 | if (*input != ' ') { 201 | return 0; 202 | } 203 | input++; 204 | 205 | c = toupper(*input); 206 | if ((c == 'A') || (c == 'E')) { 207 | tmp.arg[0].string[0] = c; 208 | tmp.arg[0].string[1] = '\0'; 209 | input++; 210 | 211 | if (*input == ' ') { 212 | input++; 213 | c = toupper(*input); 214 | if ((c != 'N') && (c != 'T') && (c != 'C')) { 215 | return 0; 216 | } 217 | tmp.arg[1].string[0] = c; 218 | tmp.arg[1].string[1] = '\0'; 219 | input++; 220 | tmp.num_arg = 2; 221 | } else { 222 | tmp.num_arg = 1; 223 | } 224 | } else if (c == 'I') { 225 | tmp.arg[0].string[0] = 'I'; 226 | tmp.arg[0].string[1] = '\0'; 227 | input++; 228 | tmp.num_arg = 1; 229 | } else if (c == 'L') { 230 | tmp.arg[0].string[0] = 'L'; 231 | tmp.arg[0].string[1] = '\0'; 232 | input++; 233 | input = parse_number(&tmp.arg[1].num, input, 255); 234 | if (input == NULL) { 235 | return 0; 236 | } 237 | tmp.num_arg = 2; 238 | } else { 239 | return 0; 240 | } 241 | 242 | break; 243 | 244 | case ARG_STRUCTURE: 245 | if (*input != ' ') { 246 | return 0; 247 | } 248 | input++; 249 | 250 | c = toupper(*input); 251 | if ((c != 'F') && (c != 'R') && (c != 'P')) { 252 | return 0; 253 | } 254 | input++; 255 | tmp.arg[0].string[0] = c; 256 | tmp.arg[0].string[1] = '\0'; 257 | tmp.num_arg = 1; 258 | break; 259 | 260 | case ARG_MODE: 261 | if (*input != ' ') { 262 | return 0; 263 | } 264 | input++; 265 | 266 | c = toupper(*input); 267 | if ((c != 'S') && (c != 'B') && (c != 'C')) { 268 | return 0; 269 | } 270 | input++; 271 | tmp.arg[0].string[0] = c; 272 | tmp.arg[0].string[1] = '\0'; 273 | tmp.num_arg = 1; 274 | break; 275 | 276 | case ARG_OFFSET: 277 | if (*input != ' ') { 278 | return 0; 279 | } 280 | input++; 281 | input = parse_offset(&tmp.arg[0].offset, input); 282 | if (input == NULL) { 283 | return 0; 284 | } 285 | tmp.num_arg = 1; 286 | break; 287 | 288 | default: 289 | daemon_assert(0); 290 | } 291 | 292 | /* check for our ending newline */ 293 | if (*input != '\n') { 294 | return 0; 295 | } 296 | 297 | /* return our result */ 298 | *cmd = tmp; 299 | return 1; 300 | } 301 | 302 | /* copy a string terminated with a newline */ 303 | static const char *copy_string(char *dst, const char *src) 304 | { 305 | int i; 306 | 307 | daemon_assert(dst != NULL); 308 | daemon_assert(src != NULL); 309 | 310 | for (i=0; (i<=MAX_STRING_LEN) && (src[i]!='\0') && (src[i]!='\n'); i++) { 311 | dst[i] = src[i]; 312 | } 313 | dst[i] = '\0'; 314 | 315 | return src+i; 316 | } 317 | 318 | 319 | static const char *parse_host_port(struct sockaddr_in *addr, const char *s) 320 | { 321 | int i; 322 | int octets[6]; 323 | char addr_str[16]; 324 | int port; 325 | struct in_addr in_addr; 326 | 327 | daemon_assert(addr != NULL); 328 | daemon_assert(s != NULL); 329 | 330 | /* scan in 5 pairs of "#," */ 331 | for (i=0; i<5; i++) { 332 | s = parse_number(&octets[i], s, 255); 333 | if (s == NULL) { 334 | return NULL; 335 | } 336 | if (*s != ',') { 337 | return NULL; 338 | } 339 | s++; 340 | } 341 | 342 | /* scan in ending "#" */ 343 | s = parse_number(&octets[5], s, 255); 344 | if (s == NULL) { 345 | return NULL; 346 | } 347 | 348 | daemon_assert(octets[0] >= 0); 349 | daemon_assert(octets[0] <= 255); 350 | daemon_assert(octets[1] >= 0); 351 | daemon_assert(octets[1] <= 255); 352 | daemon_assert(octets[2] >= 0); 353 | daemon_assert(octets[2] <= 255); 354 | daemon_assert(octets[3] >= 0); 355 | daemon_assert(octets[3] <= 255); 356 | 357 | /* convert our number to a IP/port */ 358 | sprintf(addr_str, "%d.%d.%d.%d", 359 | octets[0], octets[1], octets[2], octets[3]); 360 | port = (octets[4] << 8) | octets[5]; 361 | #ifdef HAVE_INET_ATON 362 | if (inet_aton(addr_str, &in_addr) == 0) { 363 | return NULL; 364 | } 365 | #else 366 | in_addr.s_addr = inet_addr(addr_str); 367 | if (in_addr.s_addr == -1) { 368 | return NULL; 369 | } 370 | #endif 371 | addr->sin_family = AF_INET; 372 | addr->sin_addr = in_addr; 373 | addr->sin_port = htons(port); 374 | 375 | /* return success */ 376 | return s; 377 | } 378 | 379 | /* note: returns success even for unknown address families */ 380 | /* this is okay, as long as subsequent uses VERIFY THE FAMILY first */ 381 | static const char *parse_host_port_long(sockaddr_storage_t *sa, const char *s) 382 | { 383 | int i; 384 | int family; 385 | int tmp; 386 | int addr_len; 387 | unsigned char addr[255]; 388 | int port_len; 389 | unsigned char port[255]; 390 | 391 | /* we are family */ 392 | s = parse_number(&family, s, 255); 393 | if (s == NULL) { 394 | return NULL; 395 | } 396 | if (*s != ',') { 397 | return NULL; 398 | } 399 | s++; 400 | 401 | /* parse host length */ 402 | s = parse_number(&addr_len, s, 255); 403 | if (s == NULL) { 404 | return NULL; 405 | } 406 | if (*s != ',') { 407 | return NULL; 408 | } 409 | s++; 410 | 411 | /* parse address */ 412 | for (i=0; i 126)) { 486 | return NULL; 487 | } 488 | delimeter = *s; 489 | s++; 490 | 491 | /* get address family */ 492 | if (*s == '1') { 493 | family = AF_INET; 494 | } 495 | #ifdef INET6 496 | else if (*s == '2') { 497 | family = AF_INET6; 498 | } 499 | #endif 500 | else { 501 | return NULL; 502 | } 503 | s++; 504 | if (*s != delimeter) { 505 | return NULL; 506 | } 507 | s++; 508 | 509 | /* get address string */ 510 | p = strchr(s, delimeter); 511 | if (p == NULL) { 512 | return NULL; 513 | } 514 | len = p - s; 515 | if (len >= sizeof(addr_str)) { 516 | return NULL; 517 | } 518 | memcpy(addr_str, s, len); 519 | addr_str[len] = '\0'; 520 | s = p+1; 521 | 522 | /* get port */ 523 | s = parse_number(&port, s, 65535); 524 | if (s == NULL) { 525 | return NULL; 526 | } 527 | if (*s != delimeter) { 528 | return NULL; 529 | } 530 | s++; 531 | 532 | /* now parse the value passed */ 533 | #ifndef INET6 534 | { 535 | struct in_addr in_addr; 536 | 537 | #ifdef HAVE_INET_ATON 538 | if (inet_aton(addr_str, &in_addr) == 0) { 539 | return NULL; 540 | } 541 | #else 542 | in_addr.s_addr = inet_addr(addr_str); 543 | if (in_addr.s_addr == -1) { 544 | return NULL; 545 | } 546 | #endif /* HAVE_INET_ATON */ 547 | 548 | SIN4ADDR(sa) = in_addr; 549 | } 550 | #else 551 | { 552 | struct addrinfo hints; 553 | struct *res; 554 | 555 | memset(&hints, 0, sizeof(hints)); 556 | hints.ai_flags = AI_NUMERICHOST; 557 | hints.ai_family = family; 558 | 559 | if (getaddrinfo(addr_str, NULL, &hints, &res) != 0) { 560 | return NULL; 561 | } 562 | 563 | memcpy(sa, res->ai_addr, res->ai_addrlen); 564 | 565 | freeaddrinfo(res); 566 | } 567 | #endif /* INET6 */ 568 | 569 | SAFAM(sa) = family; 570 | SINPORT(sa) = htons(port); 571 | 572 | /* return new pointer */ 573 | return s; 574 | } 575 | 576 | /* scan the string for a number from 0 to max_num */ 577 | /* returns the next non-numberic character */ 578 | /* returns NULL if not at least one digit */ 579 | static const char *parse_number(int *num, const char *s, int max_num) 580 | { 581 | int tmp; 582 | int cur_digit; 583 | 584 | daemon_assert(s != NULL); 585 | daemon_assert(num != NULL); 586 | 587 | /* handle first character */ 588 | if (!isdigit(*s)) { 589 | return NULL; 590 | } 591 | tmp = (*s - '0'); 592 | s++; 593 | 594 | /* handle subsequent characters */ 595 | while (isdigit(*s)) { 596 | cur_digit = (*s - '0'); 597 | 598 | /* check for overflow */ 599 | if ((max_num - cur_digit) < (tmp*10)) { 600 | return NULL; 601 | } 602 | 603 | tmp *= 10; 604 | tmp += cur_digit; 605 | s++; 606 | } 607 | 608 | daemon_assert(tmp >= 0); 609 | daemon_assert(tmp <= max_num); 610 | 611 | /* return the result */ 612 | *num = tmp; 613 | return s; 614 | } 615 | 616 | static const char *parse_offset(off_t *ofs, const char *s) 617 | { 618 | off_t tmp_ofs; 619 | int cur_digit; 620 | off_t max_ofs; 621 | #if SIZEOF_UNSIGNED_LONG >= SIZEOF_OFF_T 622 | unsigned long cutoff, cutlim; 623 | #elif SIZEOF_UNSIGNED_LONG_LONG && SIZEOF_UNSIGNED_LONG_LONG >= SIZEOF_OFF_T 624 | unsigned long long cutoff, cutlim; 625 | #else 626 | # error cannot figure out a type larger or equal to off_t 627 | #endif 628 | 629 | daemon_assert(ofs != NULL); 630 | daemon_assert(s != NULL); 631 | 632 | /* calculate maximum allowable offset based on size of off_t */ 633 | #if SIZEOF_OFF_T == 8 634 | max_ofs = (off_t)9223372036854775807LL; 635 | #elif SIZEOF_OFF_T == 4 636 | max_ofs = (off_t)2147483647LL; 637 | #elif SIZEOF_OFF_T == 2 638 | max_ofs = (off_t)32767LL; 639 | #else 640 | # error sizeof(off_t) unknown 641 | #endif 642 | 643 | cutoff = max_ofs / 10; 644 | cutlim = max_ofs % 10; 645 | 646 | /* handle first character */ 647 | if (!isdigit(*s)) { 648 | return NULL; 649 | } 650 | tmp_ofs = (*s - '0'); 651 | s++; 652 | 653 | /* handle subsequent characters */ 654 | while (isdigit(*s)) { 655 | cur_digit = (*s - '0'); 656 | 657 | if (tmp_ofs > cutoff || (tmp_ofs == cutoff && cur_digit > cutlim)) 658 | return NULL; /*overflow*/ 659 | 660 | tmp_ofs *= 10; 661 | tmp_ofs += cur_digit; 662 | s++; 663 | } 664 | 665 | /* return */ 666 | *ofs = tmp_ofs; 667 | return s; 668 | } 669 | 670 | -------------------------------------------------------------------------------- /src/ftp_listener.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | $Id$ 4 | 5 | This is the code that waits for client connections and creates the 6 | threads that handle them. When ftp_listener_init() is called, it binds 7 | to the appropriate socket and sets up the other values for the 8 | structure. Then, when ftp_listener_start() is called, a thread is 9 | started dedicatd to accepting connections. 10 | 11 | This thread waits for input on two file descriptors. If input arrives 12 | on the socket, then it accepts the connection and creates a thread to 13 | handle it. If input arrives on the shutdown_request pipe, then the 14 | thread terminates. This is how ftp_listener_stop() signals the listener 15 | to end. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #if TIME_WITH_SYS_TIME 37 | # include 38 | # include 39 | #else 40 | # if HAVE_SYS_TIME_H 41 | # include 42 | # else 43 | # include 44 | # endif 45 | #endif 46 | 47 | #include "daemon_assert.h" 48 | #include "telnet_session.h" 49 | #include "ftp_session.h" 50 | #include "ftp_listener.h" 51 | #include "watchdog.h" 52 | #include "af_portability.h" 53 | 54 | 55 | 56 | /* maximum number of consecutive errors in accept() 57 | before we terminate listener */ 58 | #define MAX_ACCEPT_ERROR 10 59 | 60 | /* buffer to hold address string */ 61 | #define ADDR_BUF_LEN 100 62 | 63 | /* information for a specific connection */ 64 | typedef struct connection_info { 65 | ftp_listener_t *ftp_listener; 66 | telnet_session_t telnet_session; 67 | ftp_session_t ftp_session; 68 | watched_t watched; 69 | 70 | struct connection_info *next; 71 | } connection_info_t; 72 | 73 | /* prototypes */ 74 | static int invariant(const ftp_listener_t *f); 75 | static void *connection_acceptor(ftp_listener_t *f); 76 | static char *addr2string(const sockaddr_storage_t *s); 77 | static void *connection_handler(connection_info_t *info); 78 | static void connection_handler_cleanup(connection_info_t *info); 79 | 80 | /* initialize an FTP listener */ 81 | int ftp_listener_init(ftp_listener_t *f, 82 | char *address, 83 | int port, 84 | int max_connections, 85 | int inactivity_timeout, 86 | error_t *err) 87 | { 88 | sockaddr_storage_t sock_addr; 89 | int fd; 90 | int flags; 91 | int pipefds[2]; 92 | int reuseaddr; 93 | char dir[PATH_MAX+1]; 94 | char buf[ADDR_BUF_LEN+1]; 95 | const char *inet_ntop_ret; 96 | 97 | daemon_assert(f != NULL); 98 | daemon_assert(port >= 0); 99 | daemon_assert(port < 65536); 100 | daemon_assert(max_connections > 0); 101 | daemon_assert(err != NULL); 102 | 103 | /* get our current directory */ 104 | if (getcwd(dir, sizeof(dir)) == NULL) { 105 | char errbuf[ERRBUF_SIZE]; 106 | error_init(err, errno, "error getting current directory; %s", 107 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 108 | return 0; 109 | } 110 | 111 | /* set up our socket address */ 112 | memset(&sock_addr, 0, sizeof(sockaddr_storage_t)); 113 | 114 | if (address == NULL) { 115 | #ifdef INET6 116 | SAFAM(&sock_addr) = AF_INET6; 117 | memcpy(&SIN6ADDR(&sock_addr), &in6addr_any, sizeof(struct in6_addr)); 118 | #else 119 | SAFAM(&sock_addr) = AF_INET; 120 | sock_addr.sin_addr.s_addr = INADDR_ANY; 121 | #endif 122 | } else { 123 | int gai_err; 124 | struct addrinfo hints; 125 | struct addrinfo *res; 126 | 127 | memset(&hints, 0, sizeof(hints)); 128 | 129 | /* - This code should be able to parse both IPv4 and IPv6 internet 130 | * addresses and put them in the sock_addr variable. 131 | * - Much neater now. 132 | * - Bug: Can't handle hostnames, only IP addresses. Not sure 133 | * exactly why. But then again, I'm not sure exactly why 134 | * there is a man page for getipnodebyname (which getaddrinfo 135 | * says it uses) but no corresponding function in libc. 136 | * -- Matthew Danish [3/20/2001] 137 | */ 138 | 139 | #ifdef INET6 140 | hints.ai_family = AF_INET6; 141 | #else 142 | hints.ai_family = AF_INET; 143 | #endif 144 | 145 | hints.ai_flags = AI_PASSIVE; 146 | 147 | gai_err = getaddrinfo(address, NULL, &hints, &res); 148 | if (gai_err != 0) { 149 | error_init(err, gai_err, "error parsing server socket address; %s", 150 | gai_strerror(gai_err)); 151 | return 0; 152 | } 153 | 154 | memcpy(&sock_addr, res->ai_addr, res->ai_addrlen); 155 | freeaddrinfo(res); 156 | } 157 | 158 | if (port == 0) { 159 | SINPORT(&sock_addr) = htons(DEFAULT_FTP_PORT); 160 | } else { 161 | SINPORT(&sock_addr) = htons(port); 162 | } 163 | 164 | 165 | inet_ntop_ret = inet_ntop(SAFAM(&sock_addr), 166 | (void *)&SINADDR(&sock_addr), 167 | buf, 168 | sizeof(buf)); 169 | if (inet_ntop_ret == NULL) { 170 | char errbuf[ERRBUF_SIZE]; 171 | error_init(err, errno, "error converting server address to ASCII; %s", 172 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 173 | return 0; 174 | } 175 | 176 | /* Put some information in syslog */ 177 | syslog(LOG_INFO, "Binding interface '%s', port %d, max clients %d\n", buf, 178 | ntohs(SINPORT(&sock_addr)), max_connections); 179 | 180 | 181 | /* okay, finally do some socket manipulation */ 182 | fd = socket(AF_INET, SOCK_STREAM, 0); 183 | if (fd == -1) { 184 | char errbuf[ERRBUF_SIZE]; 185 | error_init(err, errno, "error creating socket; %s", 186 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 187 | return 0; 188 | } 189 | 190 | reuseaddr = 1; 191 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuseaddr, 192 | sizeof(int)) !=0) 193 | { 194 | char errbuf[ERRBUF_SIZE]; 195 | close(fd); 196 | error_init(err, errno, "error setting socket to reuse address; %s", 197 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 198 | return 0; 199 | } 200 | 201 | if (bind(fd, (struct sockaddr *)&sock_addr, 202 | sizeof(struct sockaddr_in)) != 0) 203 | { 204 | char errbuf[ERRBUF_SIZE]; 205 | close(fd); 206 | error_init(err, errno, "error binding address; %s", 207 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 208 | return 0; 209 | } 210 | 211 | if (listen(fd, SOMAXCONN) != 0) { 212 | char errbuf[ERRBUF_SIZE]; 213 | close(fd); 214 | error_init(err, errno, "error setting socket to listen; %s", 215 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 216 | return 0; 217 | } 218 | 219 | /* prevent socket from blocking on accept() */ 220 | flags = fcntl(fd, F_GETFL); 221 | if (flags == -1) { 222 | char errbuf[ERRBUF_SIZE]; 223 | close(fd); 224 | error_init(err, errno, "error getting flags on socket; %s", 225 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 226 | return 0; 227 | } 228 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) { 229 | char errbuf[ERRBUF_SIZE]; 230 | close(fd); 231 | error_init(err, errno, "error setting socket to non-blocking; %s", 232 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 233 | return 0; 234 | } 235 | 236 | /* create a pipe to wake up our listening thread */ 237 | if (pipe(pipefds) != 0) { 238 | char errbuf[ERRBUF_SIZE]; 239 | close(fd); 240 | error_init(err, errno, "error creating pipe for internal use; %s", 241 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 242 | return 0; 243 | } 244 | 245 | /* now load the values into the structure, since we can't fail from 246 | here */ 247 | f->fd = fd; 248 | f->max_connections = max_connections; 249 | f->num_connections = 0; 250 | f->inactivity_timeout = inactivity_timeout; 251 | pthread_mutex_init(&f->mutex, NULL); 252 | 253 | daemon_assert(strlen(dir) < sizeof(f->dir)); 254 | strcpy(f->dir, dir); 255 | f->listener_running = 0; 256 | 257 | f->shutdown_request_send_fd = pipefds[1]; 258 | f->shutdown_request_recv_fd = pipefds[0]; 259 | pthread_cond_init(&f->shutdown_cond, NULL); 260 | 261 | daemon_assert(invariant(f)); 262 | return 1; 263 | } 264 | 265 | /* receive connections */ 266 | int ftp_listener_start(ftp_listener_t *f, error_t *err) 267 | { 268 | pthread_t thread_id; 269 | int ret_val; 270 | int error_code; 271 | 272 | daemon_assert(invariant(f)); 273 | daemon_assert(err != NULL); 274 | 275 | error_code = pthread_create(&thread_id, 276 | NULL, 277 | (void *(*)())connection_acceptor, 278 | f); 279 | 280 | if (error_code == 0) { 281 | f->listener_running = 1; 282 | f->listener_thread = thread_id; 283 | ret_val = 1; 284 | } else { 285 | error_init(err, error_code, "unable to create thread"); 286 | ret_val = 0; 287 | } 288 | 289 | daemon_assert(invariant(f)); 290 | 291 | return ret_val; 292 | } 293 | 294 | 295 | #ifndef NDEBUG 296 | static int invariant(const ftp_listener_t *f) 297 | { 298 | int dir_len; 299 | 300 | if (f == NULL) { 301 | return 0; 302 | } 303 | if (f->fd < 0) { 304 | return 0; 305 | } 306 | if (f->max_connections <= 0) { 307 | return 0; 308 | } 309 | if ((f->num_connections < 0) || (f->num_connections > f->max_connections)) { 310 | return 0; 311 | } 312 | dir_len = strlen(f->dir); 313 | if ((dir_len <= 0) || (dir_len > PATH_MAX)) { 314 | return 0; 315 | } 316 | if (f->shutdown_request_send_fd < 0) { 317 | return 0; 318 | } 319 | if (f->shutdown_request_recv_fd < 0) { 320 | return 0; 321 | } 322 | return 1; 323 | } 324 | #endif /* NDEBUG */ 325 | 326 | /* handle incoming connections */ 327 | static void *connection_acceptor(ftp_listener_t *f) 328 | { 329 | error_t err; 330 | int num_error; 331 | 332 | int fd; 333 | int tcp_nodelay; 334 | sockaddr_storage_t client_addr; 335 | sockaddr_storage_t server_addr; 336 | unsigned addr_len; 337 | 338 | connection_info_t *info; 339 | pthread_t thread_id; 340 | int error_code; 341 | 342 | fd_set readfds; 343 | 344 | daemon_assert(invariant(f)); 345 | 346 | if (!watchdog_init(&f->watchdog, f->inactivity_timeout, &err)) { 347 | syslog(LOG_ERR, "Error initializing watchdog thread; %s", 348 | error_get_desc(&err)); 349 | return NULL; 350 | } 351 | 352 | num_error = 0; 353 | for (;;) { 354 | 355 | /* wait for something to happen */ 356 | FD_ZERO(&readfds); 357 | FD_SET(f->fd, &readfds); 358 | FD_SET(f->shutdown_request_recv_fd, &readfds); 359 | select(FD_SETSIZE, &readfds, NULL, NULL, NULL); 360 | 361 | /* if data arrived on our pipe, we've been asked to exit */ 362 | if (FD_ISSET(f->shutdown_request_recv_fd, &readfds)) { 363 | close(f->fd); 364 | syslog(LOG_INFO, "listener no longer accepting connections"); 365 | pthread_exit(NULL); 366 | } 367 | 368 | /* otherwise accept our pending connection (if any) */ 369 | addr_len = sizeof(sockaddr_storage_t); 370 | fd = accept(f->fd, (struct sockaddr *)&client_addr, &addr_len); 371 | if (fd >= 0) { 372 | 373 | tcp_nodelay = 1; 374 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&tcp_nodelay, 375 | sizeof(int)) != 0) 376 | { 377 | char errbuf[ERRBUF_SIZE]; 378 | syslog(LOG_ERR, 379 | "error in setsockopt(), FTP server dropping connection; %s", 380 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 381 | close(fd); 382 | continue; 383 | } 384 | 385 | addr_len = sizeof(sockaddr_storage_t); 386 | if (getsockname(fd, (struct sockaddr *)&server_addr, 387 | &addr_len) == -1) 388 | { 389 | char errbuf[ERRBUF_SIZE]; 390 | syslog(LOG_ERR, 391 | "error in getsockname(), FTP server dropping connection; %s", 392 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 393 | close(fd); 394 | continue; 395 | } 396 | 397 | info = (connection_info_t *)malloc(sizeof(connection_info_t)); 398 | if (info == NULL) { 399 | syslog(LOG_CRIT, 400 | "out of memory, FTP server dropping connection"); 401 | close(fd); 402 | continue; 403 | } 404 | info->ftp_listener = f; 405 | 406 | telnet_session_init(&info->telnet_session, fd, fd); 407 | 408 | if (!ftp_session_init(&info->ftp_session, 409 | &client_addr, 410 | &server_addr, 411 | &info->telnet_session, 412 | f->dir, 413 | &err)) 414 | { 415 | syslog(LOG_ERR, 416 | "error initializing FTP session, FTP server exiting; %s", 417 | error_get_desc(&err)); 418 | close(fd); 419 | telnet_session_destroy(&info->telnet_session); 420 | free(info); 421 | continue; 422 | } 423 | 424 | 425 | error_code = pthread_create(&thread_id, 426 | NULL, 427 | (void *(*)())connection_handler, 428 | info); 429 | 430 | if (error_code != 0) { 431 | syslog(LOG_ERR, "error creating new thread; %d", error_code); 432 | close(fd); 433 | telnet_session_destroy(&info->telnet_session); 434 | free(info); 435 | } 436 | 437 | num_error = 0; 438 | } else { 439 | char errbuf[ERRBUF_SIZE]; 440 | if ((errno == ECONNABORTED) || (errno == ECONNRESET)) { 441 | syslog(LOG_NOTICE, 442 | "interruption accepting FTP connection; %s", 443 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 444 | } else { 445 | syslog(LOG_WARNING, 446 | "error accepting FTP connection; %s", 447 | strerror_r(errno, errbuf, ERRBUF_SIZE)); 448 | /* We don't bump the error counter if we are merely 449 | out of file descriptors. The hope is that some 450 | other thread will eventually finish and release 451 | its resources. We have this problem ever few week 452 | on the ftp.gnupg.org site. */ 453 | if (errno == EMFILE) { 454 | /* Wait a bit. */ 455 | struct timeval tv; 456 | 457 | tv.tv_sec = 10; 458 | tv.tv_usec = 0; 459 | select(0, NULL, NULL, NULL, &tv); 460 | } 461 | else 462 | ++num_error; 463 | } 464 | if (num_error >= MAX_ACCEPT_ERROR) { 465 | syslog(LOG_ERR, 466 | "too many consecutive errors, FTP server exiting"); 467 | return NULL; 468 | } 469 | } 470 | } 471 | } 472 | 473 | /* convert an address to a printable string */ 474 | /* NOT THREADSAFE - wrap with a mutex before calling! */ 475 | static char *addr2string(const sockaddr_storage_t *s) 476 | { 477 | #ifdef INET6 478 | static char addr[IP_ADDRSTRLEN+1]; 479 | int error; 480 | #endif 481 | char *ret_val; 482 | 483 | daemon_assert(s != NULL); 484 | 485 | #ifdef INET6 486 | error = getnameinfo((struct sockaddr *)s, 487 | sizeof(sockaddr_storage_t), 488 | addr, 489 | sizeof(addr), 490 | NULL, 491 | 0, 492 | NI_NUMERICHOST); 493 | if (error != 0) { 494 | syslog(LOG_WARN, "getnameinfo error; %s", gai_strerror(error)); 495 | ret_val = "Unknown IP"; 496 | } else { 497 | ret_val = addr; 498 | } 499 | #else 500 | ret_val = inet_ntoa(s->sin_addr); 501 | #endif 502 | 503 | return ret_val; 504 | } 505 | 506 | 507 | static void *connection_handler(connection_info_t *info) 508 | { 509 | ftp_listener_t *f; 510 | int num_connections; 511 | char drop_reason[80]; 512 | 513 | /* for ease of use only */ 514 | f = info->ftp_listener; 515 | 516 | /* don't save state for pthread_join() */ 517 | pthread_detach(pthread_self()); 518 | 519 | /* Set up our watchdog. We only enable cancellation during long 520 | operations. */ 521 | pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); 522 | watchdog_add_watched(&f->watchdog, &info->watched); 523 | 524 | /* set up our cleanup handler */ 525 | pthread_cleanup_push((void (*)())connection_handler_cleanup, info); 526 | 527 | /* process global data */ 528 | pthread_mutex_lock(&f->mutex); 529 | num_connections = ++f->num_connections; 530 | syslog(LOG_INFO, "%s port %d connection", 531 | addr2string(&info->ftp_session.client_addr), 532 | ntohs(SINPORT(&info->ftp_session.client_addr))); 533 | pthread_mutex_unlock(&f->mutex); 534 | 535 | /* handle the session */ 536 | if (num_connections <= f->max_connections) { 537 | 538 | ftp_session_run(&info->ftp_session, &info->watched); 539 | 540 | } else { 541 | 542 | /* too many users */ 543 | sprintf(drop_reason, 544 | "Too many users logged in, dropping connection (%d logins maximum)", 545 | f->max_connections); 546 | ftp_session_drop(&info->ftp_session, drop_reason); 547 | 548 | /* log the rejection */ 549 | pthread_mutex_lock(&f->mutex); 550 | syslog(LOG_WARNING, 551 | "%s port %d exceeds max users (%d), dropping connection", 552 | addr2string(&info->ftp_session.client_addr), 553 | ntohs(SINPORT(&info->ftp_session.client_addr)), 554 | num_connections); 555 | pthread_mutex_unlock(&f->mutex); 556 | 557 | } 558 | 559 | /* exunt (pop calls cleanup function) */ 560 | pthread_cleanup_pop(1); 561 | 562 | /* return for form :) */ 563 | return NULL; 564 | } 565 | 566 | /* clean up a connection */ 567 | static void connection_handler_cleanup(connection_info_t *info) 568 | { 569 | ftp_listener_t *f; 570 | 571 | f = info->ftp_listener; 572 | 573 | watchdog_remove_watched(&info->watched); 574 | 575 | pthread_mutex_lock(&f->mutex); 576 | 577 | f->num_connections--; 578 | pthread_cond_signal(&f->shutdown_cond); 579 | 580 | syslog(LOG_INFO, 581 | "%s port %d disconnected", 582 | addr2string(&info->ftp_session.client_addr), 583 | ntohs(SINPORT(&info->ftp_session.client_addr))); 584 | 585 | pthread_mutex_unlock(&f->mutex); 586 | 587 | ftp_session_destroy(&info->ftp_session); 588 | telnet_session_destroy(&info->telnet_session); 589 | 590 | free(info); 591 | } 592 | 593 | void ftp_listener_stop(ftp_listener_t *f) 594 | { 595 | daemon_assert(invariant(f)); 596 | 597 | /* write a byte to the listening thread - this will wake it up */ 598 | write(f->shutdown_request_send_fd, "", 1); 599 | 600 | /* wait for client connections to complete */ 601 | pthread_mutex_lock(&f->mutex); 602 | while (f->num_connections > 0) { 603 | pthread_cond_wait(&f->shutdown_cond, &f->mutex); 604 | } 605 | pthread_mutex_unlock(&f->mutex); 606 | } 607 | 608 | --------------------------------------------------------------------------------