From: sgunderson Date: Tue, 25 Jul 2000 23:38:21 +0000 (+0000) Subject: Initial revision X-Git-Url: https://git.sesse.net/?p=betaftpd;a=commitdiff_plain;h=4b83f8e50792b459dfd8a6ffe470c2fccb524e7b Initial revision --- 4b83f8e50792b459dfd8a6ffe470c2fccb524e7b 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 +