From 4b83f8e50792b459dfd8a6ffe470c2fccb524e7b Mon Sep 17 00:00:00 2001 From: sgunderson Date: Tue, 25 Jul 2000 23:38:21 +0000 Subject: [PATCH] Initial revision --- Makefile.in | 66 + README | 175 +++ acconfig.h | 30 + ascii.c | 85 ++ ascii.h | 20 + betaftpd.lsm | 12 + cmds.c | 1879 ++++++++++++++++++++++++++ cmds.h | 120 ++ config.h.in | 172 +++ configure | 3008 ++++++++++++++++++++++++++++++++++++++++++ configure.in | 288 ++++ disp.c | 90 ++ doc/CHANGES | 160 +++ doc/CHANGES-0.0.8 | 211 +++ doc/COPYING | 339 +++++ doc/CREDITS | 29 + doc/KNOWN-BUGS | 32 + doc/README.nonroot | 60 + doc/README.platforms | 44 + doc/README.rights | 20 + doc/RFC-COMPLIANCE | 61 + ftpd.c | 1610 ++++++++++++++++++++++ ftpd.h | 227 ++++ nonroot.c | 251 ++++ nonroot.h | 30 + strip-exec | 52 + 26 files changed, 9071 insertions(+) create mode 100644 Makefile.in create mode 100644 README create mode 100644 acconfig.h create mode 100644 ascii.c create mode 100644 ascii.h create mode 100644 betaftpd.lsm create mode 100644 cmds.c create mode 100644 cmds.h create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in create mode 100644 disp.c create mode 100644 doc/CHANGES create mode 100644 doc/CHANGES-0.0.8 create mode 100644 doc/COPYING create mode 100644 doc/CREDITS create mode 100644 doc/KNOWN-BUGS create mode 100644 doc/README.nonroot create mode 100644 doc/README.platforms create mode 100644 doc/README.rights create mode 100644 doc/RFC-COMPLIANCE create mode 100644 ftpd.c create mode 100644 ftpd.h create mode 100644 nonroot.c create mode 100644 nonroot.h create mode 100755 strip-exec diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..de5f217 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,66 @@ +VERSION=0.0.8pre17-dev + +# main target +all: betaftpd + +# various defines +CC = @CC@ +VPATH = @srcdir@ +prefix = @prefix@ + +CFLAGS = @CFLAGS@ @DEFS@ -I@srcdir@ -DVERSION=\"$(VERSION)\" +LIBS = @LIBS@ +OBJS = disp.o ftpd.o cmds.o nonroot.o ascii.o +ASSMS = disp.s ftpd.s cmds.s nonroot.s ascii.s +CPPS = disp.i ftpd.i cmds.i nonroot.i ascii.i + +# Since we use VPATH, override .c.o rule +.c.o: $*.c @srcdir@/config.h + $(CC) $(CFLAGS) -c @srcdir@/$*.c +.c.s: $*.c @srcdir@/config.h + $(CC) $(CFLAGS) -S @srcdir@/$*.c + +# deps +cmds.o: @srcdir@/cmds.c @srcdir@/ftpd.h @srcdir@/cmds.h @srcdir@/nonroot.h @srcdir@/config.h +ftpd.o: @srcdir@/ftpd.c @srcdir@/ftpd.h @srcdir@/cmds.h @srcdir@/config.h +disp.o: @srcdir@/disp.c @srcdir@/ftpd.h @srcdir@/config.h +nonroot.o: @srcdir@/nonroot.c @srcdir@/nonroot.h @srcdir@/config.h +ascii.o: @srcdir@/ascii.c @srcdir@/config.h + +betaftpd: $(OBJS) + $(CC) $(CFLAGS) $(LIBS) -o betaftpd $(OBJS) +assembly-files: $(ASSMS) +betaftpd-from-assembly-files: $(ASSMS) + $(CC) $(LIBS) -o betaftpd -Wl,--sort-common $(ASSMS) + +clean: + rm -f $(OBJS) $(ASSMS) $(CPPS) + rm -f core + rm -f betaftpd + +distclean: clean + rm -f config.log config.cache config.status Makefile config.h + +install: betaftpd + install betaftpd $(prefix)/bin/betaftpd + +# +# NOTE: This is not intended for end users. +# +package: +# strip-exec + rm -rf betaftpd-$(VERSION) + mkdir betaftpd-$(VERSION) + mkdir betaftpd-$(VERSION)/doc +# cp betaftpd betaftpd-$(VERSION) + make distclean + autoconf + autoheader configure.in > config.h.in + cp README Makefile.in acconfig.h cmds.c cmds.h betaftpd-$(VERSION) + cp config.h.in configure configure.in disp.c ftpd.c betaftpd-$(VERSION) + cp nonroot.c ascii.c ascii.h nonroot.h ftpd.h strip-exec betaftpd.lsm betaftpd-$(VERSION) + cp doc/CREDITS doc/CHANGES doc/CHANGES-0.0.8 doc/COPYING betaftpd-$(VERSION)/doc + cp doc/KNOWN-BUGS doc/RFC-COMPLIANCE doc/README.nonroot betaftpd-$(VERSION)/doc + cp doc/README.rights doc/README.platforms betaftpd-$(VERSION)/doc + tar cf - betaftpd-$(VERSION)/ | gzip -9vv > betaftpd-$(VERSION).tar.gz + tar cf - betaftpd-$(VERSION)/ | bzip2 -3vv > betaftpd-$(VERSION).tar.bz2 diff --git a/README b/README new file mode 100644 index 0000000..1c03da5 --- /dev/null +++ b/README @@ -0,0 +1,175 @@ +Hmmmmm... :-) + +[Insert insanely cool ASCII logo here yourself -- I'd rather spend my time + coding] + +0. Before you start +------------------- +This is NOT a stable version of BetaFTPD. The latest stable, tried and tested +version is 0.0.7. Use this version at your own risk. That being said, this +version is rather stable now, actually _more_ stable than 0.0.7, which contains +quite a few lethal bugs, and is almost a year old. + +1. What is it? +-------------- +BetaFTPD is a _single_-threaded FTP daemon (server). This makes it _faster_ +than most other FTP daemons (in most situations), contrary to popular belief. +BetaFTPD is, as the name implies, work in progress. Please don't expect it to +be finished yet! + +2. How do you build it? +----------------------- +BetaFTPD uses GNU autoconf. Just type: + +./configure +make + +And see what happens. (Run the resulting file called `betaftpd', of +course...) There are some flags that you can (and probably want to) give +to `configure': + +--enable-xferlog Makes the server output some logging information + to /var/log/xferlog (or /usr/adm/xferlog if that fails). +--enable-fullscreen Makes the server start in fullscreen mode, where you + can see what the users are doing, their transfer + speed etc. (There is other no way of turning on or off + fullscreen mode -- this switch is the only way.) +--enable-upload Enable STOR (uploading) and APPE commands, and all necessary + code for uploading. +--enable-stat Enable STAT command (server statistics). Note that I see + very little use for such a command, and you shouldn't enable + it unless you have a very good reason. Such commands are + one of the best weapons in a cracker's arsenal. It is only + included for RFC1123 compliance, and disabled by default. + In addition, it tries to give as little `cracker-help' data + as possible. +--enable-shadow Enable support for shadow passwords. +--enable-fork Makes the server fork into the background, so you + can logout etc. without `disturbing' it. +--enable-nonroot Enable running without root privilegies. Please read + README.nonroot _first_, it will save you a lot of + grief. +--enable-ascii Enable ASCII mode transfers (ASCII translation on-the-fly). +--enable-dcache Enable directory listing caches, for faster directory + listings (the listings are only generated the first time + they are needed). Note that this still quite experimental + code, and the cache is only invalidated when it expires + (ie. if nobody uses the entry in 15 minutes), or when + files are added or deleted in the cached directory. + (In other words, if a file is changed, the directory + cache entry is _not_ updated. Do a `touch' on the directory + to force invalidation of the dcache entry.) +--enable-message Enable welcome.msg/.message files and README notes, like + `normal' ftpds do. Note that this is relatively crude + (and probably will stay so) -- for instance, README notes + and .message files are printed every time the user enters + the directory, even if he/she has been there before. + +To get the smallest executable possible, _first_ check everything +in `strip-exec' (it's unsupported -- don't complain to me if it doesn't +work, fix it yourself! :-) ), and type: + +strip-exec + +3. Some things to note +---------------------- +a) BetaFTPD now listen on port 21 by default (it used to run on port 121). To + change it, change FTP_PORT in ftpd.h to whatever you'd like it to listen on. +b) BetaFTPD must definitely be run as root (unless you use --enable-nonroot + as mentioned above). +c) Some commands and features are missing, but probably not many that you'd + really miss. See the KNOWN-BUGS and RFC-COMPLIANCE files. +d) BetaFTPD can _not_ be run from inetd. This is a design decision. + +4. Design decisions +------------------- +BetaFTPD was originally designed to be a single-threaded FTP server, +nothing else. After a while, work was done to reduce the executable size, +and from then, design was more and more concentrated around a +`minimalistic' server (after all, the fewer lines of code there are to +execute, the faster will things go, and the fewer bugs are possible). + +However, as GNU autoconf support was added, it was realized that +features could be added and selected by users without hurting overall +speed or size for the others. Therefore, more and more features are +(and will be) implemented in BetaFTPD. + +There are intendedly no command line switches it BetaFTPD. Such switches +are generally there to make for quick change of options without +recompiling the program. This is ideal for most command-line utilities, +but is rather pointless for an FTP server. FTP servers are designed +to be online 24 hours a day, 7 days a week, and serve users with files. +Rapid confiuration changes (in the form of command line switches, +configuration files et al) are usually not important, and has thus been +omitted from BetaFTPD. + +There is no (and will never be a) ratio function in BetaFTPD, as such +features are only used for warez/MP3/porn servers, and are quite stupid +even then. + +5. Eek! `top' says BetaFTPD uses tons of memory! +------------------------------------------------ +Yes, but top really is wrong :-) Well, actually, I don't think it could +do better -- every file BetaFTPD mmap()s is counted towards the total +number. So if you were serving the same 128MB file 10 times, top would +claim you used over a gigabyte of memory, still only a couple of +megabytes would be used at most, as cache. If you ran out of memory, +this could actually shrink to zero. It's just that the kernel tries to +be more effective (and it succeeds) by reducing the number of reads, and +instead reading larger chunks at once. + +If sendfile() support is enabled, and the circumstances allow it (binary +mode downloading), BetaFTPD will not mmap() at all, bringing the memory +total down to a more realistic value. + +Bragging: + +On my Intel system, every user needs 1104 bytes (`struct conn'). +When a user has a file transfer, an additional 324 bytes (`struct ftran') +would be needed, or 1428 bytes per user in total. In addition, you have +some `startup costs', of course. `ps' (again on my system) reports a +startup memory usage of 432 kB (RSS, I don't know enough about memory +to know if this is a realistic number ;-) ), compared to 484 kB for a +single wu-ftpd session (again RSS). To be honest, I don't think BetaFTPD +uses much memory. [Note that this information is a bit old -- I haven't +tested it lately; expect 2-300 bytes extra per user.] + +Oh, while we're at it: A 486 running BetaFTPD will easily be able to +saturate a T1 line, with room to spare. The bottleneck will most likely +lie somewhere else (like the bandwidth, or how fast the disk can deliver +data). Unless you've got a 386 running gigabit, of course... (I must +admit, I've never really tried it with more than 100Mbit/sec, but the +problem then was clearly on the client machine ;-) BetaFTPD has later +shown itself to be more than able of saturating a 10mbit link in real- +world tests, with about 3% CPU utilization (PII/450MHz, IDE disks all +the way, about 2GB of files in all, 7-8 users at once).) + +Recent load tests (see http://www.kegel.com/dkftpbench/results.html) +show that a P90 is able to serve about 250 clients (each at 28.800-speeds) +on a 10Mbit line. + +6. Misc. +-------- +Have fun -- after all, it's about 5% the size of wu-ftpd (about 20k +compiled!) and provides (IMHO) about 98% of the needed, everyday +functionality -- in fact, if you don't need the _really_ exotic commands, +it's faster (=better?) and uses *much* less memory (especially when +you have many users logged in at once) than the majority of the FTP +servers out there... (6 line sentence.) + +This program is GPLed, version 2 (see the file COPYING), not any later +or earlier version (later versions will probably `upgrade' to new GPLs +as they become available, I just want to be able to choose what license +I put my code under). For more info on the GPL, visit http://www.gnu.org/ . + +7. Where to get new versions, contact me etc. +--------------------------------------------- +E-mail: sgunderson@bigfoot.com (all spam/flame go + to antispam@bigfoot.com or /dev/null) +Homepage, new versions: http://members.xoom.com/sneeze/ + (Click the link, and see under Projects.) +Mirror site: ftp://metalab.unc.edu/pub/Linux/system/network/daemons/ + (Note that Metalab will always lag behind a few days, and + doesn't carry beta versions.) + + There are more mirror sites at the homepage. diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..06373c4 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,30 @@ +/* Define if we want to do ascii transfers. */ +#undef WANT_ASCII + +/* Define if we need transfer logging. */ +#undef WANT_XFERLOG + +/* Define if we need fullscreen mode. */ +#undef WANT_FULLSCREEN + +/* Define if we need upload support. */ +#undef WANT_UPLOAD + +/* Define if we want statistics support. */ +#undef WANT_STAT + +/* Define if we need shadow support. */ +#undef WANT_SHADOW + +/* Define if we need to fork. */ +#undef WANT_FORK + +/* Define if we don't have root privilegies. */ +#undef WANT_NONROOT + +/* Define if we want directory listing cache */ +#undef WANT_DCACHE + +/* Define if we want to support .message files et al */ +#undef WANT_MESSAGE +@TOP@ diff --git a/ascii.c b/ascii.c new file mode 100644 index 0000000..3fdb6ab --- /dev/null +++ b/ascii.c @@ -0,0 +1,85 @@ +/* ascii.c: BetaFTPD ascii filters, written by Beau Kuiper + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#if HAVE_CONFIG_H +#include +#endif + +#if WANT_ASCII + +/* + * ascii_findlength(): + * Figures out how much read data was transferred if + * a full buffer wasn't sent. + */ +int ascii_findlength(const char * const buffer, const int tranlen) +{ + int count = 0, pos = 0; + + while (pos < tranlen) { + const char ch = buffer[count++]; + if (ch != 13) { + if (ch == 10) pos++; + pos++; + } + } + return count; +} + +/* + * ascii_downloadfilter(): + * Changes LF to CR/LF on the fly (from buffer to + * outbuffer), for ASCII downloads. + */ +int ascii_downloadfilter(const char * const buffer, char * const outbuffer, const int length) +{ + int count; + char *b2ptr = outbuffer; + + for (count = 0; count < length; count++) { + const char ch = buffer[count]; + + if (ch != 13) { + if (ch == 10) { + *b2ptr++ = 13; + } + *b2ptr++ = ch; + } + } + return (b2ptr - outbuffer); +} + +/* + * ascii_uploadfilter(): + * Removes all CRs (ASCII 13) from buffer on the fly, + * for ASCII uploads. + */ +int ascii_uploadfilter(char * const buffer, const int length) +{ + int count; + char *b2ptr = buffer; + + for (count = 0; count < length; count++) { + const char ch = buffer[count]; + if (ch != 13) { + *b2ptr++ = ch; + } + } + return (b2ptr - buffer); +} + +#endif /* WANT_ASCII */ diff --git a/ascii.h b/ascii.h new file mode 100644 index 0000000..7b2519c --- /dev/null +++ b/ascii.h @@ -0,0 +1,20 @@ +/* ascii.h: BetaFTPD ASCII filter prototypes + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +int ascii_findlength(const char * const buffer, const int tranlen); +int ascii_downloadfilter(const char * const buffer, char * const outbuffer, const int length); +int ascii_uploadfilter(char * const buffer, const int length); diff --git a/betaftpd.lsm b/betaftpd.lsm new file mode 100644 index 0000000..391884d --- /dev/null +++ b/betaftpd.lsm @@ -0,0 +1,12 @@ +Begin3 +Title: BetaFTPD +Version: 0.0.8 +Entered-date: 30MAY00 +Description: Single-threaded, fast, small FTP daemon (server) +Keywords: FTP server, single-threaded +Author: sgunderson@bigfoot.com (Steinar H. Gunderson) +Primary-site: metalab.unc.edu /pub/Linux/system/network/daemons + 64k betaftpd-0.0.8.tar.bz2 + 72k betaftpd-0.0.8.tar.gz +Copying-policy: GPL +End diff --git a/cmds.c b/cmds.c new file mode 100644 index 0000000..131df34 --- /dev/null +++ b/cmds.c @@ -0,0 +1,1879 @@ +/* cmds.c: BetaFTPD command handlers + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define _GNU_SOURCE + +#if HAVE_CONFIG_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_STROPTS_H +#include +#endif + +#if HAVE_SYS_CONF_H +#include +#endif + +#if HAVE_DIRENT_H +#include +#endif + +#if HAVE_CRYPT_H +#include +#endif + +#if HAVE_ERRNO_H +#include +#endif + +#if HAVE_GLOB_H +#include +#endif + +#if HAVE_STDIO_H +#include +#endif + +#if HAVE_STDLIB_H +#include +#endif + +#if HAVE_STRING_H +#include +#endif + +#if HAVE_STRINGS_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_TIME_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#endif + +#if HAVE_PWD_H +#include +#endif + +#if HAVE_GRP_H +#include +#endif + +#if HAVE_SYS_IOCTL_H +#include +#endif + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_SYS_PARAM_H +#include +#endif + +#if HAVE_STROPTS_H +#include +#endif + +#if HAVE_SYS_CONF_H +#include +#endif + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_SHADOW_H +#include +#endif + +#if HAVE_SYS_FILIO_H +#include +#endif + +#if HAVE_SYS_POLL_H +#include +#endif + +#if WANT_NONROOT +#define NO_SETUID +#define DO_SETUID +#else +#define NO_SETUID ,0 +#define DO_SETUID ,1 +#endif + +#ifndef NBBY +#define NBBY 8 +#endif + +#include +#include +#include + +#define lstat stat + +extern struct conn *first_conn; +#if WANT_DCACHE +extern struct dcache *first_dcache; +#endif + +#if HAVE_POLL +extern struct pollfd fds[]; +#else +extern fd_set master_fds, master_send_fds; +#endif + +struct handler { + char cmd_name[6]; + char add_cmlen; /* =1 if the command takes an argument */ + int (*callback)(struct conn * const); + char min_auth; +#if !WANT_NONROOT + char do_setuid; /* =1 if root is not *really* needed */ +#endif +}; + +static const struct handler handler_table[] = { + { "user ", 1, cmd_user, 0 NO_SETUID }, + { "pass ", 1, cmd_pass, 1 NO_SETUID }, + { "retr ", 1, cmd_retr, 3 DO_SETUID }, + { "acct ", 1, cmd_acct, 0 NO_SETUID }, + { "port ", 1, cmd_port, 3 DO_SETUID }, + { "pasv" , 0, cmd_pasv, 3 DO_SETUID }, + { "pwd" , 0, cmd_pwd, 3 DO_SETUID }, + { "cwd " , 1, cmd_cwd, 3 DO_SETUID }, + { "cdup" , 0, cmd_cdup, 3 DO_SETUID }, + { "rest ", 1, cmd_rest, 3 DO_SETUID }, + { "list" , 0, cmd_list, 3 DO_SETUID }, + { "nlst" , 0, cmd_nlst, 3 DO_SETUID }, + { "type ", 1, cmd_type, 3 DO_SETUID }, + { "mode ", 1, cmd_mode, 3 DO_SETUID }, + { "stru ", 1, cmd_stru, 3 DO_SETUID }, + { "size ", 1, cmd_size, 3 DO_SETUID }, + { "mdtm ", 1, cmd_mdtm, 3 DO_SETUID }, + { "abor" , 0, cmd_abor, 3 DO_SETUID }, + { "dele ", 1, cmd_dele, 3 DO_SETUID }, + { "rnfr ", 1, cmd_rnfr, 3 DO_SETUID }, + { "rnto ", 1, cmd_rnto, 3 DO_SETUID }, + { "mkd " , 1, cmd_mkd, 3 DO_SETUID }, + { "rmd " , 1, cmd_rmd, 3 DO_SETUID }, + { "allo ", 1, cmd_allo, 3 DO_SETUID }, + { "stat" , 0, cmd_stat, 0 NO_SETUID }, + { "noop" , 0, cmd_noop, 0 DO_SETUID }, + { "syst" , 0, cmd_syst, 0 DO_SETUID }, + { "help" , 0, cmd_help, 0 NO_SETUID }, + { "quit" , 0, cmd_quit, 0 DO_SETUID }, + { "rein" , 0, cmd_rein, 0 DO_SETUID }, + + /* deprecated forms */ + { "xcup" , 0, cmd_cdup, 3 DO_SETUID }, + { "xcwd ", 1, cmd_cwd, 3 DO_SETUID }, + { "xpwd" , 0, cmd_pwd, 3 DO_SETUID }, + { "xmkd ", 1, cmd_mkd, 3 DO_SETUID }, + { "xrmd ", 1, cmd_rmd, 3 DO_SETUID }, +#if WANT_UPLOAD + { "stor ", 1, cmd_stor, 3 DO_SETUID }, + { "appe ", 1, cmd_appe, 3 DO_SETUID }, +#endif +#if DOING_PROFILING +#warning Use DOING_PROFILING with caution, and NEVER on a production server! :-) + { "exit", 0, cmd_exit, 0 NO_SETUID }, +#endif + { "" , 0, NULL, 0 NO_SETUID } +}; + +/* + * do_chdir(): Does a chdir() to newd on c, staying inside the + * limits of root_dir. Use this instead of a chdir() whenever + * you can, and possibly even when you can't :-) + * + * This command quirks around some problems in the rest of + * the code (namely translate_path()), so a blank newdir is + * interpreted as the root directory. + */ +int do_chdir(struct conn * const c, const char * const newd) +{ + char chd[512], temp[512]; + + TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1); + + /* handle `absolute' paths */ + if (newd[0] == '/' || newd[0] == '\0') { + strcpy(temp, c->root_dir); + + /* + * is this the root directory? if not, remove the trailing `/' + * and concatenate the new directory on + */ + if (newd[1] != '\0' && newd[0] != '\0') { + temp[strlen(temp) - 1] = 0; + strcat(temp, newd); + } + } else { + strcpy(temp, newd); + } + +#if WANT_NONROOT + if (nr_check_permission(c->uid, temp, 1, 1, NULL) == -1) { + numeric(c, 550, "Permission denied"); + return -1; + } +#endif + + TRAP_ERROR(chdir(temp) == -1, 550, return -1); + + getcwd(chd, 254); + if (chd[strlen(chd) - 1] != '/') { + strcat(chd, "/"); + } + + if (strncmp(chd, c->root_dir, strlen(c->root_dir)) != 0) { + numeric(c, 550, "No such file or directory."); + return -1; + } + + return 0; +} + +/* + * cmd_user(): Handles the USER command, and does most of the initial + * authentication work. User names are limited to 16 + * characters, by force... + */ +int cmd_user(struct conn * const c) +{ + strncpy(c->username, c->recv_buf, 16); + c->username[16] = 0; + + if (strcasecmp(c->username, "anonymous") == 0) { + strcpy(c->username, "ftp"); + } + if (strcasecmp(c->username, "ftp") == 0) { + numeric(c, 331, "Login OK, send password (your e-mail)."); + c->auth = 1; + } else { + numeric(c, 331, "Password required for %s.", c->username); + c->auth = 2; + } + return 1; +} + +/* + * cmd_pass(): Handles the PASS command, and checks the password. + * This function is rather long and complicated, mostly + * because there are so many ways of doing users + * (including my nonroot system) out there... And we + * don't even support PAM or real shadow passwords (with + * expiry etc) yet... + */ +int cmd_pass(struct conn * const c) +{ +#if WANT_NONROOT + c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir, + c->recv_buf); +#else /* !WANT_NONROOT */ +#if WANT_SHADOW && HAVE_SHADOW_H + struct spwd *s; +#endif + struct passwd *p; + + p = getpwnam(c->username); +#if WANT_SHADOW && HAVE_SHADOW_H + s = getspnam(c->username); +#endif + + if (p == NULL) { + c->auth = 0; + } else { + c->uid = p->pw_uid; + strncpy(c->curr_dir, p->pw_dir, 254); + c->curr_dir[254] = 0; + } + + if (c->auth == 1) { + if (c->curr_dir[strlen(c->curr_dir) - 1] != '/') { + strcat(c->curr_dir, "/"); + } + strcpy(c->root_dir, c->curr_dir); + c->auth = 3; + } else if (c->auth != 0) { + strcpy(c->root_dir, "/"); + if (strcmp(crypt(c->recv_buf, p->pw_passwd), p->pw_passwd) != 0 +#if WANT_SHADOW && HAVE_SHADOW_H + && (s == NULL || strcmp(crypt(c->recv_buf, s->sp_pwdp), s->sp_pwdp) != 0) +#endif + ) { + c->auth = 0; + } else { + c->auth = 3; + } + } +#endif /* !WANT_NONROOT */ + + if (c->auth == 0) { + numeric(c, 530, "Login incorrect."); + } else { +#if WANT_MESSAGE + chdir(c->curr_dir); + dump_file(c, 230, "welcome.msg"); +#endif + numeric(c, 230, "User logged in."); + } + return 1; +} + +/* + * cmd_acct(): Handle (ignore) the ACCT command. I don't see how we + * could make use of this command... wu-ftpd doesn't, either. + * However, wu-ftpd (at least the version I have here) uses + * 502, which isn't a legal error code according to RFC959. + * 202, on the other hand, is, and seems to be applicable. + * + * I'm unsure if this one should require setuid or not, but + * I feel that the RFC959 intention is having it _before_ + * USER/PASS. Therefore, this one runs with root privilegies :-) + */ +int cmd_acct(struct conn * const c) +{ + numeric(c, 202, "ACCT ignored OK -- not applicable on this system."); + return 1; +} + +/* + * cmd_port(): Handles the PORT command, and sets up the data socket. + * Making a brief uid=0 (root) appearance (to bind the socket) -- + * I feel it's safer that way (instead of running as root + * the whole way), in case there are some weird overflows + * somewhere. + */ +int cmd_port(struct conn * const c) +{ + short int a0, a1, a2, a3, p0, p1; + int i, sock, err; + struct ftran *f; + struct sockaddr_in sin; + + if ((c->transfer != NULL) && (c->transfer->state >= 4)) { + numeric(c, 500, "Sorry, only one transfer at a time."); + return 1; + } + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + TRAP_ERROR(sock == -1, 500, return 1); + + destroy_ftran(c->transfer); + c->transfer = f = alloc_new_ftran(sock, c); + + i = sscanf(c->recv_buf, "%3hu,%3hu,%3hu,%3hu,%3hu,%3hu", &a0, &a1, &a2, &a3, &p0, &p1); + if (i < 6) { + numeric(c, 501, "Parse error."); + } else { + const int one = 1; + int tmp; + + /* bind to own address, port 20 (FTP data) */ + tmp = sizeof(sin); + err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp); + TRAP_ERROR(err == -1, 500, return 1); + sin.sin_port = FTP_PORT - 1; + + numeric(c, 200, "PORT command OK."); + + /* note that bind() might well fail, so we don't error check */ +#if !WANT_NONROOT + /* need root privilegies for a short while */ + seteuid(getuid()); +#endif + bind(sock, (struct sockaddr *)&sin, sizeof(sin)); +#if !WANT_NONROOT + seteuid(c->uid); +#endif + + f->sin.sin_family = AF_INET; + f->sin.sin_addr.s_addr = htonl( + ((unsigned char)(a0) << 24) + + ((unsigned char)(a1) << 16) + + ((unsigned char)(a2) << 8) + + ((unsigned char)(a3) )); + f->sin.sin_port = htons( + ((unsigned char)(p0) << 8) + + ((unsigned char)(p1) )); + f->sock = sock; + f->state = 3; + + i = 1; + ioctl(f->sock, FIONBIO, &one); + } + return 1; +} + +/* + * cmd_pasv(): Handles the PASV command, and sets up the data socket. + * Uses port numbers > 1024, since it doesn't run as root. + */ +int cmd_pasv(struct conn * const c) +{ + struct ftran *f; + int tmp, sock, err; + unsigned int one = 1; + struct sockaddr_in addr; + + if ((c->transfer != NULL) && (c->transfer->state >= 4)) { + numeric(c, 503, "Sorry, only one transfer at once."); + return 1; + } + destroy_ftran(c->transfer); + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + TRAP_ERROR(sock == -1, 500, return 1); + err = add_fd(sock, POLLIN); + TRAP_ERROR(err != 0, 501, return 1); + + c->transfer = f = alloc_new_ftran(sock, c); + + ioctl(sock, FIONBIO, &one); + + /* setup socket */ + tmp = sizeof(addr); + err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp); + TRAP_ERROR(err == -1, 500, return 1); + + addr.sin_port = 0; /* let the system choose */ + err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)); + TRAP_ERROR(err == -1, 500, return 1); + + tmp = sizeof(addr); + err = getsockname(sock, (struct sockaddr *)&addr, &tmp); + TRAP_ERROR(err == -1, 500, return 1); + + err = listen(f->sock, 1); + TRAP_ERROR(err == -1, 500, return 1); + f->state = 1; + + numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)", + (htonl(addr.sin_addr.s_addr) & 0xff000000) >> 24, + (htonl(addr.sin_addr.s_addr) & 0x00ff0000) >> 16, + (htonl(addr.sin_addr.s_addr) & 0x0000ff00) >> 8, + (htonl(addr.sin_addr.s_addr) & 0x000000ff), + (htons(addr.sin_port) & 0xff00) >> 8, + (htons(addr.sin_port) & 0x00ff)); + return 1; +} + +/* + * cmd_pwd(): Handles PWD command (print working directory). + * + * Note that if somebody contacts you with the message `the server + * says curr_dir() is outside root_dir()', you should fix your + * /betaftpd.users file, if you use nonroot. If not, it's a bug. + * Try to get it _reproducible_, and mail it to me. + */ +int cmd_pwd(struct conn * const c) +{ + char temp[512], *cdir = NULL; + + cdir = do_pwd(c, temp, c->curr_dir); + if (cdir != NULL) { + numeric(c, 257, "\"%s\" is current working directory.", cdir); + } + return 1; +} + +/* + * do_pwd(): Translates an absolute path to a path suitable for viewing + * to the user (ie. removes the root_dir, and removes a trailing + * slash if it exists). Note that the retbuf is only used as a + * storage place -- the pointer to the right place within retbuf + * is _returned_. + */ +char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir) +{ + char *cdir = NULL; + + strcpy(retbuf, dir); + if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) { + numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator."); + return NULL; + } + + cdir = retbuf + strlen(c->root_dir) - 1; + if (cdir[strlen(cdir) - 1] == '/' && strlen(cdir) > 1) { + cdir[strlen(cdir) - 1] = 0; + } else if (strlen(cdir) == 0) { + strcpy(cdir, "/"); + } + + return cdir; +} + +/* + * cmd_cwd(): Handles CWD command (change working directory). Uses + * cmd_cwd_internal() (see below). + */ +int cmd_cwd(struct conn * const c) +{ + cmd_cwd_internal(c, c->recv_buf); + return 1; +} + +/* + * cmd_cdup(): Handles a CDUP command (identical to `CWD ..'). Note that + * RFC959 gives two different response codes (250 and 200) -- + * 250 is the same as CWD gives, which sounds logical to me. + * wu-ftpd uses it as well. + * + * Note that using a CDUP to try to get outside root_dir returns + * an error, instead of just staying in the root directory (as + * the OS and thus wu-ftpd does). + */ +int cmd_cdup(struct conn * const c) +{ + cmd_cwd_internal(c, ".."); + return 1; +} + +/* + * cmd_cwd_internal(): + * Does the work for CWD and CDUP (modularized to save some + * space and have clearer code). Mostly, it just uses do_chdir(), + * and sees where that takes us. It adds a trailing slash if needed. + */ +void cmd_cwd_internal(struct conn * const c, const char * const newd) +{ + if (do_chdir(c, newd) != -1) { + int i; + + getcwd(c->curr_dir, 254); + i = strlen(c->curr_dir); + if (c->curr_dir[i - 1] != '/') { + c->curr_dir[i++] = '/'; + c->curr_dir[i] = '\0'; + } + +#if WANT_MESSAGE + dump_file(c, 250, ".message"); + list_readmes(c); +#endif + + numeric(c, 250, "CWD successful."); + } +} + +/* + * cmd_rest(): Handles the REST command. All it does is tell the file + * sending functions to start at the correct number. We should + * perhaps add some better error checking to this? + */ +int cmd_rest(struct conn * const c) +{ + c->rest_pos = abs(atoi(c->recv_buf)); + numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos); + return 1; +} + +/* + * cmd_retr(): Handles the RETR command. This command doesn't send the + * file, but it opens it and tells the socket handling code + * to check for activity on the data socket. When the + * connection occurs (or succeeds, if we're using PORT mode), + * the actual file transfer begins. + */ +int cmd_retr(struct conn * const c) +{ + struct ftran *f = c->transfer; + + if ((f == NULL) || ((f->state != 1) && (f->state != 3))) { + numeric(c, 425, "No data connection set up; please use PASV or PORT."); + return 1; + } + +#if WANT_ASCII + if ((c->rest_pos > 0) && (c->ascii_mode == 1)) { + numeric(c, 500, "Cannot resume while in ASCII mode."); + return 1; + } +#endif + + f->local_file = do_openfile(c, c->recv_buf, f->filename, O_RDONLY +#if WANT_NONROOT + , 4 +#endif + ); + f->dir_listing = 0; + + if (f->local_file == -1) { + numeric(f->owner, 550, strerror(errno)); + destroy_ftran(f); + } else if (f->local_file == -2) { + f->local_file = -1; + destroy_ftran(f); + } else { +#if WANT_UPLOAD + f->upload = 0; +#endif + prepare_for_transfer(f); + } + return 1; +} + +#if WANT_UPLOAD +/* + * cmd_stor(): Handles the STOR command (upload file). Pushes the + * work down to do_store(), below. + */ +int cmd_stor(struct conn * const c) +{ + do_store(c, 0); + return 1; +} + +/* + * cmd_appe(): Handles the APPE command (append to file). Pushes + * the work down to do_store(), below. + */ +int cmd_appe(struct conn * const c) +{ + do_store(c, 1); + return 1; +} + +/* + * do_store(): Initiate an upload. Most of the comments to do_retr() + * (above) apply to this one as well. + */ +void do_store(struct conn * const c, const int append) +{ + struct ftran *f = c->transfer; + + if ((f == NULL) || ((f->state != 1) && (f->state != 3))) { + numeric(c, 425, "No data connection set up; please use PASV or PORT."); + return; + } + +#if WANT_ASCII + if ((c->rest_pos > 0) && (c->ascii_mode == 1)) { + numeric(c, 500, "Cannot resume while in ASCII mode."); + return; + } +#endif + + f->local_file = do_openfile(c, c->recv_buf, f->filename, O_WRONLY | + O_CREAT | ((append || c->rest_pos > 0) ? 0 : O_TRUNC) +#if WANT_NONROOT + , 2 +#endif + ); + f->dir_listing = 0; + + if (f->local_file == -1) { + numeric(f->owner, 550, strerror(errno)); + } else if (f->local_file == -2) { + f->local_file = -1; + } else { + f->upload = 1; + f->append = append; +#if WANT_ASCII + f->ascii_mode = c->ascii_mode; +#endif + prepare_for_transfer(f); + } +} +#endif /* WANT_UPLOAD */ + +/* + * cmd_size(): Handle the SIZE command -- returns the size of a + * file. Note that this command is not part of RFC959, + * and thus there is no clear specification (except + * for some ftpext documents, which we try to follow + * as closely as we can). BetaFTPD deviates from wu-ftpd + * in that it lets you check the `size' of directories + * as well (instead of giving 550). This is _not_ the + * size of all the files in the directory, rather how + * much space the directory inode uses. + */ +int cmd_size(struct conn * const c) +{ +#if WANT_ASCII + if (c->ascii_mode) { + numeric(c, 550, "SIZE not available in ASCII mode."); + return 1; + } +#endif + { + const char * const fname = translate_path(c, c->recv_buf); + struct stat buf; + + TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1); + + numeric(c, 213, "%lu", (unsigned long)(buf.st_size)); + return 1; + } +} + +/* + * cmd_mdtm(): Handle the MDTM command -- returns the modification + * date/time of a file. See the comments on cmd_size(), + * above. + */ +int cmd_mdtm(struct conn * const c) +{ + const char * const fname = translate_path(c, c->recv_buf); + struct stat buf; + struct tm *m; + + TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1); + + m = gmtime(&(buf.st_mtime)); /* at least wu-ftpd does it in GMT */ + numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900, + m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec); + return 1; +} + +/* + * cmd_abor(): Handle the ABOR command (abort a file transfer). This should + * be clean enough, but isn't tested extensively. + */ +int cmd_abor(struct conn * const c) +{ + if (c->transfer != NULL) { + numeric(c, 426, "File transfer aborted."); + destroy_ftran(c->transfer); + } + numeric(c, 226, "ABOR command processed OK."); + return 1; +} + +/* + * cmd_dele(): Handle the DELE command (delete a file). + */ +int cmd_dele(struct conn * const c) +{ + const char * const fname = translate_path(c, c->recv_buf); + + TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1); + numeric(c, 250, "File deleted OK."); + return 1; +} + +/* + * cmd_rnfr(): Handle the RNFR command (take a filename to rename from). + */ +int cmd_rnfr(struct conn * const c) +{ + const char * const fname = translate_path(c, c->recv_buf); + char cwd[256]; + struct stat buf; + + c->rename_from[0] = '\0'; + if (fname == NULL) return 1; + + getcwd(cwd, 256); + snprintf(c->rename_from, 256, "%s/%s", cwd, fname); + + /* Just check that the file exists. */ + TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, return 1); + + numeric(c, 350, "File exists, send RNTO."); + return 1; +} + +/* + * cmd_rnto(): Handle the RNTO command (do the actual renaming). + */ +int cmd_rnto(struct conn * const c) +{ + const char * const fname = translate_path(c, c->recv_buf); + + if (fname == NULL) return 1; + if (c->rename_from[0] == '\0') { + numeric(c, 503, "Please send RNFR first."); + return 1; + } + + TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, return 1); + + numeric(c, 250, "File renamed successfulyy."); + return 1; +} + +/* + * cmd_mkd(): Handle the MKD/XMKD command (create a new directory). + * RFC959 is not clear on the error codes for this command -- + * one place, 521 is cited as the correct error, but is + * mentioned nowhere else. Different FTP servers differ here + * as well. Thus, I've followed what appears to be the intention + * (having `analogous' errors with STOR), and use 550 instead. + * + * Making directories is probably the topic covered most + * extensively by RFC959 (and in the most confusing way as + * well). I try to follow the conventions, but it isn't always + * easy :-) (This code isn't quite easy to understand, because + * temp2 is used twice, in two different roles.) + */ +int cmd_mkd(struct conn * const c) +{ + const char * const fname = translate_path(c, c->recv_buf); + char temp[512], temp2[1024], *cdir; + int i, j; + + TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1); + + chdir(fname); + getcwd(temp2, 512); + cdir = do_pwd(c, temp, temp2); + + /* double the quotes in the output */ + for (i = 0, j = 0; i <= strlen(cdir); i++, j++) { + temp2[j] = cdir[i]; + if (cdir[i] == '"') { + temp2[++j] = '"'; + } + } + numeric(c, 257, "\"%s\" created.", temp2); + return 1; +} + +/* + * cmd_rmd(): Handle the RMD/XRMD command. Works just like DELE, only for + * directories. + */ +int cmd_rmd(struct conn * const c) +{ + const char * const fname = translate_path(c, c->recv_buf); + + TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1); + numeric(c, 250, "Directory deleted."); + return 1; +} + +/* + * cmd_allo(): Handle the ALLO command. The command does not do anything, except + * sit around and play compliant. Some Windows FTP servers (Serv-U, + * for instance), verifies that there is enough space on the disk, + * but since we have no idea on what the filesystem will be stored on, + * we just ignore the command. + * + * We could theoretically use this information to give more information + * to the full-screen mode, but close to no FTP clients send this + * command, and it would touch too much code. + */ +int cmd_allo(struct conn * const c) +{ + numeric(c, 202, "No storage allocation necessary."); + return 1; +} + +/* + * cmd_stat(): Handle the STAT command. Please see README for more details. + * Note that this command is run with euid=root, since it has + * to be able to run before USER. + * + * Note that we need to bypass numeric(), to get a multi-line + * reply. + */ +#if WANT_STAT +char conn_state[5][27] = { + "Not logged in", + "Waiting for e-mail address", + "Waiting for password", + "Logged in", + "Waiting for password", /* actually non-existant user */ +}; + +char ftran_state[6][42] = { + "Not initialized", + "Decided PASV address/port", + "Waiting on PASV socket", + "Got PORT address/port", + "Connecting on PORT address/port", + "Transferring file (or connecting on PORT)" +}; +#endif + +int cmd_stat(struct conn * const c) +{ +#if WANT_STAT + char buf[1024]; + int i, err; + struct ftran *f = c->transfer; + + snprintf(buf, 1024, "211- FTP server status:\r\n" + " BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n" + " Connected to %s\r\n" + " Control connection state: %s\r\n" + " TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n" + " Data connection state: %s\r\n" + "211 End of status\r\n", + inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr), + conn_state[c->auth], (f) ? ftran_state[f->state] : ftran_state[0]); + + i = strlen(buf); + + err = send(c->sock, buf, i, 0); + if (err == -1 && errno == EPIPE) { + destroy_conn(c); + return 0; + } +#else + numeric(c, 502, "STAT command disabled for security reasons."); +#endif + return 1; +} + +#if HAVE_MMAP +/* + * _mwrite(): This define is for mmap-listing. It works as a write() + * (not in parameter, but in function), and is used in + * cmd_list() and cmd_nlst() only. + * + * Note that this function returns the new position in the + * `file'. The caller is expected to send this information + * back in `pos' at the next call to _mwrite(). + */ +int _mwrite(const char * const buf, const struct ftran * const f, + const int pos, const int count, const int size) +{ + if (pos + count >= size) return size; /* out of space */ + memcpy(f->file_data + pos, buf, count); + return pos + count; +} +#endif + +/* + * mwrite: This is a short_hand define, making calls to _mwrite() very + * similiar to calls to write(). + */ +#define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size)); + +/* + * long_listing(): + * Formats output in `ls -l' style. It returns one line for the + * file PATHNAME, and returns it in retbuf. Setting do_classify + * to nonzero has the same effect as `ls -F'. + * + * This command is so long, because simply there is so much to + * be done. GNU ls has some extra functions, but it's close to + * 3000 lines too... + */ +int long_listing(char * const retbuf, const char * const pathname, const int do_classify) +{ + int i, year; + char newd[512], temp[1026]; + struct stat buf; + struct tm *t; + time_t now; + char username[17], groupname[17]; + + time(&now); + year = localtime(&now)->tm_year; + { +#if !WANT_NONROOT + struct passwd *p; + struct group *g; +#endif + + if (lstat(pathname, &buf) == -1) return 0; + +#if WANT_NONROOT + strcpy(username, nr_get_uname(buf.st_uid)); + strcpy(groupname, nr_get_gname(buf.st_gid)); +#else + p = getpwuid(buf.st_uid); + if (p != NULL) { + strncpy(username, p->pw_name, 16); + username[16] = 0; + } else { + snprintf(username, 16, "%u", buf.st_uid); + } + + g = getgrgid(buf.st_gid); + if (g != NULL) { + strncpy(groupname, g->gr_name, 16); + groupname[16] = 0; + } else { + snprintf(groupname, 16, "%u", buf.st_gid); + } +#endif + } + + /* + * This POSIX approximation is based on GNU ls code (and obfuscated + * a bit...), to be compatible with `real' ls implementations. + */ + t = localtime(&(buf.st_mtime)); + strftime(newd, 512, ((now > buf.st_mtime + 6L * 30L * 24L * 60L * 60L) || + (now < buf.st_mtime - 60L * 60L)) + ? "%b %e %Y" : "%b %e %H:%M", t); + + { +#if WANT_NONROOT + char rights[16]; + + if (nr_check_permission(0, pathname, 0, (buf.st_mode & S_IFDIR), rights) == -1) { + /* no permission to even see this file */ + return 0; + } + + i = snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n", +#else + i = snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s", +#endif + decode_mode(buf.st_mode), +#if WANT_NONROOT + rights, +#else + (buf.st_mode & S_IRUSR) ? 'r' : '-', + (buf.st_mode & S_IWUSR) ? 'w' : '-', + (buf.st_mode & S_IXUSR) ? ((buf.st_mode & S_ISUID) ? 's' : 'x') : '-', + (buf.st_mode & S_IRGRP) ? 'r' : '-', + (buf.st_mode & S_IWGRP) ? 'w' : '-', + (buf.st_mode & S_IXGRP) ? ((buf.st_mode & S_ISGID) ? 's' : 'x') : '-', + (buf.st_mode & S_IROTH) ? 'r' : '-', + (buf.st_mode & S_IWOTH) ? 'w' : '-', + (buf.st_mode & S_IXOTH) ? ((buf.st_mode & S_ISVTX) ? 't' : 'x') : '-', +#endif + buf.st_nlink, username, groupname, + (unsigned long)(buf.st_size), newd, pathname); + +#if 0 + /* + * vim needs this extra character for some reason... It's too + * bad I'll have to do it this way, but syntax colouring + * that works properly is almost a `must' for me :-) + */ + ) +#endif + + /* add an extra classification `sign' if we got -F */ + if (do_classify) { + int len = strlen(temp); + temp[len] = classify(buf.st_mode); + temp[len + 1] = '\0'; + } + } + + strcpy(retbuf, temp); + return 1; +} + +/* + * cmd_list(): Handles the LIST command (directory listing). Does a + * long listing (of type `ls -l'). The listing work is + * done by do_listing(), below. + */ +int cmd_list(struct conn * const c) +{ + struct list_options lo; + +/* lo.recursive = 0; */ + lo.long_listing = 1; + lo.classify = 0; + + do_listing(c, &lo); + return 1; +} + +/* + * cmd_nlst(): Handles the NLST command (plain directory listing). + * Does a plain listing (no dates etc.), unless overridden + * by the `-l' or `-L' flag (case insensitivity because most + * FTP clients don't have a clue about what they send out). + * The listing work is done by do_listing(), below. + */ +int cmd_nlst(struct conn * const c) +{ + struct list_options lo; + +/* lo.recursive = 0; */ + lo.long_listing = 0; + lo.classify = 0; + + do_listing(c, &lo); + return 1; +} + +/* + * do_listing(): + * Prepares any listing buffers, temp files, etc., before + * pushing the work one step further :-) + * + * If the directory listing cache is enabled, the cache + * is checked first, to see if we still have a valid entry. + */ +void do_listing(struct conn * const c, struct list_options * const lo) +{ + int i; + char *ptr; +#if HAVE_MMAP + int size; +#endif + struct ftran * const f = c->transfer; + +#if WANT_DCACHE + char cwd[256]; +#endif + +#if WANT_NONROOT +#warning No nonroot checking for list_core() yet +#endif + + i = prepare_for_listing(c, &ptr, lo); + if (i == -1) { + destroy_ftran(c->transfer); + return; + } + +#if WANT_DCACHE + getcwd(cwd, 256); +#endif + +#if HAVE_MMAP + strcpy(f->filename, "(directory listing)"); +#endif + +#if WANT_DCACHE + { + struct dcache *d = NULL, *next = first_dcache->next_dcache; + struct stat buf; + + if (stat(cwd, &buf) > -1) { + /* run through the linked list */ + while (next != NULL) { + d = next; + next = d->next_dcache; + + if (buf.st_mtime <= d->generated && + strcmp(d->dir_name, cwd) == 0 && + strcmp(d->pattern, ptr) == 0 && + memcmp(&(d->lo), lo, + sizeof(struct list_options)) == 0) { + d->use_count++; + f->dir_cache = d; + f->file_data = d->dir_data; + f->size = d->dir_size; + f->dir_listing = 1; + f->pos = 0; + prepare_for_transfer(f); + return; + } + } + } + } +#endif + +#if HAVE_MMAP + { + int num_files = get_num_files(c, ptr, lo); + if (num_files == -1) return; + + size = num_files * 160; + f->file_data = malloc(size + 1); + TRAP_ERROR(f->file_data == NULL, 550, return); + list_core(c, ptr, lo, size); + } +#else + list_core(c, ptr, lo); +#endif + +#if WANT_DCACHE + /* populate the directory listing cache */ + { + struct stat buf; + struct dcache *d = alloc_new_dcache(); + if (d != NULL && stat(cwd, &buf) > -1) { + d->use_count++; + f->dir_cache = d; + d->dir_data = f->file_data; + d->dir_size = f->size; + d->generated = buf.st_mtime; + + strcpy(d->dir_name, cwd); + strncpy(d->pattern, ptr, 255); + d->pattern[255] = 0; + d->lo = *lo; + } + } +#endif + +#if HAVE_MMAP + f->pos = 0; +#endif + prepare_for_transfer(f); +} + +/* + * get_num_files(): + * Get the number of files in PATHNAME (optionally matching + * a pattern). Note that c is needed for TRAP_ERROR. + */ +int get_num_files(struct conn * const c, const char * const pathname, + struct list_options * const lo) +{ + int num_files; + glob_t pglob; + + /* + * glob() fails to set errno correctly, so we simply guess on + * `permission denied'... The others are far less likely to happen. + */ + switch (glob(pathname, 0, NULL, &pglob)) { +#ifdef GLOB_NOMATCH + case GLOB_NOMATCH: + return 0; +#endif + case 0: + num_files = pglob.gl_pathc; + break; + default: + numeric(c, 550, strerror(EACCES)); + return -1; + } + +#if 0 /* the rest of the code doesn't support recursion yet */ + if (lo->recursive) { + for (i = 0; i < pglob.gl_pathc; i++) { + char *temp = pglob.gl_pathv[i]; + struct stat buf; + + lstat(temp, &buf); + if (S_ISDIR(buf.st_mode)) { + chdir(temp); + num_files += get_num_files(c, "*", lo); + chdir(".."); + } + } + } +#endif + + return num_files; +} + +/* + * list_core(): Enumerate all the files in PATHNAME, and formats them + * according to list_options (calling format functions if + * required). + * + * Note that we don't do any realloc() yet, so if your + * _average_ file name length is over a certain size (a little + * under 80 for long listings, and a little under 160 for + * short listings), the list will be truncated. Fix... + * + * This function is rather long. + */ +void list_core(struct conn * const c, const char * const pathname, + struct list_options * const lo +#if HAVE_MMAP + , const int size +#endif + ) +{ + int i; + glob_t pglob; +#if HAVE_MMAP + int pos = 0; +#endif + struct ftran * const f = c->transfer; + + /* + * glob() fails to set errno correctly, so we simply guess on + * `permission denied'... The others are far less likely to happen. + */ + switch (glob(pathname, GLOB_MARK, NULL, &pglob)) { + case 0: +#ifdef GLOB_NOMATCH + case GLOB_NOMATCH: +#endif + break; + default: + numeric(c, 550, strerror(EACCES)); + return; + } + + if (lo->long_listing) { + /* FIX: we may get too high total number if we are running nonroot! */ + struct stat buf; + long unsigned int total = 0; + char temp[1024]; + + for (i = 0; i < pglob.gl_pathc; i++) { + if (lstat(pglob.gl_pathv[i], &buf) != -1) { + total += buf.st_blocks; + } + } + i = snprintf(temp, 1024, "total %lu\r\n", total >> 1); +#if HAVE_MMAP + mwrite(temp, i); +#else + write(f->local_file, temp, i); +#endif + } + + for (i = 0; i < pglob.gl_pathc; i++) { + char * const temp = pglob.gl_pathv[i]; + char buf[2048]; + + /* strip `/' away from the pathname -- add it later if -F */ + { + int len = strlen(temp); + if (temp[len - 1] == '/') { + temp[len - 1] = '\0'; + } + } + + if (lo->long_listing) { + if (long_listing(buf, temp, lo->classify) == 0) continue; + } else { + strcpy(buf, temp); + if (lo->classify) { + struct stat statbuf; + + if (lstat(buf, &statbuf) != -1) { + const int len = strlen(buf); + + buf[len] = classify(statbuf.st_mode); + buf[len + 1] = 0; + } + } + } + + /* support recursion here some day... */ + +#if HAVE_MMAP + mwrite(buf, strlen(buf)); + mwrite("\r\n", 2); +#else + write(f->local_file, buf, strlen(buf)); + write(f->local_file, "\r\n", 2); +#endif + } + +#if HAVE_MMAP + f->size = pos; +#else + lseek(f->local_file, 0, SEEK_SET); +#endif + + globfree(&pglob); +} + +/* + * cmd_noop(): Handles the NOOP command. Does nothing, doesn't even + * reset the timeout. + */ +int cmd_noop(struct conn * const c) +{ + numeric(c, 200, "NOOP command successful."); + return 1; +} + +/* + * cmd_syst(): Handles the SYST command. Returns the system identification. + */ +int cmd_syst(struct conn * const c) +{ + numeric(c, 215, "UNIX Type: L%u", NBBY); + return 1; +} + +/* + * cmd_type(): Handles the TYPE command. + */ +int cmd_type(struct conn * const c) +{ +#if WANT_ASCII + c->recv_buf[0] &= (255-32); /* convert to upper case */ + if (c->recv_buf[0] == 'A') { + c->ascii_mode = 1; + numeric(c, 200, "Type is ASCII."); + } else if (c->recv_buf[0] == 'I') { + c->ascii_mode = 0; + numeric(c, 200, "Type is IMAGE."); + } else { + numeric(c, 504, "Unknown type."); + } +#else + numeric(c, 200, "TYPE ignored (always I)"); +#endif + return 1; +} + +/* + * cmd_mode(): Handles the MODE command. We always use stream mode, + * so the argument is ignored. + */ +int cmd_mode(struct conn * const c) +{ + numeric(c, 200, "MODE ignored (always S)"); + return 1; +} + +/* + * cmd_stru(): Handles the STRU command. We always use file mode, + * so the argument is ignored. + */ +int cmd_stru(struct conn * const c) +{ + numeric(c, 200, "STRU ignored (always F)"); + return 1; +} + +/* + * cmd_help(): Handle the HELP command. I'm sorry, but I'm unwilling + * to use a lot of space to explain the RFCs in such a message, + * and BetaFTPD doesn't have any special things that should + * be noted anywhere. Thus, this message is close to empty. I + * feel that a 5xx entry would have been better, but that is + * disallowed. + * + * As with ACCT, this command is supposed to be executed from + * everywhere, so we have to run without setuid. I don't like + * it, but at the same time I have to idea what could go + * wrong... + * + * Perhaps I should make this message sound a little less + * like an error, since the error code is intended for helpful + * messages? :-) + */ +int cmd_help(struct conn * const c) +{ + numeric(c, 414, "Sorry, no detailed help; use standard FTP commands."); + return 1; +} + +/* + * cmd_quit(): Handles the QUIT command, which shuts down the control + * and data sockets. + */ +int cmd_quit(struct conn * const c) +{ + numeric(c, 221, "Have a nice day!"); + destroy_conn(c); + return 0; +} + +/* + * cmd_rein(): Handle the REIN command, which does close to a full reset + * of the connection. Much of the code here is intentionally + * copied directly from alloc_new_conn() -- perhaps we should + * modularize this? + */ +int cmd_rein(struct conn * const c) +{ + destroy_ftran(c->transfer); + c->buf_len = c->auth = c->rest_pos = 0; + + /* equals: strcpy(c->curr_dir, "/") ; strcpy(c->last_cmd, ""); */ + c->curr_dir[0] = '/'; +#if WANT_FULLSCREEN + c->curr_dir[1] = c->last_cmd[0] = '\0'; +#else + c->curr_dir[1] = '\0'; +#endif + + time(&(c->last_transfer)); + numeric(c, 220, "BetaFTPD " VERSION " ready."); + + return 1; +} + +#if DOING_PROFILING +/* + * cmd_exit(): Handles the EXIT command, my own `extension' to the + * FTP protocol... IMPORTANT: Only to be used for profiling + * purposes!! (It's needed to get some profiling data out + * of the server after compiling it with -pg, since such data + * is only written on a clear exit()). Any user (even those + * not logged in) can issue an EXIT, and make the server shut + * down without clearing any sockets etc. In other words: + * Don't use it on a production site. + */ +void cmd_exit(struct conn * const c) +{ + while (first_conn->next_conn) + destroy_conn(first_conn->next_conn); + exit(0); +} +#endif + +/* + * parse_command(): + * Gets a command from c->recv_buf, determines which command + * it is, sets proper effective user-ID and calls the command + * handler. Finally, it cleans up. + * + * To me, this command seems optimizable, but I'm not really + * sure where :-) + */ +void parse_command(struct conn *c) +{ + int cmlen; + const struct handler *h = handler_table; /* first entry */ + + if (c == NULL) return; + + /* strip any leading non-ASCII characters (including CR/LFs) */ + while (c->buf_len > 0 && (c->recv_buf[0] < 'a' || c->recv_buf[0] > 'z') + && (c->recv_buf[0] < 'A' || c->recv_buf[0] > 'Z')) { + remove_bytes(c, 1); /* not good */ + } + + /* scan, searching for CR or LF */ + cmlen = strcspn(c->recv_buf, "\r\n"); + if (cmlen >= c->buf_len) return; + +#if WANT_FULLSCREEN + strncpy(c->last_cmd, c->recv_buf, cmlen); + c->last_cmd[cmlen] = 0; +#endif + + do { + if ((cmlen >= (strlen(h->cmd_name) + h->add_cmlen)) && + (strncasecmp(c->recv_buf, h->cmd_name, strlen(h->cmd_name)) == 0)) { + if (c->auth < h->min_auth) { + numeric(c, 503, "Please login with USER and PASS."); + while (c->recv_buf[0] != '\n') remove_bytes(c, 1); + } else { + char schar; + +#if !WANT_NONROOT + if (h->do_setuid) { + seteuid(c->uid); + } else { + seteuid(0); + } +#endif + + remove_bytes(c, strlen(h->cmd_name)); + cmlen -= strlen(h->cmd_name); + while (c->recv_buf[0] == ' ') { + remove_bytes(c, 1); + cmlen--; + } + + schar = c->recv_buf[cmlen]; + c->recv_buf[cmlen] = 0; + + /* result of zero means the connection is freed */ + if (h->callback(c)) { + c->recv_buf[cmlen] = schar; +#if !WANT_NONROOT + if (h->do_setuid) seteuid(getuid()); +#endif + remove_bytes(c, cmlen); + } + } + return; + } + } while ((++h)->callback != NULL); + + numeric(c, 500, "Sorry, no such command."); + remove_bytes(c, cmlen); +} + +/* + * prepare_for_transfer(): + * Prepares an ftran object for a file transfer, setting + * file size, opening sockets etc. + * + * nonroot notice: prepare_for_transfer() assumes all access + * checks are already done. + */ +void prepare_for_transfer(struct ftran *f) +{ +#if WANT_NONROOT +#warning No nonroot checking for prepare_for_transfer() yet +#endif + +#if HAVE_MMAP + /* mmap doesn't make temp files for dir listings */ + if (!f->dir_listing) { +#endif + + f->size = lseek(f->local_file, 0, SEEK_END); + errno = 0; +#if WANT_UPLOAD + if (f->upload == 0 || f->append == 0 || f->owner->rest_pos != 0) +#endif + lseek(f->local_file, f->owner->rest_pos, SEEK_SET); +#if HAVE_MMAP + } +#endif + + if (f->state == 1) { /* PASV connection */ + f->state = 2; /* waiting */ + } else if (f->state == 3) { /* PORT connection */ + f->state = 4; + connect(f->sock, (struct sockaddr *)&f->sin, sizeof(f->sin)); + add_fd(f->sock, POLLOUT); + } + time(&(f->tran_start)); +} + +/* + * decode_mode(): + * Takes a mode_t argument (from a `struct stat'), and + * returns the proper dirlist letter for that type. + * + * Note: S_IFLNK seems to be broken, or perhaps I just have + * missed something (S_IFLNK is always set for all *files* on + * my glibc 2.0.111 system). + * + * The most common cases are put first, for speed :-) + */ +char decode_mode(mode_t mode) { + if (mode & S_IFREG) return '-'; + if (mode & S_IFDIR) return 'd'; + if (mode & S_IFLNK) return 'l'; + if (mode & S_IFBLK) return 'b'; + if (mode & S_IFCHR) return 'c'; + if (mode & S_IFSOCK) return 's'; + if (mode & S_IFIFO) return 'f'; + + return '-'; +} + +/* + * translate_path(): + * Take an FTP path, do all neccessary root_dir checks, + * change to the correct directory and return the proper + * file name to open/stat/whatever. The path returned is + * relative to the current directory (NOT absolute). chdir() + * in any way will `destroy' this argument. + * + * Note that `path' will be _changed_, and used as a return pointer + * base. Do not attempt to free the result from this function -- + * if you need to, free path instead. + */ +char *translate_path(struct conn * const c, char * const path) +{ + char *ptr = NULL; + + /* chdir to the right dir, then chop it off */ + chdir(c->curr_dir); + + ptr = strrchr(path, '/'); + if (ptr != NULL) { + char save_char = ptr[0]; + ptr[0] = 0; + + if (do_chdir(c, path) == -1) { + return NULL; + } + ptr[0] = save_char; + ptr++; + } else { + ptr = path; + } + return ptr; +} + +/* + * do_openfile(): + * Opens the file PATH with access parameters FLAGS, translating + * paths and checking permissions as neccessary. Generally, this + * should be used whenever you need an open(). + * + * The parameters might be a bit confusing. To clarify them a bit: + * c: IN/OUT (will be changed) + * path: IN (but _will_ be changed) + * filename: OUT + * flags: IN + * check_perm: IN + */ +int do_openfile(struct conn * const c, char * const path, + char * const filename, const int flags +#if WANT_NONROOT + , const int check_permission +#endif +) +{ + char *ptr; + struct stat buf; + +#if WANT_NONROOT + if (nr_check_permission(c->uid, path, check_permission, 0, NULL) == -1) { + return -1; + } +#endif + + ptr = translate_path(c, c->recv_buf); + if (ptr == NULL) return -1; + +#if WANT_UPLOAD + if ((flags & O_CREAT) == 0) { +#endif + stat(ptr, &buf); + if (!S_ISREG(buf.st_mode)) { + numeric(c, 550, "%s: Not a plain file.", ptr); + return -2; + } +#if WANT_UPLOAD + } +#endif + + if (filename != NULL) { /* filename should always be != NULL */ + strcpy(filename, ptr); + } + return open(ptr, flags, 0666); +} + +/* + * prepare_for_listing(): + * Parse list options, put them back into the list_options + * structure lo, and make temporary room for the list. + */ +int prepare_for_listing(struct conn * const c, char ** const ptr, + struct list_options * const lo) +{ +#if !HAVE_MMAP + char *tfname; +#endif + struct ftran *f = c->transfer; + char *tmp; + char *optr = NULL, *fptr = NULL; +#if WANT_NONROOT + char chd[512]; +#endif + +#if WANT_NONROOT +#warning No nonroot checking for prepare_for_listing() yet +#endif + + if ((f == NULL) || ((f->state != 1) && (f->state != 3))) { + numeric(c, 425, "No data connection set up; please use PASV or PORT."); + return -1; + } + + /* + * A little parameter scanning is required here. There can only + * be two parts: the directory name, and any options. We'll find + * any options first. + */ + if (c->recv_buf[0] == '-') { + optr = c->recv_buf; + } else { + optr = strstr(c->recv_buf, " -"); + } + + /* Then see if there are any options to parse. */ + if (optr != NULL) { + while (*++optr) { + switch (*optr & (255-32)) { /* uppercase */ +#if 0 + case 'R': /* actually case sensitive... */ + lo->recursive = 1; + break; +#endif + case 'L': + lo->long_listing = 1; + break; + case 'F': + lo->classify = 1; + break; + case ' ': + fptr = optr + 1; + *(optr--) = 0; + break; + default: + break; + } + } + } else { + fptr = c->recv_buf; + } + + /* then we chdir to the dir in fptr (if any) */ + tmp = fptr ? strrchr(fptr, '/') : NULL; + if (tmp != NULL) { + tmp[0] = 0; + if (do_chdir(c, fptr) == -1) return -1; + fptr = tmp + 1; + } else { + /* current directory */ + TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1); + } + + /* if no argument, choose all files */ + if (fptr == NULL || fptr[0] == 0) fptr = "*"; + *ptr = fptr; + +#if WANT_NONROOT + getcwd(chd, 512); + if (nr_check_permission(c->uid, chd, 4, 1, NULL) == -1) { + numeric(c, 550, "Permission denied"); + return -1; + } +#endif + +#if !HAVE_MMAP + tfname = tempnam(NULL, "ftp"); + +#if WANT_NONROOT + if (tfname == NULL) tfname = tempnam("/", "ftp"); +#endif + + TRAP_ERROR(tfname == NULL, 550, return -1); + strcpy(f->filename, tfname); + free(tfname); + + f->local_file = open(f->filename, O_RDWR | O_CREAT | O_TRUNC, 0666); + TRAP_ERROR(f->local_file == -1, 550, return -1); +#endif + f->dir_listing = 1; +#if WANT_UPLOAD + f->upload = 0; +#endif + + return 0; +} + +/* + * classify(): Takes a mode_t argument (from `struct stat'), and returns + * the parameter to be used in an `ls -F'-style listing. + */ +char classify(const mode_t mode) +{ + if (S_ISREG(mode)) { + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + return '*'; + } else { + return '\0'; + } + } + if (S_ISDIR(mode)) return '/'; + if (S_ISLNK(mode)) return '@'; + if (S_ISSOCK(mode)) return '='; + if (S_ISFIFO(mode)) return '|'; + return '\0'; +} diff --git a/cmds.h b/cmds.h new file mode 100644 index 0000000..b7735ff --- /dev/null +++ b/cmds.h @@ -0,0 +1,120 @@ +/* cmds.c: BetaFTPD command handler prototypes + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* + * TRAP_ERROR: This is a quick way of doing a test for an error condition. + * if an error occurs (or more precisely, if the value supplied is + * true), an error message is sent back to the client. This is _not_ + * a debugging macro. The condition is allowed to have side effects, + * and is only evaluated once. + * + * The second argument is the FTP error code to send back, in case + * of an error (the error message itself will be supplied by the + * system). The third argument is code to execute after the FTP error + * has been sent (typically `return' or `return -1'). + * + * If TRAP_ERROR_DEBUG is defined, some extra debugging info is + * sent. Don't enable this for a normal server, it could be a + * security risk. + */ +#undef TRAP_ERROR_DEBUG +/* #define TRAP_ERROR_DEBUG 1 */ + +#ifdef TRAP_ERROR_DEBUG +#define TRAP_ERROR(x, num, y) TRAP_ERROR_INTERNAL(x, num, y, __FILE__, __LINE__) +#define TRAP_ERROR_INTERNAL(x, num, y, fl, ln) \ + if (x) { \ + numeric(c, num, "%s (%s:%d)", strerror(errno), fl, ln); \ + y; \ + } +#else +#define TRAP_ERROR(x, num, y) \ + if (x) { \ + numeric(c, num, strerror(errno)); \ + y; \ + } +#endif + +#define CMD_PROTO(cmd) int cmd_ ## cmd ## (struct conn * const c) + +int do_chdir(struct conn * const c, const char * const newd); +CMD_PROTO(user); +CMD_PROTO(pass); +CMD_PROTO(acct); +CMD_PROTO(port); +CMD_PROTO(pasv); +CMD_PROTO(pwd); +CMD_PROTO(cwd); +CMD_PROTO(cdup); +CMD_PROTO(rest); +CMD_PROTO(retr); +CMD_PROTO(size); +CMD_PROTO(mdtm); +CMD_PROTO(abor); +CMD_PROTO(dele); +CMD_PROTO(rnfr); +CMD_PROTO(rnto); +CMD_PROTO(mkd); +CMD_PROTO(rmd); +CMD_PROTO(allo); +CMD_PROTO(stat); +CMD_PROTO(list); +CMD_PROTO(nlst); +CMD_PROTO(syst); +CMD_PROTO(noop); +CMD_PROTO(type); +CMD_PROTO(mode); +CMD_PROTO(stru); +CMD_PROTO(help); +CMD_PROTO(quit); +CMD_PROTO(rein); + +#if WANT_UPLOAD +CMD_PROTO(stor); +CMD_PROTO(appe); +#endif + +#if DOING_PROFILING +CMD_PROTO(exit); +#endif + +void cmd_cwd_internal(struct conn * const c, const char * const newd); +void parse_command(struct conn *c); +void prepare_for_transfer(struct ftran *f); +char decode_mode(mode_t mode); +char *translate_path(struct conn * const c, char * const path); +int do_openfile(struct conn * const c, char * const path, + char * const filename, const int flags +#if WANT_NONROOT + , const int check_permission +#endif +); +int prepare_for_listing(struct conn * const c, char ** const ptr, + struct list_options * const lo); +void do_listing(struct conn * const c, struct list_options * const lo); +int get_num_files(struct conn * const c, const char * const pathname, + struct list_options * const lo); +void list_core(struct conn * const c, const char * const pathname, + struct list_options * const lo +#if HAVE_MMAP + , const int size +#endif +); +char classify(const mode_t mode); +void do_store(struct conn * const c, int append); +char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir); + diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..68a07dd --- /dev/null +++ b/config.h.in @@ -0,0 +1,172 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ +/* Define if we want to do ascii transfers. */ +#undef WANT_ASCII + +/* Define if we need transfer logging. */ +#undef WANT_XFERLOG + +/* Define if we need fullscreen mode. */ +#undef WANT_FULLSCREEN + +/* Define if we need upload support. */ +#undef WANT_UPLOAD + +/* Define if we want statistics support. */ +#undef WANT_STAT + +/* Define if we need shadow support. */ +#undef WANT_SHADOW + +/* Define if we need to fork. */ +#undef WANT_FORK + +/* Define if we don't have root privilegies. */ +#undef WANT_NONROOT + +/* Define if we want directory listing cache */ +#undef WANT_DCACHE + +/* Define if we want to support .message files et al */ +#undef WANT_MESSAGE + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define if you have the header file. */ +#undef HAVE_ASSERT_H + +/* Define if you have the header file. */ +#undef HAVE_CRYPT_H + +/* Define if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define if you have the header file. */ +#undef HAVE_ERRNO_H + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_GLOB_H + +/* Define if you have the header file. */ +#undef HAVE_GRP_H + +/* Define if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_SYSTM_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IP_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_TCP_H + +/* Define if you have the header file. */ +#undef HAVE_PWD_H + +/* Define if you have the header file. */ +#undef HAVE_SHADOW_H + +/* Define if you have the header file. */ +#undef HAVE_SIGNAL_H + +/* Define if you have the header file. */ +#undef HAVE_STDARG_H + +/* Define if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the header file. */ +#undef HAVE_STROPTS_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_CONF_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SENDFILE_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SIGNAL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#undef HAVE_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have poll(). */ +#undef HAVE_POLL + +/* Define if you have sendfile() with the Linux semantics. */ +#undef HAVE_LINUX_SENDFILE + +/* Define if you have sendfile() with the BSD semantics. */ +#undef HAVE_BSD_SENDFILE + diff --git a/configure b/configure new file mode 100755 index 0000000..c5fcf7c --- /dev/null +++ b/configure @@ -0,0 +1,3008 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --enable-xferlog Enable transfer log support" +ac_help="$ac_help + --enable-ascii Enable ASCII mode support" +ac_help="$ac_help + --enable-fullscreen Run in fullscreen mode (implies --disable-fork)" +ac_help="$ac_help + --enable-fork Make the server fork into the background" +ac_help="$ac_help + --enable-upload Enable upload support" +ac_help="$ac_help + --enable-stat Enable STAT command (see README first)" +ac_help="$ac_help + --enable-dcache Enable directory listing cache" +ac_help="$ac_help + --enable-message Enable .message files etc." +ac_help="$ac_help + --enable-shadow Enable shadow password support" +ac_help="$ac_help + --enable-nonroot Do not need root access (implies --disable-shadow)" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=cmds.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + +if test "x$prefix" = xNONE; then +echo $ac_n "checking for prefix by $ac_c" 1>&6 +# Extract the first word of "ftpd", so it can be a program name with args. +set dummy ftpd; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:550: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_FTPD'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$FTPD" in + /*) + ac_cv_path_FTPD="$FTPD" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_FTPD="$FTPD" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_FTPD="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +FTPD="$ac_cv_path_FTPD" +if test -n "$FTPD"; then + echo "$ac_t""$FTPD" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -n "$ac_cv_path_FTPD"; then + prefix=`echo $ac_cv_path_FTPD|sed 's%/[^/][^/]*//*[^/][^/]*$%%'` + fi +fi + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:591: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:621: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:672: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:704: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 715 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:720: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:746: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:751: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:779: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + + +LIBS="" + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:815: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:869: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:890: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:911: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:928: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:945: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +for ac_hdr in unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:973: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:983: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_func in getpagesize +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1012: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1040: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + +echo $ac_n "checking for working mmap""... $ac_c" 1>&6 +echo "configure:1065: checking for working mmap" >&5 +if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat > conftest.$ac_ext < +#include +#include + +/* This mess was copied from the GNU getpagesize.h. */ +#ifndef HAVE_GETPAGESIZE +# ifdef HAVE_UNISTD_H +# include +# endif + +/* Assume that all systems that can run configure have sys/param.h. */ +# ifndef HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# ifdef HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +#ifdef __cplusplus +extern "C" { void *malloc(unsigned); } +#else +char *malloc(); +#endif + +int +main() +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize(); + + /* + * First, make a file with some known garbage in it. + */ + data = malloc(pagesize); + if (!data) + exit(1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand(); + umask(0); + fd = creat("conftestmmap", 0600); + if (fd < 0) + exit(1); + if (write(fd, data, pagesize) != pagesize) + exit(1); + close(fd); + + /* + * Next, try to mmap the file at a fixed address which + * already has something else allocated at it. If we can, + * also make sure that we see the same garbage. + */ + fd = open("conftestmmap", O_RDWR); + if (fd < 0) + exit(1); + data2 = malloc(2 * pagesize); + if (!data2) + exit(1); + data2 += (pagesize - ((int) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap(data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit(1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit(1); + + /* + * Finally, make sure that changes to the mapped area + * do not percolate back to the file as seen by read(). + * (This is a bug on some variants of i386 svr4.0.) + */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = malloc(pagesize); + if (!data3) + exit(1); + if (read(fd, data3, pagesize) != pagesize) + exit(1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit(1); + close(fd); + unlink("conftestmmap"); + exit(0); +} + +EOF +if { (eval echo configure:1213: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_mmap_fixed_mapped=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_mmap_fixed_mapped" 1>&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_MMAP 1 +EOF + +fi + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:1236: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:1258: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <&6 +echo "configure:1279: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1307: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + +echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 +echo "configure:1333: checking for socket in -lsocket" >&5 +ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + result=yes +else + echo "$ac_t""no" 1>&6 +result=no +fi + +if test "$result" = "yes"; then + LIBS="$LIBS -lsocket" +else + echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6 +echo "configure:1377: checking for connect in -lsocket" >&5 +ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + result=yes +else + echo "$ac_t""no" 1>&6 +result=no +fi + + if test "$result" = "yes"; then + LIBS="$LIBS -lsocket" + fi +fi + +echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6 +echo "configure:1423: checking for crypt in -lcrypt" >&5 +ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lcrypt $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + result=yes +else + echo "$ac_t""no" 1>&6 +result=no +fi + +if test "$result" = "yes"; then + LIBS="$LIBS -lcrypt" +fi + + + + +echo $ac_n "checking whether to enable xferlog""... $ac_c" 1>&6 +echo "configure:1471: checking whether to enable xferlog" >&5 +# Check whether --enable-xferlog or --disable-xferlog was given. +if test "${enable_xferlog+set}" = set; then + enableval="$enable_xferlog" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_XFERLOG 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + +echo $ac_n "checking whether to enable ascii""... $ac_c" 1>&6 +echo "configure:1494: checking whether to enable ascii" >&5 +# Check whether --enable-ascii or --disable-ascii was given. +if test "${enable_ascii+set}" = set; then + enableval="$enable_ascii" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_ASCII 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + +echo $ac_n "checking whether to enable fullscreen""... $ac_c" 1>&6 +echo "configure:1517: checking whether to enable fullscreen" >&5 +# Check whether --enable-fullscreen or --disable-fullscreen was given. +if test "${enable_fullscreen+set}" = set; then + enableval="$enable_fullscreen" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_FULLSCREEN 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + +if test "$enableval" = "no"; then + +echo $ac_n "checking whether to enable fork""... $ac_c" 1>&6 +echo "configure:1542: checking whether to enable fork" >&5 +# Check whether --enable-fork or --disable-fork was given. +if test "${enable_fork+set}" = set; then + enableval="$enable_fork" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_FORK 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + +fi + + +echo $ac_n "checking whether to enable upload""... $ac_c" 1>&6 +echo "configure:1567: checking whether to enable upload" >&5 +# Check whether --enable-upload or --disable-upload was given. +if test "${enable_upload+set}" = set; then + enableval="$enable_upload" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_UPLOAD 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + +echo $ac_n "checking whether to enable stat""... $ac_c" 1>&6 +echo "configure:1590: checking whether to enable stat" >&5 +# Check whether --enable-stat or --disable-stat was given. +if test "${enable_stat+set}" = set; then + enableval="$enable_stat" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_STAT 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + +echo $ac_n "checking whether to enable dcache""... $ac_c" 1>&6 +echo "configure:1613: checking whether to enable dcache" >&5 +# Check whether --enable-dcache or --disable-dcache was given. +if test "${enable_dcache+set}" = set; then + enableval="$enable_dcache" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_DCACHE 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + +echo $ac_n "checking whether to enable message""... $ac_c" 1>&6 +echo "configure:1636: checking whether to enable message" >&5 +# Check whether --enable-message or --disable-message was given. +if test "${enable_message+set}" = set; then + enableval="$enable_message" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_MESSAGE 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + + +echo $ac_n "checking whether to enable shadow""... $ac_c" 1>&6 +echo "configure:1660: checking whether to enable shadow" >&5 +# Check whether --enable-shadow or --disable-shadow was given. +if test "${enable_shadow+set}" = set; then + enableval="$enable_shadow" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_SHADOW 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + + +nonroot_support=yes + +if test "$enableval" = "yes"; then + for ac_hdr in shadow.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1689: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1699: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + nonroot_support=no +else + echo +fi + +if test "$nonroot_support" = "yes"; then + +echo $ac_n "checking whether to enable nonroot""... $ac_c" 1>&6 +echo "configure:1733: checking whether to enable nonroot" >&5 +# Check whether --enable-nonroot or --disable-nonroot was given. +if test "${enable_nonroot+set}" = set; then + enableval="$enable_nonroot" + result=yes +else + result=no +fi + +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define WANT_NONROOT 1 +EOF + +fi +echo "$ac_t""$enableval" 1>&6 + +else + enableval=no +fi + +if test "$enableval" = "yes"; then + echo "configure: warning: " 1>&2 + echo "configure: warning: Please read the README.nonroot file before using --enable-nonroot" 1>&2 + echo "configure: warning: " 1>&2 +else + + echo $ac_n "checking how to get effective uid""... $ac_c" 1>&6 +echo "configure:1765: checking how to get effective uid" >&5 + if test -n "$EUID"; then + B_UID=$EUID + echo "$ac_t""\$EUID ($EUID)" 1>&6 + elif test -n "$euid"; then + B_UID=$euid + echo "$ac_t""\$euid ($euid)" 1>&6 + elif test -n "$UID"; then + B_UID=$UID + echo "$ac_t""\$UID ($UID)" 1>&6 + elif test -n "$uid"; then + B_UID=$uid + echo "$ac_t""\$uid ($uid)" 1>&6 + elif test "`whoami 2>/dev/null`" = "root"; then + B_UID=0 + echo "$ac_t""whoami (root)" 1>&6 + else + # assume we're not root + B_UID=1 + echo "$ac_t""not found" 1>&6 + fi + + if test "$B_UID" -ne 0; then + echo "configure: warning: " 1>&2 + echo "configure: warning: You do not seem to have root privilegies. If you" 1>&2 + echo "configure: warning: want to run BetaFTPD without being root, please" 1>&2 + echo "configure: warning: consider giving the --enable-nonroot flag to" 1>&2 + echo "configure: warning: configure, and read the README.nonroot file." 1>&2 + echo "configure: warning: " 1>&2 + fi +fi + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1798: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1811: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1878: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +echo $ac_n "checking for uid_t""... $ac_c" 1>&6 +echo "configure:1902: checking for uid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])uid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_uid_t=yes +else + rm -rf conftest* + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_uid_t" 1>&6 +if test $ac_cv_type_uid_t = no; then + cat >> confdefs.h <<\EOF +#define uid_t int +EOF + +fi + +echo $ac_n "checking for gid_t""... $ac_c" 1>&6 +echo "configure:1935: checking for gid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_gid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])gid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_gid_t=yes +else + rm -rf conftest* + ac_cv_type_gid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_gid_t" 1>&6 +if test $ac_cv_type_gid_t = no; then + cat >> confdefs.h <<\EOF +#define gid_t int +EOF + +fi + + +for ac_hdr in crypt.h unistd.h time.h sys/time.h errno.h netinet/in.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1972: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1982: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in netinet/ip.h stropts.h sys/conf.h arpa/inet.h sys/filio.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2012: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2022: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in netinet/tcp.h sys/types.h netdb.h glob.h stdio.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2052: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2062: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in stdlib.h stdarg.h stdlib.h string.h strings.h fcntl.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2092: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2102: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in sys/ioctl.h sys/socket.h sys/stat.h sys/param.h signal.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2132: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2142: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in sys/signal.h dirent.h pwd.h grp.h netinet/in_systm.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2172: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2182: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in assert.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2212: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2222: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +ac_safe=`echo "netinet/tcp.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for netinet/tcp.h""... $ac_c" 1>&6 +echo "configure:2251: checking for netinet/tcp.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2261: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + result=yes +else + echo "$ac_t""no" 1>&6 +result=no +fi + + + +if test "$result" = "yes"; then + echo $ac_n "checking if netinet/tcp.h is enough""... $ac_c" 1>&6 +echo "configure:2287: checking if netinet/tcp.h is enough" >&5 + cat > conftest.$ac_ext < +#ifndef TCP_NODELAY + yes +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + result=no +else + rm -rf conftest* + result=yes +fi +rm -f conftest* + + echo "$ac_t""$result" 1>&6 +fi + +if test "$result" = "no"; then + ac_safe=`echo "linux/socket.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for linux/socket.h""... $ac_c" 1>&6 +echo "configure:2313: checking for linux/socket.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2323: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_hdr in sys/poll.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2350: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2360: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +echo $ac_n "checking for poll()""... $ac_c" 1>&6 +echo "configure:2388: checking for poll()" >&5 +cat > conftest.$ac_ext < +#endif + +int main() { + + struct pollfd fds[1]; + fds[0].fd = 0; + fds[0].events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL; + + poll(fds, 1, 1000); + +; return 0; } +EOF +if { (eval echo configure:2407: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + enableval=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + enableval=no +fi +rm -f conftest* +echo "$ac_t""$enableval" 1>&6 + +if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_POLL 1 +EOF + +fi + +ac_safe=`echo "sys/sendfile.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for sys/sendfile.h""... $ac_c" 1>&6 +echo "configure:2428: checking for sys/sendfile.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2438: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + result=yes +else + echo "$ac_t""no" 1>&6 +result=no +fi + + +for ac_hdr in sys/sendfile.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:2465: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2475: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +if test "$result" = "yes"; then + echo $ac_n "checking for Linux sendfile()""... $ac_c" 1>&6 +echo "configure:2504: checking for Linux sendfile()" >&5 + cat > conftest.$ac_ext < +#endif + +#if HAVE_SYS_LIMITS_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_SYS_SENDFILE_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_SYS_UIO_H +#include +#endif +int main() { + + int out_fd = 1, in_fd = 0; + off_t offset = 0; + size_t size = 1024; + + sendfile(out_fd, in_fd, &offset, size); + +; return 0; } +EOF +if { (eval echo configure:2542: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + enableval=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + enableval=no +fi +rm -f conftest* + echo "$ac_t""$enableval" 1>&6 + + if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_LINUX_SENDFILE 1 +EOF + + fi +fi + +ac_safe=`echo "sys/uio.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for sys/uio.h""... $ac_c" 1>&6 +echo "configure:2564: checking for sys/uio.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2574: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + result=yes +else + echo "$ac_t""no" 1>&6 +result=no +fi + +if test "$result" = "yes"; then + echo $ac_n "checking for BSD sendfile()""... $ac_c" 1>&6 +echo "configure:2598: checking for BSD sendfile()" >&5 + cat > conftest.$ac_ext < +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_SYS_SENDFILE_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_SYS_UIO_H +#include +#endif + +int main() { + + int in_fd = 0, out_sock = 3; + off_t offset; + size_t size = 1024; + struct sf_hdtr hdtr; + + hdtr.hdr_cnt = 0; + hdtr.trl_cnt = 0; + + sendfile(in_fd, out_sock, offset, size, &hdtr, &offset, 0); + +; return 0; } +EOF +if { (eval echo configure:2641: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + enableval=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + enableval=no +fi +rm -f conftest* + echo "$ac_t""$enableval" 1>&6 + + if test "$enableval" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_BSD_SENDFILE 1 +EOF + + fi +fi + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir + +trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@FTPD@%$FTPD%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..b280bc1 --- /dev/null +++ b/configure.in @@ -0,0 +1,288 @@ +AC_INIT(cmds.c) +AC_PREFIX_PROGRAM(ftpd) +AC_CONFIG_HEADER(config.h) +AC_PROG_CC + +dnl +dnl Set of libraries to use +dnl +LIBS="" +AC_SUBST(LIBS) + +AC_C_CONST +AC_FUNC_MMAP +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(snprintf vsnprintf) + +dnl +dnl Check if we need -lsocket -- taken from wu-ftpd +dnl +AC_CHECK_LIB(socket,socket,result=yes,result=no) +if test "$result" = "yes"; then + LIBS="$LIBS -lsocket" +else + AC_CHECK_LIB(socket,connect,result=yes,result=no) + if test "$result" = "yes"; then + LIBS="$LIBS -lsocket" + fi +fi + +dnl +dnl Check if we need -lcrypt +dnl +AC_CHECK_LIB(crypt,crypt,result=yes,result=no) +if test "$result" = "yes"; then + LIBS="$LIBS -lcrypt" +fi + +dnl +dnl The following weirdness is to support --disable as well as --enable +dnl (but long live m4!) +dnl +define(ARG_ENABLE_BETAFTPD,[ +AC_MSG_CHECKING(whether to enable $1) +AC_ARG_ENABLE($1,[$2],result=yes,result=no) +if test "$result" = "no"; then + enableval=no +fi + +if test "$enableval" = "yes"; then + $3 +fi +AC_MSG_RESULT($enableval) +]) + +dnl +dnl Check for options +dnl +ARG_ENABLE_BETAFTPD(xferlog,[ --enable-xferlog Enable transfer log support],AC_DEFINE(WANT_XFERLOG)) +ARG_ENABLE_BETAFTPD(ascii,[ --enable-ascii Enable ASCII mode support],AC_DEFINE(WANT_ASCII)) +ARG_ENABLE_BETAFTPD(fullscreen,[ --enable-fullscreen Run in fullscreen mode (implies --disable-fork)],AC_DEFINE(WANT_FULLSCREEN)) + +if test "$enableval" = "no"; then + ARG_ENABLE_BETAFTPD(fork,[ --enable-fork Make the server fork into the background],AC_DEFINE(WANT_FORK)) +fi + +ARG_ENABLE_BETAFTPD(upload,[ --enable-upload Enable upload support],AC_DEFINE(WANT_UPLOAD)) +ARG_ENABLE_BETAFTPD(stat,[ --enable-stat Enable STAT command (see README first)],AC_DEFINE(WANT_STAT)) +ARG_ENABLE_BETAFTPD(dcache,[ --enable-dcache Enable directory listing cache],AC_DEFINE(WANT_DCACHE)) +ARG_ENABLE_BETAFTPD(message,[ --enable-message Enable .message files etc.],AC_DEFINE(WANT_MESSAGE)) + +dnl +dnl The following line _must_ be last... +dnl +ARG_ENABLE_BETAFTPD(shadow,[ --enable-shadow Enable shadow password support],AC_DEFINE(WANT_SHADOW)) + +nonroot_support=yes + +if test "$enableval" = "yes"; then + AC_CHECK_HEADERS(shadow.h) + nonroot_support=no +else + dnl Causes unknown trouble on SunOS, so it's disabled + dnl if test -e "/etc/shadow"; then + dnl AC_MSG_WARN() + dnl AC_MSG_WARN([It looks like you're using shadow passwords, consider]) + dnl AC_MSG_WARN([giving the --enable-shadow flag to configure. Proceeding]) + dnl AC_MSG_WARN([_without_ support for shadow passwords -- you might not]) + dnl AC_MSG_WARN([be able to log in!]) + dnl AC_MSG_WARN() + dnl fi + echo +fi + +if test "$nonroot_support" = "yes"; then + ARG_ENABLE_BETAFTPD(nonroot,[ --enable-nonroot Do not need root access (implies --disable-shadow)],AC_DEFINE(WANT_NONROOT)) +else + enableval=no +fi + +if test "$enableval" = "yes"; then + AC_MSG_WARN() + AC_MSG_WARN([Please read the README.nonroot file before using --enable-nonroot]) + AC_MSG_WARN() +else + +dnl +dnl Try to find our effective userid (use our own C program?) +dnl + AC_MSG_CHECKING(how to get effective uid) + if test -n "$EUID"; then + B_UID=$EUID + AC_MSG_RESULT([\$EUID ($EUID)]) + elif test -n "$euid"; then + B_UID=$euid + AC_MSG_RESULT([\$euid ($euid)]) + elif test -n "$UID"; then + B_UID=$UID + AC_MSG_RESULT([\$UID ($UID)]) + elif test -n "$uid"; then + B_UID=$uid + AC_MSG_RESULT([\$uid ($uid)]) + elif test "`whoami 2>/dev/null`" = "root"; then + B_UID=0 + AC_MSG_RESULT([whoami (root)]) + else + # assume we're not root + B_UID=1 + AC_MSG_RESULT(not found, assuming not root) + fi + + if test "$B_UID" -ne 0; then + AC_MSG_WARN() + AC_MSG_WARN([You do not seem to have root privilegies. If you]) + AC_MSG_WARN([want to run BetaFTPD without being root, please]) + AC_MSG_WARN([consider giving the --enable-nonroot flag to]) + AC_MSG_WARN([configure, and read the README.nonroot file.]) + AC_MSG_WARN() + fi +fi + +AC_CHECK_TYPE(uid_t,int) +AC_CHECK_TYPE(gid_t,int) + +AC_CHECK_HEADERS(crypt.h unistd.h time.h sys/time.h errno.h netinet/in.h) +AC_CHECK_HEADERS(netinet/ip.h stropts.h sys/conf.h arpa/inet.h sys/filio.h) +AC_CHECK_HEADERS(netinet/tcp.h sys/types.h netdb.h glob.h stdio.h) +AC_CHECK_HEADERS(stdlib.h stdarg.h stdlib.h string.h strings.h fcntl.h) +AC_CHECK_HEADERS(sys/ioctl.h sys/socket.h sys/stat.h sys/param.h signal.h) +AC_CHECK_HEADERS(sys/signal.h dirent.h pwd.h grp.h netinet/in_systm.h) +AC_CHECK_HEADERS(assert.h) + +AC_CHECK_HEADER(netinet/tcp.h,result=yes,result=no) + +dnl +dnl linux/socket.h can create some problems on Debian GNU/Linux systems +dnl + +if test "$result" = "yes"; then + AC_MSG_CHECKING(if netinet/tcp.h is enough) + AC_EGREP_CPP(yes, + [#include +#ifndef TCP_NODELAY + yes +#endif + ], result=no, result=yes) + AC_MSG_RESULT($result) +fi + +if test "$result" = "no"; then + AC_CHECK_HEADER(linux/socket.h) +fi + +AC_CHECK_HEADERS(sys/poll.h) + +AC_MSG_CHECKING([for poll()]) +AC_TRY_COMPILE([ +#if HAVE_SYS_POLL_H +#include +#endif +],[ + struct pollfd fds[1]; + fds[0].fd = 0; + fds[0].events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL; + + poll(fds, 1, 1000); +],enableval=yes,enableval=no) +AC_MSG_RESULT($enableval) + +if test "$enableval" = "yes"; then + AC_DEFINE(HAVE_POLL, 1, [Define if you have poll().]) +fi + +dnl +dnl sendfile() is not standard -- we'll have to check the different +dnl versions one by one :-) +dnl +AC_CHECK_HEADER(sys/sendfile.h,result=yes,result=no) + +dnl +dnl For some reason, we have to check it AGAIN... Otherwise it's never defined! +dnl +AC_CHECK_HEADERS(sys/sendfile.h) + +if test "$result" = "yes"; then + AC_MSG_CHECKING([for Linux sendfile()]) + AC_TRY_COMPILE([ +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_SYS_LIMITS_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_SYS_SENDFILE_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_SYS_UIO_H +#include +#endif],[ + int out_fd = 1, in_fd = 0; + off_t offset = 0; + size_t size = 1024; + + sendfile(out_fd, in_fd, &offset, size); +],enableval=yes,enableval=no) + AC_MSG_RESULT($enableval) + + if test "$enableval" = "yes"; then + AC_DEFINE(HAVE_LINUX_SENDFILE, 1, [Define if you have sendfile() with the Linux semantics.]) + fi +fi + +AC_CHECK_HEADER(sys/uio.h,result=yes,result=no) +if test "$result" = "yes"; then + AC_MSG_CHECKING([for BSD sendfile()]) + AC_TRY_COMPILE([ +#if HAVE_SYS_LIMITS_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_SYS_SENDFILE_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_SYS_UIO_H +#include +#endif +],[ + int in_fd = 0, out_sock = 3; + off_t offset; + size_t size = 1024; + struct sf_hdtr hdtr; + + hdtr.hdr_cnt = 0; + hdtr.trl_cnt = 0; + + sendfile(in_fd, out_sock, offset, size, &hdtr, &offset, 0); +],enableval=yes,enableval=no) + AC_MSG_RESULT($enableval) + + if test "$enableval" = "yes"; then + AC_DEFINE(HAVE_BSD_SENDFILE, 1, [Define if you have sendfile() with the BSD semantics.]) + fi +fi + +AC_OUTPUT(Makefile) diff --git a/disp.c b/disp.c new file mode 100644 index 0000000..406e2f9 --- /dev/null +++ b/disp.c @@ -0,0 +1,90 @@ +/* disp.c: BetaFTPD full-screen stats + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#if HAVE_CONFIG_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_STDIO_H +#include +#endif + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_TIME_H +#include +#endif + +#if HAVE_SYS_TIME_H +#include +#endif + +#include + +#if WANT_FULLSCREEN +time_t last_update = 0; + +void update_display(const struct conn * const first_conn) +{ + struct conn *c = first_conn->next_conn; + int i = 0; + time_t now; + + time(&now); + if (now - last_update < 1) return; + last_update = now; + + printf("%c[H", (char)27); /* clear the screen */ + printf("%c[44m \n", (char)27); + printf("%c[44m \n", (char)27); + printf("%c[38m BetaFTPD FTP server \n", (char)27); + printf("%c[44m \n", (char)27); + printf("%c[44m \n", (char)27); + printf("%c[40m", (char)27); + while (c != NULL && ++i < 20) { + const struct ftran * const f = c->transfer; + printf("%4u ", c->sock); + if (f == NULL) { + printf("%-16s %-50s\n", c->username, c->last_cmd); + } else { + char trunc_filename[256]; + strcpy(trunc_filename, f->filename); + trunc_filename[22] = 0; + +#if WANT_UPLOAD + if (f->upload) { + printf("%-16s%-22s%12lu %7.2fkb/s (upl)\n", c->username, + trunc_filename, f->size, (float)(f->pos - c->rest_pos) / + (float)(difftime(now, f->tran_start)) / 1024); + } else +#endif + printf("%-16s%-22s%12lu %7.2fkb/s %.2f%%\n", c->username, + trunc_filename, f->size, (float)(f->pos - c->rest_pos) / + (float)(difftime(now, f->tran_start)) / 1024, + (float)(f->pos) / (float)(f->size) * 100.0f); + } + c = c->next_conn; + } + printf("%79s", ""); +} +#endif diff --git a/doc/CHANGES b/doc/CHANGES new file mode 100644 index 0000000..8057df5 --- /dev/null +++ b/doc/CHANGES @@ -0,0 +1,160 @@ +Version 0.0.8 (the Try-to-do-way-too-much-in-one-release Release, originally the +Portability Release) + +0.0.8 has been through a lot of preview/beta releases, and is a _totally_ +different beast from 0.0.7. As a consequence, there have been many minor +changes from release to release, which are uninteresting in the big picture +and thus has been removed from the list. For a complete list, see the file +CHANGES-0.0.8. + +- Support for not running as root. This implies using its own database of + rights and users. Give --enable-nonroot to configure to enable it. Users + running BetaFTPD as root are not affected by this support in any way. + BetaFTPD is still small with this feature, expect about 1 kB (6%) extra + for database routines and hooks in the main code. + + This is still very experimental. Do not expect this to be better until + _at least_ 0.0.9. +- Fixed a LOT of bugs, some segfaulting, some minor. +- MAJOR performance improvements in almost all areas. One of the main + contributors in this area has been Dan Kegel (see the CREDITS file). +- Some changes to compile better under Debian GNU/Linux. A .deb-file was + ready, but due to lost access of a Debian-system, this was discontinued :-( +- BetaFTPD will now try to install itself (wow! (not)) in the same directory + as the file called `ftpd', if you have that file. +- Support for non-Linux platforms! For now, only SunOS/SVR4 and FreeBSD. + SunOS just barely works, and FreeBSD lacks sendfile() support. Both are + generally untested. +- Gave the version its own name, like Samba does :-) +- If you have mmap(), BetaFTPD will no longer generate temporary files for directory + listings, but generate them in-place in memory. +- Completely restructured the internal listing functions, to make them more + standards-compliant and smaller. (-l and -F flags now supported, but not + -R.) +- Make an EXIT command (must be manually enabled -- NEVER use it on a + production machine, ANYONE will be able to halt your server!) to stop + the server -- useful for profiling, which requires an exit() to generate + profile data. (Be aware that gmon.out will be where the server last did + a chdir() to, though. In a debugging server, you'll know where that is + anyway...) +- strip-exec now has a warning. In addition, it gives -D__OPTIMIZE_SIZE__ + to keep glibc2 (libc6) from adding some inlines. It also kills + -fomit-frame-pointer. So now strip-exec gives a 20 kB executable instead + of 21 kB (one extra kB goes to new functionality) :-) +- Now strips out any non-ASCII chars from the commands (not any parameters), + to fix the ABOR problems. +- Documentation changes. +- Commented all the functions in cmds.c and ftpd.c, to make it easier to get + into the code. +- Implemented the APPE, DELE, MKD, RMD, ABOR, REIN, ACCT, HELP and STAT commands. +- Added makefile dependencies. +- BetaFTPD can now handle non-DES crypt()s, thanks to Beau Kuiper + . +- ASCII mode added, once again thanks to Beau Kuiper . +- Linux sendfile() support (only for mmap()-able machines -- I don't think + any machine has sendfile() but not mmap()). Other systems will be added + later. +- Moved all documentation (except README) into a separate doc/ directory. Added + CREDITS file. +- The internal ls now can cache the listings (--enable-dcache to configure). + Please see the README file for some problems that still exist. This + option requires quite a bit of code (1028 bytes compiled, on my + system), and some RAM for the directory listings and support structure. + I think, however, that the increased performance is worth it. +- BetaFTPD can now use poll() if you have it. This removes the old 1024- + socket limit, as well as reducing the code size by 496 bytes (on my + system). +- Added renaming support. (Whee! Almost full RFC1123 compliance!) +- Changed all copyright strings to say `1999-2000' (well, it only got to + May 2000 before I remembered it...) instead of just `1999'. +- A new option, --enable-message, makes the server show info about README files, + as well as printing any .message or welcome.msg files that may be available. + (This is a quite standard function in other ftpds.) +- Made a simple FTP server test suite (not included, since it's really not good + enough to live its own life as a standalone product), used in testing. +- BetaFTPD now listens on port 21. + +Version 0.0.7: +- Shadow support. (Give --enable-shadow to configure to enable it.) +- No longer ships with precompiled binary -- there are way too many + configurations to take care of now (shadow, fork, etc.). +- Cleans up a connection if no file transfer has been done in 15 minutes. +- More checks in configure script. +- Made README file a bit nicer. + +Version 0.0.6: +- Fixed PASV bug. +- Added upload support! (Give --enable-upload to configure to enable it.) + Not 100% tried and tested yet, and requires extra code a lot of places. + Amazingly enough, it only gives 700 bytes (uncompressed) extra executable + size, and requires about 4 bytes per new connection. +- Fixed cosmetic bug in configure script. +- All .c files now depend on config.h. +- Cleaned up some minor problems in building in a directory different from + the source directory. +- Added metalab as a mirror site. (The official one is still at my homepage, + but I can't offer FTP from there.) +- Added LSM entry. +- Removed bug that prevented the user from sending _anything_ (including + a re-login) if he/she hadn't opened with USER/PASS. +- Fixed some problems with compiling on mmap-less machines. + +Version 0.0.5: +- Fixed problem in tarfile (aargh!), it should (forever) unpack into its own + directory now. +- Made handler_table[] const and static (that means minor advantages in speed + and executable size). +- Added autoconf support. Still not perfect, but much better than nothing. +- Re-added support for mmap-less machines as well. +- Now only licensed under GPL v2 (_not_ any later or earlier version). + +Version 0.0.4: +- Fixed problem where Netscape 4.5 would give double slashes after directory + names. +- Specifying a non-existant user would make the server segfault ;-) +- Implemented non-RFC MDTM command. +- Changed prompt for anonymous users to "Login OK, send password (your e-mail)." + (added 'your' so people won't begin to send their mail passwords :-) +- Fixed a bug where anybody could send a fake user name, and access to a + random uid. (Don't worry, it was unexploitable -- the server crashed due + to another bug...) +- Made the command prototypes in cmds.h a single macro, to reduce source size + and improve readability (?). +- KNOWN-BUGS file added. +- Fixed a problem where the server could segfault (due to writing to free()'d + memory). +- Stripped some sections from the precompiled binary, to reduce size a little + more... (Added a script named `strip-exec' which does just that.) +- Fixed a problem where doing a `CWD /' followed by a `PWD' would raise some + error messages. +- Fixed a _possible_ (I couldn't exploit it) problem if a REST was sent, + greater than the size of the file downloaded, and libc would have some + kind of problem with negative sizes. +- Added some #define's in ftp.h for those who don't need xferlogs and/or + fullscreen. The precompiled binary is supposed to be _small_, so it + disables both. +- Removed all alignment from the precompiled binary (hooray! we're back + at 15k!) +- Compressed precompiled binary with UPX (10k isn't bad...) +- Fixed some bugs, problems with NcFTP etc. (This file got lost, and I + had to restore from a slightly older backup.) +- Started on an RFC-COMPLIANCE file. +- Support for nice, full-screen display (not in precompiled binary). +- Now uses mmap() instead of read() and write(). +- Lots of _real_ testing, in a production environment! That sure fixed + a lot of problems :-) + +Version 0.0.3: +- CHANGES file added... +- Fixed a bug that prevented lynx and IE (?) from getting dir listings. +- Added VERY crude logging (only to /var/log/xferlog for now). It fakes + some of the traditional xferlog fields. (Note: this alone was almost + 1k) +- Accepts "ftp" as well as "anonymous". +- NLST command partly implemented (errr...). +- Cleaned up some error numbers, shortened some messages. +- Added pseudo-commands MODE and STRU. +- Can recognize other home dirs than /home/ftp/ for anonymous users. +- Made FTP port a #define in ftpd.h. +- Ignores information on stdin (libc workaround). +- Globbing support! (ie. you can do things like LIST *.dat) diff --git a/doc/CHANGES-0.0.8 b/doc/CHANGES-0.0.8 new file mode 100644 index 0000000..4820144 --- /dev/null +++ b/doc/CHANGES-0.0.8 @@ -0,0 +1,211 @@ +Complete list of all changes from 0.0.7 to 0.0.8, encompassing 17 prerelases: + +- Support for not running as root. This implies using its own database of + rights and users. Give --enable-nonroot to configure to enable it. Users + running BetaFTPD as root are not affected by this support in any way. + BetaFTPD is still small with this feature, expect about 1 kB (6%) extra + for database routines and hooks in the main code. + + This is still very experimental. Do not expect this to be better until + _at least_ 0.0.9. +- Fixed a segfaulting bug. (Freeing a malloc'ed pointer that had been + incremented obviously wasn't a very good idea...) +- Some autoconf checks (fixes) for libc5. +- Now shows the sticky bit in directory listings. +- Cleaned up error cleanup :-) +- Some changes to compile better under Debian GNU/Linux. A .deb-file was + ready, but due to lost access of a Debian-system, this was discontinued :-( +- BetaFTPD will now try to install itself (wow! (not)) in the same directory + as the file called `ftpd', if you have that file. +- A little better checking for uid (at configure time). +- Support for non-Linux platforms! (For now, SunOS 5.5/SVR4 only. If this + is name confusion, sorry, but the machine belongs to the University + of Oslo, where I don't study, but still have a shell account...) These + changes should help out a _lot_ when it comes to general portability. + (Every single system header file BetaFTPD uses is now checked for. Didn't + have stdio.h? I didn't guess so either... :-) I could probably have used + AC_HEADER_STDC, but I decided to be on the safe side.) + + Still, sadly, only Linux is really supported and working. +- Added README.platforms file. +- Gave the version its own name, like Samba does :-) +- If you have mmap() (it is not strictly needed, but a _lot_ of code + needed was only in place if you used mmap, and I decided to leave it + that way), BetaFTPD will no longer generate temporary files for directory + listings, but generate them in-place in memory. +- Completely restructured the internal listing functions, to make them more + standards-compliant and smaller. (-l and -F flags now supported, but not + -R.) +- Implemented the APPE command, on user request. +- Minor speedups (and I do mean minor). +- The Makefile now uses CFLAGS in linking as well. +- Make an EXIT command (must be manually enabled -- NEVER use it on a + production machine, ANYONE will be able to halt your server!) to stop + the server -- useful for profiling, which requires an exit() to generate + profile data. (Be aware that gmon.out will be where the server last did + a chdir() to, though. In a debugging server, you'll know where that is + anyway...) +- Fixed a segfaulting bug in handling select() errors. The most common case + for this was at loads around 30-40 megabytes/sec :-) +- Fullscreen mode now looks OK for those with more than 80 columns too. +- Fixed some -pedantic warnings. +- Made mwrite() a function instead of just a #define (well, it's still a + define, but code is now shared), to save 30-40 bytes (and perhaps a + tiny bit of speed as well) :-) +- strip-exec now has a warning. In addition, it gives -D__OPTIMIZE_SIZE__ + to keep glibc2 (libc6) from adding some inlines. It also kills + -fomit-frame-pointer. So now strip-exec gives a 20 kB executable instead + of 21 kB (one extra kB goes to new functionality) :-) +- Now strips out any non-ASCII chars from the commands (not any parameters), + to fix the ABOR problems. +- Documentation changes. +- MDTM and SIZE commands do no longer need read access (this is one of the + benefits of some modularization in the code). This is more in line with + other FTP servers' behaviour. +- Commented all the functions in cmds.c and ftpd.c, to make it easier to get + into the code. +- Implemented the DELE, MKD, RMD, ABOR, REIN, ACCT, HELP and STAT commands. Note + that ABOR was previously noted as `fully RFC compliant', but due to an + error, it was not included until now. +- Added makefile dependencies. +- `make clean' and `make distclean' now also cleans up after `strip-exec'. +- NOOP can now be executed without the user being logged in. +- Fixed an autoconf bug, where --enable-shadow could sometimes be mistaken + for --enable-nonroot. +- Fixed a stupid performance bug, which hit in on systems with slightly + lower MTUs than 1500 (actually some with 1500 as well) -- I was telling + the kernel _not_ to delay, and thus ended up sending out 12-byte packets + and other stupidness. BetaFTPD now uses TCP_CORK when available (read: + Linux 2.2.x and up, perhaps some 2.1.x kernels as well), which actually + makes it _better_ than wu-ftpd and most other implementations out there + (I believe those who use sendfile() also get this done right). BIG thanks + to Alan Cox for informing me of this flag, and to the rest of the + linux-kernel mailing list for being generally helpful. +- Fixed some bugs in getting the filesystem block size: Any value over 4096 + would have segfaulted (rather unlikely on most systems). Any blocksize + over 4096 is now simply set to 4096. This value is customizable, by + changing MAX_BLOCK_SIZE in ftpd.h. +- accept() error handling should be more persistent now, before trying to + recreate the server socket. +- server_sock is now non-blocking, just in case. +- Changes (and ideas for changes) by Beau Kuiper : + Fixed an overflow bug (I'm unsure if this was exploitable or not), BetaFTPD + can now handle non-DES crypt()s and a minor optimization was made. +- Fixed a bug where listing would fail after uploading. +- A minor fix for fullscreen mode. +- non-DES crypt() is actually _working_ now (I hope)... +- Memory savings (and slight performance improvements) for non-fullscreen mode + (thanks to Ryan Cumming ). +- ASCII mode added, once again thanks to Beau Kuiper . +- SIZE is now disabled for ASCII mode, as the standards say we can't do an + estimate. +- Fixes for 64-bit platforms. +- A patch for authentication, saving a few bytes per connection and removing crypt() + DES/non-DES problems for ever, by Beau Kuiper (I think you've got his e-mail + address by now...). +- More ftpext compliant REST code, together with some duplicate code removal. +- BetaFTPD no longer crashes if /usr/adm doesn't exist, and xferlog support + is enabled. Also, it will try /var/log before /usr/adm. +- Added CREDITS file. +- The entire do_download function was rewritten, which resulted in a much + smaller and simpler function. In the process, some bugs were also fixed. +- Fixed a bug, where nonroot users couldn't use PORT... +- Added a debugging version of the TRAP_ERROR macro. +- SO_LINGER is no longer set at all. This removes a major performance problem. +- fd -2 is no longer attempted closed. +- Sockets are now only checked for timeouts every minute (at most), which + can reduce spent CPU time a lot (with 200 clients connected, it used to + need 70-80% of the total CPU time for BetaFTPD, now it is close to zero). +- Closes fd 0 on startup. +- Now doesn't hang if fd 0 is assigned to server_sock (aaaaaaaargh). +- Linux sendfile() support (only for mmap()-able machines -- I don't think + any machine has sendfile() but not mmap()). Other systems will be added + later. +- More FreeBSD fixes. sendfile() _soon_ works on FreeBSD :-) +- Nonroot fixes. +- The reading of the control connection is now done with one syscall (recv) + instead of three (recv, ioctl, recv). +- More do_openfile() fixes, for lynx. +- The PASV socket is now set in listening mode _before_ announcing its + address and port number. +- Moved all documentation (except README) into a separate doc/ directory. +- The internal lister now prints years more according to POSIX (dates older + than 6 months or in the future are printed with years instead of + timestamps -- IMHO this is unneccessary, but it's nice to do the same + thing as GNU ls). +- ascii.h is now included (you could still compile ASCII support without + it, it just fixes some warnings) in the distribution. +- The internal ls now can cache the listings (--enable-dcache to configure). + Please see the README file for some problems that still exist. This + option requires quite a bit of code (1028 bytes compiled, on my + system), and some RAM for the directory listings and support structure. + I think, however, that the increased performance is worth it. +- The 64-bit check is dropped in favour of a simple truncate. Sorry, you + can't list files >2GB correctly anymore ;-) +- BetaFTPD can now use poll() if you have it. This removes the old 1024- + socket limit, as well as reducing the code size by 496 bytes (on my + system). +- Many bugfixes and cleanups, by Dan Kegel (see CREDITS). +- Internal changes -- no more ugly `late freeing'. +- If you don't have list access and try to list, `permission denied' is + printed instead of the previous error message being repeated. (This is + actually a workaround for glob()'s weird error handling.) +- Uploading no longer gives weird permissions on the uploaded files. +- Listing a symbolic link now gives the attributes (especially size) of the + file itself, not the link. This is /bin/ls-incompatible, but can be useful + anyways, especially when it comes to anonymous FTP. (I can't think of any + real-life situations where it would break anything either.) +- You can no longer give a negative number to REST. (Thanks to Marten + for bringing this bug to my attention.) +- Fixed a possible bug in the internal listing code, triggered only if listing + a file with a 16-character username. +- If a file in a directory can't be stat()'ed, the internal ls will continue + instead of just returning. +- #ifdef'ed out some recursion-specific code, since the internal ls doesn't + support recursion as a whole. +- Added renaming support. (Whee! Almost full RFC1123 compliance!) +- Changed all copyright strings to say `1999-2000' (well, it only got to + May 2000 before I remembered it...) instead of just `1999'. +- Changed the timeout value from a hardcoded number into a #define'd constant + in ftpd.h. +- Even more recursive-listing code-outcommenting... +- Much of the linked list code is now reused, which saves 60-70 bytes and + increases code simplicity. A very positive side effect is that the dcache now + uses the same, bug-fixed linked list code as connections and file transfers. +- Fixed a few warnings in the configure program, and some in BetaFTPD itself. +- The poll() version now also closes standard input on startup (previously only the + select() version did). +- The code for accepting new clients has been moved into its own function. +- A new option, --enable-message, makes the server show info about README files, + as well as printing any .message or welcome.msg files that may be available. + (This is a quite standard function in other ftpds.) +- Fixed a bug that made shadow passwords not working at all (AGAIN!). Thanks + to Thor Bernhardsen for notifying me of this bug. +- The server didn't clean up connections properly on all errors -- fixed. This + led to all sorts of problems. Thanks to Andre Luiz dos Santos + for bringing this bug (and others, in fact, most of + those fixed in 0.0.8pre14 and 0.0.8pre15) to my attention. +- Fixed a compilation bug on machines without GLOB_NOMATCH defined. +- Fixed more ABOR bugs. +- Made a simple FTP server test suite (not included, since it's really not good + enough to live its own life as a standalone product), used in testing. +- Fixed a bug where multiple successful PORTs or PASVs would leave stale, unused + file descriptors that would never be cleaned up. (Well, previously multiple + PORTs wouldn't work at all -- again, thanks to Andre for mentioning this.) +- Sometimes, when using poll(), the highest-numbered fd would not be checked at + all, leaving a client just waiting for a reply. +- More RFC-compliant MKD handling. +- Removed a non-used variable (port_allocated) from ftpd.h. Also fixed some code + in cmd_pass() that could overwrite the last byte in that variable. +- Fixed a fatal PORT bug (I have no clue at all how this could have gone unnoticed -- + that bug must have been there since 0.0.1, but probably not exposed until now + due to other smaller bugs (?)). +- BetaFTPD writes only to the logfile if it already exists now -- this is so you + can use --enable-xferlog and not create an xferlog file until you really need + logging. Thanks to kromJx for the idea. +- BetaFTPD now listens on port 21. +- Uploading mode made quicker, by eliminating a syscall. Uploading should be + optimal with respect to syscalls now (only a poll/select, a recv and a write + per loop iteration). This also reduces the code size by a few bytes. +- More improvements to upload code. Data received in an upload now goes to a + static buffer (which also has been shrunk) instead of to the stack. diff --git a/doc/COPYING b/doc/COPYING new file mode 100644 index 0000000..e77696a --- /dev/null +++ b/doc/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/doc/CREDITS b/doc/CREDITS new file mode 100644 index 0000000..dc0a7dd --- /dev/null +++ b/doc/CREDITS @@ -0,0 +1,29 @@ +Steinar H. Gunderson +- Wrote most of the code and documentation. + +Beau Kuiper (author of muddleftpd) +- Fixes of all sorts, and ASCII mode support. + +Ryan Cumming +- Minor performance and memory improvements. + +Gianmarco Giovannelli +- Provided access to a FreeBSD system, meaning that such systems + will be supported in the future. + +Justin Hammond +- Leader of the DynamX project, for which I originally wrote the + single-threaded core which later evolved into BetaFTPD. DynamX + does not use any of my code at the moment, but the mailing list + was very helpful in improving my code and at that time. + +Dan Kegel +- Wrote dkftpbench, an FTP benchmarking server program, which in + itself helped me fix a lot of performance bugs. He's also been + very helpful in tracking down and eliminating these bugs, as + well as giving me general hints about stupid code in BetaFTPD. + +In addition, there are numerous users who by themselves may have +done very little, but together have been the driving force behind +the BetaFTPD development. To all of you who have ever sent in +feedback of some sort: Thank you. diff --git a/doc/KNOWN-BUGS b/doc/KNOWN-BUGS new file mode 100644 index 0000000..5246eff --- /dev/null +++ b/doc/KNOWN-BUGS @@ -0,0 +1,32 @@ +List of known bugs and problems: +- Some commands are still not implemented (see the file RFC-COMPLIANCE). There + are very few I actually miss now... None, actually. +- There are several hardcoded limits, instead of using constants such as + PATH_MAX. +- We should move the remaining bugs listed from the source (in the comments) + to this file ;-) +- If a command that requires a parameter doesn't have one, BetaFTPD will send + 500 instead of 501. This behaviour is very useful in the command lookup + (for code simplicity) :-) +- We could (perhaps) use a hash table instead of a straight linear command search? +- REST does not work in ASCII mode. (This is partly as a security measure -- + enabling resuming on ASCII files could need a lot of CPU power if we had to + resume in the middle of a big file, and still stay RFC/ftpext-compliant.) +- In general, nonroot support has problems. For instance, security is generally + weak, and anonymous FTP is not supported (all users will need a password). + Nonroot is not even worked on at the moment. Don't use it :-) +- Occasionally, a directory listing may cause unwanted output in the fullscreen + mode, for some weird reason. +- There are reports of the server not working at all in fullscreen mode. We're + working to find and fix these problems. +- Some features like ftpshut and ftpaccess files (yes, root can ftp :-( ) are + missing. + +Obscure bugs I haven't seen in a long time (probably fixed): +- The server is still known to segfault from time to time, but unfortunately, + most kernel versions won't dump core if the program has changed uid :-( +- Sometimes (after an error) the server doesn't respond to the connection + anymore. Weird. +- Some times, for no reason at all, mmap() on my machine will fail with a + EINPROGRESS, which is a non-legal error code. I'm not sure if this is + a bug in BetaFTPD or glibc :-) diff --git a/doc/README.nonroot b/doc/README.nonroot new file mode 100644 index 0000000..42e232f --- /dev/null +++ b/doc/README.nonroot @@ -0,0 +1,60 @@ +Experimental support for running BetaFTPD without being root (0.0.8) + +Note: THIS IS CODE IN TESTING, AND SHOULD NOT BE CONSIDERED A STABLE +PART OF BETAFTPD IN ANY WAY. (Sorry for the shouting, it was just +in case you were deaf. Well, now you are anyway...) + +BetaFTPD now has support (ahem) for running without root privilegies. +When not running as root, you can't piggyback on the OS anymore, and +have to employ some extra sort of database. More on this later. + +This is currently just a hack, with a lot of stuff missing, and lots of +debugging messages still being in. The reason I want to release it in +this verson, is to get feedback from any users (hello! where are +you?) :-) + +There are several things you must do to get nonroot support working: +1. configure BetaFTPD with `--enable-nonroot'. +2. Define FTP_PORT in ftpd.h to a port over 1024. The default setting + for nonroot users is 12121, so make sure it is available, or choose + another one. Be aware that anybody may pinch this port if you're + not holding on to it... +3. Set up a `betaftpd.users' file in GLOBAL_ROOT_DIR, with the following + format: + +uid username password homedir rootdir gid(s) + +uid: The user's uid (note that these fields do NOT have to match those + in /etc/passwd, you're your own boss here). +username: The user's username, of course. +password: The password, encrypted with crypt(). +homedir: The directory the user starts in. MUST BE UNDER ROOTDIR. +rootdir: The directory the user sees as /. (Note that homedir and + rootdir is seen from a chroot()ed perspective. So if + you had set GLOBAL_ROOT_DIR to be `/betaftpd' and wanted + a user to have the home directory `/betaftpd/foo/', you + would use just `/foo/'.) + + Both directory names must be ended with a slash (/). +gids: Any groups the user is member of, separated with a space. + Should be at least one. + +Note: No blank lines or comments allowed. The fields are in a bit unusual +order. This is to reduce parsing overhead. + +4. Set up a `betaftpd.groups' file in GLOBAL_ROOT_DIR, with the following + format: + +gid groupname + +These fields should be self-explanatory. + +5. Set up a `.rights' file in every directory you want to be accessible. + More details are in the file `README.rights'. + +I hope to add a GUI for all this one day, but for now it is more important +for me to concentrate on the core development of BetaFTPD. + +Good luck! + +/* Steinar */ diff --git a/doc/README.platforms b/doc/README.platforms new file mode 100644 index 0000000..97ece80 --- /dev/null +++ b/doc/README.platforms @@ -0,0 +1,44 @@ +These are platforms that BetaFTPD is known to run on (`Supported' means +that each new major version will be tested on this platform): + +Linux, libc5 +------------ +Slackware 3.5. Compiles, partly tested. Supported. + +Linux, libc6 (i*86-pc-linux-gnu) +-------------------------------- +Slackware 3.4 (upgraded to glibc 2.1) and Debian GNU/Linux 2.1 (glibc 2.0). +Debian is previously tested and known to work well, but since I had to +move the computer away, I can't test it anymore. It should work, though. +Tested and working on Slackware (my main system). Should work fine on most +Linux distributions. + +SunOS 5.5 (sparc-sun-solaris2.5.1) +---------------------------------- +Compiles, runs, not very much testing. + +FreeBSD +------- +Compiles, runs, not very much testing. A user reports that it actually works +well. (No sendfile() support, though.) + +In other words, as of today BetaFTPD works properly on Linux ONLY... If you +feel an urge to change this, please do step forward and offer to port it. I've +got access to both FreeBSD and SunOS-machines (thanks to very friendly people +who've given me shell access), but due to Internet time restrictions and general +lack of time, I really can't take care of all other patforms. + +All others +---------- +Totally unknown. If you want to help out, here are some things that will +help (in descending order): + +1. That you can take responsibility for adjusting each new version of + BetaFTPD to your OS. +2. If #1 isn't possible, just a note that it works (if it works!) or + patches to make the latest version work. (Note that this is now preferred + over #3 -- it wasn't so earlier.) +3. If #2 isn't possible, that I can get (permanent; I'll probably need to + check later versions...) shell access to a computer with that OS. + Optionally, you could always send me a fresh HD with that OS installed :-) + diff --git a/doc/README.rights b/doc/README.rights new file mode 100644 index 0000000..6cb573a --- /dev/null +++ b/doc/README.rights @@ -0,0 +1,20 @@ +The `.rights' file is laid out as follows: + +privilegied.file rw-r----- 0 1 +(filename) (rights) (uid) (gid) + +For any file not in the list, the special case `.default' is checked. If there +is no such file, _no access is permitted_, and the file will _not show up in +directory listings_. This is in fact handy in most cases, so be careful with +adding a `.default' entry. (Note that `.rights' is never influenced by a +`.default' entry, for security.) + +For directory permissions, the file `dir/.rights' is checked for the entry `.'. +Yes, I know, treating them like normal directories would be great, but +remember that there is a root directory as well... + +The rights are standard r, w and x for now, no setuid, setgid or sticky bit +unless we really need it. (We could perhaps need the sticky bit later.) Be +careful with the format of the `.rights' file, as everything you set in the +rights column will be copied directly to listings. Restrict yourself to +those 9 characters, no more, no less, and only use r, w, and x. diff --git a/doc/RFC-COMPLIANCE b/doc/RFC-COMPLIANCE new file mode 100644 index 0000000..0884e59 --- /dev/null +++ b/doc/RFC-COMPLIANCE @@ -0,0 +1,61 @@ +BetaFTPD does not fully meet the RFC959 minimum requirements for an FTP +server. However, for all practical uses, it should be considered a legal +implementation of the FTP protocol, and very close to being fully compliant +with RFC959. + +BetaFTPD is not RFC1123 compliant, but now that renaming is in place, the +only thing that is left (I think) before it is, would be refusing Telnet +commands. I'm not sure if I will ever do this -- I simply don't see that +this could be a problem in today's FTP world. + +These commands are believed to be fully compliant with RFC959 and RFC1123: +PORT, PASV, USER, PASS, CWD, CDUP, QUIT, DELE, PWD, SYST, NOOP, STOR, APPE, +ABOR, RNFR, RNTO, MKD, RMD, ALLO, REIN, ACCT, HELP and STAT. + +These commands are not implemented at all: SMNT, STOU and SITE. + +The rest of the commands are implemented, but have minor quirks or +problems -- see below. + +Telnet signals are ignored, to the best of my knowledge. BetaFTPD does not +speak Telnet, although RFC959 seems to require it. Note that you can still +use Telnet to connect to the FTP port (to do a manual debugging session, +e.a.) and speak raw FTP, but BetaFTPD does not follow _all_ the rules about, +say, Telnet IP and Synch signals. + +TYPE: +The TYPE command is included, but it ignores its argument and always uses +binary mode. RFC959 violation, but RFC1123 excuses the missing ASCII mode. +(If you enable ASCII mode (--enable-ascii to configure), it's RFC959- and +RFC1123-compliant.) + +STRU: +The STRU command is included, but it ignores its argument and always uses +file structure (if you really need record structure, mail me; when I'm done +laughing, I will consider implementing it). RFC959 violation, but RFC1123 +excuses the missing record structure. + +MODE: +The MODE command is included, but it ignores its argument and always uses +stream mode (the other two are never used anyway). RFC959 violation, it +requires all other modes to be refused. + +RETR: +The RETR command is believed to be compliant with RFC959. (There is no default +data port, though -- I'm unsure about this.) + +LIST/NLST: +The LIST and NLST commands ignore some flags (like `-R') given to them. (This +is much better than it was before, though.) The RFCs say nothing about +directory listing formats anyway, but I guess this is a violation of GNU ls :-) + +REST: +The REST command is implemented, but it doesn't check that its argument really +is an integer. REST does not work in ASCII mode. + +SIZE: +The SIZE command (not in any RFC at the moment) does not work in ASCII mode. +This is in compliance with the ftpext documents. If we were to enable it in +ASCII mode, a possible attacker could just enable ASCII mode, do a SIZE on a +big file and thus effectively halt a server for a shorter or longer period of +time. diff --git a/ftpd.c b/ftpd.c new file mode 100644 index 0000000..473624e --- /dev/null +++ b/ftpd.c @@ -0,0 +1,1610 @@ +/* ftpd.c: BetaFTPD main + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* + * Special note: this file has been overwritten by another (0-byte) file, been + * through the dead, and restored (with the help of dd, grep, gpm, vi and less) + * with a sucess rate of 99.9%. Show it a little respect -- don't add junk + * to it. :-) + */ + +#define _GNU_SOURCE + +#if HAVE_CONFIG_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_ERRNO_H +#include +#endif + +#if HAVE_STROPTS_H +#include +#endif + +#if HAVE_SYS_CONF_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#endif + +#if HAVE_STDIO_H +#include +#endif + +#if HAVE_ASSERT_H +#include +#endif + +#if HAVE_STRING_H +#include +#endif + +#if HAVE_STRINGS_H +#include +#endif + +#if HAVE_STDARG_H +#include +#endif + +#if HAVE_STDLIB_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_SYS_IOCTL_H +#include +#endif + +#if HAVE_NETINET_IN_SYSTM_H +#include +#endif + +#if HAVE_NETINET_IP_H +#include +#endif + +#if HAVE_NETINET_TCP_H +#include +#endif + +#if HAVE_LINUX_SOCKET_H +#include +#endif + +#if HAVE_MMAP +#include +#endif + +#if HAVE_TIME_H +#include +#endif + +#if HAVE_SYS_TIME_H +#include +#endif + +#if HAVE_SYS_TIME_H +#include +#endif + +#if HAVE_SYS_FILIO_H +#include +#endif + +#if HAVE_NETDB_H +#include +#endif + +#if HAVE_SIGNAL_H +#include +#endif + +#if HAVE_GLOB_H +#include +#endif + +#if HAVE_SYS_SIGNAL_H +#include +#endif + +#if HAVE_SYS_POLL_H +#include +#endif + +#if HAVE_SYS_SENDFILE_H +#include +#endif + +/* + * does not export this to glibc2 systems, and it isn't + * always defined anywhere else. + */ +#if !defined(TCP_CORK) && defined(__linux__) +#define TCP_CORK 3 +#endif + +#include +#include + +#if WANT_ASCII +#include +#endif + +/* Debug printing -- remove before 0.0.8 final */ +#if 1 +#define DPRINT(s) +#else +#define DPRINT(s) printf s +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED -1 +#endif + +struct conn *first_conn = NULL; +struct ftran *first_ftran = NULL; +#if WANT_DCACHE +struct dcache *first_dcache = NULL; +#endif + +#if HAVE_POLL +unsigned int highest_fds = 0; + +#define FD_MAX 1024 +#define fds_send fds +struct pollfd fds[FD_MAX]; + +#define MAXCLIENTS FD_MAX +#else +fd_set master_fds, master_send_fds; +#define MAXCLIENTS FD_SETSIZE +#endif + +#if WANT_XFERLOG +FILE *xferlog = NULL; +#endif + +/* + * This variable specifies if it's soon time to check for timed out + * clients, and timed out directory listing cache entries. It is + * set to 1 by a signal handler every minute, and set to 0 when the + * checking has been performed. + */ +int time_to_check = 1; + +#ifndef HAVE_SPRINTF +/* + * snprintf(): snprintf() replacement for systems that miss it. Note + * that this implementation does _not_ necessarily protect + * against all buffer overflows. Get a real snprintf() in + * your C library. That being said, the 8k limit is + * substantially larger than any other string in BetaFTPD, + * which should make such an attack harder. + */ +int snprintf(char *str, size_t n, const char *format, ...) +{ + char buf[8192]; + va_list args; + int err; + + va_start(args, format); + err = vsprintf(buf, format, args); + va_end(args); + + buf[(int)n] = 0; + strcpy(str, buf); + + return err; +} +#endif + +#ifndef HAVE_VSNPRINTF +/* + * vsnprintf: vsnprintf() replacement for systems that miss it. Please + * see snprintf (above) for more information. + */ +int vsnprintf(char *str, size_t n, const char *format, va_list ap) +{ + char buf[8192]; + int err; + + err = vsprintf(buf, format, ap); + buf[(int)n] = 0; + strcpy(str, buf); + return err; +} +#endif + +/* + * add_fd(): Add an fd to the set we monitor. Return 0 on success. + * This code is shared between poll() and select() versions. + */ +int add_fd(const int fd, const int events) +{ + DPRINT(("add_fd(%d, %x)\n", fd, events)); +#if HAVE_POLL + if (fd >= FD_MAX) { + printf("add_fd(%d, %x): failed\n", fd, events); + return E2BIG; + } + + fds[fd].fd = fd; + fds[fd].events = events; + if (highest_fds < fd) + highest_fds = fd; +#else + if (fd >= FD_SETSIZE) + return E2BIG; + if (events & POLLIN) + FD_SET(fd, &master_fds); + if (events & POLLOUT) + FD_SET(fd, &master_send_fds); +#endif + return 0; +} + +/* + * del_fd(): Close and remove an fd from the set(s) we monitor. (See also add_fd().) + */ +void del_fd(const int fd) +{ + DPRINT(("del_fd(%d)\n", fd)); +#if HAVE_POLL + if (fd >= FD_MAX) + return; + + fds[fd].fd = -1; + fds[fd].events = 0; + + /* Reduce poll()'s workload by not making it watch past end of array */ + while ((highest_fds > 0) && (fds[highest_fds].fd == -1)) + highest_fds--; +#else + if (fd >= FD_SETSIZE) + return; + FD_CLR(fd, &master_fds); + FD_CLR(fd, &master_send_fds); +#endif + + close(fd); +} + +#if 0 +void list_clients() +{ + struct conn *c = first_conn; + printf("list_clients:\n"); + while (c && c->next_conn) { + c = c->next_conn; + printf("list_clients: fd %d\n", c->sock); + } +} +#endif + +/* + * add_to_linked_list(): + * Inserts an element (conn, ftran or dcache) into its linked list. + * The list is placed at the beginning, right after the (bogus) + * first element of the list. + */ +void add_to_linked_list(struct list_element * const first, + struct list_element * const elem) +{ + elem->prev = first; + + if (first) { + elem->next = first->next; + if (elem->next) elem->next->prev = elem; + first->next = elem; + } else { + /* this is the bogus head of the list */ + elem->next = NULL; + } +} + +/* + * remove_from_linked_list(): + * Removes an element (conn, ftran or dcache) from its linked list, + * then frees it. + */ +void remove_from_linked_list(struct list_element * const elem) +{ + if (elem->prev != NULL) elem->prev->next = elem->next; + if (elem->next != NULL) elem->next->prev = elem->prev; + free(elem); +} + +/* + * alloc_new_conn(): + * Allocates a new control connection (type `struct conn'), + * initializes it, and adds it to the linked list. The connection + * operates on the socket SOCK. + */ +struct conn *alloc_new_conn(const int sock) +{ + const unsigned int one = 1; + struct conn *c = (struct conn *)(malloc(sizeof(struct conn))); + DPRINT(("alloc_new_conn(%d)\n", sock)); + + if (c == NULL) return c; + + if (sock != -1) { + ioctl(sock, FIONBIO, &one); + if (add_fd(sock, POLLIN) != 0) { + /* temp unavail */ + send(sock, "230 Server too busy, please try again later.\r\n", 46, 0); + close(sock); + return NULL; + } + + add_to_linked_list((struct list_element *)first_conn, + (struct list_element *)c); + } else { + /* this is the bogus head of the list */ + c->next_conn = NULL; + c->prev_conn = NULL; + } + + c->transfer = NULL; + c->sock = sock; + c->buf_len = c->auth = c->rest_pos = 0; +#if WANT_ASCII + c->ascii_mode = 0; +#endif + + /* + * equals: + * strcpy(c->curr_dir, "/"); + * strcpy(c->last_cmd, ""); + * strcpy(c->rename_from, "") + */ + c->curr_dir[0] = '/'; +#if WANT_FULLSCREEN + c->curr_dir[1] = c->last_cmd[0] = c->rename_from[0] = '\0'; +#else + c->curr_dir[1] = c->rename_from[0] = '\0'; +#endif + + time(&(c->last_transfer)); + + /*list_clients();*/ + + return c; +} + +/* + * alloc_new_ftran(): + * Allocates a new data connection (type `struct ftran'), and + * adds it to the linked list. The connection operates on the + * socket SOCK, and has the control connection C as its parent. + */ +struct ftran *alloc_new_ftran(const int sock, const struct conn * const c) +{ + struct ftran *f = (struct ftran *)(malloc(sizeof(struct ftran))); + + if (f == NULL) return f; + if (c == NULL) { + /* this is the bogus head of the list */ + f->next_ftran = NULL; + f->prev_ftran = NULL; + } else { + add_to_linked_list((struct list_element *)first_ftran, + (struct list_element *)f); + } + +#if HAVE_MMAP + f->file_data = NULL; +#endif + f->owner = (struct conn * const)c; + f->sock = sock; + f->state = 0; + f->local_file = -1; + +#if WANT_DCACHE + f->dir_cache = NULL; +#endif + + f->dir_listing = 0; + return f; +} + +#if WANT_DCACHE +/* + * alloc_new_dcache(): + * Allocates a new directory cache entry (type `struct dcache'), + * and adds it to the linked list. + */ +struct dcache *alloc_new_dcache() +{ + struct dcache *d = (struct dcache *)(malloc(sizeof(struct dcache))); + + if (d == NULL) return d; + + d->use_count = 0; + d->last_used = 0; + strcpy(d->dir_name, ""); + d->dir_data = NULL; + + add_to_linked_list((struct list_element *)first_dcache, + (struct list_element *)d); + + return d; +} +#endif + +/* + * destroy_conn(): + * Destroy a control connection, remove it from the linked + * list, and clean up after it. + */ +void destroy_conn(struct conn * const c) +{ + DPRINT(("destroy_conn:\n")); + if (c == NULL) return; + del_fd(c->sock); + + destroy_ftran(c->transfer); + remove_from_linked_list((struct list_element *)c); + + DPRINT(("destroy_conn done.\n")); +} + +/* + * destroy_ftran(): + * Destroy a data connection, remove it from the linked list, + * and clean up after it. + * + * For some reason, TCP_CORK (Linux 2.2.x-only) doesn't flush + * even _after_ the socket is closed, so we zero it just before + * closing. We also zero just before sending the last packet, + * as it seems to be needed on some systems. + * + * If you wonder why I check for `defined(SOL_TCP)' and don't + * provide an alternative, see the comments on init_file_transfer(). + */ +void destroy_ftran(struct ftran * const f) +{ + const unsigned int zero = 0; + + if (f == NULL) return; +#if defined(TCP_CORK) && defined(SOL_TCP) + setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero)); +#endif + del_fd(f->sock); + +#if WANT_DCACHE + if (f->dir_cache) { + time(&(f->dir_cache->last_used)); + f->dir_cache->use_count--; + f->dir_cache = NULL; + } else +#endif +#if HAVE_MMAP + if (f->file_data) { + if (f->dir_listing) { + free(f->file_data); + f->file_data = NULL; + } else { + munmap(f->file_data, f->size); + } + } + + if (!f->dir_listing) +#endif + if (f->local_file != -1) close(f->local_file); + +#if !HAVE_MMAP + if (f->dir_listing) unlink(f->filename); +#endif + + f->owner->transfer = NULL; + +#if WANT_DCACHE + if (f->dir_cache != NULL) f->dir_cache->use_count--; +#endif + + remove_from_linked_list((struct list_element *)f); +} + +#if WANT_DCACHE +/* + * destroy_dcache(): + * Destroy a directory listing cache entry, remove it from the + * linked list, and clean up after it. + * + * If you free a cache entry that is in use (use_count > 0), + * BetaFTPD will most likely crash (later). The thing you're supposed + * to do when you're done with a dcache entry, is to decrement + * its use_count, and let the timeout functions do the destroying + * when it's time to do so. + */ +void destroy_dcache(struct dcache * const d) +{ + if (d == NULL) return; + + if (d->dir_data != NULL) free(d->dir_data); + remove_from_linked_list((struct list_element *)d); +} +#endif + +/* + * process_all_clients(): + * Processes all the control connections in active_clients + * (normally returned from a select(), there are at max + * NUM_AC active connections in the set), sending them + * through to the command parser if a command has been + * entered. + */ +#if HAVE_POLL +int process_all_clients(const int num_ac) +#else +int process_all_clients(const fd_set * const active_clients, const int num_ac) +#endif +{ + struct conn *c = NULL, *next = first_conn->next_conn; + int checked_through = 0; + + DPRINT(("process_all_clients: num_ac %d\n", num_ac)); + + /* run through the linked list */ + while (next != NULL && checked_through < num_ac) { + int bytes_avail; + + c = next; + next = c->next_conn; +#if HAVE_POLL + if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) { + destroy_conn(c); + continue; + } + if (!fds[c->sock].revents & POLLIN) { + continue; + } + DPRINT(("process_all_clients: fd %d has POLLIN set\n", c->sock)); +#else + if (!FD_ISSET(c->sock, active_clients)) { + continue; + } +#endif + + checked_through++; + + bytes_avail = recv(c->sock, c->recv_buf + c->buf_len, + 255 - c->buf_len, 0); + if (bytes_avail <= 0) { + /* + * select() has already told us there's something about + * this socket, so if we get a return value of zero, the + * client has closed the socket. If we get a return value + * of -1 (error), we close the socket ourselves. + * + * Just to be safe, we include this code for poll() as + * well. + */ + destroy_conn(c); + continue; + } + + /* overrun = disconnect */ + if (c->buf_len + bytes_avail > 254) { + numeric(c, 503, "Buffer overrun; disconnecting."); + destroy_conn(c); + continue; + } + + c->buf_len += bytes_avail; + parse_command(c); + } + return checked_through; +} + +/* + * process_all_sendfiles(): + * Sends data to all clients that are ready to receive it. + * Also checks for data connections that are newly-connected, + * and handler xferlog entries for the files that are finished. + */ +#if HAVE_POLL +int process_all_sendfiles(const int num_ac) +#else +int process_all_sendfiles(fd_set * const active_clients, const int num_ac) +#endif +{ + struct ftran *f = NULL, *next = first_ftran->next_ftran; + int checked_through = 0; + struct sockaddr tempaddr; + int tempaddr_len = sizeof(tempaddr); + + while (next != NULL && checked_through < num_ac) { + f = next; + next = f->next_ftran; + +#if HAVE_POLL + if (fds[f->sock].revents & (POLLHUP|POLLERR|POLLNVAL)) { + destroy_ftran(f); + continue; + } +#endif + + /* state = 2: incoming PASV, state >3: send file */ +#if HAVE_POLL + if ((f->state < 2) || (f->state == 3) || (fds[f->sock].revents & (POLLIN|POLLOUT)) == 0) { +#else + if ((f->state < 2) || (f->state == 3) || !FD_ISSET(f->sock, active_clients)) { +#endif + continue; + } + + checked_through++; + +#if HAVE_POLL + /* Nothing is needed for the poll() version? */ +#else + FD_CLR(f->sock, active_clients); +#endif + + if (f->state == 2) { /* incoming PASV */ + const unsigned int one = 1; + const int tempsock = accept(f->sock, (struct sockaddr *)&tempaddr, + &tempaddr_len); + + del_fd(f->sock); + + if (tempsock == -1) { + destroy_ftran(f); + continue; + } + + f->sock = tempsock; + ioctl(f->sock, FIONBIO, &one); + init_file_transfer(f); +#if WANT_UPLOAD + if (f->upload) continue; +#endif + } + if (f->state < 5) { + init_file_transfer(f); +#if WANT_UPLOAD + if (f->upload) continue; +#endif + } + + /* for download, we send the first packets right away */ +#if WANT_UPLOAD + if (f->upload) { + if (do_upload(f)) continue; + } else +#endif + if (do_download(f)) continue; + + /* do_{upload,download} returned 0, the transfer is complete */ + numeric(f->owner, 226, "Transfer complete."); + time(&(f->owner->last_transfer)); + +#if WANT_XFERLOG + if (!f->dir_listing) { + write_xferlog(f); + } +#endif + + destroy_ftran(f); +#if WANT_FULLSCREEN + update_display(first_conn); +#endif + } + + return checked_through; +} + +#if WANT_UPLOAD +int do_upload(struct ftran *f) +{ + char upload_buf[16384]; + int avail, size; +#if WANT_ASCII + /* keep buffer size small in ascii transfers + to prevent process stalling while filtering + data on slower computers */ + + /* + * This isn't a big problem, since we won't get + * packets this big anyway, the biggest I've seen + * was 12kB on 100mbit (but that was from a Windows + * machine), so I've reduced the buffer from 64 kB + * to 16 kB :-) --Steinar + */ + const int maxlen = (f->ascii_mode == 1) ? 4096 : 16384; +#else + const int maxlen = 16384; +#endif + + errno = 0; + size = recv(f->sock, upload_buf, maxlen, 0); + if (size >= 0) { + f->pos += size; + } +#if WANT_ASCII + if (size > 0 && f->ascii_mode == 1) { + size = ascii_uploadfilter(upload_buf, size); + } +#endif + if (size > 0 && (write(f->local_file, upload_buf, size) == size)) { + return 1; + } else if (size == -1) { + /* don't write xferlog... or? */ + numeric(f->owner, 426, strerror(errno)); + destroy_ftran(f); + return 1; + } + return 0; +} +#endif + +int do_download(struct ftran *f) +{ +#if defined(TCP_CORK) && defined(SOL_TCP) + unsigned int zero = 0; +#endif + char *sendfrom_buf; + int bytes_to_send; + int more_to_send = 0; + +#if !HAVE_MMAP + char buf[MAX_BLOCK_SIZE]; +#endif +#if WANT_ASCII + char buf2[MAX_BLOCK_SIZE * 2]; +#endif + int size; + +#if HAVE_LINUX_SENDFILE + /* + * We handle the optimal case first, which is sendfile(). + * Here we use a rather simplified sending `algorithm', + * leaving most of the quirks to the system calls. + */ + if (f->dir_listing == 0 +#if WANT_UPLOAD + && f->upload == 0 +#endif + ) { + int err; + size = f->size - f->pos; + + if (size > f->block_size) size = f->block_size; + if (size < 0) size = 0; + +#ifdef TCP_CORK + if (size != f->block_size) { + setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero)); + } +#endif + + err = sendfile(f->sock, f->local_file, &f->pos, size); + return (f->pos < f->size) && (err > -1); + } +#endif + +#if HAVE_MMAP + size = f->size - f->pos; + + if (size > f->block_size) size = f->block_size; + if (size < 0) size = 0; + + bytes_to_send = size; + sendfrom_buf = f->file_data + f->pos; +#else + bytes_to_send = read(f->local_file, buf, f->block_size); + sendfrom_buf = buf; +#endif + + if (bytes_to_send == f->block_size) more_to_send = 1; + +#if WANT_ASCII + if (f->ascii_mode == 1) { + bytes_to_send = ascii_downloadfilter(sendfrom_buf, + buf2, bytes_to_send); + sendfrom_buf = buf2; + } +#endif /* WANT_ASCII */ + +#if defined(TCP_CORK) && defined(SOL_TCP) + /* if we believe this is the last packet, unset TCP_CORK */ + if (more_to_send == 0) { + setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero)); + } +#endif + + size = send(f->sock, sendfrom_buf, bytes_to_send, 0); + if (size < bytes_to_send) more_to_send = 1; + +#if WANT_ASCII + if (f->ascii_mode == 1 && size < bytes_to_send && size > 0) { + size = ascii_findlength(sendfrom_buf, size); + } +#endif + +#if HAVE_MMAP + if (size > 0) f->pos += size; +#endif + + return more_to_send; +} + +#if WANT_XFERLOG +void write_xferlog(struct ftran *f) +{ + char temp[256]; + time_t now = time(NULL); + struct tm *t = localtime(&now); + + if (xferlog == NULL) return; + + strftime(temp, 256, "%a %b %d %H:%M:%S %Y", t); +#if WANT_UPLOAD + fprintf(xferlog, "%s %u %s %lu %s b _ %c a %s ftp 0 * \n", +#else + fprintf(xferlog, "%s %u %s %lu %s b _ o a %s ftp 0 *\n", +#endif + temp, (int)(difftime(now, f->tran_start)), + inet_ntoa(f->sin.sin_addr), f->size, + f->filename, +#if WANT_UPLOAD + (f->upload) ? 'i' : 'o', +#endif + f->owner->username); + fflush(xferlog); + +#if 0 + /* vim needs this to work properly :-( */ + ) +#endif +} +#endif + +#if 0 +/* Reallocate the buggers constantly */ +void screw_clients() +{ + struct conn *c = first_conn; + int maxloops = MAXCLIENTS; + + while (c && c->next_conn) { + struct conn *temp = malloc(sizeof(*temp)); + if (!temp) break; + *temp = *(c->next_conn); + if (temp->transfer) temp->transfer->owner = temp; + memset(c->next_conn, 0, sizeof(struct conn)); + free(c->next_conn); + temp->prev_conn = c; + c->next_conn = temp; + c = c->next_conn; + maxloops--; + assert(maxloops > 0); + } +} +#endif + +/* + * main(): Main function. Does the initialization, and contains + * the main server loop. Takes no command-line arguments + * (see README for justification). + */ +int main(void) +{ + int server_sock; + +#if HAVE_POLL + /* the sets are declared globally if we use poll() */ +#else + fd_set fds, fds_send; +#endif + + /*setlinebuf(stdout);*/ + setvbuf(stdout, (char *)NULL, _IOLBF, 0); + + signal(SIGPIPE, SIG_IGN); + + printf("BetaFTPD version %s, Copyright (C) 1999-2000 Steinar H. Gunderson\n", VERSION); + puts("BetaFTPD comes with ABSOLUTELY NO WARRANTY; for details see the file"); + puts("COPYING. This is free software, and you are welcome to redistribute it"); + puts("under certain conditions; again see the file COPYING for details."); + puts(""); + + /* we don't need stdin */ + close(0); + +#if HAVE_POLL + { + int i; + for (i = 0; i < FD_MAX; i++) { + fds[i].fd = -1; + fds[i].events = 0; + } + } +#else + FD_ZERO(&master_fds); + FD_ZERO(&master_send_fds); +#endif + + server_sock = create_server_socket(); + +#if WANT_FULLSCREEN + printf("%cc", (char)27); /* reset and clear the screen */ +#endif + + /* init dummy first connection */ + first_conn = alloc_new_conn(-1); + first_ftran = alloc_new_ftran(0, NULL); +#if WANT_DCACHE + first_dcache = alloc_new_dcache(); +#endif + +#if WANT_XFERLOG +#if WANT_NONROOT +#warning No xferlog support for nonroot yet +#else + /* open xferlog */ + xferlog = fopen("/var/log/xferlog", "r+"); + if (xferlog == NULL) xferlog = fopen("/usr/adm/xferlog", "r+"); + + if (xferlog != NULL) { + fseek(xferlog, 0L, SEEK_END); + } +#endif +#endif + +#if WANT_FORK + switch (fork()) { + case -1: + perror("fork()"); + puts("fork() failed, exiting"); + exit(0); + case 0: + break; + default: + puts("BetaFTPD forked into the background"); + exit(0); + } +#else + puts("BetaFTPD active"); +#endif + + /* set timeout alarm here (after the fork) */ + alarm(60); + signal(SIGALRM, handle_alarm); + + for ( ;; ) { + int i; +#ifndef HAVE_POLL + struct timeval timeout; +#endif + + /*screw_clients(); //look for memory errors */ + +#if WANT_FULLSCREEN + update_display(first_conn); +#endif + +#if HAVE_POLL + i = poll(fds, highest_fds + 1, 60000); + DPRINT(("poll returns %d\n", i)); +#if 0 + { + int j; + for (j=0; j<=highest_fds; j++) { + if (fds[j].revents) printf("fds[%d].fd %d, .revents %x\n", j, fds[j].fd, fds[j].revents); + } + } +#endif +#else + /* reset fds (gets changed by select()) */ + fds = master_fds; + fds_send = master_send_fds; + + /* + * wait up to 60 secs for any activity + */ + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + i = select(FD_SETSIZE, &fds, &fds_send, NULL, &timeout); +#endif + + if (i == -1) { + if (errno == EBADF) { +#if !HAVE_POLL + /* don't like this, but we have to */ + clear_bad_fds(&server_sock); +#endif + } else if (errno != EINTR) { +#if HAVE_POLL + perror("poll()"); +#else + perror("select()"); +#endif + continue; + } + } + +#if HAVE_POLL + /* fix an invalid server socket */ + if (fds[server_sock].revents & POLLERR) { + del_fd(server_sock); + server_sock = create_server_socket(); + } +#endif + + /* remove any timed out sockets */ + if (time_to_check) { + time_out_sockets(); +#if WANT_DCACHE + time_out_dcache(); +#endif + time_to_check = 0; + } + + if (i <= 0) continue; + +#if HAVE_POLL + i -= process_all_sendfiles(i); + process_all_clients(i); +#else + /* sends are given highest `priority' */ + i -= process_all_sendfiles(&fds_send, i); + + /* incoming PASV connections and uploads */ + i -= process_all_sendfiles(&fds, i); + + /* + * check the incoming PASV connections first, so + * process_all_clients() won't be confused. + */ + process_all_clients(&fds, i); +#endif + +#if HAVE_POLL + if (fds[server_sock].revents & POLLIN) { +#else + if (FD_ISSET(server_sock, &fds)) { +#endif + accept_new_client(&server_sock); + i--; + } + } +} + +/* + * accept_new_client(): + * Open a socket for the new client, say hello and put it in + * among the others. + */ +void accept_new_client(int * const server_sock) +{ + struct sockaddr_in tempaddr; + int tempaddr_len = sizeof(tempaddr); + const int tempsock = accept(*server_sock, (struct sockaddr *)&tempaddr, &tempaddr_len); + + static int num_err = 0; + + if (tempsock < 0) { +#ifndef WANT_FORK + perror("accept()"); +#endif + close(tempsock); + if ((errno == EBADF || errno == EPIPE) && ++num_err >= 3) { + del_fd(*server_sock); + *server_sock = create_server_socket(); + } + } else { + struct conn * const c = alloc_new_conn(tempsock); + num_err = 0; + if (c != NULL) { + numeric(c, 220, "BetaFTPD " VERSION " ready."); +#if WANT_STAT + memcpy(&(c->addr), &tempaddr, sizeof(struct sockaddr)); +#endif + } + } +} + +/* + * time_out_sockets(): + * Times out any socket that has not had any transfer + * in the last 15 minutes (delay not customizable by FTP + * user -- you must change it in ftpd.h). + * + * Note that RFC959 explicitly states that there are no + * `spontaneous' error replies, yet we have to do it to + * get the message through at all. + * + * If we check this list for every accept() call, it's + * actually eating a lot of CPU time, so we only check + * it every minute. We used to do a time() call here, + * but we've changed to do use an alarm() call and set + * the time_to_check_flag in the SIGALRM handler. + */ +RETSIGTYPE handle_alarm(int signum) +{ + time_to_check = 1; + alarm(60); + + /* for libc5 */ + signal(SIGALRM, handle_alarm); +} + +void time_out_sockets() +{ + struct conn *c = NULL, *next = first_conn->next_conn; + time_t now = time(NULL); + + /* run through the linked list */ + while (next != NULL) { + c = next; + next = c->next_conn; + + if ((c->transfer == NULL || c->transfer->state != 5) && + (now - c->last_transfer > TIMEOUT_SECS)) { + /* RFC violation? */ + numeric(c, 421, "Timeout (%u minutes): Closing control connection.", TIMEOUT_SECS/60); + destroy_conn(c); + } + } +} + +#if WANT_DCACHE +/* + * time_out_dcache(): + * Time out expired directory listing cache entries. + * Uses much of the same code as time_out_sockets(). + */ +void time_out_dcache() +{ + struct dcache *d = NULL, *next = first_dcache->next_dcache; + time_t now = time(NULL); + + /* run through the linked list */ + while (next != NULL) { + d = next; + next = d->next_dcache; + + if (d->use_count == 0 && (now - d->last_used > 900)) { + destroy_dcache(d); + } + } +} +#endif + +/* + * remove_bytes(): + * Remove some bytes from the incoming buffer. This gives + * room for new data on the control connection, and should + * be called when the code has finished using the data. + * (This is done automatically for all commands, so you + * normally need not worry about it.) + */ +void remove_bytes(struct conn * const c, const int num) +{ + if (c->buf_len <= num) { + c->buf_len = 0; + } else { + c->buf_len -= num; + memmove(c->recv_buf, c->recv_buf + num, c->buf_len); + } +} + +/* + * numeric(): Sends a numeric FTP reply to the client. Note that + * you can use this command much the same way as you + * would use a printf() (with all the normal %s, %d, + * etc.), since it actually uses printf() internally. + */ +void numeric(struct conn * const c, const int numeric, const char * const format, ...) +{ + char buf[256], fmt[256]; + va_list args; + int i, err; + + snprintf(fmt, 256, "%03u %s\r\n", numeric, format); + + va_start(args, format); + i = vsnprintf(buf, 256, fmt, args); + va_end(args); + + DPRINT((buf)); + err = send(c->sock, buf, i, 0); + if (err == -1 && errno == EPIPE) { + destroy_conn(c); + } +} + +/* + * init_file_transfer(): + * Initiate a data connection for sending. This does not open + * any files etc., just does whatever is needed for the socket, + * if needed. It does, however, send the 150 reply to the client, + * and mmap()s if needed. + * + * Linux systems (others?) define SOL_TCP right away, which saves us + * some grief and code size. Perhaps using getprotoent() is the `right' + * way, but it's bigger :-) (Optionally, we could figure it out at + * configure time, of course...) + * + * For optimal speed, we use the Linux 2.2.x-only TCP_CORK flag if + * possible. Note that this is only defined in the first `arm' -- + * we silently assume that Linux is the only OS supporting this + * flag. This might be an over-generalization, but I it looks like + * we'll have to depend on it other places as well, so we might + * just as well be evil here. + */ +void init_file_transfer(struct ftran * const f) +{ + struct linger ling; + struct conn * const c = f->owner; + const int mode = IPTOS_THROUGHPUT, zero = 0, one = 1; + struct stat buf; + int events; + +#ifdef SOL_TCP + /* we want max throughput */ + setsockopt(f->sock, SOL_IP, IP_TOS, (void *)&mode, sizeof(mode)); + setsockopt(f->sock, SOL_TCP, TCP_NODELAY, (void *)&zero, sizeof(zero)); +#ifdef TCP_CORK + setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&one, sizeof(one)); +#endif +#else + /* should these pointers be freed afterwards? */ + { + getprotoent(); /* legal? */ + { + const struct protoent * const pe_ip = getprotobyname("ip"); + const struct protoent * const pe_tcp = getprotobyname("tcp"); + setsockopt(f->sock, pe_ip->p_proto, IP_TOS, (void *)&mode, sizeof(mode)); + setsockopt(f->sock, pe_tcp->p_proto, TCP_NODELAY, (void *)&zero, sizeof(zero)); + } + endprotoent(); + } +#endif + + if (f->dir_listing) { + f->block_size = MAX_BLOCK_SIZE; + } else { +#if WANT_ASCII + f->ascii_mode = f->owner->ascii_mode; +#endif + + /* find the preferred block size */ + f->block_size = MAX_BLOCK_SIZE; + if (fstat(f->local_file, &buf) != -1 && + buf.st_blksize < MAX_BLOCK_SIZE) { + f->block_size = buf.st_blksize; + } + } + + f->state = 5; + + events = POLLOUT; +#if WANT_UPLOAD + if (f->upload) { + events = POLLIN; + } +#endif /* WANT_UPLOAD */ + + TRAP_ERROR(add_fd(f->sock, events), 500, return); + + ling.l_onoff = 0; + ling.l_linger = 0; + setsockopt(f->sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); + +#if !HAVE_POLL && WANT_UPLOAD + /* + * if we let an upload socket stay in master_send_fds, we would + * get data that would fool us into closing the socket... (sigh) + */ + if (f->upload) { + FD_CLR(f->sock, &master_send_fds); + FD_SET(f->sock, &master_fds); + } +#endif + + time(&(f->owner->last_transfer)); + + if (f->dir_listing) { + /* include size? */ + numeric(f->owner, 150, "Opening ASCII mode data connection for directory listing."); + } else { + /* + * slightly kludged -- perhaps we should kill the second arm, + * at the expense of code size? Or perhaps we could collapse + * the two possible replies into one? + */ +#if WANT_ASCII + if (f->ascii_mode +#if WANT_UPLOAD + || f->upload +#endif /* WANT_UPLOAD */ + ) { + numeric(f->owner, 150, "Opening %s mode data connection for '%s'", + (f->ascii_mode) ? "ASCII" : "BINARY", f->filename); + } else { + numeric(f->owner, 150, "Opening %s mode data connection for '%s' (%u bytes)", + (f->ascii_mode) ? "ASCII" : "BINARY", f->filename, + f->size); + } +#else /* !WANT_ASCII */ +#if WANT_UPLOAD + if (f->upload) { + numeric(f->owner, 150, "Opening BINARY mode data connection for '%s'", f->filename); + } else +#endif /* WANT_UPLOAD */ + numeric(f->owner, 150, "Opening BINARY mode data connection for '%s' (%u bytes)", f->filename, f->size); +#endif /* !WANT_ASCII */ + } + + /* + * This section _could_ in theory be more optimized, but it's + * much easier this way, and hopefully, the compiler will be + * intelligent enough to optimize most of this away. The idea + * is, some modes _require_ use of mmap (or not). The preferred + * thing is using mmap() when we don't have sendfile(), and not + * using mmap() when we have sendfile(). + */ +#if HAVE_MMAP + if (f->dir_listing == 0) { +#if HAVE_LINUX_SENDFILE + int do_mmap = 0; +#else + int do_mmap = 1; +#endif +#if WANT_ASCII + if (f->ascii_mode == 1) do_mmap = 1; +#endif +#if WANT_UPLOAD + if (f->upload == 1) do_mmap = 0; +#endif + + if (do_mmap == 1) { + f->file_data = mmap(NULL, f->size, PROT_READ, MAP_SHARED, f->local_file, 0); + if (f->file_data == MAP_FAILED) f->file_data = NULL; + } else { + f->file_data = NULL; + } + f->pos = f->owner->rest_pos; + } +#else /* !HAVE_MMAP */ + lseek(f->local_file, f->owner->rest_pos, SEEK_SET); +#endif +} + +/* + * create_server_socket(): + * Create and bind a server socket, that we can use to + * listen to new clients on. + */ +int create_server_socket() +{ + int server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + const unsigned int one = 1; + struct sockaddr_in addr; + int err; + + /* + * In the `perfect' world, if an address was in use, we could + * just wait for the kernel to clear everything up, and everybody + * would be happy. But when you just found out your server socket + * was invalid, it has to be `re-made', and 3000 users are trying + * to access your fileserver, I think it's nice that it comes + * up right away... hence this option. + */ + setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + ioctl(server_sock, FIONBIO, &one); /* just in case */ + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(FTP_PORT); + + do { + err = bind(server_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)); + + if (err == -1) { + perror("bind()"); + + /* try to recover from recoverable errors... */ + if (errno == ENOMEM || errno == EADDRINUSE) { + puts("Waiting 1 sec before trying again..."); + sleep(1); + } else { + puts("Giving up."); + exit(1); + } + } + } while (err == -1); + + listen(server_sock, 20); + + err = add_fd(server_sock, POLLIN); + if (err) { + perror("add_fd"); + return -1; + } + + return server_sock; +} + +#if !HAVE_POLL +/* + * clear_bad_fds(): + * Try to find invalid socket descriptors, and clean them. + * The methods used are rather UGLY, but I can't think of + * any good way of checking e.g. server_sock without + * doing anything to it :-( + * + * poll() is able to do this in a much cleaner way, which + * we use if we use poll(). That checking isn't done here, + * though. + */ +void clear_bad_fds(int * const server_sock) +{ + { + fd_set fds; + struct timeval tv = { 0, 0 }; + + FD_ZERO(&fds); + FD_SET(*server_sock, &fds); + if (select(*server_sock, &fds, NULL, NULL, &tv) == -1) { + FD_CLR(*server_sock, &master_fds); + close(*server_sock); + *server_sock = create_server_socket(); + } + } + + /* could do this (conn, ftran) in any order */ + { + struct conn *c = NULL, *next = first_conn->next_conn; + + /* run through the linked list */ + while (next != NULL) { + char buf[1]; + + c = next; + next = c->next_conn; + + if (read(c->sock, &buf, 0) == -1 && + errno == EBADF) { + destroy_conn(c); + } + } + } + + { + struct ftran *f = NULL, *next = first_ftran->next_ftran; + + while (next != NULL) { + char buf[1]; + + f = next; + next = f->next_ftran; + + if (read(f->sock, &buf, 0) == -1 && + errno == EBADF) { + destroy_ftran(f); + } + } + } +} +#endif + +#if WANT_MESSAGE +/* + * dump_file(): Dumps a file on the control connection. Used for + * welcome messages and the likes. Note that outbuf + * is so big, to prevent any crashing from users creating + * weird .message files (like 1024 LFs)... + */ +void dump_file(struct conn * const c, const int num, const char * const filename) +{ + char buf[1024], outbuf[8192]; + char *ptr = outbuf + 4; + int i, j = -1; + + const int dumpfile = open(filename, O_RDONLY); + if (dumpfile == -1) return; + + i = read(dumpfile, buf, 1024); + if (i <= 0) { + close(dumpfile); + return; + } + + sprintf(outbuf, "%03u-", num); + while (++j < i) { + *ptr++ = buf[j]; + if (buf[j] == '\n') { + sprintf(ptr, "%03u-", num); + ptr += 4; + } + } + *ptr++ = '\n'; + + send(c->sock, outbuf, ptr - outbuf, 0); + close(dumpfile); +} + + +/* + * list_readme(): + * Lists all README file in the current (ie. OS current) + * directory, in a 250- message. + */ +void list_readmes(struct conn * const c) +{ + glob_t pglob; + const time_t now = time(NULL); + int i; + + if (glob("README*", 0, NULL, &pglob) == 0) { + for (i = 0; i < pglob.gl_pathc; i++) { + const char * const temp = pglob.gl_pathv[i]; + struct stat buf; + char str[2048]; + + char *tm; + + if (stat(temp, &buf) == -1) continue; + + /* remove trailing LF */ + tm = ctime(&buf.st_mtime); + tm[strlen(tm) - 1] = 0; + + sprintf(str, "250-Please read the file %s\r\n" + "250-\tIt was last modified %s - %ld days ago\r\n", + temp, tm, + (now - buf.st_mtime) / 86400); + send(c->sock, str, strlen(str), 0); + } + globfree(&pglob); + } +} +#endif + diff --git a/ftpd.h b/ftpd.h new file mode 100644 index 0000000..c479154 --- /dev/null +++ b/ftpd.h @@ -0,0 +1,227 @@ +/* ftpd.h: Prototypes for BetaFTPD + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* + * This is the port you want BetaFTPD to listen on. The standard + * FTP port is 21 -- if you really want to use BetaFTPD as your + * primary FTP server, change FTP_PORT. + */ +#if WANT_NONROOT +#define FTP_PORT 12121 +#else +#define FTP_PORT 21 +#endif + +/* + * This is the number of seconds an idle connection is allowed to + * remain idle (`idle' is defined as `no activity on the data socket', + * more or less) without getting shut down. This is not accurate, + * as such delays are only checked for every 60 seconds. + * + * The default (15 minutes) should be OK for most people. + */ +#define TIMEOUT_SECS 900 + +/* + * This is the maximum block size you think you need. (This will most + * likely be the block size of your filesystem, and you're not likely + * to need a bigger number than this, unless your TCP stack likes + * big send()s better than small ones, and still manages to `interleave' + * the framents.) If this value is too small, your performance would be + * slightly worse, but it would still work. Try to keep it at a power of + * two -- most (read: all) FS block sizes _are_ powers of two. If you + * set it too high, it won't affect performance much -- you would just + * use a bit more memory. + */ +#define MAX_BLOCK_SIZE 4096 + +#if HAVE_LINUX_SENDFILE && !HAVE_MMAP +#warning sendfile() without mmap() is not supported -- disabling sendfile() +#undef HAVE_LINUX_SENDFILE +#endif + +#if WANT_DCACHE && !HAVE_MMAP +#warning directory cache requires use of mmap() -- disabling directory cache +#undef WANT_DCACHE +#endif + +struct list_options { +/* int recursive; */ + int long_listing; + int classify; +}; + +/* + * General structure for the doubly linked lists (conn, ftran, dcache). + * This is used only by the generic linked list code (which inserts and + * removes elements from the lists). + */ +struct list_element { + struct list_element *prev; + struct list_element *next; + + /* structure specific data here */ +}; + +/* doubly linked list of active connections */ +struct conn { + struct conn *prev_conn; + struct conn *next_conn; + + int sock; +#if WANT_STAT + struct sockaddr addr; +#endif + char recv_buf[256]; +#if WANT_FULLSCREEN + char last_cmd[256]; +#endif + char rename_from[256]; + + int buf_len; + int auth; + + char username[17]; + + uid_t uid; + char root_dir[256]; + char curr_dir[256]; + + struct ftran *transfer; + + int rest_pos; +#if WANT_ASCII + int ascii_mode; +#endif + + time_t last_transfer; +}; + +#if WANT_DCACHE +/* doubly linked list of cached directory listings */ +struct dcache { + struct dcache *prev_dcache; + struct dcache *next_dcache; + + int use_count; + time_t last_used; + time_t generated; + + char dir_name[256]; + char pattern[256]; + struct list_options lo; + + char *dir_data; + int dir_size; +}; +#endif + +/* doubly linked list of file transfers */ +struct ftran { + struct ftran *prev_ftran; + struct ftran *next_ftran; + struct conn *owner; + + int state; /* + * 0 = none, 1 = got PASV addr, + * 2 = waiting on PASV socket, + * 3 = got PORT addr, 4 = waiting for + * PORT connect, + * 5 = transferring file (or waiting + * for PORT connect) + */ + struct sockaddr_in sin; + int sock; + int dir_listing; +#if WANT_DCACHE + struct dcache *dir_cache; +#endif +#if WANT_ASCII + int ascii_mode; +#endif + char filename[256]; + time_t tran_start; + long int size; + + int local_file; + int block_size; + +#if HAVE_MMAP + char *file_data; /* mmap'ed */ +#endif + long int pos; + +#if WANT_UPLOAD + int upload; + int append; +#endif +}; + +void add_to_linked_list(struct list_element * const first, + struct list_element * const elem); +void remove_from_linked_list(struct list_element * const elem); + +struct conn *alloc_new_conn(const int sock); +struct ftran *alloc_new_ftran(const int sock, const struct conn * const c); +#if WANT_DCACHE +struct dcache *alloc_new_dcache(); +#endif + +int add_fd(const int fd, const int events); +void del_fd(const int fd); + +void destroy_conn(struct conn * const c); +void destroy_ftran(struct ftran * const f); +#if WANT_DCACHE +void destroy_dcache(struct dcache * const d); +#endif + +#if HAVE_POLL +int process_all_clients(const int num_ac); +int process_all_sendfiles(const int num_ac); +#else +int process_all_clients(const fd_set * const active_clients, const int num_ac); +int process_all_sendfiles(fd_set * const active_clients, const int num_ac); +#endif + +int do_upload(struct ftran *f); +int do_download(struct ftran *f); +void write_xferlog(struct ftran *f); +int main(void); + +RETSIGTYPE handle_alarm(int signum); + +void accept_new_client(int * const server_sock); +void time_out_sockets(); +#if WANT_DCACHE +void time_out_dcache(); +#endif + +void remove_bytes(struct conn * const c, const int i); +void numeric(struct conn * const c, const int numeric, const char * const format, ...); +void init_file_transfer(struct ftran * const f); +int create_server_socket(); + +#if !HAVE_POLL +void clear_bad_fds(int * const server_sock); +#endif + +#if WANT_MESSAGE +void dump_file(struct conn * const c, const int num, const char * const filename); +void list_readmes(struct conn * const c); +#endif + diff --git a/nonroot.c b/nonroot.c new file mode 100644 index 0000000..6280557 --- /dev/null +++ b/nonroot.c @@ -0,0 +1,251 @@ +/* nonroot.c: Various database functions for rights and users + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* + * make autoconf changeable!!! + */ +#define USERS_FILE "/home/betaftpd/betaftpd.users" +#define GROUP_FILE "/home/betaftpd/betaftpd.groups" + +#if HAVE_CONFIG_H +#include +#endif + +#if WANT_NONROOT + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_STDIO_H +#include +#endif + +#if HAVE_STRING_H +#include +#endif + +#if HAVE_STRINGS_H +#include +#endif + +#if HAVE_CONFIG_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_STDLIB_H +#include +#endif + +#if HAVE_CRYPT_H +#include +#endif + +#include + +struct users { + char username[24]; + char password[14]; + char homedir[128]; + uid_t uid; + gid_t gid; +}; + +/* we will add cacheing of both users and rights LATER :-) */ + +int nr_userinfo(const char * const username, int * const uid, + char * const homedir, char * const rootdir, + const char * const password) +{ + FILE *users_file = fopen(USERS_FILE, "r"); + char this_username[256]; + char real_password[256]; + + if (users_file == NULL) return 0; /* panic, reject all users */ + + /* + * ignores gids atm, we may want to change that in the future + */ + while (!feof(users_file)) { + fscanf(users_file, "%d %s %s %s %s %*[^\n]\n", + uid, this_username, real_password, homedir, rootdir); + if (strcmp(this_username, username) != 0) continue; + + fclose(users_file); + + printf("pw = %s\n", real_password); + printf("uid = %u\n", *uid); + printf("hdir = %s\n", homedir); + printf("rdir = %s\nEND\n", rootdir); + + if (strcmp(real_password, crypt(password, real_password)) == 0) { + return 3; + } else { + return 0; + } + } + + fclose(users_file); + return 0; /* no such user */ +} + +/* + * Thank goodness for the Unix inventors, who invented all those nice flags! :-) + * Reduces my code size a lot :-) + */ +int nr_check_permission(const uid_t uid, const char * const object, + const int perm, const int is_dir, + char * const ret_rights) +{ + char temp[256]; + + if (is_dir) { + snprintf(temp, 256, "%s/.rights", object); + return nr_intperm(uid, temp, ".", perm, ret_rights); + } else { + char *ptr; + + snprintf(temp, 256, "%s", object); /* non-overflow */ + + ptr = strrchr(temp, '/'); + if (ptr == NULL) { + return nr_intperm(uid, "./.rights", temp, perm, ret_rights); + } else { + char temp2[256]; + ptr[0] = 0; + + snprintf(temp2, 256, "%s/.rights", temp); + return nr_intperm(uid, temp2, ptr + 1, perm, ret_rights); + } + } +} + +int nr_intperm(const uid_t uid, const char * const rightfile, + const char * const entry, const int perm, + char * const ret_rights) +{ + FILE *rights = fopen(rightfile, "r"); + char default_rights[] = "---------"; + char this_entry[256], these_rights[16], check_rights[16], *ptr; + int rights_assigned = 0; + uid_t this_uid, check_uid; + gid_t this_gid, check_gid; + + printf("Checking permission %u for uid %d, file `%s', entry `%s'\n", + perm, uid, rightfile, entry); + + { + char buf[256]; + getcwd(buf, 256); + printf("cwd is `%s'\n", buf); + } + + if (rights == NULL) perror(rightfile); + + if (ret_rights != NULL) strcpy(ret_rights, default_rights); + if (rights == NULL) return -1; /* no rights file -- no permission */ + + while (!feof(rights)) { + fscanf(rights, "%s %s %d %d\n", this_entry, these_rights, + &this_uid, &this_gid); + + /* + * a bit (a BIT?) ugly, perhaps (PERHAPS?) + * note: no typo, it's supposed to be a single `=' sign + */ + if ((strcmp(this_entry, entry) == 0 && (rights_assigned = 1)) || + (strcmp(this_entry, ".default") == 0 && + strcmp(this_entry, ".rights") != 0 && + rights_assigned == 0)) { + if (ret_rights != NULL) strcpy(ret_rights, these_rights); + + strcpy(check_rights, these_rights); + check_uid = this_uid; + check_gid = this_gid; + if (rights_assigned == 1) break; + rights_assigned = 1; + } + } + + if (rights_assigned == 0) puts("no entry! denying...\n"); + if (rights_assigned == 0) return -1; /* no entry, no access */ + + /* if we're only looking for at entry, return OK now */ + if (perm == 0) { + puts("Only peeking; OK\n"); + return 0; + } + + /* now on to the actual checking... */ + ptr = check_rights; + if (perm != 4) ptr++; /* check the right bits */ + if (perm == 1) ptr++; + + printf("Actual rights are `%s', filtered: `%s'\n", check_rights, ptr); + + if (ptr[6] != '-') return 0; /* all users */ + if (uid == check_uid && ptr[0] != '-') return 0; /* user owner */ + if (member_of_group(uid, check_gid) && ptr[3] != '-') return 0; + /* member of group */ + + return -1; /* no access */ +} + +int member_of_group(const uid_t uid, const gid_t gid) +{ + printf("For now, `%s' is not member of `%s'\n", + nr_get_uname(uid), nr_get_gname(gid)); + return 0; +} + +char username[256], groupname[256]; /* static buffer to return */ + +char *nr_get_uname(const uid_t uid) +{ + FILE *users = fopen(USERS_FILE, "r"); + uid_t uid_this; + + if (users == NULL) return "error"; /* panic */ + + while (!feof(users)) { + if (fscanf(users, "%d %s %*[^\n]\n", &uid_this, username) < 2) continue; + if (uid_this == uid) return username; + } + snprintf(username, 256, "%d", uid); + return username; +} + +char *nr_get_gname(const uid_t gid) +{ + FILE *group = fopen(GROUP_FILE, "r"); + gid_t gid_this; + + if (group == NULL) return "error"; /* panic */ + + while (!feof(group)) { + if (fscanf(group, "%d %s\n", &gid_this, groupname) < 2) continue; + if (gid_this == gid) return groupname; + } + snprintf(groupname, 256, "%d", gid); + return groupname; +} + +#endif /* !WANT_NONROOT */ diff --git a/nonroot.h b/nonroot.h new file mode 100644 index 0000000..fd23084 --- /dev/null +++ b/nonroot.h @@ -0,0 +1,30 @@ +/* nonroot.h: Header file for nonroot.c + Copyright (C) 1999-2000 Steinar H. Gunderson + + This program is is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 if the + License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +int nr_userinfo(const char * const username, int * const uid, + char * const homedir, char * const rootdir, + const char * const password); +int nr_check_permission(const uid_t uid, const char * const object, + const int perm, const int is_dir, + char * const ret_rights); +int nr_intperm(const uid_t uid, const char * const rightfile, + const char * const entry, const int perm, + char * const ret_rights); +int member_of_group(const uid_t uid, const gid_t gid); +char *nr_get_uname(const uid_t uid); +char *nr_get_gname(const gid_t gid); + diff --git a/strip-exec b/strip-exec new file mode 100755 index 0000000..fef2b5d --- /dev/null +++ b/strip-exec @@ -0,0 +1,52 @@ +# +# strip-exec: Try to build the smallest executable possible from the current +# source. +# + +# +# Slack 3.4 standard gcc -- change if you need to, but gcc 2.7.2.3 typically +# generates much smaller code than egcs/pgcc +# +CC="gcc -V2.7.2.3 -bi486-unknown-linux-gnulibc1" +CFLAGS="-O2 -D__OPTIMIZE_SIZE__ -m486" +SECTIONS=".note.ABI-tag .gnu.version .rel.got .dtors .comment .note" +EXEC=betaftpd + +rm -f *.s + +# +# Warning +# +echo "***" +echo "*** WARNING: This script is intended for advanced users only, and" +echo "*** is highly experimental. It will most likely fail -- please do" +echo "*** not complain if it does." +echo "***" + +# +# Compile +# +make distclean +CC="$CC" CFLAGS="$CFLAGS" ./configure --enable-dcache +make assembly-files + +# +# -malign-* doesn't remove _all_ .align occurrences :-) +# +for FILE in *.s; do + echo "Removing alignment from $FILE..." + grep -v .align < $FILE > `basename $FILE .s`2.s + mv `basename $FILE .s`2.s $FILE +done + +make betaftpd-from-assembly-files +strip --strip-all $EXEC + +for SECTION in $SECTIONS; do + echo Stripping $SECTION... + strip --remove-section=$SECTION $EXEC +done + +upx --best betaftpd +ls -l betaftpd + -- 2.39.2