From eb3efcc4507a53fa42a49838bacc005a80f1390e Mon Sep 17 00:00:00 2001 From: Cassowary Rusnov Date: Thu, 27 Apr 2023 12:44:23 -0700 Subject: [PATCH] Import from upstream 2.29 --- FILES | 51 + INSTALL | 40 + Makefile.in | 173 ++ README | 31 + TODO | 80 + aclocal.m4 | 188 ++ cgi-bin/printenv | 29 + cgi-src/Makefile.in | 86 + cgi-src/phf.c | 69 + cgi-src/redirect.8 | 79 + cgi-src/redirect.c | 215 ++ cgi-src/ssi.8 | 142 ++ cgi-src/ssi.c | 763 ++++++ config.guess | 693 ++++++ config.h | 392 +++ config.sub | 927 +++++++ configure | 2782 +++++++++++++++++++++ configure.in | 138 + contrib/redhat-rpm/thttpd.conf | 14 + contrib/redhat-rpm/thttpd.init | 47 + contrib/redhat-rpm/thttpd.spec | 154 ++ extras/Makefile.in | 86 + extras/htpasswd.1 | 16 + extras/htpasswd.c | 218 ++ extras/makeweb.1 | 34 + extras/makeweb.c | 256 ++ extras/syslogtocern | 68 + extras/syslogtocern.8 | 45 + fdwatch.c | 834 +++++++ fdwatch.h | 85 + index.html | 14 + install-sh | 250 ++ libhttpd.c | 4279 ++++++++++++++++++++++++++++++++ libhttpd.h | 291 +++ match.c | 88 + match.h | 36 + mime_encodings.txt | 8 + mime_types.txt | 198 ++ mmc.c | 531 ++++ mmc.h | 55 + scripts/500.thttpd-rotate | 19 + scripts/thttpd.sh | 47 + scripts/thttpd_wrapper | 23 + strerror.c | 38 + tdate_parse.c | 328 +++ tdate_parse.h | 33 + thttpd.8 | 596 +++++ thttpd.c | 2181 ++++++++++++++++ timers.c | 351 +++ timers.h | 109 + version.h | 9 + 51 files changed, 18219 insertions(+) create mode 100644 FILES create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 README create mode 100644 TODO create mode 100644 aclocal.m4 create mode 100755 cgi-bin/printenv create mode 100644 cgi-src/Makefile.in create mode 100644 cgi-src/phf.c create mode 100644 cgi-src/redirect.8 create mode 100644 cgi-src/redirect.c create mode 100644 cgi-src/ssi.8 create mode 100644 cgi-src/ssi.c create mode 100755 config.guess create mode 100644 config.h create mode 100755 config.sub create mode 100755 configure create mode 100644 configure.in create mode 100644 contrib/redhat-rpm/thttpd.conf create mode 100755 contrib/redhat-rpm/thttpd.init create mode 100644 contrib/redhat-rpm/thttpd.spec create mode 100644 extras/Makefile.in create mode 100644 extras/htpasswd.1 create mode 100644 extras/htpasswd.c create mode 100644 extras/makeweb.1 create mode 100644 extras/makeweb.c create mode 100755 extras/syslogtocern create mode 100644 extras/syslogtocern.8 create mode 100644 fdwatch.c create mode 100644 fdwatch.h create mode 100644 index.html create mode 100755 install-sh create mode 100644 libhttpd.c create mode 100644 libhttpd.h create mode 100644 match.c create mode 100644 match.h create mode 100644 mime_encodings.txt create mode 100644 mime_types.txt create mode 100644 mmc.c create mode 100644 mmc.h create mode 100755 scripts/500.thttpd-rotate create mode 100755 scripts/thttpd.sh create mode 100755 scripts/thttpd_wrapper create mode 100644 strerror.c create mode 100644 tdate_parse.c create mode 100644 tdate_parse.h create mode 100644 thttpd.8 create mode 100644 thttpd.c create mode 100644 timers.c create mode 100644 timers.h create mode 100644 version.h diff --git a/FILES b/FILES new file mode 100644 index 0000000..d43ff22 --- /dev/null +++ b/FILES @@ -0,0 +1,51 @@ +FILES +INSTALL +Makefile.in +README +TODO +aclocal.m4 +cgi-bin/printenv +cgi-src/Makefile.in +cgi-src/redirect.8 +cgi-src/redirect.c +cgi-src/phf.c +cgi-src/ssi.8 +cgi-src/ssi.c +config.guess +config.h +config.sub +configure +configure.in +extras/Makefile.in +extras/htpasswd.1 +extras/htpasswd.c +extras/makeweb.1 +extras/makeweb.c +extras/syslogtocern +extras/syslogtocern.8 +index.html +install-sh +libhttpd.c +libhttpd.h +match.c +match.h +mime_encodings.txt +mime_types.txt +mmc.c +mmc.h +strerror.c +tdate_parse.c +tdate_parse.h +thttpd.8 +thttpd.c +fdwatch.c +fdwatch.h +timers.c +timers.h +version.h +scripts/500.thttpd-rotate +scripts/thttpd.sh +scripts/thttpd_wrapper +contrib/redhat-rpm/thttpd.spec +contrib/redhat-rpm/thttpd.init +contrib/redhat-rpm/thttpd.conf diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..a9c9e9b --- /dev/null +++ b/INSTALL @@ -0,0 +1,40 @@ +To build: + + % ./configure + + Edit config.h to change the configuration options if necessary. + + % make + + +To install: + + % make install + + Edit one of your system rc files to run thttpd at boot time. Do NOT + run it from inetd, that setup is inefficient so thttpd doesn't support it. + + +Red Hat: + + On Red Hat Linux systems you can use RPM to install thttpd, like so: + + cd /usr/src/redhat/SOURCES + wget http://www.acme.com/software/thttpd/thttpd-2.29.tar.gz + rpm -ta thttpd-2.29.tar.gz + rpm -i /usr/src/redhat/RPMS/i386/thttpd-2.29-1.i386.rpm + + +Solaris: + + If you're running Solaris and you want to use the security-enhancing + chroot feature, then you must create the TCP device files in the chroot + tree. There is no way around this, Solaris needs these files to accept + network connections. You need /dev/tcp, which is a symbolic link like so: + /dev/tcp -> ../devices/pseudo/clone@0:tcp + And you also need the file it points to: + crw-rw-rw- bin 11, 42 May 24 21:32 /devices/pseudo/clone@0:tcp + You probably need some other files too, such as shared libraries and + a tmp directory. Check out the man page for ftpd, it has a big long + shell script for setting up an anonymous ftp area that should also + work for thttpd. diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..ded71e0 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,173 @@ +# Makefile.in for thttpd +# +# Copyright © 1995,1998 by Jef Poskanzer . +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +# Various configurable paths (remember to edit Makefile.in, not Makefile) + +# Top level hierarchy. +prefix = @prefix@ +exec_prefix = @exec_prefix@ +# Pathname of directory to install the binary. +BINDIR = @sbindir@ +# Pathname of directory to install the man page. +MANDIR = @mandir@ +# Pathname of directory to install the CGI programs. +WEBDIR = $(prefix)/www + +# CONFIGURE: The group that the web directory belongs to. This is so that +# the makeweb program can be installed set-group-id to that group, and make +# subdirectories. If you're not going to use makeweb, ignore this. +WEBGROUP = www + +# CONFIGURE: Directory for CGI executables. +CGIBINDIR = $(WEBDIR)/cgi-bin + +# You shouldn't need to edit anything below here. + +CC = @CC@ +CCOPT = @V_CCOPT@ +DEFS = @DEFS@ +INCLS = -I. +CFLAGS = $(CCOPT) $(DEFS) $(INCLS) +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +NETLIBS = @V_NETLIBS@ +INSTALL = @INSTALL@ + +@SET_MAKE@ + +.c.o: + @rm -f $@ + $(CC) $(CFLAGS) -c $*.c + +SRC = thttpd.c libhttpd.c fdwatch.c mmc.c timers.c match.c tdate_parse.c + +OBJ = $(SRC:.c=.o) @LIBOBJS@ + +ALL = thttpd + +GENHDR = mime_encodings.h mime_types.h + +CLEANFILES = $(ALL) $(OBJ) $(GENSRC) $(GENHDR) + +SUBDIRS = cgi-src extras + +all: this subdirs +this: $(ALL) + +thttpd: $(OBJ) + @rm -f $@ + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(NETLIBS) + +mime_encodings.h: mime_encodings.txt + rm -f mime_encodings.h + sed < mime_encodings.txt > mime_encodings.h \ + -e 's/#.*//' -e 's/[ ]*$$//' -e '/^$$/d' \ + -e 's/[ ][ ]*/", 0, "/' -e 's/^/{ "/' -e 's/$$/", 0 },/' + +mime_types.h: mime_types.txt + rm -f mime_types.h + sed < mime_types.txt > mime_types.h \ + -e 's/#.*//' -e 's/[ ]*$$//' -e '/^$$/d' \ + -e 's/[ ][ ]*/", 0, "/' -e 's/^/{ "/' -e 's/$$/", 0 },/' + + +subdirs: + for i in $(SUBDIRS) ; do ( \ + cd $$i ; \ + pwd ; \ + $(MAKE) $(MFLAGS) \ + WEBDIR=$(WEBDIR) \ + CGIBINDIR=$(CGIBINDIR) \ + MANDIR=$(MANDIR) \ + WEBGROUP=$(WEBGROUP) \ + ) ; done + + +install: installthis install-man installsubdirs + +installthis: + -mkdir -p $(DESTDIR)$(BINDIR) + $(INSTALL) -m 555 -o bin -g bin thttpd $(DESTDIR)$(BINDIR) + +install-man: + -mkdir -p $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -m 444 -o bin -g bin thttpd.8 $(DESTDIR)$(MANDIR)/man8 + +installsubdirs: + for i in $(SUBDIRS) ; do ( \ + cd $$i ; \ + pwd ; \ + $(MAKE) $(MFLAGS) \ + WEBDIR=$(WEBDIR) \ + CGIBINDIR=$(CGIBINDIR) \ + MANDIR=$(MANDIR) \ + WEBGROUP=$(WEBGROUP) \ + install \ + ) ; done + + +clean: cleansubdirs + rm -f $(CLEANFILES) + +distclean: distcleansubdirs + rm -f $(CLEANFILES) Makefile config.cache config.log config.status tags + +cleansubdirs: + for i in $(SUBDIRS) ; do ( \ + cd $$i ; \ + pwd ; \ + $(MAKE) $(MFLAGS) clean \ + ) ; done + +distcleansubdirs: + for i in $(SUBDIRS) ; do ( \ + cd $$i ; \ + pwd ; \ + $(MAKE) $(MFLAGS) distclean \ + ) ; done + +tags: + ctags -wtd *.c *.h + +tar: + @name=`sed -n -e '/SERVER_SOFTWARE/!d' -e 's,.*thttpd/,thttpd-,' -e 's, .*,,p' version.h` ; \ + rm -rf $$name ; \ + mkdir $$name ; \ + tar cf - `cat FILES` | ( cd $$name ; tar xfBp - ) ; \ + chmod 644 $$name/Makefile.in $$name/config.h $$name/mime_encodings.txt $$name/mime_types.txt ; \ + chmod 755 $$name/cgi-bin $$name/cgi-src $$name/contrib $$name/contrib/redhat-rpm $$name/extras $$name/scripts ; \ + tar cf $$name.tar $$name ; \ + rm -rf $$name ; \ + gzip $$name.tar + +thttpd.o: config.h version.h libhttpd.h fdwatch.h mmc.h timers.h match.h +libhttpd.o: config.h version.h libhttpd.h mime_encodings.h mime_types.h \ + mmc.h timers.h match.h tdate_parse.h +fdwatch.o: fdwatch.h +mmc.o: mmc.h libhttpd.h +timers.o: timers.h +match.o: match.h +tdate_parse.o: tdate_parse.h diff --git a/README b/README new file mode 100644 index 0000000..a63f49e --- /dev/null +++ b/README @@ -0,0 +1,31 @@ + thttpd - tiny/turbo/throttling HTTP server + version 2.29 of 23May2018 + +thttpd is a simple, small, portable, fast, and secure HTTP server. + +Simple: It handles only the minimum necessary to implement HTTP/1.1. + +Small: See the size comparison chart at +http://www.acme.com/software/thttpd/notes.html#sizes. It also has a +very small run-time size, since it does not fork and is very careful about +memory allocation. + +Portable: It compiles cleanly on FreeBSD 2.x/3.x, SunOS 4.1.x, Solaris 2.x, +BSD/OS 2.x, Linux 1.2.x, OSF/1 (on a 64-bit Alpha), and no doubt many others. + +Fast: In typical use it's about as fast as the best full-featured servers +(Apache, NCSA, Netscape). Under extreme load it's much faster. + +Secure: It goes to great lengths to protect the web server machine +against attacks and breakins from other sites. + +It also has one extremely useful feature (URL-traffic-based throttling) that +no other server currently has. + +See the manual entry for more details. See the INSTALL file for +configuration and installation instructions. Check the web page +(http://www.acme.com/software/thttpd/) for updates, or add yourself to +the mailing list by sending a "subscribe" to thttpd-announce-request@mail.acme.com. + +Comments to: + Jef Poskanzer jef@mail.acme.com http://www.acme.com/jef/ diff --git a/TODO b/TODO new file mode 100644 index 0000000..ab5dd21 --- /dev/null +++ b/TODO @@ -0,0 +1,80 @@ +- - - - - - - - - - high priority - - - - - - - - - - + +IPv6 not working right. + +Problem with ACME News downloads. PATH_INFO interferes with the authorization. + +Why is the client's IP address showing up in paths? + +Fetches with numeric IP addresses and no Host: header are screwing up the +vhost code? +143.90.193.229 - - [06/Apr/2000:09:21:34 -0700] "GET /209.133.38.22/software/thttpd/ HTTP/1.0" 200 12093 "http://www.dbphotography.demon.co.uk/index.html" "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)" +143.90.193.229 - - [06/Apr/2000:09:21:37 -0700] "GET /143.90.193.229/software/thttpd/anvil_thttpd.gif HTTP/1.0" 403 - "http://www.acme.com/software/thttpd/" "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)" + +Have directory indexing skip files that start with dot? Except ..? +In libhttpd.c: ++ if (*(de->d_name) == '.' && *(de->d_name+1) != '.') ++ continue; + namlen = NAMLEN(de); + +Add comment on INDEX_NAMES that it should be simple filenames only. + +The error page generated for non-local referers should include the +original URL as an active link. + +Make open in mmc.c use O_NONBLOCK flag, to prevent DOS attack via +a named pipe? + +- - - - - - - - - - later - - - - - - - - - - + +Document how symlinks interact with .htpasswd - authorization is checked +on the result of the symlink, and not the origin. + +SIGHUP log re-opening doesn't work if you started as root. + +Change redirect to put the Refresh command in the HTTP headers, instead of +a META tag. + +Add TCP_NODELAY, but after CGIs get spawned. + +Add stat cache? 1 minute expiry? + +Ifdef the un-close-on-exec CGI thing for Linux only. + +Add keep-alives, via a new state in thttpd.c. + +- - - - - - - - - - someday - - - - - - - - - - + +The special world-permissions checking is probably bogus. For one +thing, it doesn't handle restrictive permissions on parent directories +properly. It should probably just go away. + +redirect should interpret a path with a trailing / as /index.html + +ssi should change $cwd to the source document's location. + +Allow .throttle files in individual directories. + +Log-digesting scripts. + +Config web page. + Common errors: + Not realizing that -c overrides CGI_PATTERN instead of augmenting it. + Using a directory name for the -c pattern. + +- - - - - - - - - - 3.x - - - - - - - - - - + +Tasklets re-write. + +- - - - - - - - - - general - - - - - - - - - - + +Release process: + - update version number in version.h README INSTALL and + contrib/redhat-rpm/thttpd.spec + - do a tdiff and update the local installation + - do an rcstreeinfo, and check in all files + - make tar + - mv it to .. + - update version number in ../thttpd.html + - update ~acmeweb/updates.html + - mail announcement to thttpd-announce diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..ff35a25 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,188 @@ +dnl +dnl Improved version of AC_CHECK_LIB +dnl +dnl Thanks to John Hawkinson (jhawk@mit.edu) +dnl +dnl usage: +dnl +dnl AC_LBL_CHECK_LIB(LIBRARY, FUNCTION [, ACTION-IF-FOUND [, +dnl ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]]) +dnl +dnl results: +dnl +dnl LIBS +dnl + +define(AC_LBL_CHECK_LIB, +[AC_MSG_CHECKING([for $2 in -l$1]) +dnl Use a cache variable name containing both the library and function name, +dnl because the test really is for library $1 defining function $2, not +dnl just for library $1. Separate tests with the same $1 and different $2's +dnl may have different results. +ac_lib_var=`echo $1['_']$2['_']$5 | sed 'y%./+- %__p__%'` +AC_CACHE_VAL(ac_cv_lbl_lib_$ac_lib_var, +[ac_save_LIBS="$LIBS" +LIBS="-l$1 $5 $LIBS" +AC_TRY_LINK(dnl +ifelse([$2], [main], , dnl Avoid conflicting decl of main. +[/* Override any gcc2 internal prototype to avoid an error. */ +]ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus +extern "C" +#endif +])dnl +[/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $2(); +]), + [$2()], + eval "ac_cv_lbl_lib_$ac_lib_var=yes", + eval "ac_cv_lbl_lib_$ac_lib_var=no") +LIBS="$ac_save_LIBS" +])dnl +if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$3], , +[changequote(, )dnl + ac_tr_lib=HAVE_LIB`echo $1 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` +changequote([, ])dnl + AC_DEFINE_UNQUOTED($ac_tr_lib) + LIBS="-l$1 $LIBS" +], [$3]) +else + AC_MSG_RESULT(no) +ifelse([$4], , , [$4 +])dnl +fi +]) + +dnl +dnl AC_LBL_LIBRARY_NET +dnl +dnl This test is for network applications that need socket() and +dnl gethostbyname() -ish functions. Under Solaris, those applications +dnl need to link with "-lsocket -lnsl". Under IRIX, they need to link +dnl with "-lnsl" but should *not* link with "-lsocket" because +dnl libsocket.a breaks a number of things (for instance: +dnl gethostbyname() under IRIX 5.2, and snoop sockets under most +dnl versions of IRIX). +dnl +dnl Unfortunately, many application developers are not aware of this, +dnl and mistakenly write tests that cause -lsocket to be used under +dnl IRIX. It is also easy to write tests that cause -lnsl to be used +dnl under operating systems where neither are necessary (or useful), +dnl such as SunOS 4.1.4, which uses -lnsl for TLI. +dnl +dnl This test exists so that every application developer does not test +dnl this in a different, and subtly broken fashion. + +dnl It has been argued that this test should be broken up into two +dnl seperate tests, one for the resolver libraries, and one for the +dnl libraries necessary for using Sockets API. Unfortunately, the two +dnl are carefully intertwined and allowing the autoconf user to use +dnl them independantly potentially results in unfortunate ordering +dnl dependancies -- as such, such component macros would have to +dnl carefully use indirection and be aware if the other components were +dnl executed. Since other autoconf macros do not go to this trouble, +dnl and almost no applications use sockets without the resolver, this +dnl complexity has not been implemented. +dnl +dnl The check for libresolv is in case you are attempting to link +dnl statically and happen to have a libresolv.a lying around (and no +dnl libnsl.a). +dnl +AC_DEFUN(AC_LBL_LIBRARY_NET, [ + # Most operating systems have gethostbyname() in the default searched + # libraries (i.e. libc): + AC_CHECK_FUNC(gethostbyname, , + # Some OSes (eg. Solaris) place it in libnsl: + AC_LBL_CHECK_LIB(nsl, gethostbyname, , + # Some strange OSes (SINIX) have it in libsocket: + AC_LBL_CHECK_LIB(socket, gethostbyname, , + # Unfortunately libsocket sometimes depends on libnsl. + # AC_CHECK_LIB's API is essentially broken so the + # following ugliness is necessary: + AC_LBL_CHECK_LIB(socket, gethostbyname, + LIBS="-lsocket -lnsl $LIBS", + AC_CHECK_LIB(resolv, gethostbyname), + -lnsl)))) + AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket, , + AC_LBL_CHECK_LIB(socket, socket, LIBS="-lsocket -lnsl $LIBS", , + -lnsl))) + # DLPI needs putmsg under HPUX so test for -lstr while we're at it + AC_CHECK_LIB(str, putmsg) + ]) + +dnl +dnl Checks to see if struct tm has the BSD tm_gmtoff member +dnl +dnl usage: +dnl +dnl AC_ACME_TM_GMTOFF +dnl +dnl results: +dnl +dnl HAVE_TM_GMTOFF (defined) +dnl +AC_DEFUN(AC_ACME_TM_GMTOFF, + [AC_MSG_CHECKING(if struct tm has tm_gmtoff member) + AC_CACHE_VAL(ac_cv_acme_tm_has_tm_gmtoff, + AC_TRY_COMPILE([ +# include +# include ], + [u_int i = sizeof(((struct tm *)0)->tm_gmtoff)], + ac_cv_acme_tm_has_tm_gmtoff=yes, + ac_cv_acme_tm_has_tm_gmtoff=no)) + AC_MSG_RESULT($ac_cv_acme_tm_has_tm_gmtoff) + if test $ac_cv_acme_tm_has_tm_gmtoff = yes ; then + AC_DEFINE(HAVE_TM_GMTOFF) + fi]) + +dnl +dnl Checks to see if int64_t exists +dnl +dnl usage: +dnl +dnl AC_ACME_INT64T +dnl +dnl results: +dnl +dnl HAVE_INT64T (defined) +dnl +AC_DEFUN(AC_ACME_INT64T, + [AC_MSG_CHECKING(if int64_t exists) + AC_CACHE_VAL(ac_cv_acme_int64_t, + AC_TRY_COMPILE([ +# include ], + [int64_t i64], + ac_cv_acme_int64_t=yes, + ac_cv_acme_int64_t=no)) + AC_MSG_RESULT($ac_cv_acme_int64_t) + if test $ac_cv_acme_int64_t = yes ; then + AC_DEFINE(HAVE_INT64T) + fi]) + +dnl +dnl Checks to see if socklen_t exists +dnl +dnl usage: +dnl +dnl AC_ACME_SOCKLENT +dnl +dnl results: +dnl +dnl HAVE_SOCKLENT (defined) +dnl +AC_DEFUN(AC_ACME_SOCKLENT, + [AC_MSG_CHECKING(if socklen_t exists) + AC_CACHE_VAL(ac_cv_acme_socklen_t, + AC_TRY_COMPILE([ +# include +# include ], + [socklen_t slen], + ac_cv_acme_socklen_t=yes, + ac_cv_acme_socklen_t=no)) + AC_MSG_RESULT($ac_cv_acme_socklen_t) + if test $ac_cv_acme_socklen_t = yes ; then + AC_DEFINE(HAVE_SOCKLENT) + fi]) diff --git a/cgi-bin/printenv b/cgi-bin/printenv new file mode 100755 index 0000000..cb5df7e --- /dev/null +++ b/cgi-bin/printenv @@ -0,0 +1,29 @@ +#!/bin/sh + +date=`date -u '+%a, %d %b %Y %H:%M:%S %Z'` + +cat << EOF +Content-type: text/plain +Expires: $date + +CGI printenv + +EOF + +echo 'Date:' +date +echo +echo 'Id:' +id +echo +echo 'Env:' +printenv +echo +if [ "$CONTENT_LENGTH" != "" ] ; then + if [ "$CONTENT_LENGTH" -ne 0 ] ; then + echo 'Input:' + echo + dd bs=1 count=$CONTENT_LENGTH + echo + fi +fi diff --git a/cgi-src/Makefile.in b/cgi-src/Makefile.in new file mode 100644 index 0000000..6e4e9ba --- /dev/null +++ b/cgi-src/Makefile.in @@ -0,0 +1,86 @@ +# Makefile for cgi-src +# +# Copyright © 1995 by Jef Poskanzer . +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +WEBDIR = $(prefix)/www +CGIBINDIR = $(WEBDIR)/cgi-bin +MANDIR = @mandir@ + +CC = @CC@ +CCOPT = @V_CCOPT@ +DEFS = @DEFS@ +INCLS = -I.. +CFLAGS = $(CCOPT) $(DEFS) $(INCLS) +LDFLAGS = @LDFLAGS@ @V_STATICFLAG@ +LIBS = @LIBS@ +NETLIBS = @V_NETLIBS@ +INSTALL = @INSTALL@ + +CLEANFILES = *.o redirect ssi phf + +@SET_MAKE@ + +.c.o: + @rm -f $@ + $(CC) $(CFLAGS) -c $*.c + +all: redirect ssi phf + +redirect: redirect.o + $(CC) $(LDFLAGS) $(STATICFLAG) redirect.o $(LIBS) -o redirect + +ssi: ssi.o ../match.o + $(CC) $(LDFLAGS) $(STATICFLAG) ssi.o ../match.o $(LIBS) -o ssi + +ssi.o: ../match.h + +phf: phf.o + $(CC) $(LDFLAGS) $(STATICFLAG) phf.o $(LIBS) -o phf + +strerror.o: + @rm -f strerror.o + @ln -s ../strerror.o + cd .. ; $(MAKE) $(MFLAGS) strerror.o + +install: all + -mkdir -p $(CGIBINDIR) + rm -f $(CGIBINDIR)/redirect + cp redirect $(CGIBINDIR)/redirect + rm -f $(MANDIR)/man8/redirect.8 + cp redirect.8 $(MANDIR)/man8/redirect.8 + rm -f $(CGIBINDIR)/ssi + cp ssi $(CGIBINDIR)/ssi + rm -f $(MANDIR)/man8/ssi.8 + cp ssi.8 $(MANDIR)/man8/ssi.8 + rm -f $(CGIBINDIR)/phf + cp phf $(CGIBINDIR)/phf + +clean: + rm -f $(CLEANFILES) + +distclean: + rm -f $(CLEANFILES) Makefile diff --git a/cgi-src/phf.c b/cgi-src/phf.c new file mode 100644 index 0000000..6d06b65 --- /dev/null +++ b/cgi-src/phf.c @@ -0,0 +1,69 @@ +/* phf - cracker trap +** +** Old distributions of the NCSA and Apache web servers included a +** version of the phf program that had a bug. The program could +** easily be made to run arbitrary shell commands. There is no real +** legitimate use for phf, so any attempts to run it must be considered +** to be attacks. Accordingly, this version of phf logs the attack +** and then returns a page indicating that phf doesn't exist. +** +** +** Copyright © 1996 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "config.h" + +static char* argv0; + +int +main( int argc, char* argv[] ) + { + char* cp; + + argv0 = argv[0]; + cp = strrchr( argv0, '/' ); + if ( cp != (char*) 0 ) + ++cp; + else + cp = argv0; + openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY ); + syslog( LOG_CRIT, "phf CGI probe from %s", getenv( "REMOTE_ADDR" ) ); + (void) printf( "\ +Content-type: text/html\n\ +Status: 404/html\n\ +\n\ +404 Not Found\n\ +

404 Not Found

\n\ +The requested object does not exist on this server.\n\ +The link you followed is either outdated, inaccurate,\n\ +or the server has been instructed not to let you have it.\n\ +\n" ); + exit( 0 ); + } diff --git a/cgi-src/redirect.8 b/cgi-src/redirect.8 new file mode 100644 index 0000000..69a96e1 --- /dev/null +++ b/cgi-src/redirect.8 @@ -0,0 +1,79 @@ +.TH redirect 8 "23 September 1995" +.SH NAME +redirect - simple redirection CGI program +.SH SYNOPSIS +.B redirect +.SH DESCRIPTION +.PP +Three steps to set up a redirection: +.PP +1. Make sure your web server is set up to allow CGI programs. +.PP +2. Make a symbolic link from the file or directory you want to redirect, +pointing at this program in the CGI bin directory. +.PP +3. Add an entry to the file ".redirects" in the directory where your +http server runs CGI programs. For most servers, this is the +directory where the given CGI program lives. The format of the +file is a bunch of lines with a filename, whitespace, and the new +URL. For example: +.nf + /test/oldfile.html http://www.acme.com/test/newfile.html +.fi +The easiest way to figure out precisely what filename to put into .redirects +is to set up the symlink and then click on it. +You'll get back a "404 Not Found" page which includes the filename +as received by the redirect program, and that's what you want to use. +.PP +You can also add a wildcard specification to redirect whole groups of files. +For example: +.nf + /wildtest/* http://www.acme.com/test- +.fi +will cause an access to the /wildtest/somefile.html to be redirected to +http://www.acme.com/test-somefile.html. (Note that the asterisk need not +be preceded by a slash.) +.PP +Note: this is designed for thttpd (http://www.acme.com/software/thttpd/) +and using it with other web servers may require some hacking. A possible +gotcha is with the symbolic link from the old file pointing at this +script - servers other than thttpd may not allow that link to be run +as a CGI program, because they don't check the link to see that it +points into the allowed CGI directory. +.SH "SEE ALSO" +thttpd(8) +.SH "BUGS / DEFICIENCIES" +.PP +It would be really cool to have this program look for +the .redirects file in the same directory as the file being redirected, +instead of in the binaries directory. Unfortunately, this appears +to be impossible with the information CGI gives, plus the non-standardized +but widespread practice of running CGI programs in the directory where +the binary lives. Perhaps CGI 1.2 will address this. +.PP +The wildcard mechanism is very primitive. +In particular, any characters that follow the asterisk are blithely +ignored. +.SH AUTHOR +Copyright © 1995 by Jef Poskanzer . +All rights reserved. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. diff --git a/cgi-src/redirect.c b/cgi-src/redirect.c new file mode 100644 index 0000000..d1caac0 --- /dev/null +++ b/cgi-src/redirect.c @@ -0,0 +1,215 @@ +/* redirect - simple redirection CGI program +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +/* Three steps to set up a redirection: +** 1. Make sure your web server is set up to allow CGI programs. +** 2. Make a symbolic link from the file you want to redirect, +** pointing at this program in the CGI bin directory. +** 3. Add an entry to the file ".redirects" in the directory where your +** http server runs CGI programs. For most servers, this is the +** directory where the given CGI program lives. The format of the +** file is a bunch of lines with a filename, whitespace, and the new +** URL. For example: + +/test/oldfile.html http://www.acme.com/test/newfile.html + +** The easiest way to figure out precisely what filename to put into +** .redirects is to set up the symlink and then click on it. You'll get +** back a "404 Not Found" page which includes the filename as received by +** the redirect program, and that's what you want to use. +** +** Note: this is designed for thttpd (http://www.acme.com/software/thttpd/) +** and using it with other web servers may require some hacking. A possible +** gotcha is with the symbolic link from the old file pointing at this +** script - servers other than thttpd may not allow that link to be run +** as a CGI program, because they don't check the link to see that it +** points into the allowed CGI directory. +** +** Note two: It would be really cool to have this program look for +** the .redirects file in the same directory as the file being redirected, +** instead of in the binaries directory. Unfortunately, this appears +** to be impossible with the information CGI gives, plus the non-standardized +** but widespread practice of running CGI programs in the directory where +** the binary lives. Perhaps CGI 1.2 will address this. +*/ + +#include + +#include +#include +#include + +#include "config.h" + + +static char* argv0; + + +static void +internal_error( char* reason ) + { + char* title = "500 Internal Error"; + + (void) printf( "\ +Status: %s\n\ +Content-type: text/html\n\ +\n\ +%s\n\ +

%s

\n\ +Something unusual went wrong during a redirection request:\n\ +
\n\ +%s\n\ +
\n\ +\n", title, title, title, reason ); + } + + +static void +not_found( char* script_name ) + { + char* title = "404 Not Found"; + + (void) printf( "\ +Status: %s\n\ +Content-type: text/html\n\ +\n\ +%s\n\ +

%s

\n\ +The requested filename, %s, is set up to be redirected to another URL;\n\ +however, the new URL has not yet been specified.\n\ +\n", title, title, title, script_name ); + } + + +static void +moved( char* script_name, char* url ) + { + char* title = "Moved"; + + (void) printf( "\ +Location: %s\n\ +Content-type: text/html\n\ +\n\ +%s\n\ +

%s

\n\ +The requested filename, %s, has moved to a new URL:\n\ +%s.\n\ +\n", url, title, title, script_name, url, url ); + } + + +int +main( int argc, char** argv ) + { + char* script_name; + char* path_info; + char* cp; + FILE* fp; + char *star; + char buf[5000], file[5000], url[5000]; + + argv0 = argv[0]; + + /* Get the name that we were run as, which is the filename being + ** redirected. + */ + script_name = getenv( "SCRIPT_NAME" ); + if ( script_name == (char*) 0 ) + { + internal_error( "Couldn't get SCRIPT_NAME environment variable." ); + exit( 1 ); + } + + /* Append the PATH_INFO, if any. This allows redirection of whole + ** directories. + */ + path_info = getenv( "PATH_INFO" ); + if ( path_info != (char*) 0 ) + { + cp = (char*) malloc( strlen( script_name ) + strlen( path_info ) + 1 ); + if ( cp == (char*) 0 ) + { + internal_error( "Out of memory." ); + exit( 1 ); + } + (void) sprintf( cp, "%s%s", script_name, path_info ); + script_name = cp; + } + + /* Open the redirects file. */ + fp = fopen( ".redirects", "r" ); + if ( fp == (FILE*) 0 ) + { + internal_error( "Couldn't open .redirects file." ); + exit( 1 ); + } + + /* Search the file for a matching entry. */ + while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 ) + { + /* Remove comments. */ + cp = strchr( buf, '#' ); + if ( cp != (char*) 0 ) + *cp = '\0'; + /* Skip leading whitespace. */ + cp = buf; + cp += strspn( cp, " \t" ); + /* Check for blank line. */ + if ( *cp != '\0' ) + { + /* Parse line. */ + if ( sscanf( cp, "%[^ \t\n] %[^ \t\n]", file, url ) == 2 ) + { + /* Check for wildcard match. */ + star = strchr( file, '*' ); + if ( star != (char*) 0 ) + { + /* Check for leading match. */ + if ( strncmp( file, script_name, star - file ) == 0 ) + { + /* Got it; put together the full name. */ + strcat( url, script_name + ( star - file ) ); + /* XXX Whack the script_name, too? */ + moved( script_name, url ); + exit( 0 ); + } + } + /* Check for exact match. */ + if ( strcmp( file, script_name ) == 0 ) + { + /* Got it. */ + moved( script_name, url ); + exit( 0 ); + } + } + } + } + + /* No match found. */ + not_found( script_name ); + exit( 1 ); + } diff --git a/cgi-src/ssi.8 b/cgi-src/ssi.8 new file mode 100644 index 0000000..01c2045 --- /dev/null +++ b/cgi-src/ssi.8 @@ -0,0 +1,142 @@ +.TH ssi 8 "18 October 1995" +.SH NAME +ssi - server-side-includes CGI program +.SH SYNOPSIS +.B ssi +.SH DESCRIPTION +.PP +This is an external CGI program that gives you the same functionality +as the built-in server-side-includes feature in some HTTP daemons. +It is written for use with thttpd(8), but should be easy to adapt +to other systems. +.PP +To use this program, first make sure it is installed in your server's +CGI area, and that CGI is enabled. +Then set up your URLs with the path to the document you want parsed +as the "pathinfo". +That's the part of the URL that comes after the CGI program name. +For example, if the URL to this program is: +.nf + http://www.acme.com/cgi-bin/ssi +.fi +and the url for your document is: +.nf + http://www.acme.com/users/wecoyote/doc.html +.fi +then the compound URL that gives you the document filtered through the +program would be: +.nf + http://www.acme.com/cgi-bin/ssi/users/wecoyote/doc.html +.fi +.PP +The format description below is adapted from +http://hoohoo.ncsa.uiuc.edu/docs/tutorials/includes.html +.SH "INCLUDE FORMAT" +.PP +All directives are formatted as SGML comments within the document. +This is in case the document should ever find itself in the client's +hands unparsed. +Each directive has the following format: +.nf + +.fi +Each command takes different arguments, most only accept one tag at a time. +Here is a breakdown of the commands and their associated tags: +.IP * 4 +.BR config : +The config directive controls various aspects of the file parsing. +There are two valid tags: +.IP o 8 +.BR timefmt : +gives the server a new format to use when providing dates. +This is a string compatible with the strftime library call. +.IP o 8 +.BR sizefmt : +determines the formatting to be used when displaying the +size of a file. +Valid choices are bytes, for a formatted byte count +(formatted as 1,234,567), or abbrev for an abbreviated version +displaying the number of kilobytes or megabytes the file occupies. +.IP * 4 +.BR include : +Inserts the text of another document into the parsed document. +The inserted file is parsed recursively, so it can contain +server-side-include directives too. +This command accepts two tags: +.IP o 8 +.BR virtual : +Gives a virtual path to a document on the server. +.IP o 8 +.BR file : +Gives a pathname relative to the current directory. ../ cannot +be used in this pathname, nor can absolute paths be used. +.IP * 4 +.BR echo : +Prints the value of one of the include variables (defined below). +Any dates are printed subject to the currently configured timefmt. +The only valid tag to this command is var, whose value is the name of the +variable you wish to echo. +.IP * 4 +.BR fsize : +prints the size of the specified file, +subject to the sizefmt parameter to the config command. +Valid tags are the same as with the include command. +.IP * 4 +.BR flastmod : +prints the last modification date of the specified file, subject +to the formatting preference given by the timefmt parameter to config. +Valid tags are the same as with the include command. +.SH VARIABLES +.PP +A number of variables are made available to parsed documents. +In addition to +the CGI variable set, the following variables are made available: +.IP * 4 +.BR DOCUMENT_NAME : +The current filename. +.IP * 4 +.BR DOCUMENT_URI : +The virtual path to this document (such as /~robm/foo.shtml). +.IP * 4 +.BR QUERY_STRING_UNESCAPED : +The unescaped version of any search query the client sent. +.IP * 4 +.BR DATE_LOCAL : +The current date, local time zone. +Subject to the timefmt parameter to the config command. +.IP * 4 +.BR DATE_GMT : +Same as DATE_LOCAL but in Greenwich mean time. +.IP * 4 +.BR LAST_MODIFIED : +The last modification date of the current document. +Subject to timefmt like the others. +.SH "BUGS / DEFICIENCIES" +.PP +Does not implement the "exec" directive. +Actually, I consider this neither a bug nor a deficiency, but some may. +.SH "SEE ALSO" +thttpd(8), strftime(3) +.SH AUTHOR +Copyright © 1995 by Jef Poskanzer . +All rights reserved. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. diff --git a/cgi-src/ssi.c b/cgi-src/ssi.c new file mode 100644 index 0000000..1b0950d --- /dev/null +++ b/cgi-src/ssi.c @@ -0,0 +1,763 @@ +/* ssi - server-side-includes CGI program +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "match.h" + + +#define ST_GROUND 0 +#define ST_LESSTHAN 1 +#define ST_BANG 2 +#define ST_MINUS1 3 +#define ST_MINUS2 4 + + +static void read_file( char* vfilename, char* filename, FILE* fp ); + + +static char* argv0; +static char* url; + +static char timefmt[100]; +static int sizefmt; +#define SF_BYTES 0 +#define SF_ABBREV 1 +static struct stat sb; + + +static void +internal_error( char* reason ) + { + char* title = "500 Internal Error"; + + (void) printf( "\ +%s\n\ +

%s

\n\ +Something unusual went wrong during a server-side-includes request:\n\ +
\n\ +%s\n\ +
\n\ +\n", title, title, reason ); + } + + +static void +not_found( char* filename ) + { + char* title = "404 Not Found"; + + (void) printf( "\ +%s\n\ +

%s

\n\ +The requested server-side-includes filename, %s,\n\ +does not seem to exist.\n\ +\n", title, title, filename ); + } + + +static void +not_found2( char* directive, char* tag, char* filename2 ) + { + char* title = "Not Found"; + + (void) printf( "\ +

%s

\n\ +The filename requested in a %s %s directive, %s,\n\ +does not seem to exist.\n\ +
\n", title, directive, tag, filename2 ); + } + + +static void +not_permitted( char* directive, char* tag, char* val ) + { + char* title = "Not Permitted"; + + (void) printf( "\ +

%s

\n\ +The filename requested in the %s %s=%s directive\n\ +may not be fetched.\n\ +
\n", title, directive, tag, val ); + } + + +static void +unknown_directive( char* filename, char* directive ) + { + char* title = "Unknown Directive"; + + (void) printf( "\ +

%s

\n\ +The requested server-side-includes filename, %s,\n\ +tried to use an unknown directive, %s.\n\ +
\n", title, filename, directive ); + } + + +static void +unknown_tag( char* filename, char* directive, char* tag ) + { + char* title = "Unknown Tag"; + + (void) printf( "\ +

%s

\n\ +The requested server-side-includes filename, %s,\n\ +tried to use the directive %s with an unknown tag, %s.\n\ +
\n", title, filename, directive, tag ); + } + + +static void +unknown_value( char* filename, char* directive, char* tag, char* val ) + { + char* title = "Unknown Value"; + + (void) printf( "\ +

%s

\n\ +The requested server-side-includes filename, %s,\n\ +tried to use the directive %s %s with an unknown value, %s.\n\ +
\n", title, filename, directive, tag, val ); + } + + +static int +get_filename( char* vfilename, char* filename, char* directive, char* tag, char* val, char* fn, int fnsize ) + { + int vl, fl; + char* cp; + + /* Used for the various commands that accept a file name. + ** These commands accept two tags: + ** virtual + ** Gives a virtual path to a document on the server. + ** file + ** Gives a pathname relative to the current directory. ../ cannot + ** be used in this pathname, nor can absolute paths be used. + */ + vl = strlen( vfilename ); + fl = strlen( filename ); + if ( strcmp( tag, "virtual" ) == 0 ) + { + if ( strstr( val, "../" ) != (char*) 0 ) + { + not_permitted( directive, tag, val ); + return -1; + } + /* Figure out root using difference between vfilename and filename. */ + if ( vl > fl || + strcmp( vfilename, &filename[fl - vl] ) != 0 ) + return -1; + if ( fl - vl + strlen( val ) >= fnsize ) + return -1; + (void) strncpy( fn, filename, fl - vl ); + (void) strcpy( &fn[fl - vl], val ); + } + else if ( strcmp( tag, "file" ) == 0 ) + { + if ( val[0] == '/' || strstr( val, "../" ) != (char*) 0 ) + { + not_permitted( directive, tag, val ); + return -1; + } + if ( fl + 1 + strlen( val ) >= fnsize ) + return -1; + (void) strcpy( fn, filename ); + cp = strrchr( fn, '/' ); + if ( cp == (char*) 0 ) + { + cp = &fn[strlen( fn )]; + *cp = '/'; + } + (void) strcpy( ++cp, val ); + } + else + { + unknown_tag( filename, directive, tag ); + return -1; + } + return 0; + } + + +static int +check_filename( char* filename ) + { + static int inited = 0; + static char* cgi_pattern; + int fnl; + char* cp; + char* dirname; + char* authname; + struct stat sb2; + int r; + + if ( ! inited ) + { + /* Get the cgi pattern. */ + cgi_pattern = getenv( "CGI_PATTERN" ); +#ifdef CGI_PATTERN + if ( cgi_pattern == (char*) 0 ) + cgi_pattern = CGI_PATTERN; +#endif /* CGI_PATTERN */ + inited = 1; + } + + /* ../ is not permitted. */ + if ( strstr( filename, "../" ) != (char*) 0 ) + return 0; + +#ifdef AUTH_FILE + /* Ensure that we are not reading a basic auth password file. */ + fnl = strlen(filename); + if ( strcmp( filename, AUTH_FILE ) == 0 || + ( fnl >= sizeof(AUTH_FILE) && + strcmp( &filename[fnl - sizeof(AUTH_FILE) + 1], AUTH_FILE ) == 0 && + filename[fnl - sizeof(AUTH_FILE)] == '/' ) ) + return 0; + + /* Check for an auth file in the same directory. We can't do an actual + ** auth password check here because CGI programs are not given the + ** authorization header, for security reasons. So instead we just + ** prohibit access to all auth-protected files. + */ + dirname = strdup( filename ); + if ( dirname == (char*) 0 ) + return 0; /* out of memory */ + cp = strrchr( dirname, '/' ); + if ( cp == (char*) 0 ) + (void) strcpy( dirname, "." ); + else + *cp = '\0'; + authname = malloc( strlen( dirname ) + 1 + sizeof(AUTH_FILE) ); + if ( authname == (char*) 0 ) + return 0; /* out of memory */ + (void) sprintf( authname, "%s/%s", dirname, AUTH_FILE ); + r = stat( authname, &sb2 ); + free( dirname ); + free( authname ); + if ( r == 0 ) + return 0; +#endif /* AUTH_FILE */ + + /* Ensure that we are not reading a CGI file. */ + if ( cgi_pattern != (char*) 0 && match( cgi_pattern, filename ) ) + return 0; + + return 1; + } + + +static void +show_time( time_t t, int gmt ) + { + struct tm* tmP; + char tbuf[500]; + + if ( gmt ) + tmP = gmtime( &t ); + else + tmP = localtime( &t ); + if ( strftime( tbuf, sizeof(tbuf), timefmt, tmP ) > 0 ) + (void) fputs( tbuf, stdout ); + } + + +static void +show_size( off_t size ) + { + switch ( sizefmt ) + { + case SF_BYTES: + (void) printf( "%ld", (long) size ); /* spec says should have commas */ + break; + case SF_ABBREV: + if ( size < 1024 ) + (void) printf( "%ld", (long) size ); + else if ( size < 1024 ) + (void) printf( "%ldK", (long) size / 1024L ); + else if ( size < 1024*1024 ) + (void) printf( "%ldM", (long) size / (1024L*1024L) ); + else + (void) printf( "%ldG", (long) size / (1024L*1024L*1024L) ); + break; + } + } + + +static void +do_config( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) + { + /* The config directive controls various aspects of the file parsing. + ** There are two valid tags: + ** timefmt + ** Gives the server a new format to use when providing dates. This + ** is a string compatible with the strftime library call. + ** sizefmt + ** Determines the formatting to be used when displaying the size of + ** a file. Valid choices are bytes, for a formatted byte count + ** (formatted as 1,234,567), or abbrev for an abbreviated version + ** displaying the number of kilobytes or megabytes the file occupies. + */ + + if ( strcmp( tag, "timefmt" ) == 0 ) + { + (void) strncpy( timefmt, val, sizeof(timefmt) - 1 ); + timefmt[sizeof(timefmt) - 1] = '\0'; + } + else if ( strcmp( tag, "sizefmt" ) == 0 ) + { + if ( strcmp( val, "bytes" ) == 0 ) + sizefmt = SF_BYTES; + else if ( strcmp( val, "abbrev" ) == 0 ) + sizefmt = SF_ABBREV; + else + unknown_value( filename, directive, tag, val ); + } + else + unknown_tag( filename, directive, tag ); + } + + +static void +do_include( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) + { + char vfilename2[1000]; + char filename2[1000]; + FILE* fp2; + + /* Inserts the text of another document into the parsed document. */ + + if ( get_filename( + vfilename, filename, directive, tag, val, filename2, + sizeof(filename2) ) < 0 ) + return; + + if ( ! check_filename( filename2 ) ) + { + not_permitted( directive, tag, filename2 ); + return; + } + + fp2 = fopen( filename2, "r" ); + if ( fp2 == (FILE*) 0 ) + { + not_found2( directive, tag, filename2 ); + return; + } + + if ( strcmp( tag, "virtual" ) == 0 ) + { + if ( strlen( val ) < sizeof( vfilename2 ) ) + (void) strcpy( vfilename2, val ); + else + (void) strcpy( vfilename2, filename2 ); /* same size, has to fit */ + } + else + { + if ( strlen( vfilename ) + 1 + strlen( val ) < sizeof(vfilename2) ) + { + char* cp; + (void) strcpy( vfilename2, vfilename ); + cp = strrchr( vfilename2, '/' ); + if ( cp == (char*) 0 ) + { + cp = &vfilename2[strlen( vfilename2 )]; + *cp = '/'; + } + (void) strcpy( ++cp, val ); + } + else + (void) strcpy( vfilename2, filename2 ); /* same size, has to fit */ + } + + read_file( vfilename2, filename2, fp2 ); + (void) fclose( fp2 ); + } + + +static void +do_echo( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) + { + char* cp; + time_t t; + + /* Prints the value of one of the include variables. Any dates are + ** printed subject to the currently configured timefmt. The only valid + ** tag is var, whose value is the name of the variable you wish to echo. + */ + + if ( strcmp( tag, "var" ) != 0 ) + unknown_tag( filename, directive, tag ); + else + { + if ( strcmp( val, "DOCUMENT_NAME" ) == 0 ) + { + /* The current filename. */ + (void) fputs( filename, stdout ); + } + else if ( strcmp( val, "DOCUMENT_URI" ) == 0 ) + { + /* The virtual path to this file (such as /~robm/foo.shtml). */ + (void) fputs( vfilename, stdout ); + } + else if ( strcmp( val, "QUERY_STRING_UNESCAPED" ) == 0 ) + { + /* The unescaped version of any search query the client sent. */ + cp = getenv( "QUERY_STRING" ); + if ( cp != (char*) 0 ) + (void) fputs( cp, stdout ); + } + else if ( strcmp( val, "DATE_LOCAL" ) == 0 ) + { + /* The current date, local time zone. */ + t = time( (time_t*) 0 ); + show_time( t, 0 ); + } + else if ( strcmp( val, "DATE_GMT" ) == 0 ) + { + /* Same as DATE_LOCAL but in Greenwich mean time. */ + t = time( (time_t*) 0 ); + show_time( t, 1 ); + } + else if ( strcmp( val, "LAST_MODIFIED" ) == 0 ) + { + /* The last modification date of the current document. */ + if ( fstat( fileno( fp ), &sb ) >= 0 ) + show_time( sb.st_mtime, 0 ); + } + else + { + /* Try an environment variable. */ + cp = getenv( val ); + if ( cp == (char*) 0 ) + unknown_value( filename, directive, tag, val ); + else + (void) fputs( cp, stdout ); + } + } + } + + +static void +do_fsize( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) + { + char filename2[1000]; + + /* Prints the size of the specified file. */ + + if ( get_filename( + vfilename, filename, directive, tag, val, filename2, + sizeof(filename2) ) < 0 ) + return; + if ( stat( filename2, &sb ) < 0 ) + { + not_found2( directive, tag, filename2 ); + return; + } + show_size( sb.st_size ); + } + + +static void +do_flastmod( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) + { + char filename2[1000]; + + /* Prints the last modification date of the specified file. */ + + if ( get_filename( + vfilename, filename, directive, tag, val, filename2, + sizeof(filename2) ) < 0 ) + return; + if ( stat( filename2, &sb ) < 0 ) + { + not_found2( directive, tag, filename2 ); + return; + } + show_time( sb.st_mtime, 0 ); + } + + +static void +parse( char* vfilename, char* filename, FILE* fp, char* str ) + { + char* directive; + char* cp; + int ntags; + char* tags[200]; + int dirn; +#define DI_CONFIG 0 +#define DI_INCLUDE 1 +#define DI_ECHO 2 +#define DI_FSIZE 3 +#define DI_FLASTMOD 4 + int i; + char* val; + + directive = str; + directive += strspn( directive, " \t\n\r" ); + + ntags = 0; + cp = directive; + for (;;) + { + cp = strpbrk( cp, " \t\n\r\"" ); + if ( cp == (char*) 0 ) + break; + if ( *cp == '"' ) + { + cp = strpbrk( cp + 1, "\"" ); + ++cp; + if ( *cp == '\0' ) + break; + } + *cp++ = '\0'; + cp += strspn( cp, " \t\n\r" ); + if ( *cp == '\0' ) + break; + if ( ntags < sizeof(tags)/sizeof(*tags) ) + tags[ntags++] = cp; + } + + if ( strcmp( directive, "config" ) == 0 ) + dirn = DI_CONFIG; + else if ( strcmp( directive, "include" ) == 0 ) + dirn = DI_INCLUDE; + else if ( strcmp( directive, "echo" ) == 0 ) + dirn = DI_ECHO; + else if ( strcmp( directive, "fsize" ) == 0 ) + dirn = DI_FSIZE; + else if ( strcmp( directive, "flastmod" ) == 0 ) + dirn = DI_FLASTMOD; + else + { + unknown_directive( filename, directive ); + return; + } + + for ( i = 0; i < ntags; ++i ) + { + if ( i > 0 ) + putchar( ' ' ); + val = strchr( tags[i], '=' ); + if ( val == (char*) 0 ) + val = ""; + else + *val++ = '\0'; + if ( *val == '"' && val[strlen( val ) - 1] == '"' ) + { + val[strlen( val ) - 1] = '\0'; + ++val; + } + switch( dirn ) + { + case DI_CONFIG: + do_config( vfilename, filename, fp, directive, tags[i], val ); + break; + case DI_INCLUDE: + do_include( vfilename, filename, fp, directive, tags[i], val ); + break; + case DI_ECHO: + do_echo( vfilename, filename, fp, directive, tags[i], val ); + break; + case DI_FSIZE: + do_fsize( vfilename, filename, fp, directive, tags[i], val ); + break; + case DI_FLASTMOD: + do_flastmod( vfilename, filename, fp, directive, tags[i], val ); + break; + } + } + } + + +static void +slurp( char* vfilename, char* filename, FILE* fp ) + { + char buf[1000]; + int i; + int state; + int ich; + + /* Now slurp in the rest of the comment from the input file. */ + i = 0; + state = ST_GROUND; + while ( ( ich = getc( fp ) ) != EOF ) + { + switch ( state ) + { + case ST_GROUND: + if ( ich == '-' ) + state = ST_MINUS1; + break; + case ST_MINUS1: + if ( ich == '-' ) + state = ST_MINUS2; + else + state = ST_GROUND; + break; + case ST_MINUS2: + if ( ich == '>' ) + { + buf[i - 2] = '\0'; + parse( vfilename, filename, fp, buf ); + return; + } + else if ( ich != '-' ) + state = ST_GROUND; + break; + } + if ( i < sizeof(buf) - 1 ) + buf[i++] = (char) ich; + } + } + + +static void +read_file( char* vfilename, char* filename, FILE* fp ) + { + int ich; + int state; + + /* Copy it to output, while running a state-machine to look for + ** SSI directives. + */ + state = ST_GROUND; + while ( ( ich = getc( fp ) ) != EOF ) + { + switch ( state ) + { + case ST_GROUND: + if ( ich == '<' ) + { state = ST_LESSTHAN; continue; } + break; + case ST_LESSTHAN: + if ( ich == '!' ) + { state = ST_BANG; continue; } + else + { state = ST_GROUND; putchar( '<' ); } + break; + case ST_BANG: + if ( ich == '-' ) + { state = ST_MINUS1; continue; } + else + { state = ST_GROUND; (void) fputs ( ". +# The master version of this file is at the FSF in /home/gd/gnu/lib. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit system type (host/target name). +# +# Only a few systems have been added to this list; please add others +# (but try to keep the structure clean). +# + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 8/24/94.) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +trap 'rm -f dummy.c dummy.o dummy; exit 1' 1 2 15 + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + alpha:OSF1:*:*) + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo alpha-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//'` + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-cbm-sysv4 + exit 0;; + amiga:NetBSD:*:*) + echo m68k-cbm-netbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-cbm-openbsd${UNAME_RELEASE} + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + Pyramid*:OSx*:*:*|MIS*:OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + atari*:NetBSD:*:*) + echo m68k-atari-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-atari-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3*:NetBSD:*:*) + echo m68k-sun-netbsd${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-sun-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:NetBSD:*:*) + echo m68k-apple-netbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-apple-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >dummy.c + int main (argc, argv) int argc; char **argv; { + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + ${CC-cc} dummy.c -o dummy \ + && ./dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \ + -o ${TARGET_BINARY_INTERFACE}x = x ] ; then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i?86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:4) + if /usr/sbin/lsattr -EHl proc0 | grep POWER >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=4.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[3478]??:HP-UX:*:*) + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/7?? | 9000/8?[1679] ) HP_ARCH=hppa1.1 ;; + 9000/8?? ) HP_ARCH=hppa1.0 ;; + esac + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i?86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F300:UNIX_System_V:*:*) + FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + F301:UNIX_System_V:*:*) + echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'` + exit 0 ;; + hp3[0-9][05]:NetBSD:*:*) + echo m68k-hp-netbsd${UNAME_RELEASE} + exit 0 ;; + hp3[0-9][05]:OpenBSD:*:*) + echo m68k-hp-openbsd${UNAME_RELEASE} + exit 0 ;; + i?86:BSD/386:*:* | *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:NetBSD:*:*) + echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo i386-pc-cygwin32 + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin32 + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,/.*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. + ld_help_string=`ld --help 2>&1` + if echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations: elf_i.86"; then + echo "${UNAME_MACHINE}-pc-linux-gnu" ; exit 0 + elif echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations: i.86linux"; then + echo "${UNAME_MACHINE}-pc-linux-gnuaout" ; exit 0 + elif echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations: i.86coff"; then + echo "${UNAME_MACHINE}-pc-linux-gnucoff" ; exit 0 + elif echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations: m68kelf"; then + echo "${UNAME_MACHINE}-unknown-linux-gnu" ; exit 0 + elif echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations: m68klinux"; then + echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 + elif echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations: elf32ppc"; then + echo "powerpc-unknown-linux-gnu" ; exit 0 + elif test "${UNAME_MACHINE}" = "alpha" ; then + echo alpha-unknown-linux-gnu ; exit 0 + elif test "${UNAME_MACHINE}" = "sparc" ; then + echo sparc-unknown-linux-gnu ; exit 0 + else + # Either a pre-BFD a.out linker (linux-gnuoldld) or one that does not give us + # useful --help. Gcc wants to distinguish between linux-gnuoldld and linux-gnuaout. + test ! -d /usr/lib/ldscripts/. \ + && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0 + # Determine whether the default compiler is a.out or elf + cat >dummy.c </dev/null && ./dummy "${UNAME_MACHINE}" && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + fi ;; +# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions +# are messed up and put the nodename in both sysname and nodename. + i?86:DYNIX/ptx:4*:*) + echo i386-sequent-sysv4 + exit 0 ;; + i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*) + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} + fi + exit 0 ;; + i?86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i?86:LynxOS:2.*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + R3000:*System_V*:*:* | R4000:UNIX_SYSV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +#if !defined (ultrix) + printf ("vax-dec-bsd\n"); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy && rm dummy.c dummy && exit 0 +rm -f dummy.c dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +#echo '(Unable to guess system type)' 1>&2 + +exit 1 diff --git a/config.h b/config.h new file mode 100644 index 0000000..65ab1e3 --- /dev/null +++ b/config.h @@ -0,0 +1,392 @@ +/* config.h - configuration defines for thttpd and libhttpd +** +** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + + +/* The following configuration settings are sorted in order of decreasing +** likelihood that you'd want to change them - most likely first, least +** likely last. +** +** In case you're not familiar with the convention, "#ifdef notdef" +** is a Berkeleyism used to indicate temporarily disabled code. +** The idea here is that you re-enable it by just moving it outside +** of the ifdef. +*/ + +/* CONFIGURE: CGI programs must match this pattern to get executed. It's +** a simple shell-style wildcard pattern, with * meaning any string not +** containing a slash, ** meaning any string at all, and ? meaning any +** single character; or multiple such patterns separated by |. The +** patterns get checked against the filename part of the incoming URL. +** +** Restricting CGI programs to a single directory lets the site administrator +** review them for security holes, and is strongly recommended. If there +** are individual users that you trust, you can enable their directories too. +** +** You can also specify a CGI pattern on the command line, with the -c flag. +** Such a pattern overrides this compiled-in default. +** +** If no CGI pattern is specified, neither here nor on the command line, +** then CGI programs cannot be run at all. If you want to disable CGI +** as a security measure that's how you do it, just don't define any +** pattern here and don't run with the -c flag. +*/ +#ifdef notdef +/* Some sample patterns. Allow programs only in one central directory: */ +#define CGI_PATTERN "/cgi-bin/*" +/* Allow programs in a central directory, or anywhere in a trusted +** user's tree: */ +#define CGI_PATTERN "/cgi-bin/*|/jef/**" +/* Allow any program ending with a .cgi: */ +#define CGI_PATTERN "**.cgi" +/* When virtual hosting, enable the central directory on every host: */ +#define CGI_PATTERN "/*/cgi-bin/*" +#endif + +/* CONFIGURE: How many seconds to allow CGI programs to run before killing +** them. This is in case someone writes a CGI program that goes into an +** infinite loop, or does a massive database lookup that would take hours, +** or whatever. If you don't want any limit, comment this out, but that's +** probably a really bad idea. +*/ +#define CGI_TIMELIMIT 30 + +/* CONFIGURE: Maximum number of simultaneous CGI programs allowed. +** If this many are already running, then attempts to run more will +** return an HTTP 503 error. If this is not defined then there's +** no limit (and you'd better have a lot of memory). This can also be +** set in the runtime config file. +*/ +#ifdef notdef +#define CGI_LIMIT 50 +#endif + +/* CONFIGURE: How many seconds to allow for reading the initial request +** on a new connection. +*/ +#define IDLE_READ_TIMELIMIT 60 + +/* CONFIGURE: How many seconds before an idle connection gets closed. +*/ +#define IDLE_SEND_TIMELIMIT 300 + +/* CONFIGURE: The syslog facility to use. Using this you can set up your +** syslog.conf so that all thttpd messages go into a separate file. Note +** that even if you use the -l command line flag to send logging to a +** file, errors still get sent via syslog. +*/ +#define LOG_FACILITY LOG_DAEMON + +/* CONFIGURE: Tilde mapping. Many URLs use ~username to indicate a +** user's home directory. thttpd provides two options for mapping +** this construct to an actual filename. +** +** 1) Map ~username to /username. This is the recommended choice. +** Each user gets a subdirectory in the main chrootable web tree, and +** the tilde construct points there. The prefix could be something +** like "users", or it could be empty. See also the makeweb program +** for letting users create their own web subdirectories. +** +** 2) Map ~username to /. The postfix would be +** the name of a subdirectory off of the user's actual home dir, something +** like "public_html". This is what Apache and other servers do. The problem +** is, you can't do this and chroot() at the same time, so it's inherently +** a security hole. This is strongly dis-recommended, but it's here because +** some people really want it. Use at your own risk. +** +** You can also leave both options undefined, and thttpd will not do +** anything special about tildes. Enabling both options is an error. +*/ +#ifdef notdef +#define TILDE_MAP_1 "users" +#define TILDE_MAP_2 "public_html" +#endif + +/* CONFIGURE: The file to use for authentication. If this is defined then +** thttpd checks for this file in the local directory before every fetch. +** If the file exists then authentication is done, otherwise the fetch +** proceeds as usual. +** +** If you undefine this then thttpd will not implement authentication +** at all and will not check for auth files, which saves a bit of CPU time. +*/ +#define AUTH_FILE ".htpasswd" + +/* CONFIGURE: The default character set name to use with text MIME types. +** This gets substituted into the MIME types where they have a "%s". +** +** You can override this in the config file with the "charset" setting, +** or on the command like with the -T flag. +*/ +#define DEFAULT_CHARSET "UTF-8" + + +/* Most people won't want to change anything below here. */ + +/* CONFIGURE: This controls the SERVER_NAME environment variable that gets +** passed to CGI programs. By default thttpd does a gethostname(), which +** gives the host's canonical name. If you want to always use some other name +** you can define it here. +** +** Alternately, if you want to run the same thttpd binary on multiple +** machines, and want to build in alternate names for some or all of +** them, you can define a list of canonical name to altername name +** mappings. thttpd seatches the list and when it finds a match on +** the canonical name, that alternate name gets used. If no match +** is found, the canonical name gets used. +** +** If both SERVER_NAME and SERVER_NAME_LIST are defined here, thttpd searches +** the list as above, and if no match is found then SERVER_NAME gets used. +** +** In any case, if thttpd is started with the -h flag, that name always +** gets used. +*/ +#ifdef notdef +#define SERVER_NAME "your.hostname.here" +#define SERVER_NAME_LIST \ + "canonical.name.here/alternate.name.here", \ + "canonical.name.two/alternate.name.two" +#endif + +/* CONFIGURE: Undefine this if you want thttpd to hide its specific version +** when returning into to browsers. Instead it'll just say "thttpd" with +** no version. +*/ +#define SHOW_SERVER_VERSION + +/* CONFIGURE: Define this if you want to always chroot(), without having +** to give the -r command line flag. Some people like this as a security +** measure, to prevent inadvertant exposure by accidentally running without -r. +** You can still disable it at runtime with the -nor flag. +*/ +#ifdef notdef +#define ALWAYS_CHROOT +#endif + +/* CONFIGURE: Define this if you want to always do virtual hosting, without +** having to give the -v command line flag. You can still disable it at +** runtime with the -nov flag. +*/ +#ifdef notdef +#define ALWAYS_VHOST +#endif + +/* CONFIGURE: If you're using the vhost feature and you have a LOT of +** virtual hostnames (like, hundreds or thousands), you will want to +** enable this feature. It avoids a problem with most Unix filesystems, +** where if there are a whole lot of items in a directory then name lookup +** becomes very slow. This feature makes thttpd use subdirectories +** based on the first characters of each hostname. You can set it to use +** from one to three characters. If the hostname starts with "www.", that +** part is skipped over. Dots are also skipped over, and if the name isn't +** long enough then "_"s are used. Here are some examples of how hostnames +** would get turned into directory paths, for each different setting: +** 1: www.acme.com -> a/www.acme.com +** 1: foobar.acme.com -> f/foobar.acme.com +** 2: www.acme.com -> a/c/www.acme.com +** 2: foobar.acme.com -> f/o/foobar.acme.com +** 3: www.acme.com -> a/c/m/www.acme.com +** 3: foobar.acme.com -> f/o/o/foobar.acme.com +** 3: m.tv -> m/t/v/m.tv +** 4: m.tv -> m/t/v/_/m.tv +** Note that if you compile this setting in but then forget to set up +** the corresponding subdirectories, the only error indication you'll +** get is a "404 Not Found" when you try to visit a site. So be careful. +*/ +#ifdef notdef +#define VHOST_DIRLEVELS 1 +#define VHOST_DIRLEVELS 2 +#define VHOST_DIRLEVELS 3 +#endif + +/* CONFIGURE: Define this if you want to always use a global passwd file, +** without having to give the -P command line flag. You can still disable +** it at runtime with the -noP flag. +*/ +#ifdef notdef +#define ALWAYS_GLOBAL_PASSWD +#endif + +/* CONFIGURE: When started as root, the default username to switch to after +** initializing. If this user (or the one specified by the -u flag) does +** not exist, the program will refuse to run. +*/ +#define DEFAULT_USER "nobody" + +/* CONFIGURE: When started as root, the program can automatically chdir() +** to the home directory of the user specified by -u or DEFAULT_USER. +** An explicit -d still overrides this. +*/ +#ifdef notdef +#define USE_USER_DIR +#endif + +/* CONFIGURE: If this is defined, some of the built-in error pages will +** have more explicit information about exactly what the problem is. +** Some sysadmins don't like this, for security reasons. +*/ +#define EXPLICIT_ERROR_PAGES + +/* CONFIGURE: Subdirectory for custom error pages. The error filenames are +** $WEBDIR/$ERR_DIR/err%d.html - if virtual hosting is enabled then +** $WEBDIR/hostname/$ERR_DIR/err%d.html is searched first. This allows +** different custom error pages for each virtual hosting web server. If +** no custom page for a given error can be found, the built-in error page +** is generated. If ERR_DIR is not defined at all, only the built-in error +** pages will be generated. +*/ +#define ERR_DIR "errors" + +/* CONFIGURE: Define this if you want a standard HTML tail containing +** $SERVER_SOFTWARE and $SERVER_ADDRESS to be appended to the custom error +** pages. (It is always appended to the built-in error pages.) +*/ +#define ERR_APPEND_SERVER_INFO + +/* CONFIGURE: nice(2) value to use for CGI programs. If this is undefined, +** CGI programs run at normal priority. +*/ +#define CGI_NICE 10 + +/* CONFIGURE: $PATH to use for CGI programs. +*/ +#define CGI_PATH "/usr/local/bin:/usr/ucb:/bin:/usr/bin" + +/* CONFIGURE: If defined, $LD_LIBRARY_PATH to use for CGI programs. +*/ +#ifdef notdef +#define CGI_LD_LIBRARY_PATH "/usr/local/lib:/usr/lib" +#endif + +/* CONFIGURE: How often to run the occasional cleanup job. +*/ +#define OCCASIONAL_TIME 120 + +/* CONFIGURE: Seconds between stats syslogs. If this is undefined then +** no stats are accumulated and no stats syslogs are done. +*/ +#define STATS_TIME 3600 + +/* CONFIGURE: The mmap cache tries to keep the total number of mapped +** files below this number, so you don't run out of kernel file descriptors. +** If you have reconfigured your kernel to have more descriptors, you can +** raise this and thttpd will keep more maps cached. However it's not +** a hard limit, thttpd will go over it if you really are accessing +** a whole lot of files. +*/ +#define DESIRED_MAX_MAPPED_FILES 1000 + +/* CONFIGURE: The mmap cache also tries to keep the total mapped bytes +** below this number, so you don't run out of address space. Again +** it's not a hard limit, thttpd will go over it if you really are +** accessing a bunch of large files. +*/ +#define DESIRED_MAX_MAPPED_BYTES 1000000000 + + +/* You almost certainly don't want to change anything below here. */ + +/* CONFIGURE: When throttling CGI programs, we don't know how many bytes +** they send back to the client because it would be inefficient to +** interpose a counter. CGI programs are much more expensive than +** regular files to serve, so we set an arbitrary and high byte count +** that gets applied to all CGI programs for throttling purposes. +*/ +#define CGI_BYTECOUNT 25000 + +/* CONFIGURE: The default port to listen on. 80 is the standard HTTP port. +*/ +#define DEFAULT_PORT 80 + +/* CONFIGURE: A list of index filenames to check. The files are searched +** for in this order. +*/ +#define INDEX_NAMES "index.html", "index.htm", "index.xhtml", "index.xht", "Default.htm", "index.cgi" + +/* CONFIGURE: If this is defined then thttpd will automatically generate +** index pages for directories that don't have an explicit index file. +** If you want to disable this behavior site-wide, perhaps for security +** reasons, just undefine this. Note that you can disable indexing of +** individual directories by merely doing a "chmod 711" on them - the +** standard Unix file permission to allow file access but disable "ls". +*/ +#define GENERATE_INDEXES + +/* CONFIGURE: Whether to log unknown request headers. Most sites will not +** want to log them, which will save them a bit of CPU time. +*/ +#ifdef notdef +#define LOG_UNKNOWN_HEADERS +#endif + +/* CONFIGURE: Whether to fflush() the log file after each request. If +** this is turned off there's a slight savings in CPU cycles. +*/ +#define FLUSH_LOG_EVERY_TIME + +/* CONFIGURE: Time between updates of the throttle table's rolling averages. */ +#define THROTTLE_TIME 2 + +/* CONFIGURE: The listen() backlog queue length. The 1024 doesn't actually +** get used, the kernel uses its maximum allowed value. This is a config +** parameter only in case there's some OS where asking for too high a queue +** length causes an error. Note that on many systems the maximum length is +** way too small - see http://www.acme.com/software/thttpd/notes.html +*/ +#define LISTEN_BACKLOG 1024 + +/* CONFIGURE: Maximum number of throttle patterns that any single URL can +** be included in. This has nothing to do with the number of throttle +** patterns that you can define, which is unlimited. +*/ +#define MAXTHROTTLENUMS 10 + +/* CONFIGURE: Number of file descriptors to reserve for uses other than +** connections. Currently this is 10, representing one for the listen fd, +** one for dup()ing at connection startup time, one for reading the file, +** one for syslog, and possibly one for the regular log file, which is +** five, plus a factor of two for who knows what. +*/ +#define SPARE_FDS 10 + +/* CONFIGURE: How many milliseconds to leave a connection open while doing a +** lingering close. +*/ +#define LINGER_TIME 500 + +/* CONFIGURE: Maximum number of symbolic links to follow before +** assuming there's a loop. +*/ +#define MAX_LINKS 32 + +/* CONFIGURE: You don't even want to know. +*/ +#define MIN_WOULDBLOCK_DELAY 100L + +#endif /* _CONFIG_H_ */ diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..0432524 --- /dev/null +++ b/config.sub @@ -0,0 +1,927 @@ +#! /bin/sh +# Configuration validation subroutine script, version 1.1. +# Copyright (C) 1991, 92, 93, 94, 95, 1996 Free Software Foundation, Inc. +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +if [ x$1 = x ] +then + echo Configuration name missing. 1>&2 + echo "Usage: $0 CPU-MFR-OPSYS" 1>&2 + echo "or $0 ALIAS" 1>&2 + echo where ALIAS is a recognized configuration type. 1>&2 + exit 1 +fi + +# First pass through any local machine types. +case $1 in + *local*) + echo $1 + exit 0 + ;; + *) + ;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + linux-gnu*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple) + os= + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + tahoe | i860 | m68k | m68000 | m88k | ns32k | arm \ + | arme[lb] | pyramid \ + | tron | a29k | 580 | i960 | h8300 | hppa | hppa1.0 | hppa1.1 \ + | alpha | we32k | ns16k | clipper | i370 | sh \ + | powerpc | powerpcle | 1750a | dsp16xx | mips64 | mipsel \ + | pdp11 | mips64el | mips64orion | mips64orionel \ + | sparc | sparclet | sparclite | sparc64) + basic_machine=$basic_machine-unknown + ;; + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i[3456]86) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + vax-* | tahoe-* | i[3456]86-* | i860-* | m68k-* | m68000-* | m88k-* \ + | sparc-* | ns32k-* | fx80-* | arm-* | c[123]* \ + | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* | power-* \ + | none-* | 580-* | cray2-* | h8300-* | i960-* | xmp-* | ymp-* \ + | hppa-* | hppa1.0-* | hppa1.1-* | alpha-* | we32k-* | cydra-* | ns16k-* \ + | pn-* | np1-* | xps100-* | clipper-* | orion-* | sparclite-* \ + | pdp11-* | sh-* | powerpc-* | powerpcle-* | sparc64-* | mips64-* | mipsel-* \ + | mips64el-* | mips64orion-* | mips64orionel-* | f301-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-cbm + ;; + amigados) + basic_machine=m68k-cbm + os=-amigados + ;; + amigaunix | amix) + basic_machine=m68k-cbm + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [ctj]90-cray) + basic_machine=c90-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + os=-mvs + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i[3456]86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i[3456]86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i[3456]86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i[3456]86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + miniframe) + basic_machine=m68000-convergent + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + np1) + basic_machine=np1-gould + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5) + basic_machine=i586-intel + ;; + pentiumpro | p6) + basic_machine=i686-intel + ;; + pentium-* | p5-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + k5) + # We don't have specific support for AMD's K5 yet, so just call it a Pentium + basic_machine=i586-amd + ;; + nexen) + # We don't have specific support for Nexgen yet, so just call it a Pentium + basic_machine=i586-nexgen + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=rs6000-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + mips) + basic_machine=mips-mips + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sparc) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -unixware* | svr4*) + os=-sysv4 + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigados* | -msdos* | -newsos* | -unicos* | -aof* | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -cygwin32* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -linux-gnu* | -uxpv*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -xenix) + os=-xenix + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-semi) + os=-aout + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-ibm) + os=-aix + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigados + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f301-fujitsu) + os=-uxpv + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -hpux*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os diff --git a/configure b/configure new file mode 100755 index 0000000..bb2d78f --- /dev/null +++ b/configure @@ -0,0 +1,2782 @@ +#! /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: + +# 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=thttpd.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 + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + + +# Do some error checking and defaulting for the host and target type. +# The inputs are: +# configure --host=HOST --target=TARGET --build=BUILD NONOPT +# +# The rules are: +# 1. You are not allowed to specify --host, --target, and nonopt at the +# same time. +# 2. Host defaults to nonopt. +# 3. If nonopt is not specified, then host defaults to the current host, +# as determined by config.guess. +# 4. Target and build default to nonopt. +# 5. If nonopt is not specified, then target and build default to host. + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +case $host---$target---$nonopt in +NONE---*---* | *---NONE---* | *---*---NONE) ;; +*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;; +esac + + +# Make sure we can run config.sub. +if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then : +else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } +fi + +echo $ac_n "checking host system type""... $ac_c" 1>&6 +echo "configure:573: checking host system type" >&5 + +host_alias=$host +case "$host_alias" in +NONE) + case $nonopt in + NONE) + if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then : + else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; } + fi ;; + *) host_alias=$nonopt ;; + esac ;; +esac + +host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias` +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$host" 1>&6 + +echo $ac_n "checking target system type""... $ac_c" 1>&6 +echo "configure:594: checking target system type" >&5 + +target_alias=$target +case "$target_alias" in +NONE) + case $nonopt in + NONE) target_alias=$host_alias ;; + *) target_alias=$nonopt ;; + esac ;; +esac + +target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias` +target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$target" 1>&6 + +echo $ac_n "checking build system type""... $ac_c" 1>&6 +echo "configure:612: checking build system type" >&5 + +build_alias=$build +case "$build_alias" in +NONE) + case $nonopt in + NONE) build_alias=$host_alias ;; + *) build_alias=$nonopt ;; + esac ;; +esac + +build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias` +build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$build" 1>&6 + +test "$host_alias" != "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + + +# 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:638: 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:668: 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:719: 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:751: 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 762 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:767: \"$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:793: 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:798: 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:826: 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 + + +V_CCOPT="-O" +if test "$GCC" = yes ; then + echo $ac_n "checking gcc version""... $ac_c" 1>&6 +echo "configure:861: checking gcc version" >&5 + if eval "test \"`echo '$''{'ac_cv_lbl_gcc_vers'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_lbl_gcc_vers=`$CC -dumpversion 2>&1 | \ + sed -e 's/\..*//'` +fi + + echo "$ac_t""$ac_cv_lbl_gcc_vers" 1>&6 + if test "$ac_cv_lbl_gcc_vers" -gt 1 ; then + V_CCOPT="-O2" + fi +fi +if test -f .devel ; then + V_CCOPT="-g $V_CCOPT -ansi -pedantic -U__STRICT_ANSI__ -Wall -Wpointer-arith -Wshadow -Wcast-qual -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wno-long-long" +fi + +echo $ac_n "checking how to link static binaries""... $ac_c" 1>&6 +echo "configure:879: checking how to link static binaries" >&5 +if eval "test \"`echo '$''{'ac_cv_lbl_static_flag'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_lbl_static_flag=unknown + echo 'main() {}' > conftest.c + if test "$GCC" != yes ; then + trial_flag="-Bstatic" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + if test "$ac_cv_lbl_static_flag" = unknown ; then + trial_flag="-static" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + rm conftest.c +fi + +echo "$ac_t""$ac_cv_lbl_static_flag" 1>&6 +if test "$ac_cv_lbl_static_flag" != unknown ; then + V_STATICFLAG="$ac_cv_lbl_static_flag" +fi + +echo $ac_n "checking for __progname""... $ac_c" 1>&6 +echo "configure:910: checking for __progname" >&5 +if eval "test \"`echo '$''{'ac_cv_extern__progname'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_extern__progname=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_extern__progname=no +fi +rm -f conftest* +fi + +if test $ac_cv_extern__progname = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE__PROGNAME 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:946: 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:967: \"$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:984: \"$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:1001: \"$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 fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/devpoll.h sys/event.h osreldate.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1029: 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:1039: \"$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 whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:1066: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:1080: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 +echo "configure:1105: checking for $ac_hdr that defines DIR" >&5 +if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include <$ac_hdr> +int main() { +DIR *dirp = 0; +; return 0; } +EOF +if { (eval echo configure:1118: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$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 +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 +echo "configure:1143: checking for opendir in -ldir" >&5 +ac_lib_var=`echo dir'_'opendir | 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="-ldir $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 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&6 +fi + +else +echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 +echo "configure:1184: checking for opendir in -lx" >&5 +ac_lib_var=`echo x'_'opendir | 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="-lx $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 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&6 +fi + +fi + + +d="/usr/local/v6/lib" +echo $ac_n "checking for $d""... $ac_c" 1>&6 +echo "configure:1228: checking for $d" >&5 +if test -d $d; then + echo "$ac_t""yes (Adding -L$d to LDFLAGS)" 1>&6 + LDFLAGS="$LDFLAGS -L$d" +else + echo "$ac_t""no" 1>&6 +fi + +V_NETLIBS="" +echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6 +echo "configure:1238: checking for gethostbyname" >&5 +if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+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 gethostbyname(); + +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_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +gethostbyname(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1266: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_gethostbyname=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_gethostbyname=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +# Some OSes (eg. Solaris) place it in libnsl: + echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6 +echo "configure:1285: checking for gethostbyname in -lnsl" >&5 +ac_lib_var=`echo nsl'_'gethostbyname'_' | sed 'y%./+- %__p__%'` +if eval "test \"`echo '$''{'ac_cv_lbl_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lnsl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lbl_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lbl_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + V_NETLIBS="-lnsl $V_NETLIBS" +else + echo "$ac_t""no" 1>&6 +# Some strange OSes (SINIX) have it in libsocket: + echo $ac_n "checking for gethostbyname in -lsocket""... $ac_c" 1>&6 +echo "configure:1324: checking for gethostbyname in -lsocket" >&5 +ac_lib_var=`echo socket'_'gethostbyname'_' | sed 'y%./+- %__p__%'` +if eval "test \"`echo '$''{'ac_cv_lbl_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_lbl_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lbl_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + V_NETLIBS="-lsocket $V_NETLIBS" +else + echo "$ac_t""no" 1>&6 +# Unfortunately libsocket sometimes depends on libnsl. + # AC_CHECK_LIB's API is essentially broken so the + # following ugliness is necessary: + echo $ac_n "checking for gethostbyname in -lsocket""... $ac_c" 1>&6 +echo "configure:1365: checking for gethostbyname in -lsocket" >&5 +ac_lib_var=`echo socket'_'gethostbyname'_'-lnsl | sed 'y%./+- %__p__%'` +if eval "test \"`echo '$''{'ac_cv_lbl_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket -lnsl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lbl_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lbl_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + V_NETLIBS="-lsocket -lnsl $V_NETLIBS" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for gethostbyname in -lresolv""... $ac_c" 1>&6 +echo "configure:1403: checking for gethostbyname in -lresolv" >&5 +ac_lib_var=`echo resolv'_'gethostbyname | 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="-lresolv $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 + V_NETLIBS="-lresolv $V_NETLIBS" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +fi + +fi + +fi + +echo $ac_n "checking for socket""... $ac_c" 1>&6 +echo "configure:1451: checking for socket" >&5 +if eval "test \"`echo '$''{'ac_cv_func_socket'+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 socket(); + +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_socket) || defined (__stub___socket) +choke me +#else +socket(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1479: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_socket=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_socket=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'socket`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 +echo "configure:1497: 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 + V_NETLIBS="-lsocket $V_NETLIBS" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 +echo "configure:1535: checking for socket in -lsocket" >&5 +ac_lib_var=`echo socket'_'socket'_'-lnsl | sed 'y%./+- %__p__%'` +if eval "test \"`echo '$''{'ac_cv_lbl_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket -lnsl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lbl_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lbl_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + V_NETLIBS="-lsocket -lnsl $V_NETLIBS" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +fi + + +echo $ac_n "checking for main in -linet6""... $ac_c" 1>&6 +echo "configure:1580: checking for main in -linet6" >&5 +ac_lib_var=`echo inet6'_'main | 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="-linet6 $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 + ac_tr_lib=HAVE_LIB`echo inet6 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + +echo $ac_n "checking for crypt""... $ac_c" 1>&6 +echo "configure:1624: checking for crypt" >&5 +if eval "test \"`echo '$''{'ac_cv_func_crypt'+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 crypt(); + +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_crypt) || defined (__stub___crypt) +choke me +#else +crypt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1652: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_crypt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_crypt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'crypt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6 +echo "configure:1670: 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 + ac_tr_lib=HAVE_LIB`echo crypt | sed -e 's/^a-zA-Z0-9_/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +fi + +echo $ac_n "checking for hstrerror""... $ac_c" 1>&6 +echo "configure:1719: checking for hstrerror" >&5 +if eval "test \"`echo '$''{'ac_cv_func_hstrerror'+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 hstrerror(); + +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_hstrerror) || defined (__stub___hstrerror) +choke me +#else +hstrerror(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1747: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_hstrerror=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_hstrerror=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'hstrerror`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for hstrerror in -lresolv""... $ac_c" 1>&6 +echo "configure:1765: checking for hstrerror in -lresolv" >&5 +ac_lib_var=`echo resolv'_'hstrerror | 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="-lresolv $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 + V_NETLIBS="-lresolv $V_NETLIBS" +else + echo "$ac_t""no" 1>&6 +fi + +fi + + +for ac_func in strerror +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1810: 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:1838: \"$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 +LIBOBJS="$LIBOBJS ${ac_func}.${ac_objext}" +fi +done + + +for ac_func in waitpid vsnprintf daemon setsid setlogin getaddrinfo getnameinfo gai_strerror kqueue sigset atoll +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1867: 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:1895: \"$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 + +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:1923: 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:1933: \"$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:1962: 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:1990: \"$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:2015: 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:2163: \"$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 + + +case "$target_os" in +solaris*) + for ac_func in poll +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2191: 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:2219: \"$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 + + ;; +*) + for ac_func in select poll +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2248: 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:2276: \"$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 + + ;; +esac + +echo $ac_n "checking if struct tm has tm_gmtoff member""... $ac_c" 1>&6 +echo "configure:2304: checking if struct tm has tm_gmtoff member" >&5 + if eval "test \"`echo '$''{'ac_cv_acme_tm_has_tm_gmtoff'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +# include +int main() { +u_int i = sizeof(((struct tm *)0)->tm_gmtoff) +; return 0; } +EOF +if { (eval echo configure:2318: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_acme_tm_has_tm_gmtoff=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_acme_tm_has_tm_gmtoff=no +fi +rm -f conftest* +fi + + echo "$ac_t""$ac_cv_acme_tm_has_tm_gmtoff" 1>&6 + if test $ac_cv_acme_tm_has_tm_gmtoff = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_TM_GMTOFF 1 +EOF + + fi +echo $ac_n "checking if int64_t exists""... $ac_c" 1>&6 +echo "configure:2338: checking if int64_t exists" >&5 + if eval "test \"`echo '$''{'ac_cv_acme_int64_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +int64_t i64 +; return 0; } +EOF +if { (eval echo configure:2351: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_acme_int64_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_acme_int64_t=no +fi +rm -f conftest* +fi + + echo "$ac_t""$ac_cv_acme_int64_t" 1>&6 + if test $ac_cv_acme_int64_t = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_INT64T 1 +EOF + + fi +echo $ac_n "checking if socklen_t exists""... $ac_c" 1>&6 +echo "configure:2371: checking if socklen_t exists" >&5 + if eval "test \"`echo '$''{'ac_cv_acme_socklen_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +# include +int main() { +socklen_t slen +; return 0; } +EOF +if { (eval echo configure:2385: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_acme_socklen_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_acme_socklen_t=no +fi +rm -f conftest* +fi + + echo "$ac_t""$ac_cv_acme_socklen_t" 1>&6 + if test $ac_cv_acme_socklen_t = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_SOCKLENT 1 +EOF + + fi + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:2406: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:2444: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + + + + + + +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 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# 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 +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile cgi-src/Makefile extras/Makefile" | 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%@host@%$host%g +s%@host_alias@%$host_alias%g +s%@host_cpu@%$host_cpu%g +s%@host_vendor@%$host_vendor%g +s%@host_os@%$host_os%g +s%@target@%$target%g +s%@target_alias@%$target_alias%g +s%@target_cpu@%$target_cpu%g +s%@target_vendor@%$target_vendor%g +s%@target_os@%$target_os%g +s%@build@%$build%g +s%@build_alias@%$build_alias%g +s%@build_cpu@%$build_cpu%g +s%@build_vendor@%$build_vendor%g +s%@build_os@%$build_os%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@LIBOBJS@%$LIBOBJS%g +s%@SET_MAKE@%$SET_MAKE%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@V_CCOPT@%$V_CCOPT%g +s%@V_STATICFLAG@%$V_STATICFLAG%g +s%@V_NETLIBS@%$V_NETLIBS%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 + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + 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 +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +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..1209ddc --- /dev/null +++ b/configure.in @@ -0,0 +1,138 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(thttpd.c) + +AC_CANONICAL_SYSTEM + +AC_PROG_CC + +V_CCOPT="-O" +if test "$GCC" = yes ; then + AC_MSG_CHECKING(gcc version) + AC_CACHE_VAL(ac_cv_lbl_gcc_vers, + ac_cv_lbl_gcc_vers=`$CC -dumpversion 2>&1 | \ + sed -e 's/\..*//'`) + AC_MSG_RESULT($ac_cv_lbl_gcc_vers) + if test "$ac_cv_lbl_gcc_vers" -gt 1 ; then + V_CCOPT="-O2" + fi +fi +if test -f .devel ; then + V_CCOPT="-g $V_CCOPT -ansi -pedantic -U__STRICT_ANSI__ -Wall -Wpointer-arith -Wshadow -Wcast-qual -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wno-long-long" +fi + +dnl +dnl maybe this should be a loop +dnl +AC_MSG_CHECKING(how to link static binaries) +AC_CACHE_VAL(ac_cv_lbl_static_flag, + ac_cv_lbl_static_flag=unknown + echo 'main() {}' > conftest.c + if test "$GCC" != yes ; then + trial_flag="-Bstatic" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + if test "$ac_cv_lbl_static_flag" = unknown ; then + trial_flag="-static" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + rm conftest.c) +AC_MSG_RESULT($ac_cv_lbl_static_flag) +if test "$ac_cv_lbl_static_flag" != unknown ; then + V_STATICFLAG="$ac_cv_lbl_static_flag" +fi + +AC_MSG_CHECKING(for __progname) +AC_CACHE_VAL(ac_cv_extern__progname, + AC_TRY_LINK([], + [extern char *__progname; + puts(__progname)], + ac_cv_extern__progname=yes, + ac_cv_extern__progname=no)) +if test $ac_cv_extern__progname = yes ; then + AC_DEFINE(HAVE__PROGNAME) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AC_CHECK_HEADERS(fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/devpoll.h sys/event.h osreldate.h) +AC_HEADER_TIME +AC_HEADER_DIRENT + +d="/usr/local/v6/lib" +AC_MSG_CHECKING(for $d) +if test -d $d; then + AC_MSG_RESULT(yes (Adding -L$d to LDFLAGS)) + LDFLAGS="$LDFLAGS -L$d" +else + AC_MSG_RESULT(no) +fi + +dnl +dnl Most operating systems have gethostbyname() in the default searched +dnl libraries (i.e. libc): +dnl +V_NETLIBS="" +AC_CHECK_FUNC(gethostbyname, , + # Some OSes (eg. Solaris) place it in libnsl: + AC_LBL_CHECK_LIB(nsl, gethostbyname, + V_NETLIBS="-lnsl $V_NETLIBS", + # Some strange OSes (SINIX) have it in libsocket: + AC_LBL_CHECK_LIB(socket, gethostbyname, + V_NETLIBS="-lsocket $V_NETLIBS", + # Unfortunately libsocket sometimes depends on libnsl. + # AC_CHECK_LIB's API is essentially broken so the + # following ugliness is necessary: + AC_LBL_CHECK_LIB(socket, gethostbyname, + V_NETLIBS="-lsocket -lnsl $V_NETLIBS", + AC_CHECK_LIB(resolv, gethostbyname, + V_NETLIBS="-lresolv $V_NETLIBS"), + -lnsl)))) +AC_CHECK_FUNC(socket, , + AC_CHECK_LIB(socket, socket, + V_NETLIBS="-lsocket $V_NETLIBS", + AC_LBL_CHECK_LIB(socket, socket, + V_NETLIBS="-lsocket -lnsl $V_NETLIBS", , -lnsl))) + +AC_CHECK_LIB(inet6, main) + +AC_CHECK_FUNC(crypt, , AC_CHECK_LIB(crypt, crypt)) +AC_CHECK_FUNC(hstrerror, , + AC_CHECK_LIB(resolv, hstrerror, V_NETLIBS="-lresolv $V_NETLIBS")) + +AC_REPLACE_FUNCS(strerror) +AC_CHECK_FUNCS(waitpid vsnprintf daemon setsid setlogin getaddrinfo getnameinfo gai_strerror kqueue sigset atoll) +AC_FUNC_MMAP + +case "$target_os" in +solaris*) + dnl Solaris's select() is a bad wrapper routine. + AC_CHECK_FUNCS(poll) + ;; +*) + AC_CHECK_FUNCS(select poll) + ;; +esac + +AC_ACME_TM_GMTOFF +AC_ACME_INT64T +AC_ACME_SOCKLENT + +AC_PROG_MAKE_SET +AC_PROG_INSTALL + +AC_SUBST(DEFS) +AC_SUBST(V_CCOPT) +AC_SUBST(V_STATICFLAG) +AC_SUBST(V_NETLIBS) + +AC_OUTPUT(Makefile cgi-src/Makefile extras/Makefile) diff --git a/contrib/redhat-rpm/thttpd.conf b/contrib/redhat-rpm/thttpd.conf new file mode 100644 index 0000000..02fce15 --- /dev/null +++ b/contrib/redhat-rpm/thttpd.conf @@ -0,0 +1,14 @@ +# This section overrides defaults +dir=/home/httpd/html +chroot +user=httpd# default = nobody +logfile=/var/log/thttpd.log +pidfile=/var/run/thttpd.pid +# This section _documents_ defaults in effect +# port=80 +# nosymlink# default = !chroot +# novhost +# nocgipat +# nothrottles +# host=0.0.0.0 +# charset=iso-8859-1 diff --git a/contrib/redhat-rpm/thttpd.init b/contrib/redhat-rpm/thttpd.init new file mode 100755 index 0000000..58e3aba --- /dev/null +++ b/contrib/redhat-rpm/thttpd.init @@ -0,0 +1,47 @@ +#!/bin/bash +# The following two lines enable chkconfig(1) to manipulate this script +# chkconfig: 2345 99 01 +# description: control Jef Poskanzer's tiny/turbo/throttling http daemon + +# source function library +. /etc/rc.d/init.d/functions + +pidfile=/var/run/thttpd.pid +pid=`cat $pidfile 2>/dev/null` + +if test -n "$pid" && kill -0 $pid 2>/dev/null; then + dead=no +else + dead=yes +fi + +die(){ + echo -n "$*"; echo_failure; echo '' + exit 1; +} + +case "$1" in + start) test "$dead" = yes || die thttpd is already running + echo -n "Starting thttpd: " + daemon /usr/sbin/thttpd -C /etc/thttpd.conf + touch /var/lock/subsys/thttpd + echo_success;echo '' + exit 0 + ;; + stop) echo -n "Gently shutting down thttpd: " + signal=USR1 + ;; + kill) echo -n "Violently killing thttpd: " + signal=INT + ;; +status) status thttpd; exit $?;; +restart) $0 stop; sleep 2; exec $0 start;; + *) die "Usage: thttpd {start|stop|restart|status}";; +esac + +test "$dead" = no || die thttpd is not running +kill -$signal $pid +sleep 2 +kill -0 $pid 2>/dev/null && die "thttpd[$pid] will not die" +rm -f /var/lock/subsys/thttpd +echo_success; echo '' diff --git a/contrib/redhat-rpm/thttpd.spec b/contrib/redhat-rpm/thttpd.spec new file mode 100644 index 0000000..6ee0fee --- /dev/null +++ b/contrib/redhat-rpm/thttpd.spec @@ -0,0 +1,154 @@ +Summary: Throttleable lightweight httpd server +Name: thttpd +Version: 2.29 +Release: 1 +Group: Networking +URL: http://www.acme.com/software/thttpd +Source0: http://www.acme.com/software/thttpd/thttpd-%{PACKAGE_VERSION}.tar.gz +Copyright: distributable (BSD) +BuildRoot: /tmp/thttpd-root + +%description +Thttpd is a very compact no-frills httpd serving daemon that can handle +very high loads. While lacking many of the advanced features of +Apachee, thttpd operates without forking and is extremely efficient in +memory use. Basic support for cgi scripts, authentication, and ssi is +provided for. Advanced features include the ability to throttle traffic. + +%prep +%setup + +./configure --prefix=/usr + +%build +make \ + WEBDIR=/home/httpd/html \ + BINDIR=/usr/sbin prefix=/usr \ + CGIBINDIR=/home/httpd/cgi-bin + +%install + +mkdir -p $RPM_BUILD_ROOT/home/httpd/{cgi-bin,logs} +mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d +mkdir -p $RPM_BUILD_ROOT/usr/man/man{1,8} +mkdir -p $RPM_BUILD_ROOT/usr/sbin +install contrib/redhat-rpm/thttpd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/thttpd +install contrib/redhat-rpm/thttpd.conf $RPM_BUILD_ROOT/etc/ +make -i prefix=$RPM_BUILD_ROOT/usr install + +%pre + +grep '^httpd:' /etc/passwd >/dev/null || \ + /usr/sbin/adduser -r httpd + +%post +/sbin/chkconfig --add thttpd + +%preun +/sbin/chkconfig --del thttpd + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,bin,bin) +%doc [A-Z]* +%attr(2755, httpd, httpd) /usr/sbin/makeweb +/usr/sbin/htpasswd +/usr/sbin/syslogtocern +/usr/sbin/thttpd +%attr(-, httpd, httpd) /home/httpd +%attr(0755, root, root) /etc/rc.d/init.d/thttpd +%config /etc/thttpd.conf +%doc /usr/man/man*/* + +%changelog + +* Mon Dec 29 2003 Jef Poskanzer + - Updated to 2.26 + +* Sat Dec 20 2003 Jef Poskanzer + - Updated to 2.25b + +* Mon Oct 27 2003 Jef Poskanzer + - Updated to 2.25 + +* Sat Sep 13 2003 Jef Poskanzer + - Updated to 2.24 + +* Sat May 25 2002 Jef Poskanzer + - Updated to 2.23 + +* Mon Jul 09 2001 Jef Poskanzer + - Updated to 2.22 + +* Thu Apr 26 2001 Jef Poskanzer + - Updated to 2.21c + +* Mon Apr 23 2001 Jef Poskanzer + - Updated to 2.21b + +* Mon Oct 02 2000 Jef Poskanzer + - Updated to 2.21 + +* Wed Sep 13 2000 Jef Poskanzer + - Updated to 2.20 + +* Mon Sep 11 2000 Bennett Todd + - added thttpd.conf, took config info out of init script + - switched to logging in /var/log, used pidfile + +* Thu Jun 15 2000 Jef Poskanzer + - Updated to 2.19 + +* Thu May 18 2000 Jef Poskanzer + - Updated to 2.18 + +* Fri Mar 17 2000 Jef Poskanzer + - Updated to 2.17 + +* Mon Feb 28 2000 Jef Poskanzer + - Updated to 2.16 + +* Thu Feb 03 2000 Jef Poskanzer + - Updated to 2.15 + +* Thu Jan 21 2000 Jef Poskanzer + - Updated to 2.14 + +* Thu Jan 6 2000 Jef Poskanzer + - Updated to 2.13 + +* Mon Jan 3 2000 Bennett Todd + - updated to 2.12, tweaked to move thttpd.init into tarball + +* Mon Dec 13 1999 Bennett Todd + - Updated to 2.09 + +* Fri Dec 10 1999 Bennett Todd + - Updated to 2.08 + +* Wed Nov 24 1999 Bennett Todd + - updated to 2.06, parameterized Version string in source url + - changed to use "make install", simplified %files list + +* Wed Nov 10 1999 Bennett Todd + - Version 2.05, reset release to 1 + - dropped bugfix patch since Jef included that + - streamlined install + +* Sun Jul 25 1999 Bennett Todd + - Release 4, added mime type swf + +* Mon May 3 1999 Bennett Todd + - Release 2, added patch to set cgi-timelimit up to 10 minutes + fm default 30 seconds + +* Wed Feb 10 1999 Bennett Todd + - based on 2.00-2, bumped to 2.04, reset release back to 1 + - fixed a couple of broken entries in %install to reference $RPM_BUILD_ROOT + - simplified %files to populate /usr/doc/... with just [A-Z]* (TODO had gone + away, this simplification makes it liklier to be trivially portable to + future releases). + - added %doc tags for the man pages + diff --git a/extras/Makefile.in b/extras/Makefile.in new file mode 100644 index 0000000..a29780f --- /dev/null +++ b/extras/Makefile.in @@ -0,0 +1,86 @@ +# Makefile for extras. +# +# Copyright © 1995,1998 by Jef Poskanzer . +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +BINDIR = @sbindir@ +WEBDIR = $(prefix)/www +CGIBINDIR = $(WEBDIR)/cgi-bin +MANDIR = @mandir@ + +CC = @CC@ +CCOPT = @V_CCOPT@ +DEFS = @DEFS@ +INCLS = -I.. +CFLAGS = $(CCOPT) $(DEFS) $(INCLS) +STATICFLAG = @V_STATICFLAG@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +NETLIBS = @V_NETLIBS@ +INSTALL = @INSTALL@ + +CLEANFILES = *.o makeweb htpasswd + +@SET_MAKE@ + +.c.o: + @rm -f $@ + $(CC) $(CFLAGS) -c $*.c + +all: makeweb htpasswd + +makeweb: makeweb.o + $(CC) $(LDFLAGS) makeweb.o -o makeweb $(LIBS) $(NETLIBS) + +makeweb.o: makeweb.c ../config.h + $(CC) $(CFLAGS) -DWEBDIR=\"$(WEBDIR)\" -c makeweb.c + +htpasswd: htpasswd.o + $(CC) $(LDFLAGS) $(STATICFLAG) htpasswd.o -o htpasswd $(LIBS) + +htpasswd.o: htpasswd.c ../config.h + $(CC) $(CFLAGS) -DWEBDIR=\"$(WEBDIR)\" -c htpasswd.c + + +install: all + rm -f $(BINDIR)/makeweb $(BINDIR)/htpasswd $(BINDIR)/syslogtocern + cp makeweb $(BINDIR)/makeweb + chgrp $(WEBGROUP) $(BINDIR)/makeweb + chmod 2755 $(BINDIR)/makeweb + cp htpasswd $(BINDIR)/htpasswd + cp syslogtocern $(BINDIR)/syslogtocern + rm -f $(MANDIR)/man1/makeweb.1 + cp makeweb.1 $(MANDIR)/man1/makeweb.1 + rm -f $(MANDIR)/man1/htpasswd.1 + cp htpasswd.1 $(MANDIR)/man1/htpasswd.1 + rm -f $(MANDIR)/man8/syslogtocern.8 + cp syslogtocern.8 $(MANDIR)/man8/syslogtocern.8 + +clean: + rm -f $(CLEANFILES) + +distclean: + rm -f $(CLEANFILES) Makefile diff --git a/extras/htpasswd.1 b/extras/htpasswd.1 new file mode 100644 index 0000000..1124b02 --- /dev/null +++ b/extras/htpasswd.1 @@ -0,0 +1,16 @@ +.TH htpasswd 1 "05 May 1998" +.SH NAME +htpasswd - manipulate HTTP-server password files +.SH SYNOPSIS +.B htpasswd +.RB [ -c ] +.I passwordfile +.I username +.SH DESCRIPTION +.PP +Sets a user's password in an httpd-style password file. +The -c flag creates a new file. +.SH AUTHOR +Rob McCool. +Modified 29aug97 by Jef Poskanzer to accept new password on stdin, +if stdin is a pipe or file. This is necessary for use from CGI. diff --git a/extras/htpasswd.c b/extras/htpasswd.c new file mode 100644 index 0000000..31ac769 --- /dev/null +++ b/extras/htpasswd.c @@ -0,0 +1,218 @@ +/* + * htpasswd.c: simple program for manipulating password file for NCSA httpd + * + * Rob McCool + */ + +/* Modified 29aug97 by Jef Poskanzer to accept new password on stdin, +** if stdin is a pipe or file. This is necessary for use from CGI. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define LF 10 +#define CR 13 + +#define MAX_STRING_LEN 256 + +int tfd; +char temp_template[] = "/tmp/htp.XXXXXX"; + +void interrupted(int); + +static char * strd(char *s) { + char *d; + + d=(char *)malloc(strlen(s) + 1); + strcpy(d,s); + return(d); +} + +static void getword(char *word, char *line, char stop) { + int x = 0,y; + + for(x=0;((line[x]) && (line[x] != stop));x++) + word[x] = line[x]; + + word[x] = '\0'; + if(line[x]) ++x; + y=0; + + while((line[y++] = line[x++])); +} + +static int my_getline(char *s, int n, FILE *f) { + int i=0; + + while(1) { + s[i] = (char)fgetc(f); + + if(s[i] == CR) + s[i] = fgetc(f); + + if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) { + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + ++i; + } +} + +static void putline(FILE *f,char *l) { + int x; + + for(x=0;l[x];x++) fputc(l[x],f); + fputc('\n',f); +} + + +/* From local_passwd.c (C) Regents of Univ. of California blah blah */ +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, long v, int n) { + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +#ifdef MPE +/* MPE lacks getpass() and a way to suppress stdin echo. So for now, just +issue the prompt and read the results with echo. (Ugh). */ + +char *getpass(const char *prompt) { + +static char password[81]; + +fputs(prompt,stderr); +gets((char *)&password); + +if (strlen((char *)&password) > 8) { + password[8]='\0'; +} + +return (char *)&password; +} +#endif + +static void +add_password( char* user, FILE* f ) + { + char pass[100]; + char* pw; + char* cpw; + char salt[3]; + + if ( ! isatty( fileno( stdin ) ) ) + { + (void) fgets( pass, sizeof(pass), stdin ); + if ( pass[strlen(pass) - 1] == '\n' ) + pass[strlen(pass) - 1] = '\0'; + pw = pass; + } + else + { + pw = strd( (char*) getpass( "New password:" ) ); + if ( strcmp( pw, (char*) getpass( "Re-type new password:" ) ) != 0 ) + { + (void) fprintf( stderr, "They don't match, sorry.\n" ); + if ( tfd != -1 ) + unlink( temp_template ); + exit( 1 ); + } + } + (void) srandom( (int) time( (time_t*) 0 ) ); + to64( &salt[0], random(), 2 ); + cpw = crypt( pw, salt ); + (void) fprintf( f, "%s:%s\n", user, cpw ); + } + +static void usage(void) { + fprintf(stderr,"Usage: htpasswd [-c] passwordfile username\n"); + fprintf(stderr,"The -c flag creates a new file.\n"); + exit(1); +} + +void interrupted(int signo) { + fprintf(stderr,"Interrupted.\n"); + if(tfd != -1) unlink(temp_template); + exit(1); +} + +int main(int argc, char *argv[]) { + FILE *tfp,*f; + char user[MAX_STRING_LEN]; + char line[MAX_STRING_LEN]; + char l[MAX_STRING_LEN]; + char w[MAX_STRING_LEN]; + char command[MAX_STRING_LEN]; + int found; + + tfd = -1; + signal(SIGINT,(void (*)(int))interrupted); + if(argc == 4) { + if(strcmp(argv[1],"-c")) + usage(); + if(!(tfp = fopen(argv[2],"w"))) { + fprintf(stderr,"Could not open passwd file %s for writing.\n", + argv[2]); + perror("fopen"); + exit(1); + } + printf("Adding password for %s.\n",argv[3]); + add_password(argv[3],tfp); + fclose(tfp); + exit(0); + } else if(argc != 3) usage(); + + tfd = mkstemp(temp_template); + if(!(tfp = fdopen(tfd,"w"))) { + fprintf(stderr,"Could not open temp file.\n"); + exit(1); + } + + if(!(f = fopen(argv[1],"r"))) { + fprintf(stderr, + "Could not open passwd file %s for reading.\n",argv[1]); + fprintf(stderr,"Use -c option to create new one.\n"); + exit(1); + } + strncpy(user,argv[2],sizeof(user)-1); + user[sizeof(user)-1] = '\0'; + + found = 0; + while(!(my_getline(line,MAX_STRING_LEN,f))) { + if(found || (line[0] == '#') || (!line[0])) { + putline(tfp,line); + continue; + } + strcpy(l,line); + getword(w,l,':'); + if(strcmp(user,w)) { + putline(tfp,line); + continue; + } + else { + printf("Changing password for user %s\n",user); + add_password(user,tfp); + found = 1; + } + } + if(!found) { + printf("Adding user %s\n",user); + add_password(user,tfp); + } + fclose(f); + fclose(tfp); + sprintf(command,"cp %s %s",temp_template,argv[1]); + system(command); + unlink(temp_template); + exit(0); +} diff --git a/extras/makeweb.1 b/extras/makeweb.1 new file mode 100644 index 0000000..ff729c9 --- /dev/null +++ b/extras/makeweb.1 @@ -0,0 +1,34 @@ +.TH makeweb 1 "06 September 1995" +.SH NAME +makeweb - create user web directory +.SH SYNOPSIS +.B makeweb +.SH DESCRIPTION +.PP +This program allows users to create their own web subdirectories off +of the main web directory. +.SH "SEE ALSO +thttpd(8) +.SH AUTHOR +Copyright © 1995 by Jef Poskanzer . +All rights reserved. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. diff --git a/extras/makeweb.c b/extras/makeweb.c new file mode 100644 index 0000000..aa7b22f --- /dev/null +++ b/extras/makeweb.c @@ -0,0 +1,256 @@ +/* makeweb.c - let a user create a web subdirectory +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +/* This is intended to be installed setgid to a group that has +** write access to the system web directory. It allows any user +** to create a subdirectory there. It also makes a symbolic link +** in the user's home directory pointing at the new web subdir. +*/ + + +#include "../config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LINK "public_html" + +static char* argv0; + + +static void +check_room( int size, int len ) + { + if ( len > size ) + { + (void) fprintf( stderr, "%s: internal error, out of room\n", argv0 ); + exit( 1 ); + } + } + + +static void +end_with_slash( char* str ) + { + if ( str[strlen( str ) - 1] != '/' ) + (void) strcat( str, "/" ); + } + + +static void +check_dir( char* dirname, uid_t uid, gid_t gid ) + { + struct stat sb; + + /* Check the directory. */ + if ( stat( dirname, &sb ) < 0 ) + { + if ( errno != ENOENT ) + { + perror( dirname ); + exit( 1 ); + } + /* Doesn't exist. Try to make it. */ + if ( mkdir( dirname, 0755 ) < 0 ) + { + if ( errno == ENOENT ) + (void) printf( "\ +Some part of the path %s does not exist.\n\ +This is probably a configuration error.\n", dirname ); + else + perror( dirname ); + exit( 1 ); + } + (void) printf( "Created web directory %s\n", dirname ); + /* Try to change the group of the new dir to the user's group. */ + (void) chown( dirname, -1, gid ); + } + else + { + /* The directory already exists. Well, check that it is in + ** fact a directory. + */ + if ( ! S_ISDIR( sb.st_mode ) ) + { + (void) printf( + "%s already exists but is not a directory!\n", dirname ); + exit( 1 ); + } + if ( sb.st_uid != uid ) + { + (void) printf( + "%s already exists but you don't own it!\n", dirname ); + exit( 1 ); + } + (void) printf( "Web directory %s already existed.\n", dirname ); + } + } + + +int +main( int argc, char** argv ) + { + char* webdir; + char* prefix; + struct passwd* pwd; + char* username; + char* homedir; + char dirname[5000]; + char linkname[5000]; + char linkbuf[5000]; + struct stat sb; + + argv0 = argv[0]; + if ( argc != 1 ) + { + (void) fprintf( stderr, "usage: %s\n", argv0 ); + exit( 1 ); + } + + pwd = getpwuid( getuid() ); + if ( pwd == (struct passwd*) 0 ) + { + (void) fprintf( stderr, "%s: can't find your username\n", argv0 ); + exit( 1 ); + } + username = pwd->pw_name; + homedir = pwd->pw_dir; + +#ifdef TILDE_MAP_2 + + /* All we have to do for the TILDE_MAP_2 case is make sure there's + ** a public_html subdirectory. + */ + check_room( + sizeof(dirname), strlen( homedir ) + strlen( TILDE_MAP_2 ) + 2 ); + (void) strcpy( dirname, homedir ); + end_with_slash( dirname ); + (void) strcat( dirname, TILDE_MAP_2 ); + + check_dir( dirname, pwd->pw_uid, pwd->pw_gid ); + +#else /* TILDE_MAP_2 */ + + /* Gather the pieces. */ + webdir = WEBDIR; +#ifdef TILDE_MAP_1 + prefix = TILDE_MAP_1; +#else /* TILDE_MAP_1 */ + prefix = ""; +#endif /* TILDE_MAP_1 */ + + /* Assemble the directory name. Be paranoid cause we're sgid. */ + check_room( + sizeof(dirname), + strlen( webdir ) + strlen( prefix ) + strlen( username ) + 3 ); + (void) strcpy( dirname, webdir ); + end_with_slash( dirname ); + if ( strlen( prefix ) != 0 ) + { + (void) strcat( dirname, prefix ); + end_with_slash( dirname ); + } + (void) strcat( dirname, username ); + + /* Assemble the link name. */ + check_room( sizeof(linkname), strlen( homedir ) + strlen( LINK ) + 2 ); + (void) strcpy( linkname, homedir ); + end_with_slash( linkname ); + (void) strcat( linkname, LINK ); + + check_dir( dirname, pwd->pw_uid, pwd->pw_gid ); + + /* Check the symlink. */ + try_link_again: ; + if ( lstat( linkname, &sb ) < 0 ) + { + if ( errno != ENOENT ) + { + perror( linkname ); + exit( 1 ); + } + /* Doesn't exist. Try to make it. */ + if ( symlink( dirname, linkname ) < 0 ) + { + if ( errno == ENOENT ) + (void) printf( "\ +Some part of the path %s does not exist.\n\ +This is probably a configuration error.\n", linkname ); + else + perror( linkname ); + exit( 1 ); + } + (void) printf( "Created symbolic link %s\n", linkname ); + } + else + { + /* The link already exists. Well, check that it is in + ** fact a link. + */ + if ( ! S_ISLNK( sb.st_mode ) ) + { + (void) printf( "\ +%s already exists but is not a\n\ +symbolic link! Perhaps you have a real web subdirectory in your\n\ +home dir from a previous web server configuration? You may have\n\ +to rename it, run %s again, and then copy in the old\n\ +contents.\n", linkname, argv0 ); + exit( 1 ); + } + /* Check the existing link's contents. */ + if ( readlink( linkname, linkbuf, sizeof(linkbuf) ) < 0 ) + { + perror( linkname ); + exit( 1 ); + } + if ( strcmp( dirname, linkbuf ) == 0 ) + (void) printf( "Symbolic link %s already existed.\n", linkname ); + else + { + (void) printf( "\ +Symbolic link %s already existed\n\ +but it points to the wrong place! Attempting to remove and\n\ +recreate it.\n", linkname ); + if ( unlink( linkname ) < 0 ) + { + perror( linkname ); + exit( 1 ); + } + goto try_link_again; + } + } +#endif /* TILDE_MAP_2 */ + + exit( 0 ); + } diff --git a/extras/syslogtocern b/extras/syslogtocern new file mode 100755 index 0000000..d4a6dfa --- /dev/null +++ b/extras/syslogtocern @@ -0,0 +1,68 @@ +#!/bin/sh +# +# syslogtocern - convert thttpd syslog entries into CERN Combined Log Format +# +# Copyright © 1995,1998 by Jef Poskanzer . +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +if [ $# -lt 1 ] ; then + echo "usage: $0 logfile ..." >&2 + exit 1 +fi + +tmp1=/tmp/stc1.$$ +rm -f $tmp1 + +# Gather up all the thttpd entries. +egrep -h ' thttpd\[' "$@" > $tmp1 + +# Figure out the current year - it's not in syslog's output. Some versions +# of date have the %Y directive to give the full four-digit year, but others +# only have %y. +year=`date +%y` +if [ $year -gt 70 ] ; then + year=19$year +else + year=20$year +fi + +# If the current year isn't the year that the logfile was generated, we need +# to fix it. This will most likely happen once a year, when this script is +# run on January 1st for December 31st's logfile. So, if the current month +# is January and there are December dates in the log file, we subtract one. +# This should cover most cases. +if [ `date +%m` -eq 1 -a `head -1 $tmp1 | awk '{print $1}'` = "Dec" ] ; then + year=`echo $year - 1 | bc` +fi + +# Do access_log. +awk < $tmp1 '{if ( NF >= 15 && $7 == "-" && $12 >= 100 && $12 < 510) print;}' | + sed -e "s,\([A-Z][a-z][a-z]\) \([0-9 ][0-9]\) \([0-9][0-9]:[0-9][0-9]:[0-9][0-9]\) [^ ]* thttpd\[[0-9]*\]: \([^ ]* [^ ]* [^ ]*\) \(.*\),\4 [\2/\1/${year}:\3] \5," -e 's,\[ ,[0,' > access_log + +# Do error_log. +awk < $tmp1 '{if ( ! ( NF >= 15 && $7 == "-" && $12 >= 100 && $12 < 510) ) print;}' | + sed -e "s,\([A-Z][a-z][a-z] [0-9 ][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]\) [^ ]* thttpd\[[0-9]*\]: \(.*\),[\1 ${year}] \2," > error_log + +# Done. +rm -f $tmp1 diff --git a/extras/syslogtocern.8 b/extras/syslogtocern.8 new file mode 100644 index 0000000..351e385 --- /dev/null +++ b/extras/syslogtocern.8 @@ -0,0 +1,45 @@ +.TH syslogtocern 8 "12 October 1995" +.SH NAME +syslogtocern - convert thttpd syslog entries into CERN Common Log format +.SH SYNOPSIS +.B syslogtocern +.I logfile +.RI ... +.SH DESCRIPTION +.PP +Reads one or more syslog files as input. +Takes the thttpd entries, and converts them into CERN Combined Common +Log format. +Produces two files as output: access_log and error_log. +If files with those names already exist in the current directory, they +are overwritten. +.SH "SEE ALSO" +thttpd(8) +.SH "BUGS / DEFICIENCIES" +Lumps all thttpd processes together. +It ought to produce separate files for each, identified by IP address and +port number. +However, that change represents a huge increase in complexity, so next version. +.SH AUTHOR +Copyright © 1995 by Jef Poskanzer . +All rights reserved. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. diff --git a/fdwatch.c b/fdwatch.c new file mode 100644 index 0000000..2b3de74 --- /dev/null +++ b/fdwatch.c @@ -0,0 +1,834 @@ +/* fdwatch.c - fd watcher routines, either select() or poll() +** +** Copyright © 1999,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifdef HAVE_POLL_H +#include +#else /* HAVE_POLL_H */ +#ifdef HAVE_SYS_POLL_H +#include +#endif /* HAVE_SYS_POLL_H */ +#endif /* HAVE_POLL_H */ + +#ifdef HAVE_SYS_DEVPOLL_H +#include +#ifndef HAVE_DEVPOLL +#define HAVE_DEVPOLL +#endif /* !HAVE_DEVPOLL */ +#endif /* HAVE_SYS_DEVPOLL_H */ + +#ifdef HAVE_SYS_EVENT_H +#include +#endif /* HAVE_SYS_EVENT_H */ + +#include "fdwatch.h" + +#ifdef HAVE_SELECT +#ifndef FD_SET +#define NFDBITS 32 +#define FD_SETSIZE 32 +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) bzero((char*)(p), sizeof(*(p))) +#endif /* !FD_SET */ +#endif /* HAVE_SELECT */ + +static int nfiles; +static long nwatches; +static int* fd_rw; +static void** fd_data; +static int nreturned, next_ridx; + +#ifdef HAVE_KQUEUE + +#define WHICH "kevent" +#define INIT( nf ) kqueue_init( nf ) +#define ADD_FD( fd, rw ) kqueue_add_fd( fd, rw ) +#define DEL_FD( fd ) kqueue_del_fd( fd ) +#define WATCH( timeout_msecs ) kqueue_watch( timeout_msecs ) +#define CHECK_FD( fd ) kqueue_check_fd( fd ) +#define GET_FD( ridx ) kqueue_get_fd( ridx ) + +static int kqueue_init( int nf ); +static void kqueue_add_fd( int fd, int rw ); +static void kqueue_del_fd( int fd ); +static int kqueue_watch( long timeout_msecs ); +static int kqueue_check_fd( int fd ); +static int kqueue_get_fd( int ridx ); + +#else /* HAVE_KQUEUE */ +# ifdef HAVE_DEVPOLL + +#define WHICH "devpoll" +#define INIT( nf ) devpoll_init( nf ) +#define ADD_FD( fd, rw ) devpoll_add_fd( fd, rw ) +#define DEL_FD( fd ) devpoll_del_fd( fd ) +#define WATCH( timeout_msecs ) devpoll_watch( timeout_msecs ) +#define CHECK_FD( fd ) devpoll_check_fd( fd ) +#define GET_FD( ridx ) devpoll_get_fd( ridx ) + +static int devpoll_init( int nf ); +static void devpoll_add_fd( int fd, int rw ); +static void devpoll_del_fd( int fd ); +static int devpoll_watch( long timeout_msecs ); +static int devpoll_check_fd( int fd ); +static int devpoll_get_fd( int ridx ); + +# else /* HAVE_DEVPOLL */ +# ifdef HAVE_POLL + +#define WHICH "poll" +#define INIT( nf ) poll_init( nf ) +#define ADD_FD( fd, rw ) poll_add_fd( fd, rw ) +#define DEL_FD( fd ) poll_del_fd( fd ) +#define WATCH( timeout_msecs ) poll_watch( timeout_msecs ) +#define CHECK_FD( fd ) poll_check_fd( fd ) +#define GET_FD( ridx ) poll_get_fd( ridx ) + +static int poll_init( int nf ); +static void poll_add_fd( int fd, int rw ); +static void poll_del_fd( int fd ); +static int poll_watch( long timeout_msecs ); +static int poll_check_fd( int fd ); +static int poll_get_fd( int ridx ); + +# else /* HAVE_POLL */ +# ifdef HAVE_SELECT + +#define WHICH "select" +#define INIT( nf ) select_init( nf ) +#define ADD_FD( fd, rw ) select_add_fd( fd, rw ) +#define DEL_FD( fd ) select_del_fd( fd ) +#define WATCH( timeout_msecs ) select_watch( timeout_msecs ) +#define CHECK_FD( fd ) select_check_fd( fd ) +#define GET_FD( ridx ) select_get_fd( ridx ) + +static int select_init( int nf ); +static void select_add_fd( int fd, int rw ); +static void select_del_fd( int fd ); +static int select_watch( long timeout_msecs ); +static int select_check_fd( int fd ); +static int select_get_fd( int ridx ); + +# endif /* HAVE_SELECT */ +# endif /* HAVE_POLL */ +# endif /* HAVE_DEVPOLL */ +#endif /* HAVE_KQUEUE */ + + +/* Routines. */ + +/* Figure out how many file descriptors the system allows, and +** initialize the fdwatch data structures. Returns -1 on failure. +*/ +int +fdwatch_get_nfiles( void ) + { + int i; +#ifdef RLIMIT_NOFILE + struct rlimit rl; +#endif /* RLIMIT_NOFILE */ + + /* Figure out how many fd's we can have. */ + nfiles = getdtablesize(); +#ifdef RLIMIT_NOFILE + /* If we have getrlimit(), use that, and attempt to raise the limit. */ + if ( getrlimit( RLIMIT_NOFILE, &rl ) == 0 ) + { + nfiles = rl.rlim_cur; + if ( rl.rlim_max == RLIM_INFINITY ) + rl.rlim_cur = 8192; /* arbitrary */ + else if ( rl.rlim_max > rl.rlim_cur ) + rl.rlim_cur = rl.rlim_max; + if ( setrlimit( RLIMIT_NOFILE, &rl ) == 0 ) + nfiles = rl.rlim_cur; + } +#endif /* RLIMIT_NOFILE */ + +#if defined(HAVE_SELECT) && ! ( defined(HAVE_POLL) || defined(HAVE_DEVPOLL) || defined(HAVE_KQUEUE) ) + /* If we use select(), then we must limit ourselves to FD_SETSIZE. */ + nfiles = MIN( nfiles, FD_SETSIZE ); +#endif /* HAVE_SELECT && ! ( HAVE_POLL || HAVE_DEVPOLL || HAVE_KQUEUE ) */ + + /* Initialize the fdwatch data structures. */ + nwatches = 0; + fd_rw = (int*) malloc( sizeof(int) * nfiles ); + fd_data = (void**) malloc( sizeof(void*) * nfiles ); + if ( fd_rw == (int*) 0 || fd_data == (void**) 0 ) + return -1; + for ( i = 0; i < nfiles; ++i ) + fd_rw[i] = -1; + if ( INIT( nfiles ) == -1 ) + return -1; + + return nfiles; + } + + +/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ +void +fdwatch_add_fd( int fd, void* client_data, int rw ) + { + if ( fd < 0 || fd >= nfiles || fd_rw[fd] != -1 ) + { + syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_add_fd!", fd ); + return; + } + ADD_FD( fd, rw ); + fd_rw[fd] = rw; + fd_data[fd] = client_data; + } + + +/* Remove a descriptor from the watch list. */ +void +fdwatch_del_fd( int fd ) + { + if ( fd < 0 || fd >= nfiles || fd_rw[fd] == -1 ) + { + syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_del_fd!", fd ); + return; + } + DEL_FD( fd ); + fd_rw[fd] = -1; + fd_data[fd] = (void*) 0; + } + +/* Do the watch. Return value is the number of descriptors that are ready, +** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means +** wait indefinitely. +*/ +int +fdwatch( long timeout_msecs ) + { + ++nwatches; + nreturned = WATCH( timeout_msecs ); + next_ridx = 0; + return nreturned; + } + + +/* Check if a descriptor was ready. */ +int +fdwatch_check_fd( int fd ) + { + if ( fd < 0 || fd >= nfiles || fd_rw[fd] == -1 ) + { + syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_check_fd!", fd ); + return 0; + } + return CHECK_FD( fd ); + } + + +void* +fdwatch_get_next_client_data( void ) + { + int fd; + + if ( next_ridx >= nreturned ) + return (void*) -1; + fd = GET_FD( next_ridx++ ); + if ( fd < 0 || fd >= nfiles ) + return (void*) 0; + return fd_data[fd]; + } + + +/* Generate debugging statistics syslog message. */ +void +fdwatch_logstats( long secs ) + { + if ( secs > 0 ) + syslog( + LOG_NOTICE, " fdwatch - %ld %ss (%g/sec)", + nwatches, WHICH, (float) nwatches / secs ); + nwatches = 0; + } + + +#ifdef HAVE_KQUEUE + +static int maxkqevents; +static struct kevent* kqevents; +static int nkqevents; +static struct kevent* kqrevents; +static int* kqrfdidx; +static int kq; + + +static int +kqueue_init( int nf ) + { + kq = kqueue(); + if ( kq == -1 ) + return -1; + maxkqevents = nf * 2; + kqevents = (struct kevent*) malloc( sizeof(struct kevent) * maxkqevents ); + kqrevents = (struct kevent*) malloc( sizeof(struct kevent) * nf ); + kqrfdidx = (int*) malloc( sizeof(int) * nf ); + if ( kqevents == (struct kevent*) 0 || kqrevents == (struct kevent*) 0 || + kqrfdidx == (int*) 0 ) + return -1; + (void) memset( kqevents, 0, sizeof(struct kevent) * maxkqevents ); + (void) memset( kqrfdidx, 0, sizeof(int) * nf ); + return 0; + } + + +static void +kqueue_add_fd( int fd, int rw ) + { + if ( nkqevents >= maxkqevents ) + { + syslog( LOG_ERR, "too many kqevents in kqueue_add_fd!" ); + return; + } + kqevents[nkqevents].ident = fd; + kqevents[nkqevents].flags = EV_ADD; + switch ( rw ) + { + case FDW_READ: kqevents[nkqevents].filter = EVFILT_READ; break; + case FDW_WRITE: kqevents[nkqevents].filter = EVFILT_WRITE; break; + default: break; + } + ++nkqevents; + } + + +static void +kqueue_del_fd( int fd ) + { + if ( nkqevents >= maxkqevents ) + { + syslog( LOG_ERR, "too many kqevents in kqueue_del_fd!" ); + return; + } + kqevents[nkqevents].ident = fd; + kqevents[nkqevents].flags = EV_DELETE; + switch ( fd_rw[fd] ) + { + case FDW_READ: kqevents[nkqevents].filter = EVFILT_READ; break; + case FDW_WRITE: kqevents[nkqevents].filter = EVFILT_WRITE; break; + } + ++nkqevents; + } + + +static int +kqueue_watch( long timeout_msecs ) + { + int i, r; + + if ( timeout_msecs == INFTIM ) + r = kevent( + kq, kqevents, nkqevents, kqrevents, nfiles, (struct timespec*) 0 ); + else + { + struct timespec ts; + ts.tv_sec = timeout_msecs / 1000L; + ts.tv_nsec = ( timeout_msecs % 1000L ) * 1000000L; + r = kevent( kq, kqevents, nkqevents, kqrevents, nfiles, &ts ); + } + nkqevents = 0; + if ( r == -1 ) + return -1; + + for ( i = 0; i < r; ++i ) + kqrfdidx[kqrevents[i].ident] = i; + + return r; + } + + +static int +kqueue_check_fd( int fd ) + { + int ridx = kqrfdidx[fd]; + + if ( ridx < 0 || ridx >= nfiles ) + { + syslog( LOG_ERR, "bad ridx (%d) in kqueue_check_fd!", ridx ); + return 0; + } + if ( ridx >= nreturned ) + return 0; + if ( kqrevents[ridx].ident != fd ) + return 0; + if ( kqrevents[ridx].flags & EV_ERROR ) + return 0; + switch ( fd_rw[fd] ) + { + case FDW_READ: return kqrevents[ridx].filter == EVFILT_READ; + case FDW_WRITE: return kqrevents[ridx].filter == EVFILT_WRITE; + default: return 0; + } + } + + +static int +kqueue_get_fd( int ridx ) + { + if ( ridx < 0 || ridx >= nfiles ) + { + syslog( LOG_ERR, "bad ridx (%d) in kqueue_get_fd!", ridx ); + return -1; + } + return kqrevents[ridx].ident; + } + +#else /* HAVE_KQUEUE */ + + +# ifdef HAVE_DEVPOLL + +static int maxdpevents; +static struct pollfd* dpevents; +static int ndpevents; +static struct pollfd* dprevents; +static int* dp_rfdidx; +static int dp; + + +static int +devpoll_init( int nf ) + { + dp = open( "/dev/poll", O_RDWR ); + if ( dp == -1 ) + return -1; + (void) fcntl( dp, F_SETFD, 1 ); + maxdpevents = nf * 2; + dpevents = (struct pollfd*) malloc( sizeof(struct pollfd) * maxdpevents ); + dprevents = (struct pollfd*) malloc( sizeof(struct pollfd) * nf ); + dp_rfdidx = (int*) malloc( sizeof(int) * nf ); + if ( dpevents == (struct pollfd*) 0 || dprevents == (struct pollfd*) 0 || + dp_rfdidx == (int*) 0 ) + return -1; + (void) memset( dp_rfdidx, 0, sizeof(int) * nf ); + return 0; + } + + +static void +devpoll_add_fd( int fd, int rw ) + { + if ( ndpevents >= maxdpevents ) + { + syslog( LOG_ERR, "too many fds in devpoll_add_fd!" ); + return; + } + dpevents[ndpevents].fd = fd; + switch ( rw ) + { + case FDW_READ: dpevents[ndpevents].events = POLLIN; break; + case FDW_WRITE: dpevents[ndpevents].events = POLLOUT; break; + default: break; + } + ++ndpevents; + } + + +static void +devpoll_del_fd( int fd ) + { + if ( ndpevents >= maxdpevents ) + { + syslog( LOG_ERR, "too many fds in devpoll_del_fd!" ); + return; + } + dpevents[ndpevents].fd = fd; + dpevents[ndpevents].events = POLLREMOVE; + ++ndpevents; + } + + +static int +devpoll_watch( long timeout_msecs ) + { + int i, r; + struct dvpoll dvp; + + r = sizeof(struct pollfd) * ndpevents; + if ( r > 0 && write( dp, dpevents, r ) != r ) + return -1; + + ndpevents = 0; + dvp.dp_fds = dprevents; + dvp.dp_nfds = nfiles; + dvp.dp_timeout = (int) timeout_msecs; + + r = ioctl( dp, DP_POLL, &dvp ); + if ( r == -1 ) + return -1; + + for ( i = 0; i < r; ++i ) + dp_rfdidx[dprevents[i].fd] = i; + + return r; + } + + +static int +devpoll_check_fd( int fd ) + { + int ridx = dp_rfdidx[fd]; + + if ( ridx < 0 || ridx >= nfiles ) + { + syslog( LOG_ERR, "bad ridx (%d) in devpoll_check_fd!", ridx ); + return 0; + } + if ( ridx >= nreturned ) + return 0; + if ( dprevents[ridx].fd != fd ) + return 0; + if ( dprevents[ridx].revents & POLLERR ) + return 0; + switch ( fd_rw[fd] ) + { + case FDW_READ: return dprevents[ridx].revents & ( POLLIN | POLLHUP | POLLNVAL ); + case FDW_WRITE: return dprevents[ridx].revents & ( POLLOUT | POLLHUP | POLLNVAL ); + default: return 0; + } + } + + +static int +devpoll_get_fd( int ridx ) + { + if ( ridx < 0 || ridx >= nfiles ) + { + syslog( LOG_ERR, "bad ridx (%d) in devpoll_get_fd!", ridx ); + return -1; + } + return dprevents[ridx].fd; + } + + +# else /* HAVE_DEVPOLL */ + + +# ifdef HAVE_POLL + +static struct pollfd* pollfds; +static int npoll_fds; +static int* poll_fdidx; +static int* poll_rfdidx; + + +static int +poll_init( int nf ) + { + int i; + + pollfds = (struct pollfd*) malloc( sizeof(struct pollfd) * nf ); + poll_fdidx = (int*) malloc( sizeof(int) * nf ); + poll_rfdidx = (int*) malloc( sizeof(int) * nf ); + if ( pollfds == (struct pollfd*) 0 || poll_fdidx == (int*) 0 || + poll_rfdidx == (int*) 0 ) + return -1; + for ( i = 0; i < nf; ++i ) + pollfds[i].fd = poll_fdidx[i] = -1; + return 0; + } + + +static void +poll_add_fd( int fd, int rw ) + { + if ( npoll_fds >= nfiles ) + { + syslog( LOG_ERR, "too many fds in poll_add_fd!" ); + return; + } + pollfds[npoll_fds].fd = fd; + switch ( rw ) + { + case FDW_READ: pollfds[npoll_fds].events = POLLIN; break; + case FDW_WRITE: pollfds[npoll_fds].events = POLLOUT; break; + default: break; + } + poll_fdidx[fd] = npoll_fds; + ++npoll_fds; + } + + +static void +poll_del_fd( int fd ) + { + int idx = poll_fdidx[fd]; + + if ( idx < 0 || idx >= nfiles ) + { + syslog( LOG_ERR, "bad idx (%d) in poll_del_fd!", idx ); + return; + } + --npoll_fds; + pollfds[idx] = pollfds[npoll_fds]; + poll_fdidx[pollfds[idx].fd] = idx; + pollfds[npoll_fds].fd = -1; + poll_fdidx[fd] = -1; + } + + +static int +poll_watch( long timeout_msecs ) + { + int r, ridx, i; + + r = poll( pollfds, npoll_fds, (int) timeout_msecs ); + if ( r <= 0 ) + return r; + + ridx = 0; + for ( i = 0; i < npoll_fds; ++i ) + if ( pollfds[i].revents & + ( POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL ) ) + { + poll_rfdidx[ridx++] = pollfds[i].fd; + if ( ridx == r ) + break; + } + + return ridx; /* should be equal to r */ + } + + +static int +poll_check_fd( int fd ) + { + int fdidx = poll_fdidx[fd]; + + if ( fdidx < 0 || fdidx >= nfiles ) + { + syslog( LOG_ERR, "bad fdidx (%d) in poll_check_fd!", fdidx ); + return 0; + } + if ( pollfds[fdidx].revents & POLLERR ) + return 0; + switch ( fd_rw[fd] ) + { + case FDW_READ: return pollfds[fdidx].revents & ( POLLIN | POLLHUP | POLLNVAL ); + case FDW_WRITE: return pollfds[fdidx].revents & ( POLLOUT | POLLHUP | POLLNVAL ); + default: return 0; + } + } + + +static int +poll_get_fd( int ridx ) + { + if ( ridx < 0 || ridx >= nfiles ) + { + syslog( LOG_ERR, "bad ridx (%d) in poll_get_fd!", ridx ); + return -1; + } + return poll_rfdidx[ridx]; + } + +# else /* HAVE_POLL */ + + +# ifdef HAVE_SELECT + +static fd_set master_rfdset; +static fd_set master_wfdset; +static fd_set working_rfdset; +static fd_set working_wfdset; +static int* select_fds; +static int* select_fdidx; +static int* select_rfdidx; +static int nselect_fds; +static int maxfd; +static int maxfd_changed; + + +static int +select_init( int nf ) + { + int i; + + FD_ZERO( &master_rfdset ); + FD_ZERO( &master_wfdset ); + select_fds = (int*) malloc( sizeof(int) * nf ); + select_fdidx = (int*) malloc( sizeof(int) * nf ); + select_rfdidx = (int*) malloc( sizeof(int) * nf ); + if ( select_fds == (int*) 0 || select_fdidx == (int*) 0 || + select_rfdidx == (int*) 0 ) + return -1; + nselect_fds = 0; + maxfd = -1; + maxfd_changed = 0; + for ( i = 0; i < nf; ++i ) + select_fds[i] = select_fdidx[i] = -1; + return 0; + } + + +static void +select_add_fd( int fd, int rw ) + { + if ( nselect_fds >= nfiles ) + { + syslog( LOG_ERR, "too many fds in select_add_fd!" ); + return; + } + select_fds[nselect_fds] = fd; + switch ( rw ) + { + case FDW_READ: FD_SET( fd, &master_rfdset ); break; + case FDW_WRITE: FD_SET( fd, &master_wfdset ); break; + default: break; + } + if ( fd > maxfd ) + maxfd = fd; + select_fdidx[fd] = nselect_fds; + ++nselect_fds; + } + + +static void +select_del_fd( int fd ) + { + int idx = select_fdidx[fd]; + + if ( idx < 0 || idx >= nfiles ) + { + syslog( LOG_ERR, "bad idx (%d) in select_del_fd!", idx ); + return; + } + + --nselect_fds; + select_fds[idx] = select_fds[nselect_fds]; + select_fdidx[select_fds[idx]] = idx; + select_fds[nselect_fds] = -1; + select_fdidx[fd] = -1; + + FD_CLR( fd, &master_rfdset ); + FD_CLR( fd, &master_wfdset ); + + if ( fd >= maxfd ) + maxfd_changed = 1; + } + + +static int +select_get_maxfd( void ) + { + if ( maxfd_changed ) + { + int i; + maxfd = -1; + for ( i = 0; i < nselect_fds; ++i ) + if ( select_fds[i] > maxfd ) + maxfd = select_fds[i]; + maxfd_changed = 0; + } + return maxfd; + } + + +static int +select_watch( long timeout_msecs ) + { + int mfd; + int r, idx, ridx; + + working_rfdset = master_rfdset; + working_wfdset = master_wfdset; + mfd = select_get_maxfd(); + if ( timeout_msecs == INFTIM ) + r = select( + mfd + 1, &working_rfdset, &working_wfdset, (fd_set*) 0, + (struct timeval*) 0 ); + else + { + struct timeval timeout; + timeout.tv_sec = timeout_msecs / 1000L; + timeout.tv_usec = ( timeout_msecs % 1000L ) * 1000L; + r = select( + mfd + 1, &working_rfdset, &working_wfdset, (fd_set*) 0, &timeout ); + } + if ( r <= 0 ) + return r; + + ridx = 0; + for ( idx = 0; idx < nselect_fds; ++idx ) + if ( select_check_fd( select_fds[idx] ) ) + { + select_rfdidx[ridx++] = select_fds[idx]; + if ( ridx == r ) + break; + } + + return ridx; /* should be equal to r */ + } + + +static int +select_check_fd( int fd ) + { + switch ( fd_rw[fd] ) + { + case FDW_READ: return FD_ISSET( fd, &working_rfdset ); + case FDW_WRITE: return FD_ISSET( fd, &working_wfdset ); + default: return 0; + } + } + + +static int +select_get_fd( int ridx ) + { + if ( ridx < 0 || ridx >= nfiles ) + { + syslog( LOG_ERR, "bad ridx (%d) in select_get_fd!", ridx ); + return -1; + } + return select_rfdidx[ridx]; + } + +# endif /* HAVE_SELECT */ + +# endif /* HAVE_POLL */ + +# endif /* HAVE_DEVPOLL */ + +#endif /* HAVE_KQUEUE */ diff --git a/fdwatch.h b/fdwatch.h new file mode 100644 index 0000000..96984cd --- /dev/null +++ b/fdwatch.h @@ -0,0 +1,85 @@ +/* fdwatch.h - header file for fdwatch package +** +** This package abstracts the use of the select()/poll()/kqueue() +** system calls. The basic function of these calls is to watch a set +** of file descriptors for activity. select() originated in the BSD world, +** while poll() came from SysV land, and their interfaces are somewhat +** different. fdwatch lets you write your code to a single interface, +** with the portability differences hidden inside the package. +** +** Usage is fairly simple. Call fdwatch_get_nfiles() to initialize +** the package and find out how many fine descriptors are available. +** Then each time through your main loop, call fdwatch_clear(), then +** fdwatch_add_fd() for each of the descriptors you want to watch, +** then call fdwatch() to actually perform the watch. After it returns +** you can check which descriptors are ready via fdwatch_check_fd(). +** +** If your descriptor set hasn't changed from the last time through +** the loop, you can skip calling fdwatch_clear() and fdwatch_add_fd() +** to save a little CPU time. +** +** +** Copyright © 1999 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _FDWATCH_H_ +#define _FDWATCH_H_ + +#define FDW_READ 0 +#define FDW_WRITE 1 + +#ifndef INFTIM +#define INFTIM -1 +#endif /* INFTIM */ + +/* Figure out how many file descriptors the system allows, and +** initialize the fdwatch data structures. Returns -1 on failure. +*/ +int fdwatch_get_nfiles( void ); + +/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ +void fdwatch_add_fd( int fd, void* client_data, int rw ); + +/* Delete a descriptor from the watch list. */ +void fdwatch_del_fd( int fd ); + +/* Do the watch. Return value is the number of descriptors that are ready, +** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means +** wait indefinitely. +*/ +int fdwatch( long timeout_msecs ); + +/* Check if a descriptor was ready. */ +int fdwatch_check_fd( int fd ); + +/* Get the client data for the next returned event. Returns -1 when there +** are no more events. +*/ +void* fdwatch_get_next_client_data( void ); + +/* Generate debugging statistics syslog message. */ +void fdwatch_logstats( long secs ); + +#endif /* _FDWATCH_H_ */ diff --git a/index.html b/index.html new file mode 100644 index 0000000..9131592 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + +thttpd is running + + +

thttpd is running

+ +

+Looks like you got it working. Congrats. + +

+Here's a link to the thttpd web pages. + + + diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..ebc6691 --- /dev/null +++ b/install-sh @@ -0,0 +1,250 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/libhttpd.c b/libhttpd.c new file mode 100644 index 0000000..c6b1622 --- /dev/null +++ b/libhttpd.c @@ -0,0 +1,4279 @@ +/* libhttpd.c - HTTP protocol library +** +** Copyright © 1995,1998,1999,2000,2001,2015 by +** Jef Poskanzer . All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include "config.h" +#include "version.h" + +#ifdef SHOW_SERVER_VERSION +#define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE +#else /* SHOW_SERVER_VERSION */ +#define EXPOSED_SERVER_SOFTWARE "thttpd" +#endif /* SHOW_SERVER_VERSION */ + +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif /* HAVE_MEMORY_H */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_OSRELDATE_H +#include +#endif /* HAVE_OSRELDATE_H */ + +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif + +#include "libhttpd.h" +#include "mmc.h" +#include "timers.h" +#include "match.h" +#include "tdate_parse.h" + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + +#ifndef HAVE_SOCKLENT +typedef int socklen_t; +#endif + +#ifdef __CYGWIN__ +#define timezone _timezone +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +/* Forwards. */ +static void check_options( void ); +static void free_httpd_server( httpd_server* hs ); +static int initialize_listen_socket( httpd_sockaddr* saP ); +static void add_response( httpd_conn* hc, char* str ); +static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod ); +static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ); +static void send_response_tail( httpd_conn* hc ); +static void defang( char* str, char* dfstr, int dfsize ); +#ifdef ERR_DIR +static int send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename ); +#endif /* ERR_DIR */ +#ifdef AUTH_FILE +static void send_authenticate( httpd_conn* hc, char* realm ); +static int b64_decode( const char* str, unsigned char* space, int size ); +static int auth_check( httpd_conn* hc, char* dirname ); +static int auth_check2( httpd_conn* hc, char* dirname ); +#endif /* AUTH_FILE */ +static void send_dirredirect( httpd_conn* hc ); +static int hexit( char c ); +static void strdecode( char* to, char* from ); +#ifdef GENERATE_INDEXES +static void strencode( char* to, int tosize, char* from ); +#endif /* GENERATE_INDEXES */ +#ifdef TILDE_MAP_1 +static int tilde_map_1( httpd_conn* hc ); +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 +static int tilde_map_2( httpd_conn* hc ); +#endif /* TILDE_MAP_2 */ +static int vhost_map( httpd_conn* hc ); +static char* expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped ); +static char* bufgets( httpd_conn* hc ); +static void de_dotdot( char* file ); +static void init_mime( void ); +static void figure_mime( httpd_conn* hc ); +#ifdef CGI_TIMELIMIT +static void cgi_kill2( ClientData client_data, struct timeval* nowP ); +static void cgi_kill( ClientData client_data, struct timeval* nowP ); +#endif /* CGI_TIMELIMIT */ +#ifdef GENERATE_INDEXES +static int ls( httpd_conn* hc ); +#endif /* GENERATE_INDEXES */ +static char* build_env( char* fmt, char* arg ); +#ifdef SERVER_NAME_LIST +static char* hostname_map( char* hostname ); +#endif /* SERVER_NAME_LIST */ +static char** make_envp( httpd_conn* hc ); +static char** make_argp( httpd_conn* hc ); +static void cgi_interpose_input( httpd_conn* hc, int wfd ); +static void post_post_garbage_hack( httpd_conn* hc ); +static void cgi_interpose_output( httpd_conn* hc, int rfd ); +static void cgi_child( httpd_conn* hc ); +static int cgi( httpd_conn* hc ); +static int really_start_request( httpd_conn* hc, struct timeval* nowP ); +static void make_log_entry( httpd_conn* hc, struct timeval* nowP ); +static int check_referrer( httpd_conn* hc ); +static int really_check_referrer( httpd_conn* hc ); +static int sockaddr_check( httpd_sockaddr* saP ); +static size_t sockaddr_len( httpd_sockaddr* saP ); +static int my_snprintf( char* str, size_t size, const char* format, ... ); +#ifndef HAVE_ATOLL +static long long atoll( const char* str ); +#endif /* HAVE_ATOLL */ + + +/* This global keeps track of whether we are in the main process or a +** sub-process. The reason is that httpd_write_response() can get called +** in either context; when it is called from the main process it must use +** non-blocking I/O to avoid stalling the server, but when it is called +** from a sub-process it wants to use blocking I/O so that the whole +** response definitely gets written. So, it checks this variable. A bit +** of a hack but it seems to do the right thing. +*/ +static int sub_process = 0; + + +static void +check_options( void ) + { +#if defined(TILDE_MAP_1) && defined(TILDE_MAP_2) + syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" ); + exit( 1 ); +#endif /* both */ + } + + +static void +free_httpd_server( httpd_server* hs ) + { + if ( hs->binding_hostname != (char*) 0 ) + free( (void*) hs->binding_hostname ); + if ( hs->cwd != (char*) 0 ) + free( (void*) hs->cwd ); + if ( hs->cgi_pattern != (char*) 0 ) + free( (void*) hs->cgi_pattern ); + if ( hs->charset != (char*) 0 ) + free( (void*) hs->charset ); + if ( hs->p3p != (char*) 0 ) + free( (void*) hs->p3p ); + if ( hs->url_pattern != (char*) 0 ) + free( (void*) hs->url_pattern ); + if ( hs->local_pattern != (char*) 0 ) + free( (void*) hs->local_pattern ); + free( (void*) hs ); + } + + +httpd_server* +httpd_initialize( + char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, + unsigned short port, char* cgi_pattern, int cgi_limit, char* charset, + char* p3p, int max_age, char* cwd, int no_log, FILE* logfp, + int no_symlink_check, int vhost, int global_passwd, char* url_pattern, + char* local_pattern, int no_empty_referrers ) + { + httpd_server* hs; + static char ghnbuf[256]; + char* cp; + + check_options(); + + hs = NEW( httpd_server, 1 ); + if ( hs == (httpd_server*) 0 ) + { + syslog( LOG_CRIT, "out of memory allocating an httpd_server" ); + return (httpd_server*) 0; + } + + if ( hostname != (char*) 0 ) + { + hs->binding_hostname = strdup( hostname ); + if ( hs->binding_hostname == (char*) 0 ) + { + syslog( LOG_CRIT, "out of memory copying hostname" ); + return (httpd_server*) 0; + } + hs->server_hostname = hs->binding_hostname; + } + else + { + hs->binding_hostname = (char*) 0; + hs->server_hostname = (char*) 0; + if ( gethostname( ghnbuf, sizeof(ghnbuf) ) < 0 ) + ghnbuf[0] = '\0'; +#ifdef SERVER_NAME_LIST + if ( ghnbuf[0] != '\0' ) + hs->server_hostname = hostname_map( ghnbuf ); +#endif /* SERVER_NAME_LIST */ + if ( hs->server_hostname == (char*) 0 ) + { +#ifdef SERVER_NAME + hs->server_hostname = SERVER_NAME; +#else /* SERVER_NAME */ + if ( ghnbuf[0] != '\0' ) + hs->server_hostname = ghnbuf; +#endif /* SERVER_NAME */ + } + } + + hs->port = port; + if ( cgi_pattern == (char*) 0 ) + hs->cgi_pattern = (char*) 0; + else + { + /* Nuke any leading slashes. */ + if ( cgi_pattern[0] == '/' ) + ++cgi_pattern; + hs->cgi_pattern = strdup( cgi_pattern ); + if ( hs->cgi_pattern == (char*) 0 ) + { + syslog( LOG_CRIT, "out of memory copying cgi_pattern" ); + return (httpd_server*) 0; + } + /* Nuke any leading slashes in the cgi pattern. */ + while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 ) + (void) ol_strcpy( cp + 1, cp + 2 ); + } + hs->cgi_limit = cgi_limit; + hs->cgi_count = 0; + hs->charset = strdup( charset ); + hs->p3p = strdup( p3p ); + hs->max_age = max_age; + hs->cwd = strdup( cwd ); + if ( hs->cwd == (char*) 0 ) + { + syslog( LOG_CRIT, "out of memory copying cwd" ); + return (httpd_server*) 0; + } + if ( url_pattern == (char*) 0 ) + hs->url_pattern = (char*) 0; + else + { + hs->url_pattern = strdup( url_pattern ); + if ( hs->url_pattern == (char*) 0 ) + { + syslog( LOG_CRIT, "out of memory copying url_pattern" ); + return (httpd_server*) 0; + } + } + if ( local_pattern == (char*) 0 ) + hs->local_pattern = (char*) 0; + else + { + hs->local_pattern = strdup( local_pattern ); + if ( hs->local_pattern == (char*) 0 ) + { + syslog( LOG_CRIT, "out of memory copying local_pattern" ); + return (httpd_server*) 0; + } + } + hs->no_log = no_log; + hs->logfp = (FILE*) 0; + httpd_set_logfp( hs, logfp ); + hs->no_symlink_check = no_symlink_check; + hs->vhost = vhost; + hs->global_passwd = global_passwd; + hs->no_empty_referrers = no_empty_referrers; + + /* Initialize listen sockets. Try v6 first because of a Linux peculiarity; + ** like some other systems, it has magical v6 sockets that also listen for + ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails. + */ + if ( sa6P == (httpd_sockaddr*) 0 ) + hs->listen6_fd = -1; + else + hs->listen6_fd = initialize_listen_socket( sa6P ); + if ( sa4P == (httpd_sockaddr*) 0 ) + hs->listen4_fd = -1; + else + hs->listen4_fd = initialize_listen_socket( sa4P ); + /* If we didn't get any valid sockets, fail. */ + if ( hs->listen4_fd == -1 && hs->listen6_fd == -1 ) + { + free_httpd_server( hs ); + return (httpd_server*) 0; + } + + init_mime(); + + /* Done initializing. */ + if ( hs->binding_hostname == (char*) 0 ) + syslog( + LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE, + (int) hs->port ); + else + syslog( + LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE, + httpd_ntoa( hs->listen4_fd != -1 ? sa4P : sa6P ), + (int) hs->port ); + return hs; + } + + +static int +initialize_listen_socket( httpd_sockaddr* saP ) + { + int listen_fd; + int on, flags; + + /* Check sockaddr. */ + if ( ! sockaddr_check( saP ) ) + { + syslog( LOG_CRIT, "unknown sockaddr family on listen socket" ); + return -1; + } + + /* Create socket. */ + listen_fd = socket( saP->sa.sa_family, SOCK_STREAM, 0 ); + if ( listen_fd < 0 ) + { + syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) ); + return -1; + } + (void) fcntl( listen_fd, F_SETFD, 1 ); + + /* Allow reuse of local addresses. */ + on = 1; + if ( setsockopt( + listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &on, + sizeof(on) ) < 0 ) + syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" ); + + /* Bind to it. */ + if ( bind( listen_fd, &saP->sa, sockaddr_len( saP ) ) < 0 ) + { + syslog( + LOG_CRIT, "bind %.80s - %m", httpd_ntoa( saP ) ); + (void) close( listen_fd ); + return -1; + } + + /* Set the listen file descriptor to no-delay / non-blocking mode. */ + flags = fcntl( listen_fd, F_GETFL, 0 ); + if ( flags == -1 ) + { + syslog( LOG_CRIT, "fcntl F_GETFL - %m" ); + (void) close( listen_fd ); + return -1; + } + if ( fcntl( listen_fd, F_SETFL, flags | O_NDELAY ) < 0 ) + { + syslog( LOG_CRIT, "fcntl O_NDELAY - %m" ); + (void) close( listen_fd ); + return -1; + } + + /* Start a listen going. */ + if ( listen( listen_fd, LISTEN_BACKLOG ) < 0 ) + { + syslog( LOG_CRIT, "listen - %m" ); + (void) close( listen_fd ); + return -1; + } + + /* Use accept filtering, if available. */ +#ifdef SO_ACCEPTFILTER + { +#if ( __FreeBSD_version >= 411000 ) +#define ACCEPT_FILTER_NAME "httpready" +#else +#define ACCEPT_FILTER_NAME "dataready" +#endif + struct accept_filter_arg af; + (void) bzero( &af, sizeof(af) ); + (void) strcpy( af.af_name, ACCEPT_FILTER_NAME ); + (void) setsockopt( + listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) ); + } +#endif /* SO_ACCEPTFILTER */ + + return listen_fd; + } + + +void +httpd_set_logfp( httpd_server* hs, FILE* logfp ) + { + if ( hs->logfp != (FILE*) 0 ) + (void) fclose( hs->logfp ); + hs->logfp = logfp; + } + + +void +httpd_terminate( httpd_server* hs ) + { + httpd_unlisten( hs ); + if ( hs->logfp != (FILE*) 0 ) + (void) fclose( hs->logfp ); + free_httpd_server( hs ); + } + + +void +httpd_unlisten( httpd_server* hs ) + { + if ( hs->listen4_fd != -1 ) + { + (void) close( hs->listen4_fd ); + hs->listen4_fd = -1; + } + if ( hs->listen6_fd != -1 ) + { + (void) close( hs->listen6_fd ); + hs->listen6_fd = -1; + } + } + + +/* Conditional macro to allow two alternate forms for use in the built-in +** error pages. If EXPLICIT_ERROR_PAGES is defined, the second and more +** explicit error form is used; otherwise, the first and more generic +** form is used. +*/ +#ifdef EXPLICIT_ERROR_PAGES +#define ERROR_FORM(a,b) b +#else /* EXPLICIT_ERROR_PAGES */ +#define ERROR_FORM(a,b) a +#endif /* EXPLICIT_ERROR_PAGES */ + + +static char* ok200title = "OK"; +static char* ok206title = "Partial Content"; + +static char* err302title = "Found"; +static char* err302form = "The actual URL is '%.80s'.\n"; + +static char* err304title = "Not Modified"; + +char* httpd_err400title = "Bad Request"; +char* httpd_err400form = + "Your request has bad syntax or is inherently impossible to satisfy.\n"; + +#ifdef AUTH_FILE +static char* err401title = "Unauthorized"; +static char* err401form = + "Authorization required for the URL '%.80s'.\n"; +#endif /* AUTH_FILE */ + +static char* err403title = "Forbidden"; +#ifndef EXPLICIT_ERROR_PAGES +static char* err403form = + "You do not have permission to get URL '%.80s' from this server.\n"; +#endif /* !EXPLICIT_ERROR_PAGES */ + +static char* err404title = "Not Found"; +static char* err404form = + "The requested URL '%.80s' was not found on this server.\n"; + +char* httpd_err408title = "Request Timeout"; +char* httpd_err408form = + "No request appeared within a reasonable time period.\n"; + +static char* err451title = "Unavailable For Legal Reasons"; +static char* err451form = + "You do not have legal permission to get URL '%.80s' from this server.\n"; + +static char* err500title = "Internal Error"; +static char* err500form = + "There was an unusual problem serving the requested URL '%.80s'.\n"; + +static char* err501title = "Not Implemented"; +static char* err501form = + "The requested method '%.80s' is not implemented by this server.\n"; + +char* httpd_err503title = "Service Temporarily Overloaded"; +char* httpd_err503form = + "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n"; + + +/* Append a string to the buffer waiting to be sent as response. */ +static void +add_response( httpd_conn* hc, char* str ) + { + size_t len; + + len = strlen( str ); + httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len ); + (void) memmove( &(hc->response[hc->responselen]), str, len ); + hc->responselen += len; + } + +/* Send the buffered response. */ +void +httpd_write_response( httpd_conn* hc ) + { + /* If we are in a sub-process, turn off no-delay mode. */ + if ( sub_process ) + httpd_clear_ndelay( hc->conn_fd ); + /* Send the response, if necessary. */ + if ( hc->responselen > 0 ) + { + (void) httpd_write_fully( hc->conn_fd, hc->response, hc->responselen ); + hc->responselen = 0; + } + } + + +/* Set no-delay / non-blocking mode on a socket. */ +void +httpd_set_ndelay( int fd ) + { + int flags, newflags; + + flags = fcntl( fd, F_GETFL, 0 ); + if ( flags != -1 ) + { + newflags = flags | (int) O_NDELAY; + if ( newflags != flags ) + (void) fcntl( fd, F_SETFL, newflags ); + } + } + + +/* Clear no-delay / non-blocking mode on a socket. */ +void +httpd_clear_ndelay( int fd ) + { + int flags, newflags; + + flags = fcntl( fd, F_GETFL, 0 ); + if ( flags != -1 ) + { + newflags = flags & ~ (int) O_NDELAY; + if ( newflags != flags ) + (void) fcntl( fd, F_SETFL, newflags ); + } + } + + +static void +send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod ) + { + time_t now, expires; + const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT"; + char nowbuf[100]; + char modbuf[100]; + char expbuf[100]; + char fixed_type[500]; + char buf[1000]; + int partial_content; + int s100; + + hc->status = status; + hc->bytes_to_send = length; + if ( hc->mime_flag ) + { + if ( status == 200 && hc->got_range && + ( hc->last_byte_index >= hc->first_byte_index ) && + ( ( hc->last_byte_index != length - 1 ) || + ( hc->first_byte_index != 0 ) ) && + ( hc->range_if == (time_t) -1 || + hc->range_if == hc->sb.st_mtime ) ) + { + partial_content = 1; + hc->status = status = 206; + title = ok206title; + } + else + { + partial_content = 0; + hc->got_range = 0; + } + + now = time( (time_t*) 0 ); + if ( mod == (time_t) 0 ) + mod = now; + (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) ); + (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) ); + (void) my_snprintf( + fixed_type, sizeof(fixed_type), type, hc->hs->charset ); + (void) my_snprintf( buf, sizeof(buf), + "%.20s %d %s\015\012Server: %s\015\012Content-Type: %s\015\012Date: %s\015\012Last-Modified: %s\015\012Accept-Ranges: bytes\015\012Connection: close\015\012", + hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type, + nowbuf, modbuf ); + add_response( hc, buf ); + s100 = status / 100; + if ( s100 != 2 && s100 != 3 ) + { + (void) my_snprintf( buf, sizeof(buf), + "Cache-Control: no-cache,no-store\015\012" ); + add_response( hc, buf ); + } + if ( encodings[0] != '\0' ) + { + (void) my_snprintf( buf, sizeof(buf), + "Content-Encoding: %s\015\012", encodings ); + add_response( hc, buf ); + } + if ( partial_content ) + { + (void) my_snprintf( buf, sizeof(buf), + "Content-Range: bytes %lld-%lld/%lld\015\012Content-Length: %lld\015\012", + (long long) hc->first_byte_index, + (long long) hc->last_byte_index, + (long long) length, + (long long) ( hc->last_byte_index - hc->first_byte_index + 1 ) ); + add_response( hc, buf ); + } + else if ( length >= 0 ) + { + (void) my_snprintf( buf, sizeof(buf), + "Content-Length: %lld\015\012", (long long) length ); + add_response( hc, buf ); + } + if ( hc->hs->p3p[0] != '\0' ) + { + (void) my_snprintf( buf, sizeof(buf), "P3P: %s\015\012", hc->hs->p3p ); + add_response( hc, buf ); + } + if ( hc->hs->max_age >= 0 ) + { + expires = now + hc->hs->max_age; + (void) strftime( + expbuf, sizeof(expbuf), rfc1123fmt, gmtime( &expires ) ); + (void) my_snprintf( buf, sizeof(buf), + "Cache-Control: max-age=%d\015\012Expires: %s\015\012", + hc->hs->max_age, expbuf ); + add_response( hc, buf ); + } + if ( extraheads[0] != '\0' ) + add_response( hc, extraheads ); + add_response( hc, "\015\012" ); + } + } + + +static int str_alloc_count = 0; +static size_t str_alloc_size = 0; + +void +httpd_realloc_str( char** strP, size_t* maxsizeP, size_t size ) + { + if ( *maxsizeP == 0 ) + { + *maxsizeP = MAX( 200, size + 100 ); + *strP = NEW( char, *maxsizeP + 1 ); + ++str_alloc_count; + str_alloc_size += *maxsizeP; + } + else if ( size > *maxsizeP ) + { + str_alloc_size -= *maxsizeP; + *maxsizeP = MAX( *maxsizeP * 2, size * 5 / 4 ); + *strP = RENEW( *strP, char, *maxsizeP + 1 ); + str_alloc_size += *maxsizeP; + } + else + return; + if ( *strP == (char*) 0 ) + { + syslog( + LOG_ERR, "out of memory reallocating a string to %ld bytes", + (long) *maxsizeP ); + exit( 1 ); + } + } + + +static void +send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ) + { + char defanged_arg[1000], buf[2000]; + + send_mime( + hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1, + (time_t) 0 ); + (void) my_snprintf( buf, sizeof(buf), "\ +\n\ +\n\ +\n\ +\n\ + \n\ + \n\ + %d %s\n\ + \n\ +\n\ + \n\ +\n\ +

%d %s

\n", + status, title, status, title ); + add_response( hc, buf ); + defang( arg, defanged_arg, sizeof(defanged_arg) ); + (void) my_snprintf( buf, sizeof(buf), form, defanged_arg ); + add_response( hc, buf ); + if ( match( "**MSIE**", hc->useragent ) ) + { + int n; + add_response( hc, "\n" ); + } + send_response_tail( hc ); + } + + +static void +send_response_tail( httpd_conn* hc ) + { + char buf[1000]; + + (void) my_snprintf( buf, sizeof(buf), "\ +
\n\ +\n\ +
%s
\n\ +\n\ + \n\ +\n\ +\n", + SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE ); + add_response( hc, buf ); + } + + +static void +defang( char* str, char* dfstr, int dfsize ) + { + char* cp1; + char* cp2; + + for ( cp1 = str, cp2 = dfstr; + *cp1 != '\0' && cp2 - dfstr < dfsize - 5; + ++cp1, ++cp2 ) + { + switch ( *cp1 ) + { + case '<': + *cp2++ = '&'; + *cp2++ = 'l'; + *cp2++ = 't'; + *cp2 = ';'; + break; + case '>': + *cp2++ = '&'; + *cp2++ = 'g'; + *cp2++ = 't'; + *cp2 = ';'; + break; + default: + *cp2 = *cp1; + break; + } + } + *cp2 = '\0'; + } + + +void +httpd_send_err( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ) + { +#ifdef ERR_DIR + + char filename[1000]; + + /* Try virtual host error page. */ + if ( hc->hs->vhost && hc->hostdir[0] != '\0' ) + { + (void) my_snprintf( filename, sizeof(filename), + "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status ); + if ( send_err_file( hc, status, title, extraheads, filename ) ) + return; + } + + /* Try server-wide error page. */ + (void) my_snprintf( filename, sizeof(filename), + "%s/err%d.html", ERR_DIR, status ); + if ( send_err_file( hc, status, title, extraheads, filename ) ) + return; + + /* Fall back on built-in error page. */ + send_response( hc, status, title, extraheads, form, arg ); + +#else /* ERR_DIR */ + + send_response( hc, status, title, extraheads, form, arg ); + +#endif /* ERR_DIR */ + } + + +#ifdef ERR_DIR +static int +send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename ) + { + FILE* fp; + char buf[1000]; + size_t r; + + fp = fopen( filename, "r" ); + if ( fp == (FILE*) 0 ) + return 0; + send_mime( + hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1, + (time_t) 0 ); + for (;;) + { + r = fread( buf, 1, sizeof(buf) - 1, fp ); + if ( r == 0 ) + break; + buf[r] = '\0'; + add_response( hc, buf ); + } + (void) fclose( fp ); + +#ifdef ERR_APPEND_SERVER_INFO + send_response_tail( hc ); +#endif /* ERR_APPEND_SERVER_INFO */ + + return 1; + } +#endif /* ERR_DIR */ + + +#ifdef AUTH_FILE + +static void +send_authenticate( httpd_conn* hc, char* realm ) + { + static char* header; + static size_t maxheader = 0; + static char headstr[] = "WWW-Authenticate: Basic realm=\""; + + httpd_realloc_str( + &header, &maxheader, sizeof(headstr) + strlen( realm ) + 3 ); + (void) my_snprintf( header, maxheader, "%s%s\"\015\012", headstr, realm ); + httpd_send_err( hc, 401, err401title, header, err401form, hc->encodedurl ); + /* If the request was a POST then there might still be data to be read, + ** so we need to do a lingering close. + */ + if ( hc->method == METHOD_POST ) + hc->should_linger = 1; + } + + +/* Base-64 decoding. This represents binary data as printable ASCII +** characters. Three 8-bit binary bytes are turned into four 6-bit +** values, like so: +** +** [11111111] [22222222] [33333333] +** +** [111111] [112222] [222233] [333333] +** +** Then the 6-bit values are represented using the characters "A-Za-z0-9+/". +*/ + +static int b64_decode_table[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ + }; + +/* Do base-64 decoding on a string. Ignore any non-base64 bytes. +** Return the actual number of bytes generated. The decoded size will +** be at most 3/4 the size of the encoded, and may be smaller if there +** are padding characters (blanks, newlines). +*/ +static int +b64_decode( const char* str, unsigned char* space, int size ) + { + const char* cp; + int space_idx, phase; + int d, prev_d = 0; + unsigned char c; + + space_idx = 0; + phase = 0; + for ( cp = str; *cp != '\0'; ++cp ) + { + d = b64_decode_table[(int) ((unsigned char) *cp)]; + if ( d != -1 ) + { + switch ( phase ) + { + case 0: + ++phase; + break; + case 1: + c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) ); + if ( space_idx < size ) + space[space_idx++] = c; + ++phase; + break; + case 2: + c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) ); + if ( space_idx < size ) + space[space_idx++] = c; + ++phase; + break; + case 3: + c = ( ( ( prev_d & 0x03 ) << 6 ) | d ); + if ( space_idx < size ) + space[space_idx++] = c; + phase = 0; + break; + } + prev_d = d; + } + } + return space_idx; + } + + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int +auth_check( httpd_conn* hc, char* dirname ) + { + if ( hc->hs->global_passwd ) + { + char* topdir; + if ( hc->hs->vhost && hc->hostdir[0] != '\0' ) + topdir = hc->hostdir; + else + topdir = "."; + switch ( auth_check2( hc, topdir ) ) + { + case -1: + return -1; + case 1: + return 1; + } + } + return auth_check2( hc, dirname ); + } + + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int +auth_check2( httpd_conn* hc, char* dirname ) + { + static char* authpath; + static size_t maxauthpath = 0; + struct stat sb; + char authinfo[500]; + char* authpass; + char* colon; + int l; + FILE* fp; + char line[500]; + char* cryp; + static char* prevauthpath; + static size_t maxprevauthpath = 0; + static time_t prevmtime; + static char* prevuser; + static size_t maxprevuser = 0; + static char* prevcryp; + static size_t maxprevcryp = 0; + + /* Construct auth filename. */ + httpd_realloc_str( + &authpath, &maxauthpath, strlen( dirname ) + 1 + sizeof(AUTH_FILE) ); + (void) my_snprintf( authpath, maxauthpath, "%s/%s", dirname, AUTH_FILE ); + + /* Does this directory have an auth file? */ + if ( stat( authpath, &sb ) < 0 ) + /* Nope, let the request go through. */ + return 0; + + /* Does this request contain basic authorization info? */ + if ( hc->authorization[0] == '\0' || + strncmp( hc->authorization, "Basic ", 6 ) != 0 ) + { + /* Nope, return a 401 Unauthorized. */ + send_authenticate( hc, dirname ); + return -1; + } + + /* Decode it. */ + l = b64_decode( + &(hc->authorization[6]), (unsigned char*) authinfo, + sizeof(authinfo) - 1 ); + authinfo[l] = '\0'; + /* Split into user and password. */ + authpass = strchr( authinfo, ':' ); + if ( authpass == (char*) 0 ) + { + /* No colon? Bogus auth info. */ + send_authenticate( hc, dirname ); + return -1; + } + *authpass++ = '\0'; + /* If there are more fields, cut them off. */ + colon = strchr( authpass, ':' ); + if ( colon != (char*) 0 ) + *colon = '\0'; + + /* See if we have a cached entry and can use it. */ + if ( maxprevauthpath != 0 && + strcmp( authpath, prevauthpath ) == 0 && + sb.st_mtime == prevmtime && + strcmp( authinfo, prevuser ) == 0 ) + { + /* Yes. Check against the cached encrypted password. */ + if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 ) + { + /* Ok! */ + httpd_realloc_str( + &hc->remoteuser, &hc->maxremoteuser, strlen( authinfo ) ); + (void) strcpy( hc->remoteuser, authinfo ); + return 1; + } + else + { + /* No. */ + send_authenticate( hc, dirname ); + return -1; + } + } + + /* Open the password file. */ + fp = fopen( authpath, "r" ); + if ( fp == (FILE*) 0 ) + { + /* The file exists but we can't open it? Disallow access. */ + syslog( + LOG_ERR, "%.80s auth file %.80s could not be opened - %m", + httpd_ntoa( &hc->client_addr ), authpath ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n" ), + hc->encodedurl ); + return -1; + } + + /* Read it. */ + while ( fgets( line, sizeof(line), fp ) != (char*) 0 ) + { + /* Nuke newline. */ + l = strlen( line ); + if ( line[l - 1] == '\n' ) + line[l - 1] = '\0'; + /* Split into user and encrypted password. */ + cryp = strchr( line, ':' ); + if ( cryp == (char*) 0 ) + continue; + *cryp++ = '\0'; + /* Is this the right user? */ + if ( strcmp( line, authinfo ) == 0 ) + { + /* Yes. */ + (void) fclose( fp ); + /* So is the password right? */ + if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 ) + { + /* Ok! */ + httpd_realloc_str( + &hc->remoteuser, &hc->maxremoteuser, strlen( line ) ); + (void) strcpy( hc->remoteuser, line ); + /* And cache this user's info for next time. */ + httpd_realloc_str( + &prevauthpath, &maxprevauthpath, strlen( authpath ) ); + (void) strcpy( prevauthpath, authpath ); + prevmtime = sb.st_mtime; + httpd_realloc_str( + &prevuser, &maxprevuser, strlen( authinfo ) ); + (void) strcpy( prevuser, authinfo ); + httpd_realloc_str( &prevcryp, &maxprevcryp, strlen( cryp ) ); + (void) strcpy( prevcryp, cryp ); + return 1; + } + else + { + /* No. */ + send_authenticate( hc, dirname ); + return -1; + } + } + } + + /* Didn't find that user. Access denied. */ + (void) fclose( fp ); + send_authenticate( hc, dirname ); + return -1; + } + +#endif /* AUTH_FILE */ + + +static void +send_dirredirect( httpd_conn* hc ) + { + static char* location; + static char* header; + static size_t maxlocation = 0, maxheader = 0; + static char headstr[] = "Location: "; + + if ( hc->query[0] != '\0') + { + char* cp = strchr( hc->encodedurl, '?' ); + if ( cp != (char*) 0 ) /* should always find it */ + *cp = '\0'; + httpd_realloc_str( + &location, &maxlocation, + strlen( hc->encodedurl ) + 2 + strlen( hc->query ) ); + (void) my_snprintf( location, maxlocation, + "%s/?%s", hc->encodedurl, hc->query ); + } + else + { + httpd_realloc_str( + &location, &maxlocation, strlen( hc->encodedurl ) + 1 ); + (void) my_snprintf( location, maxlocation, + "%s/", hc->encodedurl ); + } + httpd_realloc_str( + &header, &maxheader, sizeof(headstr) + strlen( location ) ); + (void) my_snprintf( header, maxheader, + "%s%s\015\012", headstr, location ); + send_response( hc, 302, err302title, header, err302form, location ); + } + + +char* +httpd_method_str( int method ) + { + switch ( method ) + { + case METHOD_GET: return "GET"; + case METHOD_HEAD: return "HEAD"; + case METHOD_POST: return "POST"; + case METHOD_PUT: return "PUT"; + case METHOD_DELETE: return "DELETE"; + case METHOD_TRACE: return "TRACE"; + default: return "UNKNOWN"; + } + } + + +static int +hexit( char c ) + { + if ( c >= '0' && c <= '9' ) + return c - '0'; + if ( c >= 'a' && c <= 'f' ) + return c - 'a' + 10; + if ( c >= 'A' && c <= 'F' ) + return c - 'A' + 10; + return 0; /* shouldn't happen, we're guarded by isxdigit() */ + } + + +/* Copies and decodes a string. It's ok for from and to to be the +** same string. +*/ +static void +strdecode( char* to, char* from ) + { + for ( ; *from != '\0'; ++to, ++from ) + { + if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) ) + { + *to = hexit( from[1] ) * 16 + hexit( from[2] ); + from += 2; + } + else + *to = *from; + } + *to = '\0'; + } + + +#ifdef GENERATE_INDEXES +/* Copies and encodes a string. */ +static void +strencode( char* to, int tosize, char* from ) + { + int tolen; + + for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from ) + { + if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 ) + { + *to = *from; + ++to; + ++tolen; + } + else + { + (void) sprintf( to, "%%%02x", (int) *from & 0xff ); + to += 3; + tolen += 3; + } + } + *to = '\0'; + } +#endif /* GENERATE_INDEXES */ + + +#ifdef TILDE_MAP_1 +/* Map a ~username/whatever URL into /username. */ +static int +tilde_map_1( httpd_conn* hc ) + { + static char* temp; + static size_t maxtemp = 0; + int len; + static char* prefix = TILDE_MAP_1; + + len = strlen( hc->expnfilename ) - 1; + httpd_realloc_str( &temp, &maxtemp, len ); + (void) strcpy( temp, &hc->expnfilename[1] ); + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, strlen( prefix ) + 1 + len ); + (void) strcpy( hc->expnfilename, prefix ); + if ( prefix[0] != '\0' ) + (void) strcat( hc->expnfilename, "/" ); + (void) strcat( hc->expnfilename, temp ); + return 1; + } +#endif /* TILDE_MAP_1 */ + +#ifdef TILDE_MAP_2 +/* Map a ~username/whatever URL into /. */ +static int +tilde_map_2( httpd_conn* hc ) + { + static char* temp; + static size_t maxtemp = 0; + static char* postfix = TILDE_MAP_2; + char* cp; + struct passwd* pw; + char* alt; + char* rest; + + /* Get the username. */ + httpd_realloc_str( &temp, &maxtemp, strlen( hc->expnfilename ) - 1 ); + (void) strcpy( temp, &hc->expnfilename[1] ); + cp = strchr( temp, '/' ); + if ( cp != (char*) 0 ) + *cp++ = '\0'; + else + cp = ""; + + /* Get the passwd entry. */ + pw = getpwnam( temp ); + if ( pw == (struct passwd*) 0 ) + return 0; + + /* Set up altdir. */ + httpd_realloc_str( + &hc->altdir, &hc->maxaltdir, + strlen( pw->pw_dir ) + 1 + strlen( postfix ) ); + (void) strcpy( hc->altdir, pw->pw_dir ); + if ( postfix[0] != '\0' ) + { + (void) strcat( hc->altdir, "/" ); + (void) strcat( hc->altdir, postfix ); + } + alt = expand_symlinks( hc->altdir, &rest, 0, 1 ); + if ( rest[0] != '\0' ) + return 0; + httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) ); + (void) strcpy( hc->altdir, alt ); + + /* And the filename becomes altdir plus the post-~ part of the original. */ + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, + strlen( hc->altdir ) + 1 + strlen( cp ) ); + (void) my_snprintf( hc->expnfilename, hc->maxexpnfilename, + "%s/%s", hc->altdir, cp ); + + /* For this type of tilde mapping, we want to defeat vhost mapping. */ + hc->tildemapped = 1; + + return 1; + } +#endif /* TILDE_MAP_2 */ + + +/* Virtual host mapping. */ +static int +vhost_map( httpd_conn* hc ) + { + httpd_sockaddr sa; + socklen_t sz; + static char* tempfilename; + static size_t maxtempfilename = 0; + char* cp1; + int len; +#ifdef VHOST_DIRLEVELS + int i; + char* cp2; +#endif /* VHOST_DIRLEVELS */ + + /* Figure out the virtual hostname. */ + if ( hc->reqhost[0] != '\0' ) + hc->hostname = hc->reqhost; + else if ( hc->hdrhost[0] != '\0' ) + hc->hostname = hc->hdrhost; + else + { + sz = sizeof(sa); + if ( getsockname( hc->conn_fd, &sa.sa, &sz ) < 0 ) + { + syslog( LOG_ERR, "getsockname - %m" ); + return 0; + } + hc->hostname = httpd_ntoa( &sa ); + } + /* Pound it to lower case. */ + for ( cp1 = hc->hostname; *cp1 != '\0'; ++cp1 ) + if ( isupper( *cp1 ) ) + *cp1 = tolower( *cp1 ); + + if ( hc->tildemapped ) + return 1; + + /* Figure out the host directory. */ +#ifdef VHOST_DIRLEVELS + httpd_realloc_str( + &hc->hostdir, &hc->maxhostdir, + strlen( hc->hostname ) + 2 * VHOST_DIRLEVELS ); + if ( strncmp( hc->hostname, "www.", 4 ) == 0 ) + cp1 = &hc->hostname[4]; + else + cp1 = hc->hostname; + for ( cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i ) + { + /* Skip dots in the hostname. If we don't, then we get vhost + ** directories in higher level of filestructure if dot gets + ** involved into path construction. It's `while' used here instead + ** of `if' for it's possible to have a hostname formed with two + ** dots at the end of it. + */ + while ( *cp1 == '.' ) + ++cp1; + /* Copy a character from the hostname, or '_' if we ran out. */ + if ( *cp1 != '\0' ) + *cp2++ = *cp1++; + else + *cp2++ = '_'; + /* Copy a slash. */ + *cp2++ = '/'; + } + (void) strcpy( cp2, hc->hostname ); +#else /* VHOST_DIRLEVELS */ + httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, strlen( hc->hostname ) ); + (void) strcpy( hc->hostdir, hc->hostname ); +#endif /* VHOST_DIRLEVELS */ + + /* Prepend hostdir to the filename. */ + len = strlen( hc->expnfilename ); + httpd_realloc_str( &tempfilename, &maxtempfilename, len ); + (void) strcpy( tempfilename, hc->expnfilename ); + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, + strlen( hc->hostdir ) + 1 + len ); + (void) strcpy( hc->expnfilename, hc->hostdir ); + (void) strcat( hc->expnfilename, "/" ); + (void) strcat( hc->expnfilename, tempfilename ); + return 1; + } + + +/* Expands all symlinks in the given filename, eliding ..'s and leading /'s. +** Returns the expanded path (pointer to static string), or (char*) 0 on +** errors. Also returns, in the string pointed to by restP, any trailing +** parts of the path that don't exist. +** +** This is a fairly nice little routine. It handles any size filenames +** without excessive mallocs. +*/ +static char* +expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped ) + { + static char* checked; + static char* rest; + char lnk[5000]; + static size_t maxchecked = 0, maxrest = 0; + size_t checkedlen, restlen, linklen, prevcheckedlen, prevrestlen; + int nlinks, i; + char* r; + char* cp1; + char* cp2; + + if ( no_symlink_check ) + { + /* If we are chrooted, we can actually skip the symlink-expansion, + ** since it's impossible to get out of the tree. However, we still + ** need to do the pathinfo check, and the existing symlink expansion + ** code is a pretty reasonable way to do this. So, what we do is + ** a single stat() of the whole filename - if it exists, then we + ** return it as is with nothing in restP. If it doesn't exist, we + ** fall through to the existing code. + ** + ** One side-effect of this is that users can't symlink to central + ** approved CGIs any more. The workaround is to use the central + ** URL for the CGI instead of a local symlinked one. + */ + struct stat sb; + if ( stat( path, &sb ) != -1 ) + { + checkedlen = strlen( path ); + httpd_realloc_str( &checked, &maxchecked, checkedlen ); + (void) strcpy( checked, path ); + /* Trim trailing slashes. */ + while ( checked[checkedlen - 1] == '/' ) + { + checked[checkedlen - 1] = '\0'; + --checkedlen; + } + httpd_realloc_str( &rest, &maxrest, 0 ); + rest[0] = '\0'; + *restP = rest; + return checked; + } + } + + /* Start out with nothing in checked and the whole filename in rest. */ + httpd_realloc_str( &checked, &maxchecked, 1 ); + checked[0] = '\0'; + checkedlen = 0; + restlen = strlen( path ); + httpd_realloc_str( &rest, &maxrest, restlen ); + (void) strcpy( rest, path ); + if ( rest[restlen - 1] == '/' ) + rest[--restlen] = '\0'; /* trim trailing slash */ + if ( ! tildemapped ) + /* Remove any leading slashes. */ + while ( rest[0] == '/' ) + { + (void) ol_strcpy( rest, &(rest[1]) ); + --restlen; + } + r = rest; + nlinks = 0; + + /* While there are still components to check... */ + while ( restlen > 0 ) + { + /* Save current checkedlen in case we get a symlink. Save current + ** restlen in case we get a non-existant component. + */ + prevcheckedlen = checkedlen; + prevrestlen = restlen; + + /* Grab one component from r and transfer it to checked. */ + cp1 = strchr( r, '/' ); + if ( cp1 != (char*) 0 ) + { + i = cp1 - r; + if ( i == 0 ) + { + /* Special case for absolute paths. */ + httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 ); + (void) strncpy( &checked[checkedlen], r, 1 ); + checkedlen += 1; + } + else if ( strncmp( r, "..", MAX( i, 2 ) ) == 0 ) + { + /* Ignore ..'s that go above the start of the path. */ + if ( checkedlen != 0 ) + { + cp2 = strrchr( checked, '/' ); + if ( cp2 == (char*) 0 ) + checkedlen = 0; + else if ( cp2 == checked ) + checkedlen = 1; + else + checkedlen = cp2 - checked; + } + } + else + { + httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 + i ); + if ( checkedlen > 0 && checked[checkedlen-1] != '/' ) + checked[checkedlen++] = '/'; + (void) strncpy( &checked[checkedlen], r, i ); + checkedlen += i; + } + checked[checkedlen] = '\0'; + r += i + 1; + restlen -= i + 1; + } + else + { + /* No slashes remaining, r is all one component. */ + if ( strcmp( r, ".." ) == 0 ) + { + /* Ignore ..'s that go above the start of the path. */ + if ( checkedlen != 0 ) + { + cp2 = strrchr( checked, '/' ); + if ( cp2 == (char*) 0 ) + checkedlen = 0; + else if ( cp2 == checked ) + checkedlen = 1; + else + checkedlen = cp2 - checked; + checked[checkedlen] = '\0'; + } + } + else + { + httpd_realloc_str( + &checked, &maxchecked, checkedlen + 1 + restlen ); + if ( checkedlen > 0 && checked[checkedlen-1] != '/' ) + checked[checkedlen++] = '/'; + (void) strcpy( &checked[checkedlen], r ); + checkedlen += restlen; + } + r += restlen; + restlen = 0; + } + + /* Try reading the current filename as a symlink */ + if ( checked[0] == '\0' ) + continue; + linklen = readlink( checked, lnk, sizeof(lnk) - 1 ); + if ( linklen == -1 ) + { + if ( errno == EINVAL ) + continue; /* not a symlink */ + if ( errno == EACCES || errno == ENOENT || errno == ENOTDIR ) + { + /* That last component was bogus. Restore and return. */ + *restP = r - ( prevrestlen - restlen ); + if ( prevcheckedlen == 0 ) + (void) strcpy( checked, "." ); + else + checked[prevcheckedlen] = '\0'; + return checked; + } + syslog( LOG_ERR, "readlink %.80s - %m", checked ); + return (char*) 0; + } + ++nlinks; + if ( nlinks > MAX_LINKS ) + { + syslog( LOG_ERR, "too many symlinks in %.80s", path ); + return (char*) 0; + } + lnk[linklen] = '\0'; + if ( lnk[linklen - 1] == '/' ) + lnk[--linklen] = '\0'; /* trim trailing slash */ + + /* Insert the link contents in front of the rest of the filename. */ + if ( restlen != 0 ) + { + (void) ol_strcpy( rest, r ); + httpd_realloc_str( &rest, &maxrest, restlen + linklen + 1 ); + for ( i = restlen; i >= 0; --i ) + rest[i + linklen + 1] = rest[i]; + (void) strcpy( rest, lnk ); + rest[linklen] = '/'; + restlen += linklen + 1; + r = rest; + } + else + { + /* There's nothing left in the filename, so the link contents + ** becomes the rest. + */ + httpd_realloc_str( &rest, &maxrest, linklen ); + (void) strcpy( rest, lnk ); + restlen = linklen; + r = rest; + } + + if ( rest[0] == '/' ) + { + /* There must have been an absolute symlink - zero out checked. */ + checked[0] = '\0'; + checkedlen = 0; + } + else + { + /* Re-check this component. */ + checkedlen = prevcheckedlen; + checked[checkedlen] = '\0'; + } + } + + /* Ok. */ + *restP = r; + if ( checked[0] == '\0' ) + (void) strcpy( checked, "." ); + return checked; + } + + +int +httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc ) + { + httpd_sockaddr sa; + socklen_t sz; + + if ( ! hc->initialized ) + { + hc->read_size = 0; + httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 ); + hc->maxdecodedurl = + hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings = + hc->maxpathinfo = hc->maxquery = hc->maxaccept = + hc->maxaccepte = hc->maxreqhost = hc->maxhostdir = + hc->maxremoteuser = hc->maxresponse = 0; +#ifdef TILDE_MAP_2 + hc->maxaltdir = 0; +#endif /* TILDE_MAP_2 */ + httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 ); + httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 ); + httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 ); + httpd_realloc_str( &hc->encodings, &hc->maxencodings, 0 ); + httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, 0 ); + httpd_realloc_str( &hc->query, &hc->maxquery, 0 ); + httpd_realloc_str( &hc->accept, &hc->maxaccept, 0 ); + httpd_realloc_str( &hc->accepte, &hc->maxaccepte, 0 ); + httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 ); + httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 ); + httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 ); + httpd_realloc_str( &hc->response, &hc->maxresponse, 0 ); +#ifdef TILDE_MAP_2 + httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 ); +#endif /* TILDE_MAP_2 */ + hc->initialized = 1; + } + + /* Accept the new connection. */ + sz = sizeof(sa); + hc->conn_fd = accept( listen_fd, &sa.sa, &sz ); + if ( hc->conn_fd < 0 ) + { + if ( errno == EWOULDBLOCK ) + return GC_NO_MORE; + /* ECONNABORTED means the connection was closed by the client while + ** it was waiting in the listen queue. It's not worth logging. + */ + if ( errno != ECONNABORTED ) + syslog( LOG_ERR, "accept - %m" ); + return GC_FAIL; + } + if ( ! sockaddr_check( &sa ) ) + { + syslog( LOG_ERR, "unknown sockaddr family" ); + close( hc->conn_fd ); + hc->conn_fd = -1; + return GC_FAIL; + } + (void) fcntl( hc->conn_fd, F_SETFD, 1 ); + hc->hs = hs; + (void) memset( &hc->client_addr, 0, sizeof(hc->client_addr) ); + (void) memmove( &hc->client_addr, &sa, sockaddr_len( &sa ) ); + hc->read_idx = 0; + hc->checked_idx = 0; + hc->checked_state = CHST_FIRSTWORD; + hc->method = METHOD_UNKNOWN; + hc->status = 0; + hc->bytes_to_send = 0; + hc->bytes_sent = 0; + hc->encodedurl = ""; + hc->decodedurl[0] = '\0'; + hc->protocol = "UNKNOWN"; + hc->origfilename[0] = '\0'; + hc->expnfilename[0] = '\0'; + hc->encodings[0] = '\0'; + hc->pathinfo[0] = '\0'; + hc->query[0] = '\0'; + hc->referrer = ""; + hc->useragent = ""; + hc->accept[0] = '\0'; + hc->accepte[0] = '\0'; + hc->acceptl = ""; + hc->cookie = ""; + hc->contenttype = ""; + hc->reqhost[0] = '\0'; + hc->hdrhost = ""; + hc->hostdir[0] = '\0'; + hc->authorization = ""; + hc->remoteuser[0] = '\0'; + hc->response[0] = '\0'; +#ifdef TILDE_MAP_2 + hc->altdir[0] = '\0'; +#endif /* TILDE_MAP_2 */ + hc->responselen = 0; + hc->if_modified_since = (time_t) -1; + hc->range_if = (time_t) -1; + hc->contentlength = -1; + hc->type = ""; + hc->hostname = (char*) 0; + hc->mime_flag = 1; + hc->one_one = 0; + hc->got_range = 0; + hc->tildemapped = 0; + hc->first_byte_index = 0; + hc->last_byte_index = -1; + hc->keep_alive = 0; + hc->should_linger = 0; + hc->file_address = (char*) 0; + return GC_OK; + } + + +/* Checks hc->read_buf to see whether a complete request has been read so far; +** either the first line has two words (an HTTP/0.9 request), or the first +** line has three words and there's a blank line present. +** +** hc->read_idx is how much has been read in; hc->checked_idx is how much we +** have checked so far; and hc->checked_state is the current state of the +** finite state machine. +*/ +int +httpd_got_request( httpd_conn* hc ) + { + char c; + + for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx ) + { + c = hc->read_buf[hc->checked_idx]; + switch ( hc->checked_state ) + { + case CHST_FIRSTWORD: + switch ( c ) + { + case ' ': case '\t': + hc->checked_state = CHST_FIRSTWS; + break; + case '\012': case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_FIRSTWS: + switch ( c ) + { + case ' ': case '\t': + break; + case '\012': case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_SECONDWORD; + break; + } + break; + case CHST_SECONDWORD: + switch ( c ) + { + case ' ': case '\t': + hc->checked_state = CHST_SECONDWS; + break; + case '\012': case '\015': + /* The first line has only two words - an HTTP/0.9 request. */ + return GR_GOT_REQUEST; + } + break; + case CHST_SECONDWS: + switch ( c ) + { + case ' ': case '\t': + break; + case '\012': case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_THIRDWORD; + break; + } + break; + case CHST_THIRDWORD: + switch ( c ) + { + case ' ': case '\t': + hc->checked_state = CHST_THIRDWS; + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_THIRDWS: + switch ( c ) + { + case ' ': case '\t': + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_LINE: + switch ( c ) + { + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_LF: + switch ( c ) + { + case '\012': + /* Two newlines in a row - a blank line - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CR: + switch ( c ) + { + case '\012': + hc->checked_state = CHST_CRLF; + break; + case '\015': + /* Two returns in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLF: + switch ( c ) + { + case '\012': + /* Two newlines in a row - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CRLFCR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLFCR: + switch ( c ) + { + case '\012': case '\015': + /* Two CRLFs or two CRs in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_BOGUS: + return GR_BAD_REQUEST; + } + } + return GR_NO_REQUEST; + } + + +int +httpd_parse_request( httpd_conn* hc ) + { + char* buf; + char* method_str; + char* url; + char* protocol; + char* reqhost; + char* eol; + char* cp; + char* pi; + + hc->checked_idx = 0; /* reset */ + method_str = bufgets( hc ); + url = strpbrk( method_str, " \t\012\015" ); + if ( url == (char*) 0 ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + *url++ = '\0'; + url += strspn( url, " \t\012\015" ); + protocol = strpbrk( url, " \t\012\015" ); + if ( protocol == (char*) 0 ) + { + protocol = "HTTP/0.9"; + hc->mime_flag = 0; + } + else + { + *protocol++ = '\0'; + protocol += strspn( protocol, " \t\012\015" ); + if ( *protocol != '\0' ) + { + eol = strpbrk( protocol, " \t\012\015" ); + if ( eol != (char*) 0 ) + *eol = '\0'; + if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 ) + hc->one_one = 1; + } + } + hc->protocol = protocol; + + /* Check for HTTP/1.1 absolute URL. */ + if ( strncasecmp( url, "http://", 7 ) == 0 ) + { + if ( ! hc->one_one ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + reqhost = url + 7; + url = strchr( reqhost, '/' ); + if ( url == (char*) 0 ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + *url = '\0'; + if ( strchr( reqhost, '/' ) != (char*) 0 || reqhost[0] == '.' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, strlen( reqhost ) ); + (void) strcpy( hc->reqhost, reqhost ); + *url = '/'; + } + + if ( *url != '/' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + + if ( strcasecmp( method_str, httpd_method_str( METHOD_GET ) ) == 0 ) + hc->method = METHOD_GET; + else if ( strcasecmp( method_str, httpd_method_str( METHOD_HEAD ) ) == 0 ) + hc->method = METHOD_HEAD; + else if ( strcasecmp( method_str, httpd_method_str( METHOD_POST ) ) == 0 ) + hc->method = METHOD_POST; + else if ( strcasecmp( method_str, httpd_method_str( METHOD_PUT ) ) == 0 ) + hc->method = METHOD_PUT; + else if ( strcasecmp( method_str, httpd_method_str( METHOD_DELETE ) ) == 0 ) + hc->method = METHOD_DELETE; + else if ( strcasecmp( method_str, httpd_method_str( METHOD_TRACE ) ) == 0 ) + hc->method = METHOD_TRACE; + else + { + httpd_send_err( hc, 501, err501title, "", err501form, method_str ); + return -1; + } + + hc->encodedurl = url; + httpd_realloc_str( + &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) ); + strdecode( hc->decodedurl, hc->encodedurl ); + + httpd_realloc_str( + &hc->origfilename, &hc->maxorigfilename, strlen( hc->decodedurl ) ); + (void) strcpy( hc->origfilename, &hc->decodedurl[1] ); + /* Special case for top-level URL. */ + if ( hc->origfilename[0] == '\0' ) + (void) strcpy( hc->origfilename, "." ); + + /* Extract query string from encoded URL. */ + cp = strchr( hc->encodedurl, '?' ); + if ( cp != (char*) 0 ) + { + ++cp; + httpd_realloc_str( &hc->query, &hc->maxquery, strlen( cp ) ); + (void) strcpy( hc->query, cp ); + /* Remove query from (decoded) origfilename. */ + cp = strchr( hc->origfilename, '?' ); + if ( cp != (char*) 0 ) + *cp = '\0'; + } + + de_dotdot( hc->origfilename ); + if ( hc->origfilename[0] == '/' || + ( hc->origfilename[0] == '.' && hc->origfilename[1] == '.' && + ( hc->origfilename[2] == '\0' || hc->origfilename[2] == '/' ) ) ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + + if ( hc->mime_flag ) + { + /* Read the MIME headers. */ + while ( ( buf = bufgets( hc ) ) != (char*) 0 ) + { + if ( buf[0] == '\0' ) + break; + if ( strncasecmp( buf, "Referer:", 8 ) == 0 ) + { + cp = &buf[8]; + cp += strspn( cp, " \t" ); + hc->referrer = cp; + } + else if ( strncasecmp( buf, "Referrer:", 9 ) == 0 ) + { + cp = &buf[9]; + cp += strspn( cp, " \t" ); + hc->referrer = cp; + } + else if ( strncasecmp( buf, "User-Agent:", 11 ) == 0 ) + { + cp = &buf[11]; + cp += strspn( cp, " \t" ); + hc->useragent = cp; + } + else if ( strncasecmp( buf, "Host:", 5 ) == 0 ) + { + cp = &buf[5]; + cp += strspn( cp, " \t" ); + hc->hdrhost = cp; + cp = strchr( hc->hdrhost, ':' ); + if ( cp != (char*) 0 ) + *cp = '\0'; + if ( strchr( hc->hdrhost, '/' ) != (char*) 0 || hc->hdrhost[0] == '.' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + } + else if ( strncasecmp( buf, "Accept:", 7 ) == 0 ) + { + cp = &buf[7]; + cp += strspn( cp, " \t" ); + if ( hc->accept[0] != '\0' ) + { + if ( strlen( hc->accept ) > 5000 ) + { + syslog( + LOG_ERR, "%.80s way too much Accept: data", + httpd_ntoa( &hc->client_addr ) ); + continue; + } + httpd_realloc_str( + &hc->accept, &hc->maxaccept, + strlen( hc->accept ) + 2 + strlen( cp ) ); + (void) strcat( hc->accept, ", " ); + } + else + httpd_realloc_str( + &hc->accept, &hc->maxaccept, strlen( cp ) ); + (void) strcat( hc->accept, cp ); + } + else if ( strncasecmp( buf, "Accept-Encoding:", 16 ) == 0 ) + { + cp = &buf[16]; + cp += strspn( cp, " \t" ); + if ( hc->accepte[0] != '\0' ) + { + if ( strlen( hc->accepte ) > 5000 ) + { + syslog( + LOG_ERR, "%.80s way too much Accept-Encoding: data", + httpd_ntoa( &hc->client_addr ) ); + continue; + } + httpd_realloc_str( + &hc->accepte, &hc->maxaccepte, + strlen( hc->accepte ) + 2 + strlen( cp ) ); + (void) strcat( hc->accepte, ", " ); + } + else + httpd_realloc_str( + &hc->accepte, &hc->maxaccepte, strlen( cp ) ); + (void) strcpy( hc->accepte, cp ); + } + else if ( strncasecmp( buf, "Accept-Language:", 16 ) == 0 ) + { + cp = &buf[16]; + cp += strspn( cp, " \t" ); + hc->acceptl = cp; + } + else if ( strncasecmp( buf, "If-Modified-Since:", 18 ) == 0 ) + { + cp = &buf[18]; + hc->if_modified_since = tdate_parse( cp ); + if ( hc->if_modified_since == (time_t) -1 ) + syslog( LOG_DEBUG, "unparsable time: %.80s", cp ); + } + else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 ) + { + cp = &buf[7]; + cp += strspn( cp, " \t" ); + hc->cookie = cp; + } + else if ( strncasecmp( buf, "Range:", 6 ) == 0 ) + { + /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */ + if ( strchr( buf, ',' ) == (char*) 0 ) + { + char* cp_dash; + cp = strpbrk( buf, "=" ); + if ( cp != (char*) 0 ) + { + cp_dash = strchr( cp + 1, '-' ); + if ( cp_dash != (char*) 0 && cp_dash != cp + 1 ) + { + *cp_dash = '\0'; + hc->got_range = 1; + hc->first_byte_index = atoll( cp + 1 ); + if ( hc->first_byte_index < 0 ) + hc->first_byte_index = 0; + if ( isdigit( (int) cp_dash[1] ) ) + { + hc->last_byte_index = atoll( cp_dash + 1 ); + if ( hc->last_byte_index < 0 ) + hc->last_byte_index = -1; + } + } + } + } + } + else if ( strncasecmp( buf, "Range-If:", 9 ) == 0 || + strncasecmp( buf, "If-Range:", 9 ) == 0 ) + { + cp = &buf[9]; + hc->range_if = tdate_parse( cp ); + if ( hc->range_if == (time_t) -1 ) + syslog( LOG_DEBUG, "unparsable time: %.80s", cp ); + } + else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 ) + { + cp = &buf[13]; + cp += strspn( cp, " \t" ); + hc->contenttype = cp; + } + else if ( strncasecmp( buf, "Content-Length:", 15 ) == 0 ) + { + cp = &buf[15]; + hc->contentlength = atol( cp ); + } + else if ( strncasecmp( buf, "Authorization:", 14 ) == 0 ) + { + cp = &buf[14]; + cp += strspn( cp, " \t" ); + hc->authorization = cp; + } + else if ( strncasecmp( buf, "Connection:", 11 ) == 0 ) + { + cp = &buf[11]; + cp += strspn( cp, " \t" ); + if ( strcasecmp( cp, "keep-alive" ) == 0 ) + hc->keep_alive = 1; + } +#ifdef LOG_UNKNOWN_HEADERS + else if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 || + strncasecmp( buf, "Accept-Language:", 16 ) == 0 || + strncasecmp( buf, "Agent:", 6 ) == 0 || + strncasecmp( buf, "Cache-Control:", 14 ) == 0 || + strncasecmp( buf, "Cache-Info:", 11 ) == 0 || + strncasecmp( buf, "Charge-To:", 10 ) == 0 || + strncasecmp( buf, "Client-IP:", 10 ) == 0 || + strncasecmp( buf, "Date:", 5 ) == 0 || + strncasecmp( buf, "Extension:", 10 ) == 0 || + strncasecmp( buf, "Forwarded:", 10 ) == 0 || + strncasecmp( buf, "From:", 5 ) == 0 || + strncasecmp( buf, "HTTP-Version:", 13 ) == 0 || + strncasecmp( buf, "Max-Forwards:", 13 ) == 0 || + strncasecmp( buf, "Message-Id:", 11 ) == 0 || + strncasecmp( buf, "MIME-Version:", 13 ) == 0 || + strncasecmp( buf, "Negotiate:", 10 ) == 0 || + strncasecmp( buf, "Pragma:", 7 ) == 0 || + strncasecmp( buf, "Proxy-Agent:", 12 ) == 0 || + strncasecmp( buf, "Proxy-Connection:", 17 ) == 0 || + strncasecmp( buf, "Security-Scheme:", 16 ) == 0 || + strncasecmp( buf, "Session-Id:", 11 ) == 0 || + strncasecmp( buf, "UA-Color:", 9 ) == 0 || + strncasecmp( buf, "UA-CPU:", 7 ) == 0 || + strncasecmp( buf, "UA-Disp:", 8 ) == 0 || + strncasecmp( buf, "UA-OS:", 6 ) == 0 || + strncasecmp( buf, "UA-Pixels:", 10 ) == 0 || + strncasecmp( buf, "User:", 5 ) == 0 || + strncasecmp( buf, "Via:", 4 ) == 0 || + strncasecmp( buf, "X-", 2 ) == 0 ) + ; /* ignore */ + else + syslog( LOG_DEBUG, "unknown request header: %.80s", buf ); +#endif /* LOG_UNKNOWN_HEADERS */ + } + } + + if ( hc->one_one ) + { + /* Check that HTTP/1.1 requests specify a host, as required. */ + if ( hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + + /* If the client wants to do keep-alives, it might also be doing + ** pipelining. There's no way for us to tell. Since we don't + ** implement keep-alives yet, if we close such a connection there + ** might be unread pipelined requests waiting. So, we have to + ** do a lingering close. + */ + if ( hc->keep_alive ) + hc->should_linger = 1; + } + + /* Ok, the request has been parsed. Now we resolve stuff that + ** may require the entire request. + */ + + /* Copy original filename to expanded filename. */ + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->origfilename ) ); + (void) strcpy( hc->expnfilename, hc->origfilename ); + + /* Tilde mapping. */ + if ( hc->expnfilename[0] == '~' ) + { +#ifdef TILDE_MAP_1 + if ( ! tilde_map_1( hc ) ) + { + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 + if ( ! tilde_map_2( hc ) ) + { + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } +#endif /* TILDE_MAP_2 */ + } + + /* Virtual host mapping. */ + if ( hc->hs->vhost ) + if ( ! vhost_map( hc ) ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + + /* Expand all symbolic links in the filename. This also gives us + ** any trailing non-existing components, for pathinfo. + */ + cp = expand_symlinks( hc->expnfilename, &pi, hc->hs->no_symlink_check, hc->tildemapped ); + if ( cp == (char*) 0 ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( cp ) ); + (void) strcpy( hc->expnfilename, cp ); + httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, strlen( pi ) ); + (void) strcpy( hc->pathinfo, pi ); + + /* Remove pathinfo stuff from the original filename too. */ + if ( hc->pathinfo[0] != '\0' ) + { + int i; + i = strlen( hc->origfilename ) - strlen( hc->pathinfo ); + if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 ) + hc->origfilename[i - 1] = '\0'; + } + + /* If the expanded filename is an absolute path, check that it's still + ** within the current directory or the alternate directory. + */ + if ( hc->expnfilename[0] == '/' ) + { + if ( strncmp( + hc->expnfilename, hc->hs->cwd, strlen( hc->hs->cwd ) ) == 0 ) + { + /* Elide the current directory. */ + (void) ol_strcpy( + hc->expnfilename, &hc->expnfilename[strlen( hc->hs->cwd )] ); + } +#ifdef TILDE_MAP_2 + else if ( hc->altdir[0] != '\0' && + ( strncmp( + hc->expnfilename, hc->altdir, + strlen( hc->altdir ) ) == 0 && + ( hc->expnfilename[strlen( hc->altdir )] == '\0' || + hc->expnfilename[strlen( hc->altdir )] == '/' ) ) ) + {} +#endif /* TILDE_MAP_2 */ + else + { + syslog( + LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ), + hc->encodedurl ); + return -1; + } + } + + return 0; + } + + +static char* +bufgets( httpd_conn* hc ) + { + int i; + char c; + + for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx ) + { + c = hc->read_buf[hc->checked_idx]; + if ( c == '\012' || c == '\015' ) + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + if ( c == '\015' && hc->checked_idx < hc->read_idx && + hc->read_buf[hc->checked_idx] == '\012' ) + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + } + return &(hc->read_buf[i]); + } + } + return (char*) 0; + } + + +static void +de_dotdot( char* file ) + { + char* cp; + char* cp2; + int l; + + /* Collapse any multiple / sequences. */ + while ( ( cp = strstr( file, "//") ) != (char*) 0 ) + { + for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 ) + continue; + (void) ol_strcpy( cp + 1, cp2 ); + } + + /* Remove leading ./ and any /./ sequences. */ + while ( strncmp( file, "./", 2 ) == 0 ) + (void) ol_strcpy( file, file + 2 ); + while ( ( cp = strstr( file, "/./") ) != (char*) 0 ) + (void) ol_strcpy( cp, cp + 2 ); + + /* Alternate between removing leading ../ and removing xxx/../ */ + for (;;) + { + while ( strncmp( file, "../", 3 ) == 0 ) + (void) ol_strcpy( file, file + 3 ); + cp = strstr( file, "/../" ); + if ( cp == (char*) 0 ) + break; + for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 ) + continue; + (void) ol_strcpy( cp2 + 1, cp + 4 ); + } + + /* Also elide any xxx/.. at the end. */ + while ( ( l = strlen( file ) ) > 3 && + strcmp( ( cp = file + l - 3 ), "/.." ) == 0 ) + { + for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 ) + continue; + if ( cp2 < file ) + break; + *cp2 = '\0'; + } + } + + +void +httpd_close_conn( httpd_conn* hc, struct timeval* nowP ) + { + make_log_entry( hc, nowP ); + + if ( hc->file_address != (char*) 0 ) + { + mmc_unmap( hc->file_address, &(hc->sb), nowP ); + hc->file_address = (char*) 0; + } + if ( hc->conn_fd >= 0 ) + { + (void) close( hc->conn_fd ); + hc->conn_fd = -1; + } + } + +void +httpd_destroy_conn( httpd_conn* hc ) + { + if ( hc->initialized ) + { + free( (void*) hc->read_buf ); + free( (void*) hc->decodedurl ); + free( (void*) hc->origfilename ); + free( (void*) hc->expnfilename ); + free( (void*) hc->encodings ); + free( (void*) hc->pathinfo ); + free( (void*) hc->query ); + free( (void*) hc->accept ); + free( (void*) hc->accepte ); + free( (void*) hc->reqhost ); + free( (void*) hc->hostdir ); + free( (void*) hc->remoteuser ); + free( (void*) hc->response ); +#ifdef TILDE_MAP_2 + free( (void*) hc->altdir ); +#endif /* TILDE_MAP_2 */ + hc->initialized = 0; + } + } + + +struct mime_entry { + char* ext; + size_t ext_len; + char* val; + size_t val_len; + }; +static struct mime_entry enc_tab[] = { +#include "mime_encodings.h" + }; +static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab); +static struct mime_entry typ_tab[] = { +#include "mime_types.h" + }; +static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab); + + +/* qsort comparison routine */ +static int +ext_compare( const void* v1, const void* v2 ) + { + const struct mime_entry* m1 = (const struct mime_entry*) v1; + const struct mime_entry* m2 = (const struct mime_entry*) v2; + + return strcmp( m1->ext, m2->ext ); + } + + +static void +init_mime( void ) + { + int i; + + /* Sort the tables so we can do binary search. */ + qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare ); + qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare ); + + /* Fill in the lengths. */ + for ( i = 0; i < n_enc_tab; ++i ) + { + enc_tab[i].ext_len = strlen( enc_tab[i].ext ); + enc_tab[i].val_len = strlen( enc_tab[i].val ); + } + for ( i = 0; i < n_typ_tab; ++i ) + { + typ_tab[i].ext_len = strlen( typ_tab[i].ext ); + typ_tab[i].val_len = strlen( typ_tab[i].val ); + } + + } + + +/* Figure out MIME encodings and type based on the filename. Multiple +** encodings are separated by commas, and are listed in the order in +** which they were applied to the file. +*/ +static void +figure_mime( httpd_conn* hc ) + { + char* prev_dot; + char* dot; + char* ext; + int me_indexes[100], n_me_indexes; + size_t ext_len, encodings_len; + int i, top, bot, mid; + int r; + char* default_type = "text/plain; charset=%s"; + + /* Peel off encoding extensions until there aren't any more. */ + n_me_indexes = 0; + for ( prev_dot = &hc->expnfilename[strlen(hc->expnfilename)]; ; prev_dot = dot ) + { + for ( dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot ) + ; + if ( dot < hc->expnfilename ) + { + /* No dot found. No more encoding extensions, and no type + ** extension either. + */ + hc->type = default_type; + goto done; + } + ext = dot + 1; + ext_len = prev_dot - ext; + /* Search the encodings table. Linear search is fine here, there + ** are only a few entries. + */ + for ( i = 0; i < n_enc_tab; ++i ) + { + if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 ) + { + if ( n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes) ) + { + me_indexes[n_me_indexes] = i; + ++n_me_indexes; + } + goto next; + } + } + /* No encoding extension found. Break and look for a type extension. */ + break; + + next: ; + } + + /* Binary search for a matching type extension. */ + top = n_typ_tab - 1; + bot = 0; + while ( top >= bot ) + { + mid = ( top + bot ) / 2; + r = strncasecmp( ext, typ_tab[mid].ext, ext_len ); + if ( r < 0 ) + top = mid - 1; + else if ( r > 0 ) + bot = mid + 1; + else + if ( ext_len < typ_tab[mid].ext_len ) + top = mid - 1; + else if ( ext_len > typ_tab[mid].ext_len ) + bot = mid + 1; + else + { + hc->type = typ_tab[mid].val; + goto done; + } + } + hc->type = default_type; + + done: + + /* The last thing we do is actually generate the mime-encoding header. */ + hc->encodings[0] = '\0'; + encodings_len = 0; + for ( i = n_me_indexes - 1; i >= 0; --i ) + { + httpd_realloc_str( + &hc->encodings, &hc->maxencodings, + encodings_len + enc_tab[me_indexes[i]].val_len + 1 ); + if ( hc->encodings[0] != '\0' ) + { + (void) strcpy( &hc->encodings[encodings_len], "," ); + ++encodings_len; + } + (void) strcpy( &hc->encodings[encodings_len], enc_tab[me_indexes[i]].val ); + encodings_len += enc_tab[me_indexes[i]].val_len; + } + + } + + +#ifdef CGI_TIMELIMIT +static void +cgi_kill2( ClientData client_data, struct timeval* nowP ) + { + pid_t pid; + + pid = (pid_t) client_data.i; + if ( kill( pid, SIGKILL ) == 0 ) + syslog( LOG_WARNING, "hard-killed CGI process %d", pid ); + } + +static void +cgi_kill( ClientData client_data, struct timeval* nowP ) + { + pid_t pid; + + pid = (pid_t) client_data.i; + if ( kill( pid, SIGINT ) == 0 ) + { + syslog( LOG_WARNING, "killed CGI process %d", pid ); + /* In case this isn't enough, schedule an uncatchable kill. */ + if ( tmr_create( nowP, cgi_kill2, client_data, 5 * 1000L, 0 ) == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(cgi_kill2) failed" ); + exit( 1 ); + } + } + } +#endif /* CGI_TIMELIMIT */ + + +#ifdef GENERATE_INDEXES + +/* qsort comparison routine */ +static int +name_compare( const void* v1, const void* v2 ) + { + const char** c1 = (const char**) v1; + const char** c2 = (const char**) v2; + return strcmp( *c1, *c2 ); + } + + +static int +ls( httpd_conn* hc ) + { + DIR* dirp; + struct dirent* de; + int namlen; + static int maxnames = 0; + int nnames; + static char* names; + static char** nameptrs; + static char* name; + static size_t maxname = 0; + static char* rname; + static size_t maxrname = 0; + static char* encrname; + static size_t maxencrname = 0; + FILE* fp; + int i, r; + struct stat sb; + struct stat lsb; + char modestr[20]; + char* linkprefix; + char lnk[MAXPATHLEN+1]; + int linklen; + char* fileclass; + time_t now; + char* timestr; + ClientData client_data; + + dirp = opendir( hc->expnfilename ); + if ( dirp == (DIR*) 0 ) + { + syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename ); + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } + + if ( hc->method == METHOD_HEAD ) + { + closedir( dirp ); + send_mime( + hc, 200, ok200title, "", "", "text/html; charset=%s", (off_t) -1, + hc->sb.st_mtime ); + } + else if ( hc->method == METHOD_GET ) + { + if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit ) + { + closedir( dirp ); + httpd_send_err( + hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl ); + return -1; + } + ++hc->hs->cgi_count; + r = fork( ); + if ( r < 0 ) + { + syslog( LOG_ERR, "fork - %m" ); + closedir( dirp ); + httpd_send_err( + hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + if ( r == 0 ) + { + /* Child process. */ + sub_process = 1; + httpd_unlisten( hc->hs ); + send_mime( + hc, 200, ok200title, "", "", "text/html; charset=%s", + (off_t) -1, hc->sb.st_mtime ); + httpd_write_response( hc ); + +#ifdef CGI_NICE + /* Set priority. */ + (void) nice( CGI_NICE ); +#endif /* CGI_NICE */ + + /* Open a stdio stream so that we can use fprintf, which is more + ** efficient than a bunch of separate write()s. We don't have + ** to worry about double closes or file descriptor leaks cause + ** we're in a subprocess. + */ + fp = fdopen( hc->conn_fd, "w" ); + if ( fp == (FILE*) 0 ) + { + syslog( LOG_ERR, "fdopen - %m" ); + httpd_send_err( + hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + closedir( dirp ); + exit( 1 ); + } + + (void) fprintf( fp, "\ +\n\ +\n\ +\n\ +\n\ + \n\ + \n\ + Index of %.80s\n\ + \n\ +\n\ + \n\ +\n\ +

Index of %.80s

\n\ +\n\ +
\n\
+mode  links    bytes  last-changed  name\n\
+    
", + hc->encodedurl, hc->encodedurl ); + + /* Read in names. */ + nnames = 0; + while ( ( de = readdir( dirp ) ) != 0 ) /* dirent or direct */ + { + if ( nnames >= maxnames ) + { + if ( maxnames == 0 ) + { + maxnames = 100; + names = NEW( char, maxnames * ( MAXPATHLEN + 1 ) ); + nameptrs = NEW( char*, maxnames ); + } + else + { + maxnames *= 2; + names = RENEW( names, char, maxnames * ( MAXPATHLEN + 1 ) ); + nameptrs = RENEW( nameptrs, char*, maxnames ); + } + if ( names == (char*) 0 || nameptrs == (char**) 0 ) + { + syslog( LOG_ERR, "out of memory reallocating directory names" ); + exit( 1 ); + } + for ( i = 0; i < maxnames; ++i ) + nameptrs[i] = &names[i * ( MAXPATHLEN + 1 )]; + } + namlen = NAMLEN(de); + (void) strncpy( nameptrs[nnames], de->d_name, namlen ); + nameptrs[nnames][namlen] = '\0'; + ++nnames; + } + closedir( dirp ); + + /* Sort the names. */ + qsort( nameptrs, nnames, sizeof(*nameptrs), name_compare ); + + /* Generate output. */ + for ( i = 0; i < nnames; ++i ) + { + httpd_realloc_str( + &name, &maxname, + strlen( hc->expnfilename ) + 1 + strlen( nameptrs[i] ) ); + httpd_realloc_str( + &rname, &maxrname, + strlen( hc->origfilename ) + 1 + strlen( nameptrs[i] ) ); + if ( hc->expnfilename[0] == '\0' || + strcmp( hc->expnfilename, "." ) == 0 ) + { + (void) strcpy( name, nameptrs[i] ); + (void) strcpy( rname, nameptrs[i] ); + } + else + { + (void) my_snprintf( name, maxname, + "%s/%s", hc->expnfilename, nameptrs[i] ); + if ( strcmp( hc->origfilename, "." ) == 0 ) + (void) my_snprintf( rname, maxrname, + "%s", nameptrs[i] ); + else + (void) my_snprintf( rname, maxrname, + "%s%s", hc->origfilename, nameptrs[i] ); + } + httpd_realloc_str( + &encrname, &maxencrname, 3 * strlen( rname ) + 1 ); + strencode( encrname, maxencrname, rname ); + + if ( stat( name, &sb ) < 0 || lstat( name, &lsb ) < 0 ) + continue; + + linkprefix = ""; + lnk[0] = '\0'; + /* Break down mode word. First the file type. */ + switch ( lsb.st_mode & S_IFMT ) + { + case S_IFIFO: modestr[0] = 'p'; break; + case S_IFCHR: modestr[0] = 'c'; break; + case S_IFDIR: modestr[0] = 'd'; break; + case S_IFBLK: modestr[0] = 'b'; break; + case S_IFREG: modestr[0] = '-'; break; + case S_IFSOCK: modestr[0] = 's'; break; + case S_IFLNK: modestr[0] = 'l'; + linklen = readlink( name, lnk, sizeof(lnk) - 1 ); + if ( linklen != -1 ) + { + lnk[linklen] = '\0'; + linkprefix = " -> "; + } + break; + default: modestr[0] = '?'; break; + } + /* Now the world permissions. Owner and group permissions + ** are not of interest to web clients. + */ + modestr[1] = ( lsb.st_mode & S_IROTH ) ? 'r' : '-'; + modestr[2] = ( lsb.st_mode & S_IWOTH ) ? 'w' : '-'; + modestr[3] = ( lsb.st_mode & S_IXOTH ) ? 'x' : '-'; + modestr[4] = '\0'; + + /* We also leave out the owner and group name, they are + ** also not of interest to web clients. Plus if we're + ** running under chroot(), they would require a copy + ** of /etc/passwd and /etc/group, which we want to avoid. + */ + + /* Get time string. */ + now = time( (time_t*) 0 ); + timestr = ctime( &lsb.st_mtime ); + timestr[ 0] = timestr[ 4]; + timestr[ 1] = timestr[ 5]; + timestr[ 2] = timestr[ 6]; + timestr[ 3] = ' '; + timestr[ 4] = timestr[ 8]; + timestr[ 5] = timestr[ 9]; + timestr[ 6] = ' '; + if ( now - lsb.st_mtime > 60*60*24*182 ) /* 1/2 year */ + { + timestr[ 7] = ' '; + timestr[ 8] = timestr[20]; + timestr[ 9] = timestr[21]; + timestr[10] = timestr[22]; + timestr[11] = timestr[23]; + } + else + { + timestr[ 7] = timestr[11]; + timestr[ 8] = timestr[12]; + timestr[ 9] = ':'; + timestr[10] = timestr[14]; + timestr[11] = timestr[15]; + } + timestr[12] = '\0'; + + /* The ls -F file class. */ + switch ( sb.st_mode & S_IFMT ) + { + case S_IFDIR: fileclass = "/"; break; + case S_IFSOCK: fileclass = "="; break; + case S_IFLNK: fileclass = "@"; break; + default: + fileclass = ( sb.st_mode & S_IXOTH ) ? "*" : ""; + break; + } + + /* And print. */ + (void) fprintf( fp, + "%s %3ld %10lld %s %.80s%s%s%s\n", + modestr, (long) lsb.st_nlink, (long long) lsb.st_size, + timestr, encrname, S_ISDIR(sb.st_mode) ? "/" : "", + nameptrs[i], linkprefix, lnk, fileclass ); + } + + (void) fprintf( fp, "
\n \n\n" ); + (void) fclose( fp ); + exit( 0 ); + } + + /* Parent process. */ + closedir( dirp ); + syslog( LOG_DEBUG, "spawned indexing process %d for directory '%.200s'", r, hc->expnfilename ); +#ifdef CGI_TIMELIMIT + /* Schedule a kill for the child process, in case it runs too long */ + client_data.i = r; + if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(cgi_kill ls) failed" ); + exit( 1 ); + } +#endif /* CGI_TIMELIMIT */ + hc->status = 200; + hc->bytes_sent = CGI_BYTECOUNT; + hc->should_linger = 0; + } + else + { + closedir( dirp ); + httpd_send_err( + hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) ); + return -1; + } + + return 0; + } + +#endif /* GENERATE_INDEXES */ + + +static char* +build_env( char* fmt, char* arg ) + { + char* cp; + size_t size; + static char* buf; + static size_t maxbuf = 0; + + size = strlen( fmt ) + strlen( arg ); + if ( size > maxbuf ) + httpd_realloc_str( &buf, &maxbuf, size ); + (void) my_snprintf( buf, maxbuf, fmt, arg ); + cp = strdup( buf ); + if ( cp == (char*) 0 ) + { + syslog( LOG_ERR, "out of memory copying environment variable" ); + exit( 1 ); + } + return cp; + } + + +#ifdef SERVER_NAME_LIST +static char* +hostname_map( char* hostname ) + { + int len, n; + static char* list[] = { SERVER_NAME_LIST }; + + len = strlen( hostname ); + for ( n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n ) + if ( strncasecmp( hostname, list[n], len ) == 0 ) + if ( list[n][len] == '/' ) /* check in case of a substring match */ + return &list[n][len + 1]; + return (char*) 0; + } +#endif /* SERVER_NAME_LIST */ + + +/* Set up environment variables. Be real careful here to avoid +** letting malicious clients overrun a buffer. We don't have +** to worry about freeing stuff since we're a sub-process. +*/ +static char** +make_envp( httpd_conn* hc ) + { + static char* envp[50]; + int envn; + char* cp; + char buf[256]; + + envn = 0; + envp[envn++] = build_env( "PATH=%s", CGI_PATH ); +#ifdef CGI_LD_LIBRARY_PATH + envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH ); +#endif /* CGI_LD_LIBRARY_PATH */ + envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE ); + if ( hc->hs->vhost && hc->hostname != (char*) 0 && hc->hostname[0] != '\0' ) + cp = hc->hostname; + else if ( hc->hdrhost != (char*) 0 && hc->hdrhost[0] != '\0' ) + cp = hc->hdrhost; + else if ( hc->reqhost != (char*) 0 && hc->reqhost[0] != '\0' ) + cp = hc->reqhost; + else + cp = hc->hs->server_hostname; + if ( cp != (char*) 0 ) + envp[envn++] = build_env( "SERVER_NAME=%s", cp ); + envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1"; + envp[envn++] = build_env("SERVER_PROTOCOL=%s", hc->protocol); + (void) my_snprintf( buf, sizeof(buf), "%d", (int) hc->hs->port ); + envp[envn++] = build_env( "SERVER_PORT=%s", buf ); + envp[envn++] = build_env( + "REQUEST_METHOD=%s", httpd_method_str( hc->method ) ); + if ( hc->pathinfo[0] != '\0' ) + { + char* cp2; + size_t l; + envp[envn++] = build_env( "PATH_INFO=/%s", hc->pathinfo ); + l = strlen( hc->hs->cwd ) + strlen( hc->pathinfo ) + 1; + cp2 = NEW( char, l ); + if ( cp2 != (char*) 0 ) + { + (void) my_snprintf( cp2, l, "%s%s", hc->hs->cwd, hc->pathinfo ); + envp[envn++] = build_env( "PATH_TRANSLATED=%s", cp2 ); + } + } + envp[envn++] = build_env( + "SCRIPT_NAME=/%s", strcmp( hc->origfilename, "." ) == 0 ? + "" : hc->origfilename ); + if ( hc->query[0] != '\0') + envp[envn++] = build_env( "QUERY_STRING=%s", hc->query ); + envp[envn++] = build_env( + "REMOTE_ADDR=%s", httpd_ntoa( &hc->client_addr ) ); + if ( hc->referrer[0] != '\0' ) + { + envp[envn++] = build_env( "HTTP_REFERER=%s", hc->referrer ); + envp[envn++] = build_env( "HTTP_REFERRER=%s", hc->referrer ); + } + if ( hc->useragent[0] != '\0' ) + envp[envn++] = build_env( "HTTP_USER_AGENT=%s", hc->useragent ); + if ( hc->accept[0] != '\0' ) + envp[envn++] = build_env( "HTTP_ACCEPT=%s", hc->accept ); + if ( hc->accepte[0] != '\0' ) + envp[envn++] = build_env( "HTTP_ACCEPT_ENCODING=%s", hc->accepte ); + if ( hc->acceptl[0] != '\0' ) + envp[envn++] = build_env( "HTTP_ACCEPT_LANGUAGE=%s", hc->acceptl ); + if ( hc->cookie[0] != '\0' ) + envp[envn++] = build_env( "HTTP_COOKIE=%s", hc->cookie ); + if ( hc->contenttype[0] != '\0' ) + envp[envn++] = build_env( "CONTENT_TYPE=%s", hc->contenttype ); + if ( hc->hdrhost[0] != '\0' ) + envp[envn++] = build_env( "HTTP_HOST=%s", hc->hdrhost ); + if ( hc->contentlength != -1 ) + { + (void) my_snprintf( + buf, sizeof(buf), "%lu", (unsigned long) hc->contentlength ); + envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf ); + } + if ( hc->remoteuser[0] != '\0' ) + envp[envn++] = build_env( "REMOTE_USER=%s", hc->remoteuser ); + if ( hc->authorization[0] != '\0' ) + envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" ); + /* We only support Basic auth at the moment. */ + if ( getenv( "TZ" ) != (char*) 0 ) + envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) ); + envp[envn++] = build_env( "CGI_PATTERN=%s", hc->hs->cgi_pattern ); + + envp[envn] = (char*) 0; + return envp; + } + + +/* Set up argument vector. Again, we don't have to worry about freeing stuff +** since we're a sub-process. This gets done after make_envp() because we +** scribble on hc->query. +*/ +static char** +make_argp( httpd_conn* hc ) + { + char** argp; + int argn; + char* cp1; + char* cp2; + + /* By allocating an arg slot for every character in the query, plus + ** one for the filename and one for the NULL, we are guaranteed to + ** have enough. We could actually use strlen/2. + */ + argp = NEW( char*, strlen( hc->query ) + 2 ); + if ( argp == (char**) 0 ) + return (char**) 0; + + argp[0] = strrchr( hc->expnfilename, '/' ); + if ( argp[0] != (char*) 0 ) + ++argp[0]; + else + argp[0] = hc->expnfilename; + + argn = 1; + /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html, + ** "The server should search the query information for a non-encoded = + ** character to determine if the command line is to be used, if it finds + ** one, the command line is not to be used." + */ + if ( strchr( hc->query, '=' ) == (char*) 0 ) + { + for ( cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2 ) + { + if ( *cp2 == '+' ) + { + *cp2 = '\0'; + strdecode( cp1, cp1 ); + argp[argn++] = cp1; + cp1 = cp2 + 1; + } + } + if ( cp2 != cp1 ) + { + strdecode( cp1, cp1 ); + argp[argn++] = cp1; + } + } + + argp[argn] = (char*) 0; + return argp; + } + + +/* This routine is used only for POST requests. It reads the data +** from the request and sends it to the child process. The only reason +** we need to do it this way instead of just letting the child read +** directly is that we have already read part of the data into our +** buffer. +*/ +static void +cgi_interpose_input( httpd_conn* hc, int wfd ) + { + size_t c; + ssize_t r; + char buf[1024]; + + c = hc->read_idx - hc->checked_idx; + if ( c > 0 ) + { + if ( httpd_write_fully( wfd, &(hc->read_buf[hc->checked_idx]), c ) != c ) + return; + } + while ( c < hc->contentlength ) + { + r = read( hc->conn_fd, buf, MIN( sizeof(buf), hc->contentlength - c ) ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + { + sleep( 1 ); + continue; + } + if ( r <= 0 ) + return; + if ( httpd_write_fully( wfd, buf, r ) != r ) + return; + c += r; + } + post_post_garbage_hack( hc ); + } + + +/* Special hack to deal with broken browsers that send a LF or CRLF +** after POST data, causing TCP resets - we just read and discard up +** to 2 bytes. Unfortunately this doesn't fix the problem for CGIs +** which avoid the interposer process due to their POST data being +** short. Creating an interposer process for all POST CGIs is +** unacceptably expensive. The eventual fix will come when interposing +** gets integrated into the main loop as a tasklet instead of a process. +*/ +static void +post_post_garbage_hack( httpd_conn* hc ) + { + char buf[2]; + + /* If we are in a sub-process, turn on no-delay mode in case we + ** previously cleared it. + */ + if ( sub_process ) + httpd_set_ndelay( hc->conn_fd ); + /* And read up to 2 bytes. */ + (void) read( hc->conn_fd, buf, sizeof(buf) ); + } + + +/* This routine is used for parsed-header CGIs. The idea here is that the +** CGI can return special headers such as "Status:" and "Location:" which +** change the return status of the response. Since the return status has to +** be the very first line written out, we have to accumulate all the headers +** and check for the special ones before writing the status. Then we write +** out the saved headers and proceed to echo the rest of the response. +*/ +static void +cgi_interpose_output( httpd_conn* hc, int rfd ) + { + int r; + char buf[1024]; + size_t headers_size, headers_len; + char* headers; + char* br; + int status; + char* title; + char* cp; + + /* Make sure the connection is in blocking mode. It should already + ** be blocking, but we might as well be sure. + */ + httpd_clear_ndelay( hc->conn_fd ); + + /* Slurp in all headers. */ + headers_size = 0; + httpd_realloc_str( &headers, &headers_size, 500 ); + headers_len = 0; + for (;;) + { + r = read( rfd, buf, sizeof(buf) ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + { + sleep( 1 ); + continue; + } + if ( r <= 0 ) + { + br = &(headers[headers_len]); + break; + } + httpd_realloc_str( &headers, &headers_size, headers_len + r ); + (void) memmove( &(headers[headers_len]), buf, r ); + headers_len += r; + headers[headers_len] = '\0'; + if ( ( br = strstr( headers, "\015\012\015\012" ) ) != (char*) 0 || + ( br = strstr( headers, "\012\012" ) ) != (char*) 0 ) + break; + } + + /* If there were no headers, bail. */ + if ( headers[0] == '\0' ) + return; + + /* Figure out the status. Look for a Status: or Location: header; + ** else if there's an HTTP header line, get it from there; else + ** default to 200. + */ + status = 200; + if ( strncmp( headers, "HTTP/", 5 ) == 0 ) + { + cp = headers; + cp += strcspn( cp, " \t" ); + status = atoi( cp ); + } + if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 && + cp < br && + ( cp == headers || *(cp-1) == '\012' ) ) + status = 302; + if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 && + cp < br && + ( cp == headers || *(cp-1) == '\012' ) ) + { + cp += 7; + cp += strspn( cp, " \t" ); + status = atoi( cp ); + } + + /* Write the status line. */ + switch ( status ) + { + case 200: title = ok200title; break; + case 302: title = err302title; break; + case 304: title = err304title; break; + case 400: title = httpd_err400title; break; +#ifdef AUTH_FILE + case 401: title = err401title; break; +#endif /* AUTH_FILE */ + case 403: title = err403title; break; + case 404: title = err404title; break; + case 408: title = httpd_err408title; break; + case 451: title = err451title; break; + case 500: title = err500title; break; + case 501: title = err501title; break; + case 503: title = httpd_err503title; break; + default: title = "Something"; break; + } + (void) my_snprintf( buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, title ); + (void) httpd_write_fully( hc->conn_fd, buf, strlen( buf ) ); + + /* Write the saved headers. */ + (void) httpd_write_fully( hc->conn_fd, headers, headers_len ); + + /* Echo the rest of the output. */ + for (;;) + { + r = read( rfd, buf, sizeof(buf) ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + { + sleep( 1 ); + continue; + } + if ( r <= 0 ) + break; + if ( httpd_write_fully( hc->conn_fd, buf, r ) != r ) + break; + } + shutdown( hc->conn_fd, SHUT_WR ); + } + + +/* CGI child process. */ +static void +cgi_child( httpd_conn* hc ) + { + int r; + char** argp; + char** envp; + char* binary; + char* directory; + + /* Unset close-on-exec flag for this socket. This actually shouldn't + ** be necessary, according to POSIX a dup()'d file descriptor does + ** *not* inherit the close-on-exec flag, its flag is always clear. + ** However, Linux messes this up and does copy the flag to the + ** dup()'d descriptor, so we have to clear it. This could be + ** ifdeffed for Linux only. + */ + (void) fcntl( hc->conn_fd, F_SETFD, 0 ); + + /* Close the syslog descriptor so that the CGI program can't + ** mess with it. All other open descriptors should be either + ** the listen socket(s), sockets from accept(), or the file-logging + ** fd, and all of those are set to close-on-exec, so we don't + ** have to close anything else. + */ + closelog(); + + /* If the socket happens to be using one of the stdin/stdout/stderr + ** descriptors, move it to another descriptor so that the dup2 calls + ** below don't screw things up. We arbitrarily pick fd 3 - if there + ** was already something on it, we clobber it, but that doesn't matter + ** since at this point the only fd of interest is the connection. + ** All others will be closed on exec. + */ + if ( hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO || hc->conn_fd == STDERR_FILENO ) + { + int newfd = dup2( hc->conn_fd, STDERR_FILENO + 1 ); + if ( newfd >= 0 ) + hc->conn_fd = newfd; + /* If the dup2 fails, shrug. We'll just take our chances. + ** Shouldn't happen though. + */ + } + + /* Make the environment vector. */ + envp = make_envp( hc ); + + /* Make the argument vector. */ + argp = make_argp( hc ); + + /* Set up stdin. For POSTs we may have to set up a pipe from an + ** interposer process, depending on if we've read some of the data + ** into our buffer. + */ + if ( hc->method == METHOD_POST && hc->read_idx > hc->checked_idx ) + { + int p[2]; + + if ( pipe( p ) < 0 ) + { + syslog( LOG_ERR, "pipe - %m" ); + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + exit( 1 ); + } + r = fork( ); + if ( r < 0 ) + { + syslog( LOG_ERR, "fork - %m" ); + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + exit( 1 ); + } + if ( r == 0 ) + { + /* Interposer process. */ + sub_process = 1; + (void) close( p[0] ); + cgi_interpose_input( hc, p[1] ); + exit( 0 ); + } + /* Need to schedule a kill for process r; but in the main process! */ + (void) close( p[1] ); + if ( p[0] != STDIN_FILENO ) + { + (void) dup2( p[0], STDIN_FILENO ); + (void) close( p[0] ); + } + } + else + { + /* Otherwise, the request socket is stdin. */ + if ( hc->conn_fd != STDIN_FILENO ) + (void) dup2( hc->conn_fd, STDIN_FILENO ); + } + + /* Set up stdout/stderr. If we're doing CGI header parsing, + ** we need an output interposer too. + */ + if ( strncmp( argp[0], "nph-", 4 ) != 0 && hc->mime_flag ) + { + int p[2]; + + if ( pipe( p ) < 0 ) + { + syslog( LOG_ERR, "pipe - %m" ); + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + exit( 1 ); + } + r = fork( ); + if ( r < 0 ) + { + syslog( LOG_ERR, "fork - %m" ); + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + exit( 1 ); + } + if ( r == 0 ) + { + /* Interposer process. */ + sub_process = 1; + (void) close( p[1] ); + cgi_interpose_output( hc, p[0] ); + exit( 0 ); + } + /* Need to schedule a kill for process r; but in the main process! */ + (void) close( p[0] ); + if ( p[1] != STDOUT_FILENO ) + (void) dup2( p[1], STDOUT_FILENO ); + if ( p[1] != STDERR_FILENO ) + (void) dup2( p[1], STDERR_FILENO ); + if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO ) + (void) close( p[1] ); + } + else + { + /* Otherwise, the request socket is stdout/stderr. */ + if ( hc->conn_fd != STDOUT_FILENO ) + (void) dup2( hc->conn_fd, STDOUT_FILENO ); + if ( hc->conn_fd != STDERR_FILENO ) + (void) dup2( hc->conn_fd, STDERR_FILENO ); + } + + /* At this point we would like to set close-on-exec again for hc->conn_fd + ** (see previous comments on Linux's broken behavior re: close-on-exec + ** and dup.) Unfortunately there seems to be another Linux problem, or + ** perhaps a different aspect of the same problem - if we do this + ** close-on-exec in Linux, the socket stays open but stderr gets + ** closed - the last fd duped from the socket. What a mess. So we'll + ** just leave the socket as is, which under other OSs means an extra + ** file descriptor gets passed to the child process. Since the child + ** probably already has that file open via stdin stdout and/or stderr, + ** this is not a problem. + */ + /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */ + +#ifdef CGI_NICE + /* Set priority. */ + (void) nice( CGI_NICE ); +#endif /* CGI_NICE */ + + /* Split the program into directory and binary, so we can chdir() + ** to the program's own directory. This isn't in the CGI 1.1 + ** spec, but it's what other HTTP servers do. + */ + directory = strdup( hc->expnfilename ); + if ( directory == (char*) 0 ) + binary = hc->expnfilename; /* ignore errors */ + else + { + binary = strrchr( directory, '/' ); + if ( binary == (char*) 0 ) + binary = hc->expnfilename; + else + { + *binary++ = '\0'; + (void) chdir( directory ); /* ignore errors */ + } + } + + /* Default behavior for SIGPIPE. */ +#ifdef HAVE_SIGSET + (void) sigset( SIGPIPE, SIG_DFL ); +#else /* HAVE_SIGSET */ + (void) signal( SIGPIPE, SIG_DFL ); +#endif /* HAVE_SIGSET */ + + /* Run the program. */ + (void) execve( binary, argp, envp ); + + /* Something went wrong. */ + syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename ); + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + _exit( 1 ); + } + + +static int +cgi( httpd_conn* hc ) + { + int r; + ClientData client_data; + + if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit ) + { + httpd_send_err( + hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl ); + return -1; + } + ++hc->hs->cgi_count; + httpd_clear_ndelay( hc->conn_fd ); + r = fork( ); + if ( r < 0 ) + { + syslog( LOG_ERR, "fork - %m" ); + httpd_send_err( + hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + if ( r == 0 ) + { + /* Child process. */ + sub_process = 1; + httpd_unlisten( hc->hs ); + cgi_child( hc ); + } + + /* Parent process. */ + syslog( LOG_DEBUG, "spawned CGI process %d for file '%.200s'", r, hc->expnfilename ); +#ifdef CGI_TIMELIMIT + /* Schedule a kill for the child process, in case it runs too long */ + client_data.i = r; + if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(cgi_kill child) failed" ); + exit( 1 ); + } +#endif /* CGI_TIMELIMIT */ + hc->status = 200; + hc->bytes_sent = CGI_BYTECOUNT; + hc->should_linger = 0; + + return 0; + } + + +static int +really_start_request( httpd_conn* hc, struct timeval* nowP ) + { + static char* indexname; + static size_t maxindexname = 0; + static const char* index_names[] = { INDEX_NAMES }; + int i; +#ifdef AUTH_FILE + static char* dirname; + static size_t maxdirname = 0; +#endif /* AUTH_FILE */ + size_t expnlen, indxlen; + char* cp; + char* pi; + + expnlen = strlen( hc->expnfilename ); + + /* Stat the file. */ + if ( stat( hc->expnfilename, &hc->sb ) < 0 ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + + /* Is it world-readable or world-executable? We check explicitly instead + ** of just trying to open it, so that no one ever gets surprised by + ** a file that's not set world-readable and yet somehow is + ** readable by the HTTP server and therefore the *whole* world. + */ + if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) ) + { + syslog( + LOG_INFO, + "%.80s URL \"%.80s\" resolves to a non world-readable file", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ), + hc->encodedurl ); + return -1; + } + + /* Is it a directory? */ + if ( S_ISDIR(hc->sb.st_mode) ) + { + /* If there's pathinfo, it's just a non-existent file. */ + if ( hc->pathinfo[0] != '\0' ) + { + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } + + /* Special handling for directory URLs that don't end in a slash. + ** We send back an explicit redirect with the slash, because + ** otherwise many clients can't build relative URLs properly. + */ + if ( strcmp( hc->origfilename, "" ) != 0 && + strcmp( hc->origfilename, "." ) != 0 && + hc->origfilename[strlen( hc->origfilename ) - 1] != '/' ) + { + send_dirredirect( hc ); + return -1; + } + + /* Check for an index file. */ + for ( i = 0; i < sizeof(index_names) / sizeof(char*); ++i ) + { + httpd_realloc_str( + &indexname, &maxindexname, + expnlen + 1 + strlen( index_names[i] ) ); + (void) strcpy( indexname, hc->expnfilename ); + indxlen = strlen( indexname ); + if ( indxlen == 0 || indexname[indxlen - 1] != '/' ) + (void) strcat( indexname, "/" ); + if ( strcmp( indexname, "./" ) == 0 ) + indexname[0] = '\0'; + (void) strcat( indexname, index_names[i] ); + if ( stat( indexname, &hc->sb ) >= 0 ) + goto got_one; + } + + /* Nope, no index file, so it's an actual directory request. */ +#ifdef GENERATE_INDEXES + /* Directories must be readable for indexing. */ + if ( ! ( hc->sb.st_mode & S_IROTH ) ) + { + syslog( + LOG_INFO, + "%.80s URL \"%.80s\" tried to index a directory with indexing disabled", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ), + hc->encodedurl ); + return -1; + } +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + if ( auth_check( hc, hc->expnfilename ) == -1 ) + return -1; +#endif /* AUTH_FILE */ + /* Referrer check. */ + if ( ! check_referrer( hc ) ) + return -1; + /* Ok, generate an index. */ + return ls( hc ); +#else /* GENERATE_INDEXES */ + syslog( + LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n" ), + hc->encodedurl ); + return -1; +#endif /* GENERATE_INDEXES */ + + got_one: ; + /* Got an index file. Expand symlinks again. More pathinfo means + ** something went wrong. + */ + cp = expand_symlinks( indexname, &pi, hc->hs->no_symlink_check, hc->tildemapped ); + if ( cp == (char*) 0 || pi[0] != '\0' ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + expnlen = strlen( cp ); + httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, expnlen ); + (void) strcpy( hc->expnfilename, cp ); + + /* Now, is the index version world-readable or world-executable? */ + if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) ) + { + syslog( + LOG_INFO, + "%.80s URL \"%.80s\" resolves to a non-world-readable index file", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ), + hc->encodedurl ); + return -1; + } + } + +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + httpd_realloc_str( &dirname, &maxdirname, expnlen ); + (void) strcpy( dirname, hc->expnfilename ); + cp = strrchr( dirname, '/' ); + if ( cp == (char*) 0 ) + (void) strcpy( dirname, "." ); + else + *cp = '\0'; + if ( auth_check( hc, dirname ) == -1 ) + return -1; + + /* Check if the filename is the AUTH_FILE itself - that's verboten. */ + if ( expnlen == sizeof(AUTH_FILE) - 1 ) + { + if ( strcmp( hc->expnfilename, AUTH_FILE ) == 0 ) + { + syslog( + LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an auth file", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ), + hc->encodedurl ); + return -1; + } + } + else if ( expnlen >= sizeof(AUTH_FILE) && + strcmp( &(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 && + hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/' ) + { + syslog( + LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an auth file", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ), + hc->encodedurl ); + return -1; + } +#endif /* AUTH_FILE */ + + /* Referrer check. */ + if ( ! check_referrer( hc ) ) + return -1; + + /* Is it world-executable and in the CGI area? */ + if ( hc->hs->cgi_pattern != (char*) 0 && + ( hc->sb.st_mode & S_IXOTH ) && + match( hc->hs->cgi_pattern, hc->expnfilename ) ) + return cgi( hc ); + + /* It's not CGI. If it's executable or there's pathinfo, someone's + ** trying to either serve or run a non-CGI file as CGI. Either case + ** is prohibited. + */ + if ( hc->sb.st_mode & S_IXOTH ) + { + syslog( + LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ), + hc->encodedurl ); + return -1; + } + if ( hc->pathinfo[0] != '\0' ) + { + syslog( + LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ), + hc->encodedurl ); + return -1; + } + + if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD ) + { + httpd_send_err( + hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) ); + return -1; + } + + /* Fill in last_byte_index, if necessary. */ + if ( hc->got_range && + ( hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.st_size ) ) + hc->last_byte_index = hc->sb.st_size - 1; + + figure_mime( hc ); + + if ( hc->method == METHOD_HEAD ) + { + send_mime( + hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, + hc->sb.st_mtime ); + } + else if ( hc->if_modified_since != (time_t) -1 && + hc->if_modified_since >= hc->sb.st_mtime ) + { + send_mime( + hc, 304, err304title, hc->encodings, "", hc->type, (off_t) -1, + hc->sb.st_mtime ); + } + else + { + hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP ); + if ( hc->file_address == (char*) 0 ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + send_mime( + hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, + hc->sb.st_mtime ); + } + + return 0; + } + + +int +httpd_start_request( httpd_conn* hc, struct timeval* nowP ) + { + int r; + + /* Really start the request. */ + r = really_start_request( hc, nowP ); + + /* And return the status. */ + return r; + } + + +static void +make_log_entry( httpd_conn* hc, struct timeval* nowP ) + { + char* ru; + char url[305]; + char bytes[40]; + + if ( hc->hs->no_log ) + return; + + /* This is straight CERN Combined Log Format - the only tweak + ** being that if we're using syslog() we leave out the date, because + ** syslogd puts it in. The included syslogtocern script turns the + ** results into true CERN format. + */ + + /* Format remote user. */ + if ( hc->remoteuser[0] != '\0' ) + ru = hc->remoteuser; + else + ru = "-"; + /* If we're vhosting, prepend the hostname to the url. This is + ** a little weird, perhaps writing separate log files for + ** each vhost would make more sense. + */ + if ( hc->hs->vhost && ! hc->tildemapped ) + (void) my_snprintf( url, sizeof(url), + "/%.100s%.200s", + hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname, + hc->encodedurl ); + else + (void) my_snprintf( url, sizeof(url), + "%.200s", hc->encodedurl ); + /* Format the bytes. */ + if ( hc->bytes_sent >= 0 ) + (void) my_snprintf( + bytes, sizeof(bytes), "%lld", (long long) hc->bytes_sent ); + else + (void) strcpy( bytes, "-" ); + + /* Logfile or syslog? */ + if ( hc->hs->logfp != (FILE*) 0 ) + { + time_t now; + struct tm* t; + const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S"; + char date_nozone[100]; + int zone; + char sign; + char date[100]; + + /* Get the current time, if necessary. */ + if ( nowP != (struct timeval*) 0 ) + now = nowP->tv_sec; + else + now = time( (time_t*) 0 ); + /* Format the time, forcing a numeric timezone (some log analyzers + ** are stoooopid about this). + */ + t = localtime( &now ); + (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t ); +#ifdef HAVE_TM_GMTOFF + zone = t->tm_gmtoff / 60L; +#else + zone = -timezone / 60L; + /* Probably have to add something about daylight time here. */ +#endif + if ( zone >= 0 ) + sign = '+'; + else + { + sign = '-'; + zone = -zone; + } + zone = ( zone / 60 ) * 100 + zone % 60; + (void) my_snprintf( date, sizeof(date), + "%s %c%04d", date_nozone, sign, zone ); + /* And write the log entry. */ + (void) fprintf( hc->hs->logfp, + "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n", + httpd_ntoa( &hc->client_addr ), ru, date, + httpd_method_str( hc->method ), url, hc->protocol, + hc->status, bytes, hc->referrer, hc->useragent ); +#ifdef FLUSH_LOG_EVERY_TIME + (void) fflush( hc->hs->logfp ); +#endif + } + else + syslog( LOG_INFO, + "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"", + httpd_ntoa( &hc->client_addr ), ru, + httpd_method_str( hc->method ), url, hc->protocol, + hc->status, bytes, hc->referrer, hc->useragent ); + } + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int +check_referrer( httpd_conn* hc ) + { + int r; + char* cp; + + /* Are we doing referrer checking at all? */ + if ( hc->hs->url_pattern == (char*) 0 ) + return 1; + + r = really_check_referrer( hc ); + + if ( ! r ) + { + if ( hc->hs->vhost && hc->hostname != (char*) 0 ) + cp = hc->hostname; + else + cp = hc->hs->server_hostname; + if ( cp == (char*) 0 ) + cp = ""; + syslog( + LOG_INFO, "%.80s non-local referrer \"%.80s%.80s\" \"%.80s\"", + httpd_ntoa( &hc->client_addr ), cp, hc->encodedurl, hc->referrer ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "You must supply a local referrer to get URL '%.80s' from this server.\n" ), + hc->encodedurl ); + } + return r; + } + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int +really_check_referrer( httpd_conn* hc ) + { + httpd_server* hs; + char* cp1; + char* cp2; + char* cp3; + static char* refhost = (char*) 0; + static size_t refhost_size = 0; + char *lp; + + hs = hc->hs; + + /* Check for an empty referrer. */ + if ( hc->referrer == (char*) 0 || hc->referrer[0] == '\0' || + ( cp1 = strstr( hc->referrer, "//" ) ) == (char*) 0 ) + { + /* Disallow if we require a referrer and the url matches. */ + if ( hs->no_empty_referrers && match( hs->url_pattern, hc->origfilename ) ) + return 0; + /* Otherwise ok. */ + return 1; + } + + /* Extract referrer host. */ + cp1 += 2; + for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 ) + continue; + httpd_realloc_str( &refhost, &refhost_size, cp2 - cp1 ); + for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 ) + if ( isupper(*cp1) ) + *cp3 = tolower(*cp1); + else + *cp3 = *cp1; + *cp3 = '\0'; + + /* Local pattern? */ + if ( hs->local_pattern != (char*) 0 ) + lp = hs->local_pattern; + else + { + /* No local pattern. What's our hostname? */ + if ( ! hs->vhost ) + { + /* Not vhosting, use the server name. */ + lp = hs->server_hostname; + if ( lp == (char*) 0 ) + /* Couldn't figure out local hostname - give up. */ + return 1; + } + else + { + /* We are vhosting, use the hostname on this connection. */ + lp = hc->hostname; + if ( lp == (char*) 0 ) + /* Oops, no hostname. Maybe it's an old browser that + ** doesn't send a Host: header. We could figure out + ** the default hostname for this IP address, but it's + ** not worth it for the few requests like this. + */ + return 1; + } + } + + /* If the referrer host doesn't match the local host pattern, and + ** the filename does match the url pattern, it's an illegal reference. + */ + if ( ! match( lp, refhost ) && match( hs->url_pattern, hc->origfilename ) ) + return 0; + /* Otherwise ok. */ + return 1; + } + + +char* +httpd_ntoa( httpd_sockaddr* saP ) + { +#ifdef USE_IPV6 + static char str[200]; + + if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 ) + { + str[0] = '?'; + str[1] = '\0'; + } + else if ( IN6_IS_ADDR_V4MAPPED( &saP->sa_in6.sin6_addr ) && strncmp( str, "::ffff:", 7 ) == 0 ) + /* Elide IPv6ish prefix for IPv4 addresses. */ + (void) ol_strcpy( str, &str[7] ); + + return str; + +#else /* USE_IPV6 */ + + return inet_ntoa( saP->sa_in.sin_addr ); + +#endif /* USE_IPV6 */ + } + + +static int +sockaddr_check( httpd_sockaddr* saP ) + { + switch ( saP->sa.sa_family ) + { + case AF_INET: return 1; +#ifdef USE_IPV6 + case AF_INET6: return 1; +#endif /* USE_IPV6 */ + default: + return 0; + } + } + + +static size_t +sockaddr_len( httpd_sockaddr* saP ) + { + switch ( saP->sa.sa_family ) + { + case AF_INET: return sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + case AF_INET6: return sizeof(struct sockaddr_in6); +#endif /* USE_IPV6 */ + default: + return 0; /* shouldn't happen */ + } + } + + +/* Some systems don't have snprintf(), so we make our own that uses +** either vsnprintf() or vsprintf(). If your system doesn't have +** vsnprintf(), it is probably vulnerable to buffer overruns. +** Upgrade! +*/ +static int +my_snprintf( char* str, size_t size, const char* format, ... ) + { + va_list ap; + int r; + + va_start( ap, format ); +#ifdef HAVE_VSNPRINTF + r = vsnprintf( str, size, format, ap ); +#else /* HAVE_VSNPRINTF */ + r = vsprintf( str, format, ap ); +#endif /* HAVE_VSNPRINTF */ + va_end( ap ); + return r; + } + + +#ifndef HAVE_ATOLL +static long long +atoll( const char* str ) + { + long long value; + long long sign; + + while ( isspace( *str ) ) + ++str; + switch ( *str ) + { + case '-': sign = -1; ++str; break; + case '+': sign = 1; ++str; break; + default: sign = 1; break; + } + value = 0; + while ( isdigit( *str ) ) + { + value = value * 10 + ( *str - '0' ); + ++str; + } + return sign * value; + } +#endif /* HAVE_ATOLL */ + + +/* Read the requested buffer completely, accounting for interruptions. */ +int +httpd_read_fully( int fd, void* buf, size_t nbytes ) + { + int nread; + + nread = 0; + while ( nread < nbytes ) + { + int r; + + r = read( fd, (char*) buf + nread, nbytes - nread ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + { + sleep( 1 ); + continue; + } + if ( r < 0 ) + return r; + if ( r == 0 ) + break; + nread += r; + } + + return nread; + } + + +/* Write the requested buffer completely, accounting for interruptions. */ +int +httpd_write_fully( int fd, const char* buf, size_t nbytes ) + { + int nwritten; + + nwritten = 0; + while ( nwritten < nbytes ) + { + int r; + + r = write( fd, buf + nwritten, nbytes - nwritten ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + { + sleep( 1 ); + continue; + } + if ( r < 0 ) + return r; + if ( r == 0 ) + break; + nwritten += r; + } + + return nwritten; + } + + +/* Generate debugging statistics syslog message. */ +void +httpd_logstats( long secs ) + { + if ( str_alloc_count > 0 ) + syslog( LOG_NOTICE, + " libhttpd - %d strings allocated, %lu bytes (%g bytes/str)", + str_alloc_count, (unsigned long) str_alloc_size, + (float) str_alloc_size / str_alloc_count ); + } diff --git a/libhttpd.h b/libhttpd.h new file mode 100644 index 0000000..e707bc7 --- /dev/null +++ b/libhttpd.h @@ -0,0 +1,291 @@ +/* libhttpd.h - defines for libhttpd +** +** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _LIBHTTPD_H_ +#define _LIBHTTPD_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) +#define USE_IPV6 +#endif + + +/* A few convenient defines. */ + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#define NEW(t,n) ((t*) malloc( sizeof(t) * (n) )) +#define RENEW(o,t,n) ((t*) realloc( (void*) o, sizeof(t) * (n) )) + +/* Do overlapping strcpy safely, by using memmove. */ +#define ol_strcpy(dst,src) memmove(dst,src,strlen(src)+1) + + +/* The httpd structs. */ + +/* A multi-family sockaddr. */ +typedef union { + struct sockaddr sa; + struct sockaddr_in sa_in; +#ifdef USE_IPV6 + struct sockaddr_in6 sa_in6; + struct sockaddr_storage sa_stor; +#endif /* USE_IPV6 */ + } httpd_sockaddr; + +/* A server. */ +typedef struct { + char* binding_hostname; + char* server_hostname; + unsigned short port; + char* cgi_pattern; + int cgi_limit, cgi_count; + char* charset; + char* p3p; + int max_age; + char* cwd; + int listen4_fd, listen6_fd; + int no_log; + FILE* logfp; + int no_symlink_check; + int vhost; + int global_passwd; + char* url_pattern; + char* local_pattern; + int no_empty_referrers; + } httpd_server; + +/* A connection. */ +typedef struct { + int initialized; + httpd_server* hs; + httpd_sockaddr client_addr; + char* read_buf; + size_t read_size, read_idx, checked_idx; + int checked_state; + int method; + int status; + off_t bytes_to_send; + off_t bytes_sent; + char* encodedurl; + char* decodedurl; + char* protocol; + char* origfilename; + char* expnfilename; + char* encodings; + char* pathinfo; + char* query; + char* referrer; + char* useragent; + char* accept; + char* accepte; + char* acceptl; + char* cookie; + char* contenttype; + char* reqhost; + char* hdrhost; + char* hostdir; + char* authorization; + char* remoteuser; + char* response; + size_t maxdecodedurl, maxorigfilename, maxexpnfilename, maxencodings, + maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost, maxhostdir, + maxremoteuser, maxresponse; +#ifdef TILDE_MAP_2 + char* altdir; + size_t maxaltdir; +#endif /* TILDE_MAP_2 */ + size_t responselen; + time_t if_modified_since, range_if; + size_t contentlength; + char* type; /* not malloc()ed */ + char* hostname; /* not malloc()ed */ + int mime_flag; + int one_one; /* HTTP/1.1 or better */ + int got_range; + int tildemapped; /* this connection got tilde-mapped */ + off_t first_byte_index, last_byte_index; + int keep_alive; + int should_linger; + struct stat sb; + int conn_fd; + char* file_address; + } httpd_conn; + +/* Methods. */ +#define METHOD_UNKNOWN 0 +#define METHOD_GET 1 +#define METHOD_HEAD 2 +#define METHOD_POST 3 +#define METHOD_PUT 4 +#define METHOD_DELETE 5 +#define METHOD_TRACE 6 + +/* States for checked_state. */ +#define CHST_FIRSTWORD 0 +#define CHST_FIRSTWS 1 +#define CHST_SECONDWORD 2 +#define CHST_SECONDWS 3 +#define CHST_THIRDWORD 4 +#define CHST_THIRDWS 5 +#define CHST_LINE 6 +#define CHST_LF 7 +#define CHST_CR 8 +#define CHST_CRLF 9 +#define CHST_CRLFCR 10 +#define CHST_BOGUS 11 + + +/* Initializes. Does the socket(), bind(), and listen(). Returns an +** httpd_server* which includes a socket fd that you can select() on. +** Return (httpd_server*) 0 on error. +*/ +httpd_server* httpd_initialize( + char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, + unsigned short port, char* cgi_pattern, int cgi_limit, char* charset, + char* p3p, int max_age, char* cwd, int no_log, FILE* logfp, + int no_symlink_check, int vhost, int global_passwd, char* url_pattern, + char* local_pattern, int no_empty_referrers ); + +/* Change the log file. */ +void httpd_set_logfp( httpd_server* hs, FILE* logfp ); + +/* Call to unlisten/close socket(s) listening for new connections. */ +void httpd_unlisten( httpd_server* hs ); + +/* Call to shut down. */ +void httpd_terminate( httpd_server* hs ); + + +/* When a listen fd is ready to read, call this. It does the accept() and +** returns an httpd_conn* which includes the fd to read the request from and +** write the response to. Returns an indication of whether the accept() +** failed, succeeded, or if there were no more connections to accept. +** +** In order to minimize malloc()s, the caller passes in the httpd_conn. +** The caller is also responsible for setting initialized to zero before the +** first call using each different httpd_conn. +*/ +int httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc ); +#define GC_FAIL 0 +#define GC_OK 1 +#define GC_NO_MORE 2 + +/* Checks whether the data in hc->read_buf constitutes a complete request +** yet. The caller reads data into hc->read_buf[hc->read_idx] and advances +** hc->read_idx. This routine checks what has been read so far, using +** hc->checked_idx and hc->checked_state to keep track, and returns an +** indication of whether there is no complete request yet, there is a +** complete request, or there won't be a valid request due to a syntax error. +*/ +int httpd_got_request( httpd_conn* hc ); +#define GR_NO_REQUEST 0 +#define GR_GOT_REQUEST 1 +#define GR_BAD_REQUEST 2 + +/* Parses the request in hc->read_buf. Fills in lots of fields in hc, +** like the URL and the various headers. +** +** Returns -1 on error. +*/ +int httpd_parse_request( httpd_conn* hc ); + +/* Starts sending data back to the client. In some cases (directories, +** CGI programs), finishes sending by itself - in those cases, hc->file_fd +** is <0. If there is more data to be sent, then hc->file_fd is a file +** descriptor for the file to send. If you don't have a current timeval +** handy just pass in 0. +** +** Returns -1 on error. +*/ +int httpd_start_request( httpd_conn* hc, struct timeval* nowP ); + +/* Actually sends any buffered response text. */ +void httpd_write_response( httpd_conn* hc ); + +/* Call this to close down a connection and free the data. A fine point, +** if you fork() with a connection open you should still call this in the +** parent process - the connection will stay open in the child. +** If you don't have a current timeval handy just pass in 0. +*/ +void httpd_close_conn( httpd_conn* hc, struct timeval* nowP ); + +/* Call this to de-initialize a connection struct and *really* free the +** mallocced strings. +*/ +void httpd_destroy_conn( httpd_conn* hc ); + + +/* Send an error message back to the client. */ +void httpd_send_err( + httpd_conn* hc, int status, char* title, char* extraheads, char* form, + char* arg ); + +/* Some error messages. */ +extern char* httpd_err400title; +extern char* httpd_err400form; +extern char* httpd_err408title; +extern char* httpd_err408form; +extern char* httpd_err503title; +extern char* httpd_err503form; + +/* Generate a string representation of a method number. */ +char* httpd_method_str( int method ); + +/* Reallocate a string. */ +void httpd_realloc_str( char** strP, size_t* maxsizeP, size_t size ); + +/* Format a network socket to a string representation. */ +char* httpd_ntoa( httpd_sockaddr* saP ); + +/* Set NDELAY mode on a socket. */ +void httpd_set_ndelay( int fd ); + +/* Clear NDELAY mode on a socket. */ +void httpd_clear_ndelay( int fd ); + +/* Read the requested buffer completely, accounting for interruptions. */ +int httpd_read_fully( int fd, void* buf, size_t nbytes ); + +/* Write the requested buffer completely, accounting for interruptions. */ +int httpd_write_fully( int fd, const char* buf, size_t nbytes ); + +/* Generate debugging statistics syslog message. */ +void httpd_logstats( long secs ); + +#endif /* _LIBHTTPD_H_ */ diff --git a/match.c b/match.c new file mode 100644 index 0000000..6facccb --- /dev/null +++ b/match.c @@ -0,0 +1,88 @@ +/* match.c - simple shell-style filename matcher +** +** Only does ? * and **, and multiple patterns separated by |. Returns 1 or 0. +** +** Copyright © 1995,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include + +#include "match.h" + +static int match_one( const char* pattern, int patternlen, const char* string ); + +int +match( const char* pattern, const char* string ) + { + const char* or; + + for (;;) + { + or = strchr( pattern, '|' ); + if ( or == (char*) 0 ) + return match_one( pattern, strlen( pattern ), string ); + if ( match_one( pattern, or - pattern, string ) ) + return 1; + pattern = or + 1; + } + } + + +static int +match_one( const char* pattern, int patternlen, const char* string ) + { + const char* p; + + for ( p = pattern; p - pattern < patternlen; ++p, ++string ) + { + if ( *p == '?' && *string != '\0' ) + continue; + if ( *p == '*' ) + { + int i, pl; + ++p; + if ( *p == '*' ) + { + /* Double-wildcard matches anything. */ + ++p; + i = strlen( string ); + } + else + /* Single-wildcard matches anything but slash. */ + i = strcspn( string, "/" ); + pl = patternlen - ( p - pattern ); + for ( ; i >= 0; --i ) + if ( match_one( p, pl, &(string[i]) ) ) + return 1; + return 0; + } + if ( *p != *string ) + return 0; + } + if ( *string == '\0' ) + return 1; + return 0; + } diff --git a/match.h b/match.h new file mode 100644 index 0000000..01c5f2a --- /dev/null +++ b/match.h @@ -0,0 +1,36 @@ +/* match.h - simple shell-style filename patcher +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _MATCH_H_ +#define _MATCH_H_ + +/* Simple shell-style filename pattern matcher. Only does ? * and **, and +** multiple patterns separated by |. Returns 1 or 0. +*/ +int match( const char* pattern, const char* string ); + +#endif /* _MATCH_H_ */ diff --git a/mime_encodings.txt b/mime_encodings.txt new file mode 100644 index 0000000..2d3952d --- /dev/null +++ b/mime_encodings.txt @@ -0,0 +1,8 @@ +# mime_encodings.txt +# +# A list of file extensions followed by the corresponding MIME encoding. +# Extensions not found in the table proceed to the mime_types table. + +Z compress +gz gzip +uu x-uuencode diff --git a/mime_types.txt b/mime_types.txt new file mode 100644 index 0000000..d4725d9 --- /dev/null +++ b/mime_types.txt @@ -0,0 +1,198 @@ +# mime_types.txt +# +# A list of file extensions followed by the corresponding MIME type. +# Extensions not found in the table are returned as text/plain. + +a application/octet-stream +aab application/x-authorware-bin +aam application/x-authorware-map +aas application/x-authorware-seg +ai application/postscript +aif audio/x-aiff +aifc audio/x-aiff +aiff audio/x-aiff +asc text/plain; charset=%s +asf video/x-ms-asf +asx video/x-ms-asf +au audio/basic +avi video/x-msvideo +bcpio application/x-bcpio +bin application/octet-stream +bmp image/bmp +cdf application/x-netcdf +class application/x-java-vm +cpio application/x-cpio +cpt application/mac-compactpro +crl application/x-pkcs7-crl +crt application/x-x509-ca-cert +csh application/x-csh +css text/css; charset=%s +dcr application/x-director +dir application/x-director +djv image/vnd.djvu +djvu image/vnd.djvu +dll application/octet-stream +dms application/octet-stream +doc application/msword +dtd text/xml; charset=%s +dump application/octet-stream +dvi application/x-dvi +dxr application/x-director +eps application/postscript +etx text/x-setext +exe application/octet-stream +ez application/andrew-inset +fgd application/x-director +fh image/x-freehand +fh4 image/x-freehand +fh5 image/x-freehand +fh7 image/x-freehand +fhc image/x-freehand +gif image/gif +gtar application/x-gtar +hdf application/x-hdf +hqx application/mac-binhex40 +htm text/html; charset=%s +html text/html; charset=%s +ice x-conference/x-cooltalk +ief image/ief +iges model/iges +igs model/iges +iv application/x-inventor +jar application/x-java-archive +jfif image/jpeg +jpe image/jpeg +jpeg image/jpeg +jpg image/jpeg +js application/x-javascript +kar audio/midi +kml application/vnd.google-earth.kml+xml +kmz application/vnd.google-earth.kmz +latex application/x-latex +lha application/octet-stream +loc application/xml-loc +lzh application/octet-stream +m3u audio/x-mpegurl +man application/x-troff-man +mathml application/mathml+xml +me application/x-troff-me +mesh model/mesh +mid audio/midi +midi audio/midi +mif application/vnd.mif +mime message/rfc822 +mml application/mathml+xml +mov video/quicktime +movie video/x-sgi-movie +mp2 audio/mpeg +mp3 audio/mpeg +mp4 video/mp4 +mpe video/mpeg +mpeg video/mpeg +mpg video/mpeg +mpga audio/mpeg +ms application/x-troff-ms +msh model/mesh +mv video/x-sgi-movie +mxu video/vnd.mpegurl +nc application/x-netcdf +o application/octet-stream +oda application/oda +ogg application/ogg +pac application/x-ns-proxy-autoconfig +pbm image/x-portable-bitmap +pdb chemical/x-pdb +pdf application/pdf +pgm image/x-portable-graymap +pgn application/x-chess-pgn +png image/png +pnm image/x-portable-anymap +ppm image/x-portable-pixmap +ppt application/vnd.ms-powerpoint +ps application/postscript +qt video/quicktime +ra audio/x-realaudio +ram audio/x-pn-realaudio +ras image/x-cmu-raster +rdf application/rdf+xml +rgb image/x-rgb +rm audio/x-pn-realaudio +roff application/x-troff +rpm audio/x-pn-realaudio-plugin +rss application/rss+xml +rtf text/rtf; charset=%s +rtx text/richtext; charset=%s +sgm text/sgml; charset=%s +sgml text/sgml; charset=%s +sh application/x-sh +shar application/x-shar +silo model/mesh +sit application/x-stuffit +skd application/x-koan +skm application/x-koan +skp application/x-koan +skt application/x-koan +smi application/smil +smil application/smil +snd audio/basic +so application/octet-stream +spl application/x-futuresplash +src application/x-wais-source +stc application/vnd.sun.xml.calc.template +std application/vnd.sun.xml.draw.template +sti application/vnd.sun.xml.impress.template +stw application/vnd.sun.xml.writer.template +sv4cpio application/x-sv4cpio +sv4crc application/x-sv4crc +svg image/svg+xml +svgz image/svg+xml +swf application/x-shockwave-flash +sxc application/vnd.sun.xml.calc +sxd application/vnd.sun.xml.draw +sxg application/vnd.sun.xml.writer.global +sxi application/vnd.sun.xml.impress +sxm application/vnd.sun.xml.math +sxw application/vnd.sun.xml.writer +t application/x-troff +tar application/x-tar +tcl application/x-tcl +tex application/x-tex +texi application/x-texinfo +texinfo application/x-texinfo +tif image/tiff +tiff image/tiff +tr application/x-troff +tsp application/dsptype +tsv text/tab-separated-values; charset=%s +txt text/plain; charset=%s +ustar application/x-ustar +vcd application/x-cdlink +vrml model/vrml +vx video/x-rad-screenplay +wav audio/x-wav +wax audio/x-ms-wax +wbmp image/vnd.wap.wbmp +wbxml application/vnd.wap.wbxml +wm video/x-ms-wm +wma audio/x-ms-wma +wmd application/x-ms-wmd +wml text/vnd.wap.wml +wmlc application/vnd.wap.wmlc +wmls text/vnd.wap.wmlscript +wmlsc application/vnd.wap.wmlscriptc +wmv video/x-ms-wmv +wmx video/x-ms-wmx +wmz application/x-ms-wmz +wrl model/vrml +wsrc application/x-wais-source +wvx video/x-ms-wvx +xbm image/x-xbitmap +xht application/xhtml+xml; charset=%s +xhtml application/xhtml+xml; charset=%s +xls application/vnd.ms-excel +xml text/xml; charset=%s +xpm image/x-xpixmap +xsl text/xml; charset=%s +xwd image/x-xwindowdump +xyz chemical/x-xyz +zip application/zip diff --git a/mmc.c b/mmc.c new file mode 100644 index 0000000..93d3baa --- /dev/null +++ b/mmc.c @@ -0,0 +1,531 @@ +/* mmc.c - mmap cache +** +** Copyright © 1998,2001,2014 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_MMAP +#include +#endif /* HAVE_MMAP */ + +#include "mmc.h" +#include "libhttpd.h" + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + + +/* Defines. */ +#ifndef DEFAULT_EXPIRE_AGE +#define DEFAULT_EXPIRE_AGE 600 +#endif +#ifndef DESIRED_FREE_COUNT +#define DESIRED_FREE_COUNT 100 +#endif +#ifndef DESIRED_MAX_MAPPED_FILES +#define DESIRED_MAX_MAPPED_FILES 2000 +#endif +#ifndef DESIRED_MAX_MAPPED_BYTES +#define DESIRED_MAX_MAPPED_BYTES 1000000000 +#endif +#ifndef INITIAL_HASH_SIZE +#define INITIAL_HASH_SIZE (1 << 10) +#endif + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + + +/* The Map struct. */ +typedef struct MapStruct { + ino_t ino; + dev_t dev; + off_t size; + time_t ct; + int refcount; + time_t reftime; + void* addr; + unsigned int hash; + int hash_idx; + struct MapStruct* next; + } Map; + + +/* Globals. */ +static Map* maps = (Map*) 0; +static Map* free_maps = (Map*) 0; +static int alloc_count = 0, map_count = 0, free_count = 0; +static Map** hash_table = (Map**) 0; +static int hash_size; +static unsigned int hash_mask; +static time_t expire_age = DEFAULT_EXPIRE_AGE; +static off_t mapped_bytes = 0; + + + +/* Forwards. */ +static void panic( void ); +static void really_unmap( Map** mm ); +static int check_hash_size( void ); +static int add_hash( Map* m ); +static Map* find_hash( ino_t ino, dev_t dev, off_t size, time_t ct ); +static unsigned int hash( ino_t ino, dev_t dev, off_t size, time_t ct ); + + +void* +mmc_map( char* filename, struct stat* sbP, struct timeval* nowP ) + { + time_t now; + struct stat sb; + Map* m; + int fd; + + /* Stat the file, if necessary. */ + if ( sbP != (struct stat*) 0 ) + sb = *sbP; + else + { + if ( stat( filename, &sb ) != 0 ) + { + syslog( LOG_ERR, "stat - %m" ); + return (void*) 0; + } + } + + /* Get the current time, if necessary. */ + if ( nowP != (struct timeval*) 0 ) + now = nowP->tv_sec; + else + now = time( (time_t*) 0 ); + + /* See if we have it mapped already, via the hash table. */ + if ( check_hash_size() < 0 ) + { + syslog( LOG_ERR, "check_hash_size() failure" ); + return (void*) 0; + } + m = find_hash( sb.st_ino, sb.st_dev, sb.st_size, sb.st_ctime ); + if ( m != (Map*) 0 ) + { + /* Yep. Just return the existing map */ + ++m->refcount; + m->reftime = now; + return m->addr; + } + + /* Open the file. */ + fd = open( filename, O_RDONLY ); + if ( fd < 0 ) + { + syslog( LOG_ERR, "open - %m" ); + return (void*) 0; + } + + /* Find a free Map entry or make a new one. */ + if ( free_maps != (Map*) 0 ) + { + m = free_maps; + free_maps = m->next; + --free_count; + } + else + { + m = (Map*) malloc( sizeof(Map) ); + if ( m == (Map*) 0 ) + { + (void) close( fd ); + syslog( LOG_ERR, "out of memory allocating a Map" ); + return (void*) 0; + } + ++alloc_count; + } + + /* Fill in the Map entry. */ + m->ino = sb.st_ino; + m->dev = sb.st_dev; + m->size = sb.st_size; + m->ct = sb.st_ctime; + m->refcount = 1; + m->reftime = now; + + /* Avoid doing anything for zero-length files; some systems don't like + ** to mmap them, other systems dislike mallocing zero bytes. + */ + if ( m->size == 0 ) + m->addr = (void*) 1; /* arbitrary non-NULL address */ + else + { + size_t size_size = (size_t) m->size; /* loses on files >2GB */ +#ifdef HAVE_MMAP + /* Map the file into memory. */ + m->addr = mmap( 0, size_size, PROT_READ, MAP_PRIVATE, fd, 0 ); + if ( m->addr == (void*) -1 && errno == ENOMEM ) + { + /* Ooo, out of address space. Free all unreferenced maps + ** and try again. + */ + panic(); + m->addr = mmap( 0, size_size, PROT_READ, MAP_PRIVATE, fd, 0 ); + } + if ( m->addr == (void*) -1 ) + { + syslog( LOG_ERR, "mmap - %m" ); + (void) close( fd ); + free( (void*) m ); + --alloc_count; + return (void*) 0; + } +#else /* HAVE_MMAP */ + /* Read the file into memory. */ + m->addr = (void*) malloc( size_size ); + if ( m->addr == (void*) 0 ) + { + /* Ooo, out of memory. Free all unreferenced maps + ** and try again. + */ + panic(); + m->addr = (void*) malloc( size_size ); + } + if ( m->addr == (void*) 0 ) + { + syslog( LOG_ERR, "out of memory storing a file" ); + (void) close( fd ); + free( (void*) m ); + --alloc_count; + return (void*) 0; + } + if ( httpd_read_fully( fd, m->addr, size_size ) != size_size ) + { + syslog( LOG_ERR, "read - %m" ); + (void) close( fd ); + free( (void*) m ); + --alloc_count; + return (void*) 0; + } +#endif /* HAVE_MMAP */ + } + (void) close( fd ); + + /* Put the Map into the hash table. */ + if ( add_hash( m ) < 0 ) + { + syslog( LOG_ERR, "add_hash() failure" ); + free( (void*) m ); + --alloc_count; + return (void*) 0; + } + + /* Put the Map on the active list. */ + m->next = maps; + maps = m; + ++map_count; + + /* Update the total byte count. */ + mapped_bytes += m->size; + + /* And return the address. */ + return m->addr; + } + + +void +mmc_unmap( void* addr, struct stat* sbP, struct timeval* nowP ) + { + Map* m = (Map*) 0; + + /* Find the Map entry for this address. First try a hash. */ + if ( sbP != (struct stat*) 0 ) + { + m = find_hash( sbP->st_ino, sbP->st_dev, sbP->st_size, sbP->st_ctime ); + if ( m != (Map*) 0 && m->addr != addr ) + m = (Map*) 0; + } + /* If that didn't work, try a full search. */ + if ( m == (Map*) 0 ) + for ( m = maps; m != (Map*) 0; m = m->next ) + if ( m->addr == addr ) + break; + if ( m == (Map*) 0 ) + syslog( LOG_ERR, "mmc_unmap failed to find entry!" ); + else if ( m->refcount <= 0 ) + syslog( LOG_ERR, "mmc_unmap found zero or negative refcount!" ); + else + { + --m->refcount; + if ( nowP != (struct timeval*) 0 ) + m->reftime = nowP->tv_sec; + else + m->reftime = time( (time_t*) 0 ); + } + } + + +void +mmc_cleanup( struct timeval* nowP ) + { + time_t now; + Map** mm; + Map* m; + + /* Get the current time, if necessary. */ + if ( nowP != (struct timeval*) 0 ) + now = nowP->tv_sec; + else + now = time( (time_t*) 0 ); + + /* Really unmap any unreferenced entries older than the age limit. */ + for ( mm = &maps; *mm != (Map*) 0; ) + { + m = *mm; + if ( m->refcount == 0 && now - m->reftime >= expire_age ) + really_unmap( mm ); + else + mm = &(*mm)->next; + } + + /* Adjust the age limit if there are too many bytes mapped, or + ** too many or too few files mapped. + */ + if ( mapped_bytes > DESIRED_MAX_MAPPED_BYTES ) + expire_age = MAX( ( expire_age * 2 ) / 3, DEFAULT_EXPIRE_AGE / 10 ); + else if ( map_count > DESIRED_MAX_MAPPED_FILES ) + expire_age = MAX( ( expire_age * 2 ) / 3, DEFAULT_EXPIRE_AGE / 10 ); + else if ( map_count < DESIRED_MAX_MAPPED_FILES / 2 ) + expire_age = MIN( ( expire_age * 5 ) / 4, DEFAULT_EXPIRE_AGE * 3 ); + + /* Really free excess blocks on the free list. */ + while ( free_count > DESIRED_FREE_COUNT ) + { + m = free_maps; + free_maps = m->next; + --free_count; + free( (void*) m ); + --alloc_count; + } + } + + +static void +panic( void ) + { + Map** mm; + Map* m; + + syslog( LOG_ERR, "mmc panic - freeing all unreferenced maps" ); + + /* Really unmap all unreferenced entries. */ + for ( mm = &maps; *mm != (Map*) 0; ) + { + m = *mm; + if ( m->refcount == 0 ) + really_unmap( mm ); + else + mm = &(*mm)->next; + } + } + + +static void +really_unmap( Map** mm ) + { + Map* m; + + m = *mm; + if ( m->size != 0 ) + { +#ifdef HAVE_MMAP + if ( munmap( m->addr, m->size ) < 0 ) + syslog( LOG_ERR, "munmap - %m" ); +#else /* HAVE_MMAP */ + free( (void*) m->addr ); +#endif /* HAVE_MMAP */ + } + /* Update the total byte count. */ + mapped_bytes -= m->size; + /* And move the Map to the free list. */ + *mm = m->next; + --map_count; + m->next = free_maps; + free_maps = m; + ++free_count; + /* This will sometimes break hash chains, but that's harmless; the + ** unmapping code that searches the hash table knows to keep searching. + */ + hash_table[m->hash_idx] = (Map*) 0; + } + + +void +mmc_term( void ) + { + Map* m; + + while ( maps != (Map*) 0 ) + really_unmap( &maps ); + while ( free_maps != (Map*) 0 ) + { + m = free_maps; + free_maps = m->next; + --free_count; + free( (void*) m ); + --alloc_count; + } + } + + +/* Make sure the hash table is big enough. */ +static int +check_hash_size( void ) + { + int i; + Map* m; + + /* Are we just starting out? */ + if ( hash_table == (Map**) 0 ) + { + hash_size = INITIAL_HASH_SIZE; + hash_mask = hash_size - 1; + } + /* Is it at least three times bigger than the number of entries? */ + else if ( hash_size >= map_count * 3 ) + return 0; + else + { + /* No, got to expand. */ + free( (void*) hash_table ); + /* Double the hash size until it's big enough. */ + do + { + hash_size = hash_size << 1; + } + while ( hash_size < map_count * 6 ); + hash_mask = hash_size - 1; + } + /* Make the new table. */ + hash_table = (Map**) malloc( hash_size * sizeof(Map*) ); + if ( hash_table == (Map**) 0 ) + return -1; + /* Clear it. */ + for ( i = 0; i < hash_size; ++i ) + hash_table[i] = (Map*) 0; + /* And rehash all entries. */ + for ( m = maps; m != (Map*) 0; m = m->next ) + if ( add_hash( m ) < 0 ) + return -1; + return 0; + } + + +static int +add_hash( Map* m ) + { + unsigned int h, he, i; + + h = hash( m->ino, m->dev, m->size, m->ct ); + he = ( h + hash_size - 1 ) & hash_mask; + for ( i = h; ; i = ( i + 1 ) & hash_mask ) + { + if ( hash_table[i] == (Map*) 0 ) + { + hash_table[i] = m; + m->hash = h; + m->hash_idx = i; + return 0; + } + if ( i == he ) + break; + } + return -1; + } + + +static Map* +find_hash( ino_t ino, dev_t dev, off_t size, time_t ct ) + { + unsigned int h, he, i; + Map* m; + + h = hash( ino, dev, size, ct ); + he = ( h + hash_size - 1 ) & hash_mask; + for ( i = h; ; i = ( i + 1 ) & hash_mask ) + { + m = hash_table[i]; + if ( m == (Map*) 0 ) + break; + if ( m->hash == h && m->ino == ino && m->dev == dev && + m->size == size && m->ct == ct ) + return m; + if ( i == he ) + break; + } + return (Map*) 0; + } + + +static unsigned int +hash( ino_t ino, dev_t dev, off_t size, time_t ct ) + { + unsigned int h = 177573; + + h ^= ino; + h += h << 5; + h ^= dev; + h += h << 5; + h ^= size; + h += h << 5; + h ^= ct; + + return h & hash_mask; + } + + +/* Generate debugging statistics syslog message. */ +void +mmc_logstats( long secs ) + { + syslog( + LOG_NOTICE, " map cache - %d allocated, %d active (%lld bytes), %d free; hash size: %d; expire age: %lld", + alloc_count, map_count, (long long) mapped_bytes, free_count, hash_size, + (long long) expire_age ); + if ( map_count + free_count != alloc_count ) + syslog( LOG_ERR, "map counts don't add up!" ); + } diff --git a/mmc.h b/mmc.h new file mode 100644 index 0000000..7ca0448 --- /dev/null +++ b/mmc.h @@ -0,0 +1,55 @@ +/* mmc.h - header file for mmap cache package +** +** Copyright © 1998,2014 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _MMC_H_ +#define _MMC_H_ + +/* Returns an mmap()ed area for the given file, or (void*) 0 on errors. +** If you have a stat buffer on the file, pass it in, otherwise pass 0. +** Same for the current time. +*/ +void* mmc_map( char* filename, struct stat* sbP, struct timeval* nowP ); + +/* Done with an mmap()ed area that was returned by mmc_map(). +** If you have a stat buffer on the file, pass it in, otherwise pass 0. +** Same for the current time. +*/ +void mmc_unmap( void* addr, struct stat* sbP, struct timeval* nowP ); + +/* Clean up the mmc package, freeing any unused storage. +** This should be called periodically, say every five minutes. +** If you have the current time, pass it in, otherwise pass 0. +*/ +void mmc_cleanup( struct timeval* nowP ); + +/* Free all storage, usually in preparation for exitting. */ +void mmc_term( void ); + +/* Generate debugging statistics syslog message. */ +void mmc_logstats( long secs ); + +#endif /* _MMC_H_ */ diff --git a/scripts/500.thttpd-rotate b/scripts/500.thttpd-rotate new file mode 100755 index 0000000..a172b83 --- /dev/null +++ b/scripts/500.thttpd-rotate @@ -0,0 +1,19 @@ +#!/bin/sh +# +# thttpd-rotate - nightly script to rotate thttpd's log files on FreeBSD +# +# This goes in /etc/periodic/daily. It rotates the log files and then +# tells thttpd to re-open its log file. + +cd /usr/local/www/chroot/logs +rm -f thttpd_log.7.gz +mv thttpd_log.6.gz thttpd_log.7.gz +mv thttpd_log.5.gz thttpd_log.6.gz +mv thttpd_log.4.gz thttpd_log.5.gz +mv thttpd_log.3.gz thttpd_log.4.gz +mv thttpd_log.2.gz thttpd_log.3.gz +mv thttpd_log.1.gz thttpd_log.2.gz +mv thttpd_log thttpd_log.1 +kill -HUP `cat /var/run/thttpd.pid` +sleep 1 +gzip -f thttpd_log.1 diff --git a/scripts/thttpd.sh b/scripts/thttpd.sh new file mode 100755 index 0000000..22564ba --- /dev/null +++ b/scripts/thttpd.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# thttpd.sh - startup script for thttpd on FreeBSD +# +# This should be manually installed as: +# /usr/local/etc/rc.d/thttpd +# It gets run at boot-time. +# +# Variables available: +# thttpd_enable='YES' +# thttpd_program='/usr/local/sbin/thttpd' +# thttpd_pidfile='/var/run/thttpd.pid' +# thttpd_devfs=... +# thttpd_flags=... +# +# PROVIDE: thttpd +# REQUIRE: LOGIN FILESYSTEMS +# KEYWORD: shutdown + +. /etc/rc.subr + +name='thttpd' +rcvar='thttpd_enable' +start_precmd='thttpd_precmd' +stop_cmd='thttpd_stop' +thttpd_enable_defval='NO' + +load_rc_config "$name" +command="${thttpd_program:-/usr/local/sbin/${name}}" +pidfile="${thttpd_pidfile:-/var/run/${name}.pid}" +command_args="-i ${pidfile}" + +thttpd_precmd () +{ + if [ -n "$thttpd_devfs" ] ; then + mount -t devfs devfs "$thttpd_devfs" + devfs -m "$thttpd_devfs" rule -s 1 applyset + devfs -m "$thttpd_devfs" rule -s 2 applyset + fi +} + +thttpd_stop () +{ + kill -USR1 `cat "$pidfile"` +} + +run_rc_command "$1" diff --git a/scripts/thttpd_wrapper b/scripts/thttpd_wrapper new file mode 100755 index 0000000..631c34d --- /dev/null +++ b/scripts/thttpd_wrapper @@ -0,0 +1,23 @@ +#!/bin/sh +# +# thttpd_wrapper - wrapper script for thttpd on FreeBSD +# +# This goes in /usr/local/sbin. It backgrounds itself, and then runs +# thttpd in a loop. If thttpd exits then the script restarts it automatically. +# +# The -D flag tells thttpd to *not* put itself into the background, +# and the -C flag tells it to get the rest of its configuration from +# the specified config file. + +( + while true ; do + /usr/local/sbin/thttpd -D -C /usr/local/www/thttpd_config + if [ -f /var/run/nologin ] ; then + exit + fi + sleep 10 + egrep ' thttpd[:\[]' /var/log/messages | + tail -33 | + mail -s "thttpd on `hostname` restarted" root + done +) & diff --git a/strerror.c b/strerror.c new file mode 100644 index 0000000..6a161e6 --- /dev/null +++ b/strerror.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strerror.c 5.1 (Berkeley) 4/9/89"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +char * +strerror(errnum) + int errnum; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + static char ebuf[20]; + + if ((unsigned int)errnum < sys_nerr) + return(sys_errlist[errnum]); + (void)sprintf(ebuf, "Unknown error: %d", errnum); + return(ebuf); +} diff --git a/tdate_parse.c b/tdate_parse.c new file mode 100644 index 0000000..a7ea7fa --- /dev/null +++ b/tdate_parse.c @@ -0,0 +1,328 @@ +/* tdate_parse - parse string dates into internal form, stripped-down version +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +/* This is a stripped-down version of date_parse.c, available at +** http://www.acme.com/software/date_parse/ +*/ + +#include + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include +#include + +#include "tdate_parse.h" + + +struct strlong { + char* s; + long l; + }; + + +static void +pound_case( char* str ) + { + for ( ; *str != '\0'; ++str ) + { + if ( isupper( (int) *str ) ) + *str = tolower( (int) *str ); + } + } + + +static int +strlong_compare( const void* v1, const void* v2 ) + { + const struct strlong* s1 = (const struct strlong*) v1; + const struct strlong* s2 = (const struct strlong*) v2; + return strcmp( s1->s, s2->s ); + } + + +static int +strlong_search( char* str, struct strlong* tab, int n, long* lP ) + { + int i, h, l, r; + + l = 0; + h = n - 1; + for (;;) + { + i = ( h + l ) / 2; + r = strcmp( str, tab[i].s ); + if ( r < 0 ) + h = i - 1; + else if ( r > 0 ) + l = i + 1; + else + { + *lP = tab[i].l; + return 1; + } + if ( h < l ) + return 0; + } + } + + +static int +scan_wday( char* str_wday, long* tm_wdayP ) + { + static struct strlong wday_tab[] = { + { "sun", 0 }, { "sunday", 0 }, + { "mon", 1 }, { "monday", 1 }, + { "tue", 2 }, { "tuesday", 2 }, + { "wed", 3 }, { "wednesday", 3 }, + { "thu", 4 }, { "thursday", 4 }, + { "fri", 5 }, { "friday", 5 }, + { "sat", 6 }, { "saturday", 6 }, + }; + static int sorted = 0; + + if ( ! sorted ) + { + (void) qsort( + wday_tab, sizeof(wday_tab)/sizeof(struct strlong), + sizeof(struct strlong), strlong_compare ); + sorted = 1; + } + pound_case( str_wday ); + return strlong_search( + str_wday, wday_tab, sizeof(wday_tab)/sizeof(struct strlong), tm_wdayP ); + } + + +static int +scan_mon( char* str_mon, long* tm_monP ) + { + static struct strlong mon_tab[] = { + { "jan", 0 }, { "january", 0 }, + { "feb", 1 }, { "february", 1 }, + { "mar", 2 }, { "march", 2 }, + { "apr", 3 }, { "april", 3 }, + { "may", 4 }, + { "jun", 5 }, { "june", 5 }, + { "jul", 6 }, { "july", 6 }, + { "aug", 7 }, { "august", 7 }, + { "sep", 8 }, { "september", 8 }, + { "oct", 9 }, { "october", 9 }, + { "nov", 10 }, { "november", 10 }, + { "dec", 11 }, { "december", 11 }, + }; + static int sorted = 0; + + if ( ! sorted ) + { + (void) qsort( + mon_tab, sizeof(mon_tab)/sizeof(struct strlong), + sizeof(struct strlong), strlong_compare ); + sorted = 1; + } + pound_case( str_mon ); + return strlong_search( + str_mon, mon_tab, sizeof(mon_tab)/sizeof(struct strlong), tm_monP ); + } + + +static int +is_leap( int year ) + { + return year % 400? ( year % 100 ? ( year % 4 ? 0 : 1 ) : 0 ) : 1; + } + + +/* Basically the same as mktime(). */ +static time_t +tm_to_time( struct tm* tmP ) + { + time_t t; + static int monthtab[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + /* Years since epoch, converted to days. */ + t = ( tmP->tm_year - 70 ) * 365; + /* Leap days for previous years - this will break in 2100! */ + t += ( tmP->tm_year - 69 ) / 4; + /* Days for the beginning of this month. */ + t += monthtab[tmP->tm_mon]; + /* Leap day for this year. */ + if ( tmP->tm_mon >= 2 && is_leap( tmP->tm_year + 1900 ) ) + ++t; + /* Days since the beginning of this month. */ + t += tmP->tm_mday - 1; /* 1-based field */ + /* Hours, minutes, and seconds. */ + t = t * 24 + tmP->tm_hour; + t = t * 60 + tmP->tm_min; + t = t * 60 + tmP->tm_sec; + + return t; + } + + +time_t +tdate_parse( char* str ) + { + struct tm tm; + char* cp; + char str_mon[500], str_wday[500]; + int tm_sec, tm_min, tm_hour, tm_mday, tm_year; + long tm_mon, tm_wday; + time_t t; + + /* Initialize. */ + (void) memset( (char*) &tm, 0, sizeof(struct tm) ); + + /* Skip initial whitespace ourselves - sscanf is clumsy at this. */ + for ( cp = str; *cp == ' ' || *cp == '\t'; ++cp ) + continue; + + /* And do the sscanfs. WARNING: you can add more formats here, + ** but be careful! You can easily screw up the parsing of existing + ** formats when you add new ones. The order is important. + */ + + /* DD-mth-YY HH:MM:SS GMT */ + if ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* DD mth YY HH:MM:SS GMT */ + else if ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* HH:MM:SS GMT DD-mth-YY */ + else if ( sscanf( cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year ) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* HH:MM:SS GMT DD mth YY */ + else if ( sscanf( cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year ) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* wdy, DD-mth-YY HH:MM:SS GMT */ + else if ( sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 7 && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy, DD mth YY HH:MM:SS GMT */ + else if ( sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 7 && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy mth DD HH:MM:SS GMT YY */ + else if ( sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + &tm_year ) == 7 && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_year = tm_year; + } + else + return (time_t) -1; + + if ( tm.tm_year > 1900 ) + tm.tm_year -= 1900; + else if ( tm.tm_year < 70 ) + tm.tm_year += 100; + + t = tm_to_time( &tm ); + + return t; + } diff --git a/tdate_parse.h b/tdate_parse.h new file mode 100644 index 0000000..aaf0b66 --- /dev/null +++ b/tdate_parse.h @@ -0,0 +1,33 @@ +/* tdate_parse.h - parse string dates into internal form, stripped-down version +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _TDATE_PARSE_H_ +#define _TDATE_PARSE_H_ + +time_t tdate_parse( char* str ); + +#endif /* _TDATE_PARSE_H_ */ diff --git a/thttpd.8 b/thttpd.8 new file mode 100644 index 0000000..040f74c --- /dev/null +++ b/thttpd.8 @@ -0,0 +1,596 @@ +.TH thttpd 8 "29 February 2000" +.SH NAME +thttpd - tiny/turbo/throttling HTTP server +.SH SYNOPSIS +.B thttpd +.RB [ -C +.IR configfile ] +.RB [ -p +.IR port ] +.RB [ -d +.IR dir ] +.RB [ -dd +.IR data_dir ] +.RB [ -r | -nor ] +.RB [ -s | -nos ] +.RB [ -v | -nov ] +.RB [ -g | -nog ] +.RB [ -u +.IR user ] +.RB [ -c +.IR cgipat ] +.RB [ -t +.IR throttles ] +.RB [ -h +.IR host ] +.RB [ -l +.IR logfile ] +.RB [ -i +.IR pidfile ] +.RB [ -T +.IR charset ] +.RB [ -P +.IR P3P ] +.RB [ -M +.IR maxage ] +.RB [ -V ] +.RB [ -D ] +.SH DESCRIPTION +.PP +.I thttpd +is a simple, small, fast, and secure HTTP server. +It doesn't have a lot of special features, but it suffices for most uses of +the web, it's about as fast as the best full-featured servers (Apache, NCSA, +Netscape), +and it has one extremely useful feature (URL-traffic-based throttling) +that no other server currently has. +.SH OPTIONS +.TP +.B -C +Specifies a config-file to read. +All options can be set either by command-line flags or in the config file. +See below for details. +.TP +.B -p +Specifies an alternate port number to listen on. +The default is 80. +The config-file option name for this flag is "port", +and the config.h option is DEFAULT_PORT. +.TP +.B -d +Specifies a directory to chdir() to at startup. +This is merely a convenience - you could just as easily +do a cd in the shell script that invokes the program. +The config-file option name for this flag is "dir", +and the config.h options are WEBDIR, USE_USER_DIR. +.TP +.B -r +Do a chroot() at initialization time, restricting file access +to the program's current directory. +If -r is the compiled-in default, then -nor disables it. +See below for details. +The config-file option names for this flag are "chroot" and "nochroot", +and the config.h option is ALWAYS_CHROOT. +.TP +.B -dd +Specifies a directory to chdir() to after chrooting. +If you're not chrooting, you might as well do a single chdir() with +the -d flag. +If you are chrooting, this lets you put the web files in a subdirectory +of the chroot tree, instead of in the top level mixed in with the +chroot files. +The config-file option name for this flag is "data_dir". +.TP +.B -nos +Don't do explicit symbolic link checking. +Normally, thttpd explicitly expands any symbolic links in filenames, +to check that the resulting path stays within the original document tree. +If you want to turn off this check and save some CPU time, you can use +the -nos flag, however this is not recommended. +Note, though, that if you are using the chroot option, the symlink +checking is unnecessary and is turned off, so the safe way to save +those CPU cycles is to use chroot. +The config-file option names for this flag are "symlinkcheck" and "nosymlinkcheck". +.TP +.B -v +Do el-cheapo virtual hosting. +If -v is the compiled-in default, then -nov disables it. +See below for details. +The config-file option names for this flag are "vhost" and "novhost", +and the config.h option is ALWAYS_VHOST. +.TP +.B -g +Use a global passwd file. +This means that every file in the entire document tree is protected by +the single .htpasswd file at the top of the tree. +Otherwise the semantics of the .htpasswd file are the same. +If this option is set but there is no .htpasswd file in +the top-level directory, then thttpd proceeds as if the option was +not set - first looking for a local .htpasswd file, and if that doesn't +exist either then serving the file without any password. +If -g is the compiled-in default, then -nog disables it. +The config-file option names for this flag are "globalpasswd" and +"noglobalpasswd", +and the config.h option is ALWAYS_GLOBAL_PASSWD. +.TP +.B -u +Specifies what user to switch to after initialization when started as root. +The default is "nobody". +The config-file option name for this flag is "user", +and the config.h option is DEFAULT_USER. +.TP +.B -c +Specifies a wildcard pattern for CGI programs, for instance "**.cgi" +or "/cgi-bin/*". +See below for details. +The config-file option name for this flag is "cgipat", +and the config.h option is CGI_PATTERN. +.TP +.B -t +Specifies a file of throttle settings. +See below for details. +The config-file option name for this flag is "throttles". +.TP +.B -h +Specifies a hostname to bind to, for multihoming. +The default is to bind to all hostnames supported on the local machine. +See below for details. +The config-file option name for this flag is "host", +and the config.h option is SERVER_NAME. +.TP +.B -l +Specifies a file for logging. +If no -l argument is specified, thttpd logs via syslog(). +If "-l /dev/null" is specified, thttpd doesn't log at all. +The config-file option name for this flag is "logfile". +.TP +.B -i +Specifies a file to write the process-id to. +If no file is specified, no process-id is written. +You can use this file to send signals to thttpd. +See below for details. +The config-file option name for this flag is "pidfile". +.TP +.B -T +Specifies the character set to use with text MIME types. +The default is UTF-8. +The config-file option name for this flag is "charset", +and the config.h option is DEFAULT_CHARSET. +.TP +.B -P +Specifies a P3P server privacy header to be returned with all responses. +See http://www.w3.org/P3P/ for details. +Thttpd doesn't do anything at all with the string except put it in the +P3P: response header. +The config-file option name for this flag is "p3p". +.TP +.B -M +Specifies the number of seconds to be used in a "Cache-Control: max-age" +header to be returned with all responses. +An equivalent "Expires" header is also generated. +The default is no Cache-Control or Expires headers, +which is just fine for most sites. +The config-file option name for this flag is "max_age". +.TP +.B -V +Shows the current version info. +.TP +.B -D +This was originally just a debugging flag, however it's worth mentioning +because one of the things it does is prevent thttpd from making itself +a background daemon. +Instead it runs in the foreground like a regular program. +This is necessary when you want to run thttpd wrapped in a little shell +script that restarts it if it exits. +.SH "CONFIG-FILE" +.PP +All the command-line options can also be set in a config file. +One advantage of using a config file is that the file can be changed, +and thttpd will pick up the changes with a restart. +.PP +The syntax of the config file is simple, a series of "option" or +"option=value" separated by whitespace. +The option names are listed above with their corresponding command-line flags. +.SH "CHROOT" +.PP +chroot() is a system call that restricts the program's view +of the filesystem to the current directory and directories +below it. +It becomes impossible for remote users to access any file +outside of the initial directory. +The restriction is inherited by child processes, so CGI programs get it too. +This is a very strong security measure, and is recommended. +The only downside is that only root can call chroot(), so this means +the program must be started as root. +However, the last thing it does during initialization is to +give up root access by becoming another user, so this is safe. +.PP +The program can also be compile-time configured to always +do a chroot(), without needing the -r flag. +.PP +Note that with some other web servers, such as NCSA httpd, setting +up a directory tree for use with chroot() is complicated, involving +creating a bunch of special directories and copying in various files. +With thttpd it's a lot easier, all you have to do is make sure +any shells, utilities, and config files used by your CGI programs and +scripts are available. +If you have CGI disabled, or if you make a policy that all CGI programs +must be written in a compiled language such as C and statically linked, +then you probably don't have to do any setup at all. +.PP +However, one thing you should do is tell syslogd about the chroot tree, +so that thttpd can still generate syslog messages. +Check your system's syslodg man page for how to do this. +In FreeBSD you would put something like this in /etc/rc.conf: +.nf + syslogd_flags="-l /usr/local/www/data/dev/log" +.fi +Substitute in your own chroot tree's pathname, of course. +Don't worry about creating the log socket, syslogd wants to do that itself. +(You may need to create the dev directory.) +In Linux the flag is -a instead of -l, and there may be other differences. +.PP +Relevant config.h option: ALWAYS_CHROOT. +.SH "CGI" +.PP +thttpd supports the CGI 1.1 spec. +.PP +In order for a CGI program to be run, its name must match the pattern +specified either at compile time or on the command line with the -c flag. +This is a simple shell-style filename pattern. +You can use * to match any string not including a slash, +or ** to match any string including slashes, +or ? to match any single character. +You can also use multiple such patterns separated by |. +The patterns get checked against the filename +part of the incoming URL. +Don't forget to quote any wildcard characters so that the shell doesn't +mess with them. +.PP +Restricting CGI programs to a single directory lets the site administrator +review them for security holes, and is strongly recommended. +If there are individual users that you trust, you can enable their +directories too. +.PP +If no CGI pattern is specified, neither here nor at compile time, +then CGI programs cannot be run at all. +If you want to disable CGI as a security measure, that's how you do it, just +comment out the patterns in the config file and don't run with the -c flag. +.PP +Note: the current working directory when a CGI program gets run is +the directory that the CGI program lives in. +This isn't in the CGI 1.1 spec, but it's what most other HTTP servers do. +.PP +Relevant config.h options: CGI_PATTERN, CGI_TIMELIMIT, CGI_NICE, CGI_PATH, CGI_LD_LIBRARY_PATH, CGIBINDIR. +.SH "BASIC AUTHENTICATION" +.PP +Basic Authentication is available as an option at compile time. +If enabled, it uses a password file in the directory to be protected, +called .htpasswd by default. +This file is formatted as the familiar colon-separated +username/encrypted-password pair, records delimited by newlines. +The protection does not carry over to subdirectories. +The utility program htpasswd(1) is included to help create and +modify .htpasswd files. +.PP +Relevant config.h option: AUTH_FILE +.SH "THROTTLING" +.PP +The throttle file lets you set maximum byte rates on URLs or URL groups. +You can optionally set a minimum rate too. +The format of the throttle file is very simple. +A # starts a comment, and the rest of the line is ignored. +Blank lines are ignored. +The rest of the lines should consist of a pattern, whitespace, and a number. +The pattern is a simple shell-style filename pattern, using ?/**/*, or +multiple such patterns separated by |. +.PP +The numbers in the file are byte rates, specified in units of bytes per second. +For comparison, a v.90 modem gives about 5000 B/s depending on compression, +a double-B-channel ISDN line about 12800 B/s, and a T1 line is about +150000 B/s. +If you want to set a minimum rate as well, use number-number. +.PP +Example: +.nf + # throttle file for www.acme.com + + ** 2000-100000 # limit total web usage to 2/3 of our T1, + # but never go below 2000 B/s + **.jpg|**.gif 50000 # limit images to 1/3 of our T1 + **.mpg 20000 # and movies to even less + jef/** 20000 # jef's pages are too popular +.fi +.PP +Throttling is implemented by checking each incoming URL filename against all +of the patterns in the throttle file. +The server accumulates statistics on how much bandwidth each pattern +has accounted for recently (via a rolling average). +If a URL matches a pattern that has been exceeding its specified limit, +then the data returned is actually slowed down, with +pauses between each block. +If that's not possible (e.g. for CGI programs) or if the bandwidth has gotten +way larger than the limit, then the server returns a special code +saying 'try again later'. +.PP +The minimum rates are implemented similarly. +If too many people are trying to fetch something at the same time, +throttling may slow down each connection so much that it's not really +useable. +Furthermore, all those slow connections clog up the server, using +up file handles and connection slots. +Setting a minimum rate says that past a certain point you should not +even bother - the server returns the 'try again later" code and the +connection isn't even started. +.PP +There is no provision for setting a maximum connections/second throttle, +because throttling a request uses as much cpu as handling it, so +there would be no point. +There is also no provision for throttling the number of simultaneous +connections on a per-URL basis. +However you can control the overall number of connections for the whole +server very simply, by setting the operating system's per-process file +descriptor limit before starting thttpd. +Be sure to set the hard limit, not the soft limit. +.SH "MULTIHOMING" +.PP +Multihoming means using one machine to serve multiple hostnames. +For instance, if you're an internet provider and you want to let +all of your customers have customized web addresses, you might +have www.joe.acme.com, www.jane.acme.com, and your own www.acme.com, +all running on the same physical hardware. +This feature is also known as "virtual hosts". +There are three steps to setting this up. +.PP +One, make DNS entries for all of the hostnames. +The current way to do this, allowed by HTTP/1.1, is to use CNAME aliases, +like so: +.nf + www.acme.com IN A 192.100.66.1 + www.joe.acme.com IN CNAME www.acme.com + www.jane.acme.com IN CNAME www.acme.com +.fi +However, this is incompatible with older HTTP/1.0 browsers. +If you want to stay compatible, there's a different way - use A records +instead, each with a different IP address, like so: +.nf + www.acme.com IN A 192.100.66.1 + www.joe.acme.com IN A 192.100.66.200 + www.jane.acme.com IN A 192.100.66.201 +.fi +This is bad because it uses extra IP addresses, a somewhat scarce resource. +But if you want people with older browsers to be able to visit your +sites, you still have to do it this way. +.PP +Step two. +If you're using the modern CNAME method of multihoming, then you can +skip this step. +Otherwise, using the older multiple-IP-address method you +must set up IP aliases or multiple interfaces for the extra addresses. +You can use ifconfig(8)'s alias command to tell the machine to answer to +all of the different IP addresses. +Example: +.nf + ifconfig le0 www.acme.com + ifconfig le0 www.joe.acme.com alias + ifconfig le0 www.jane.acme.com alias +.fi +If your OS's version of ifconfig doesn't have an alias command, you're +probably out of luck (but see http://www.acme.com/software/thttpd/notes.html). +.PP +Third and last, you must set up thttpd to handle the multiple hosts. +The easiest way is with the -v flag, or the ALWAYS_VHOST config.h option. +This works with either CNAME multihosting or multiple-IP multihosting. +What it does is send each incoming request to a subdirectory based on the +hostname it's intended for. +All you have to do in order to set things up is to create those subdirectories +in the directory where thttpd will run. +With the example above, you'd do like so: +.nf + mkdir www.acme.com www.joe.acme.com www.jane.acme.com +.fi +If you're using old-style multiple-IP multihosting, you should also create +symbolic links from the numeric addresses to the names, like so: +.nf + ln -s www.acme.com 192.100.66.1 + ln -s www.joe.acme.com 192.100.66.200 + ln -s www.jane.acme.com 192.100.66.201 +.fi +This lets the older HTTP/1.0 browsers find the right subdirectory. +.PP +There's an optional alternate step three if you're using multiple-IP +multihosting: run a separate thttpd process for each hostname, using +the -h flag to specify which one is which. +This gives you more flexibility, since you can run each of these processes +in separate directories, with different throttle files, etc. +Example: +.nf + thttpd -r -d /usr/www -h www.acme.com + thttpd -r -d /usr/www/joe -u joe -h www.joe.acme.com + thttpd -r -d /usr/www/jane -u jane -h www.jane.acme.com +.fi +But remember, this multiple-process method does not work with CNAME +multihosting - for that, you must use a single thttpd process with +the -v flag. +.SH "CUSTOM ERRORS" +.PP +thttpd lets you define your own custom error pages for the various +HTTP errors. +There's a separate file for each error number, all stored in one +special directory. +The directory name is "errors", at the top of the web directory tree. +The error files should be named "errNNN.html", where NNN is the error number. +So for example, to make a custom error page for the authentication failure +error, which is number 401, you would put your HTML into the file +"errors/err401.html". +If no custom error file is found for a given error number, then the +usual built-in error page is generated. +.PP +If you're using the virtual hosts option, you can also have different +custom error pages for each different virtual host. +In this case you put another "errors" directory in the top of that +virtual host's web tree. +thttpd will look first in the virtual host errors directory, and +then in the server-wide errors directory, and if neither of those +has an appropriate error file then it will generate the built-in error. +.SH "NON-LOCAL REFERRERS" +.PP +Sometimes another site on the net will embed your image files in their +HTML files, which basically means they're stealing your bandwidth. +You can prevent them from doing this by using non-local referrer filtering. +With this option, certain files can only be fetched via a local referrer. +The files have to be referenced by a local web page. +If a web page on some other site references the files, that fetch will +be blocked. +There are three config-file variables for this feature: +.TP +.B urlpat +A wildcard pattern for the URLs that should require a local referrer. +This is typically just image files, sound files, and so on. +For example: +.nf + urlpat=**.jpg|**.gif|**.au|**.wav +.fi +For most sites, that one setting is all you need to enable referrer filtering. +.TP +.B noemptyreferrers +By default, requests with no referrer at all, or a null referrer, or a +referrer with no apparent hostname, are allowed. +With this variable set, such requests are disallowed. +.TP +.B localpat +A wildcard pattern that specifies the local host or hosts. +This is used to determine if the host in the referrer is local or not. +If not specified it defaults to the actual local hostname. +.SH SYMLINKS +.PP +thttpd is very picky about symbolic links. +Before delivering any file, it first checks each element in the path +to see if it's a symbolic link, and expands them all out to get the final +actual filename. +Along the way it checks for things like links with ".." that go above +the server's directory, and absolute symlinks (ones that start with a /). +These are prohibited as security holes, so the server returns an +error page for them. +This means you can't set up your web directory with a bunch of symlinks +pointing to individual users' home web directories. +Instead you do it the other way around - the user web directories are +real subdirs of the main web directory, and in each user's home +dir there's a symlink pointing to their actual web dir. +.PP +The CGI pattern is also affected - it gets matched against the fully-expanded +filename. So, if you have a single CGI directory but then put a symbolic +link in it pointing somewhere else, that won't work. The CGI program will be +treated as a regular file and returned to the client, instead of getting run. +This could be confusing. +.SH PERMISSIONS +.PP +thttpd is also picky about file permissions. +It wants data files (HTML, images) to be world readable. +Readable by the group that the thttpd process runs as is not enough - thttpd +checks explicitly for the world-readable bit. +This is so that no one ever gets surprised by a file that's not set +world-readable and yet somehow is readable by the HTTP server and +therefore the *whole* world. +.PP +The same logic applies to directories. +As with the standard Unix "ls" program, thttpd will only let you +look at the contents of a directory if its read bit is on; but +as with data files, this must be the world-read bit, not just the +group-read bit. +.PP +thttpd also wants the execute bit to be *off* for data files. +A file that is marked executable but doesn't match the CGI pattern +might be a script or program that got accidentally left in the +wrong directory. +Allowing people to fetch the contents of the file might be a security breach, +so this is prohibited. +Of course if an executable file *does* match the CGI pattern, then it +just gets run as a CGI. +.PP +In summary, data files should be mode 644 (rw-r--r--), +directories should be 755 (rwxr-xr-x) if you want to allow indexing and +711 (rwx--x--x) to disallow it, and CGI programs should be mode +755 (rwxr-xr-x) or 711 (rwx--x--x). +.SH LOGS +.PP +thttpd does all of its logging via syslog(3). +The facility it uses is configurable. +Aside from error messages, there are only a few log entry types of interest, +all fairly similar to CERN Common Log Format: +.nf + Aug 6 15:40:34 acme thttpd[583]: 165.113.207.103 - - "GET /file" 200 357 + Aug 6 15:40:43 acme thttpd[583]: 165.113.207.103 - - "HEAD /file" 200 0 + Aug 6 15:41:16 acme thttpd[583]: referrer http://www.acme.com/ -> /dir + Aug 6 15:41:16 acme thttpd[583]: user-agent Mozilla/1.1N +.fi +The package includes a script for translating these log entries info +CERN-compatible files. +Note that thttpd does not translate numeric IP addresses into domain names. +This is both to save time and as a minor security measure (the numeric +address is harder to spoof). +.PP +Relevant config.h option: LOG_FACILITY. +.PP +If you'd rather log directly to a file, you can use the -l command-line +flag. But note that error messages still go to syslog. +.SH SIGNALS +.PP +thttpd handles a couple of signals, which you can send via the +standard Unix kill(1) command: +.TP +.B INT,TERM +These signals tell thttpd to shut down immediately. +Any requests in progress get aborted. +.TP +.B USR1 +This signal tells thttpd to shut down as soon as it's done servicing +all current requests. +In addition, the network socket it uses to accept new connections gets +closed immediately, which means a fresh thttpd can be started up +immediately. +.TP +.B USR2 +This signal tells thttpd to generate the statistics syslog messages +immediately, instead of waiting for the regular hourly update. +.TP +.B HUP +This signal tells thttpd to close and re-open its (non-syslog) log file, +for instance if you rotated the logs and want it to start using the +new one. +This is a little tricky to set up correctly, for instance if you are using +chroot() then the log file must be within the chroot tree, but it's +definitely doable. +.SH "SEE ALSO" +redirect(8), ssi(8), makeweb(1), htpasswd(1), syslogtocern(8), weblog_parse(1), http_get(1) +.SH THANKS +.PP +Many thanks to contributors, reviewers, testers: +John LoVerso, Jordan Hayes, Chris Torek, Jim Thompson, Barton Schaffer, +Geoff Adams, Dan Kegel, John Hascall, Bennett Todd, KIKUCHI Takahiro, +Catalin Ionescu. +Special thanks to Craig Leres for substantial debugging and development, +and for not complaining about my coding style very much. +.SH AUTHOR +Copyright © 1995,1998,1999,2000 by Jef Poskanzer . +All rights reserved. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. diff --git a/thttpd.c b/thttpd.c new file mode 100644 index 0000000..3be7546 --- /dev/null +++ b/thttpd.c @@ -0,0 +1,2181 @@ +/* thttpd.c - tiny/turbo/throttling HTTP server +** +** Copyright © 1995,1998,1999,2000,2001,2015 by +** Jef Poskanzer . All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include "config.h" +#include "version.h" + +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#ifdef HAVE_GRP_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef TIME_WITH_SYS_TIME +#include +#endif +#include + +#include "fdwatch.h" +#include "libhttpd.h" +#include "mmc.h" +#include "timers.h" +#include "match.h" + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + + +static char* argv0; +static int debug; +static unsigned short port; +static char* dir; +static char* data_dir; +static int do_chroot, no_log, no_symlink_check, do_vhost, do_global_passwd; +static char* cgi_pattern; +static int cgi_limit; +static char* url_pattern; +static int no_empty_referrers; +static char* local_pattern; +static char* logfile; +static char* throttlefile; +static char* hostname; +static char* pidfile; +static char* user; +static char* charset; +static char* p3p; +static int max_age; + + +typedef struct { + char* pattern; + long max_limit, min_limit; + long rate; + off_t bytes_since_avg; + int num_sending; + } throttletab; +static throttletab* throttles; +static int numthrottles, maxthrottles; + +#define THROTTLE_NOLIMIT -1 + + +typedef struct { + int conn_state; + int next_free_connect; + httpd_conn* hc; + int tnums[MAXTHROTTLENUMS]; /* throttle indexes */ + int numtnums; + long max_limit, min_limit; + time_t started_at, active_at; + Timer* wakeup_timer; + Timer* linger_timer; + long wouldblock_delay; + off_t bytes; + off_t end_byte_index; + off_t next_byte_index; + } connecttab; +static connecttab* connects; +static int num_connects, max_connects, first_free_connect; +static int httpd_conn_count; + +/* The connection states. */ +#define CNST_FREE 0 +#define CNST_READING 1 +#define CNST_SENDING 2 +#define CNST_PAUSING 3 +#define CNST_LINGERING 4 + + +static httpd_server* hs = (httpd_server*) 0; +int terminate = 0; +time_t start_time, stats_time; +long stats_connections; +off_t stats_bytes; +int stats_simultaneous; + +static volatile int got_hup, got_usr1, watchdog_flag; + + +/* Forwards. */ +static void parse_args( int argc, char** argv ); +static void usage( void ); +static void read_config( char* filename ); +static void value_required( char* name, char* value ); +static void no_value_required( char* name, char* value ); +static char* e_strdup( char* oldstr ); +static void lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P ); +static void read_throttlefile( char* tf ); +static void shut_down( void ); +static int handle_newconnect( struct timeval* tvP, int listen_fd ); +static void handle_read( connecttab* c, struct timeval* tvP ); +static void handle_send( connecttab* c, struct timeval* tvP ); +static void handle_linger( connecttab* c, struct timeval* tvP ); +static int check_throttles( connecttab* c ); +static void clear_throttles( connecttab* c, struct timeval* tvP ); +static void update_throttles( ClientData client_data, struct timeval* nowP ); +static void finish_connection( connecttab* c, struct timeval* tvP ); +static void clear_connection( connecttab* c, struct timeval* tvP ); +static void really_clear_connection( connecttab* c, struct timeval* tvP ); +static void idle( ClientData client_data, struct timeval* nowP ); +static void wakeup_connection( ClientData client_data, struct timeval* nowP ); +static void linger_clear_connection( ClientData client_data, struct timeval* nowP ); +static void occasional( ClientData client_data, struct timeval* nowP ); +#ifdef STATS_TIME +static void show_stats( ClientData client_data, struct timeval* nowP ); +#endif /* STATS_TIME */ +static void logstats( struct timeval* nowP ); +static void thttpd_logstats( long secs ); + + +/* SIGTERM and SIGINT say to exit immediately. */ +static void +handle_term( int sig ) + { + /* Don't need to set up the handler again, since it's a one-shot. */ + + shut_down(); + syslog( LOG_NOTICE, "exiting due to signal %d", sig ); + closelog(); + exit( 1 ); + } + + +/* SIGCHLD - a chile process exitted, so we need to reap the zombie */ +static void +handle_chld( int sig ) + { + const int oerrno = errno; + pid_t pid; + int status; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal( SIGCHLD, handle_chld ); +#endif /* ! HAVE_SIGSET */ + + /* Reap defunct children until there aren't any more. */ + for (;;) + { +#ifdef HAVE_WAITPID + pid = waitpid( (pid_t) -1, &status, WNOHANG ); +#else /* HAVE_WAITPID */ + pid = wait3( &status, WNOHANG, (struct rusage*) 0 ); +#endif /* HAVE_WAITPID */ + if ( (int) pid == 0 ) /* none left */ + break; + if ( (int) pid < 0 ) + { + if ( errno == EINTR || errno == EAGAIN ) + continue; + /* ECHILD shouldn't happen with the WNOHANG option, + ** but with some kernels it does anyway. Ignore it. + */ + if ( errno != ECHILD ) + syslog( LOG_ERR, "child wait - %m" ); + break; + } + /* Decrement the CGI count. Note that this is not accurate, since + ** each CGI can involve two or even three child processes. + ** Decrementing for each child means that when there is heavy CGI + ** activity, the count will be lower than it should be, and therefore + ** more CGIs will be allowed than should be. + */ + if ( hs != (httpd_server*) 0 ) + { + --hs->cgi_count; + if ( hs->cgi_count < 0 ) + hs->cgi_count = 0; + } + } + + /* Restore previous errno. */ + errno = oerrno; + } + + +/* SIGHUP says to re-open the log file. */ +static void +handle_hup( int sig ) + { + const int oerrno = errno; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal( SIGHUP, handle_hup ); +#endif /* ! HAVE_SIGSET */ + + /* Just set a flag that we got the signal. */ + got_hup = 1; + + /* Restore previous errno. */ + errno = oerrno; + } + + +/* SIGUSR1 says to exit as soon as all current connections are done. */ +static void +handle_usr1( int sig ) + { + /* Don't need to set up the handler again, since it's a one-shot. */ + + if ( num_connects == 0 ) + { + /* If there are no active connections we want to exit immediately + ** here. Not only is it faster, but without any connections the + ** main loop won't wake up until the next new connection. + */ + shut_down(); + syslog( LOG_NOTICE, "exiting" ); + closelog(); + exit( 0 ); + } + + /* Otherwise, just set a flag that we got the signal. */ + got_usr1 = 1; + + /* Don't need to restore old errno, since we didn't do any syscalls. */ + } + + +/* SIGUSR2 says to generate the stats syslogs immediately. */ +static void +handle_usr2( int sig ) + { + const int oerrno = errno; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal( SIGUSR2, handle_usr2 ); +#endif /* ! HAVE_SIGSET */ + + logstats( (struct timeval*) 0 ); + + /* Restore previous errno. */ + errno = oerrno; + } + + +/* SIGALRM is used as a watchdog. */ +static void +handle_alrm( int sig ) + { + const int oerrno = errno; + + /* If nothing has been happening */ + if ( ! watchdog_flag ) + { + /* Try changing dirs to someplace we can write. */ + (void) chdir( "/tmp" ); + /* Dump core. */ + abort(); + } + watchdog_flag = 0; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal( SIGALRM, handle_alrm ); +#endif /* ! HAVE_SIGSET */ + /* Set up alarm again. */ + (void) alarm( OCCASIONAL_TIME * 3 ); + + /* Restore previous errno. */ + errno = oerrno; + } + + +static void +re_open_logfile( void ) + { + FILE* logfp; + + if ( no_log || hs == (httpd_server*) 0 ) + return; + + /* Re-open the log file. */ + if ( logfile != (char*) 0 && strcmp( logfile, "-" ) != 0 ) + { + syslog( LOG_NOTICE, "re-opening logfile" ); + logfp = fopen( logfile, "a" ); + if ( logfp == (FILE*) 0 ) + { + syslog( LOG_CRIT, "re-opening %.80s - %m", logfile ); + return; + } + (void) fcntl( fileno( logfp ), F_SETFD, 1 ); + httpd_set_logfp( hs, logfp ); + } + } + + +int +main( int argc, char** argv ) + { + char* cp; + struct passwd* pwd; + uid_t uid = 32767; + gid_t gid = 32767; + char cwd[MAXPATHLEN+1]; + FILE* logfp; + int num_ready; + int cnum; + connecttab* c; + httpd_conn* hc; + httpd_sockaddr sa4; + httpd_sockaddr sa6; + int gotv4, gotv6; + struct timeval tv; + + argv0 = argv[0]; + + cp = strrchr( argv0, '/' ); + if ( cp != (char*) 0 ) + ++cp; + else + cp = argv0; + openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY ); + + /* Handle command-line arguments. */ + parse_args( argc, argv ); + + /* Read zone info now, in case we chroot(). */ + tzset(); + + /* Look up hostname now, in case we chroot(). */ + lookup_hostname( &sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6 ); + if ( ! ( gotv4 || gotv6 ) ) + { + syslog( LOG_ERR, "can't find any valid address" ); + (void) fprintf( stderr, "%s: can't find any valid address\n", argv0 ); + exit( 1 ); + } + + /* Throttle file. */ + numthrottles = 0; + maxthrottles = 0; + throttles = (throttletab*) 0; + if ( throttlefile != (char*) 0 ) + read_throttlefile( throttlefile ); + + /* If we're root and we're going to become another user, get the uid/gid + ** now. + */ + if ( getuid() == 0 ) + { + pwd = getpwnam( user ); + if ( pwd == (struct passwd*) 0 ) + { + syslog( LOG_CRIT, "unknown user - '%.80s'", user ); + (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user ); + exit( 1 ); + } + uid = pwd->pw_uid; + gid = pwd->pw_gid; + } + + /* Log file. */ + if ( logfile != (char*) 0 ) + { + if ( strcmp( logfile, "/dev/null" ) == 0 ) + { + no_log = 1; + logfp = (FILE*) 0; + } + else if ( strcmp( logfile, "-" ) == 0 ) + logfp = stdout; + else + { + logfp = fopen( logfile, "a" ); + if ( logfp == (FILE*) 0 ) + { + syslog( LOG_CRIT, "%.80s - %m", logfile ); + perror( logfile ); + exit( 1 ); + } + if ( logfile[0] != '/' ) + { + syslog( LOG_WARNING, "logfile is not an absolute path, you may not be able to re-open it" ); + (void) fprintf( stderr, "%s: logfile is not an absolute path, you may not be able to re-open it\n", argv0 ); + } + (void) fcntl( fileno( logfp ), F_SETFD, 1 ); + if ( getuid() == 0 ) + { + /* If we are root then we chown the log file to the user we'll + ** be switching to. + */ + if ( fchown( fileno( logfp ), uid, gid ) < 0 ) + { + syslog( LOG_WARNING, "fchown logfile - %m" ); + perror( "fchown logfile" ); + } + } + } + } + else + logfp = (FILE*) 0; + + /* Switch directories if requested. */ + if ( dir != (char*) 0 ) + { + if ( chdir( dir ) < 0 ) + { + syslog( LOG_CRIT, "chdir - %m" ); + perror( "chdir" ); + exit( 1 ); + } + } +#ifdef USE_USER_DIR + else if ( getuid() == 0 ) + { + /* No explicit directory was specified, we're root, and the + ** USE_USER_DIR option is set - switch to the specified user's + ** home dir. + */ + if ( chdir( pwd->pw_dir ) < 0 ) + { + syslog( LOG_CRIT, "chdir - %m" ); + perror( "chdir" ); + exit( 1 ); + } + } +#endif /* USE_USER_DIR */ + + /* Get current directory. */ + (void) getcwd( cwd, sizeof(cwd) - 1 ); + if ( cwd[strlen( cwd ) - 1] != '/' ) + (void) strcat( cwd, "/" ); + + if ( ! debug ) + { + /* We're not going to use stdin stdout or stderr from here on, so close + ** them to save file descriptors. + */ + (void) fclose( stdin ); + if ( logfp != stdout ) + (void) fclose( stdout ); + (void) fclose( stderr ); + + /* Daemonize - make ourselves a subprocess. */ +#ifdef HAVE_DAEMON + if ( daemon( 1, 1 ) < 0 ) + { + syslog( LOG_CRIT, "daemon - %m" ); + exit( 1 ); + } +#else /* HAVE_DAEMON */ + switch ( fork() ) + { + case 0: + break; + case -1: + syslog( LOG_CRIT, "fork - %m" ); + exit( 1 ); + default: + exit( 0 ); + } +#ifdef HAVE_SETSID + (void) setsid(); +#endif /* HAVE_SETSID */ +#endif /* HAVE_DAEMON */ + } + else + { + /* Even if we don't daemonize, we still want to disown our parent + ** process. + */ +#ifdef HAVE_SETSID + (void) setsid(); +#endif /* HAVE_SETSID */ + } + + if ( pidfile != (char*) 0 ) + { + /* Write the PID file. */ + FILE* pidfp = fopen( pidfile, "w" ); + if ( pidfp == (FILE*) 0 ) + { + syslog( LOG_CRIT, "%.80s - %m", pidfile ); + exit( 1 ); + } + (void) fprintf( pidfp, "%d\n", (int) getpid() ); + (void) fclose( pidfp ); + } + + /* Initialize the fdwatch package. Have to do this before chroot, + ** if /dev/poll is used. + */ + max_connects = fdwatch_get_nfiles(); + if ( max_connects < 0 ) + { + syslog( LOG_CRIT, "fdwatch initialization failure" ); + exit( 1 ); + } + max_connects -= SPARE_FDS; + + /* Chroot if requested. */ + if ( do_chroot ) + { + if ( chroot( cwd ) < 0 ) + { + syslog( LOG_CRIT, "chroot - %m" ); + perror( "chroot" ); + exit( 1 ); + } + /* If we're logging and the logfile's pathname begins with the + ** chroot tree's pathname, then elide the chroot pathname so + ** that the logfile pathname still works from inside the chroot + ** tree. + */ + if ( logfile != (char*) 0 && strcmp( logfile, "-" ) != 0 ) + { + if ( strncmp( logfile, cwd, strlen( cwd ) ) == 0 ) + { + (void) ol_strcpy( logfile, &logfile[strlen( cwd ) - 1] ); + /* (We already guaranteed that cwd ends with a slash, so leaving + ** that slash in logfile makes it an absolute pathname within + ** the chroot tree.) + */ + } + else + { + syslog( LOG_WARNING, "logfile is not within the chroot tree, you will not be able to re-open it" ); + (void) fprintf( stderr, "%s: logfile is not within the chroot tree, you will not be able to re-open it\n", argv0 ); + } + } + (void) strcpy( cwd, "/" ); + /* Always chdir to / after a chroot. */ + if ( chdir( cwd ) < 0 ) + { + syslog( LOG_CRIT, "chroot chdir - %m" ); + perror( "chroot chdir" ); + exit( 1 ); + } + } + + /* Switch directories again if requested. */ + if ( data_dir != (char*) 0 ) + { + if ( chdir( data_dir ) < 0 ) + { + syslog( LOG_CRIT, "data_dir chdir - %m" ); + perror( "data_dir chdir" ); + exit( 1 ); + } + } + + /* Set up to catch signals. */ +#ifdef HAVE_SIGSET + (void) sigset( SIGTERM, handle_term ); + (void) sigset( SIGINT, handle_term ); + (void) sigset( SIGCHLD, handle_chld ); + (void) sigset( SIGPIPE, SIG_IGN ); /* get EPIPE instead */ + (void) sigset( SIGHUP, handle_hup ); + (void) sigset( SIGUSR1, handle_usr1 ); + (void) sigset( SIGUSR2, handle_usr2 ); + (void) sigset( SIGALRM, handle_alrm ); +#else /* HAVE_SIGSET */ + (void) signal( SIGTERM, handle_term ); + (void) signal( SIGINT, handle_term ); + (void) signal( SIGCHLD, handle_chld ); + (void) signal( SIGPIPE, SIG_IGN ); /* get EPIPE instead */ + (void) signal( SIGHUP, handle_hup ); + (void) signal( SIGUSR1, handle_usr1 ); + (void) signal( SIGUSR2, handle_usr2 ); + (void) signal( SIGALRM, handle_alrm ); +#endif /* HAVE_SIGSET */ + got_hup = 0; + got_usr1 = 0; + watchdog_flag = 0; + (void) alarm( OCCASIONAL_TIME * 3 ); + + /* Initialize the timer package. */ + tmr_init(); + + /* Initialize the HTTP layer. Got to do this before giving up root, + ** so that we can bind to a privileged port. + */ + hs = httpd_initialize( + hostname, + gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0, + port, cgi_pattern, cgi_limit, charset, p3p, max_age, cwd, no_log, logfp, + no_symlink_check, do_vhost, do_global_passwd, url_pattern, + local_pattern, no_empty_referrers ); + if ( hs == (httpd_server*) 0 ) + exit( 1 ); + + /* Set up the occasional timer. */ + if ( tmr_create( (struct timeval*) 0, occasional, JunkClientData, OCCASIONAL_TIME * 1000L, 1 ) == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(occasional) failed" ); + exit( 1 ); + } + /* Set up the idle timer. */ + if ( tmr_create( (struct timeval*) 0, idle, JunkClientData, 5 * 1000L, 1 ) == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(idle) failed" ); + exit( 1 ); + } + if ( numthrottles > 0 ) + { + /* Set up the throttles timer. */ + if ( tmr_create( (struct timeval*) 0, update_throttles, JunkClientData, THROTTLE_TIME * 1000L, 1 ) == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(update_throttles) failed" ); + exit( 1 ); + } + } +#ifdef STATS_TIME + /* Set up the stats timer. */ + if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(show_stats) failed" ); + exit( 1 ); + } +#endif /* STATS_TIME */ + start_time = stats_time = time( (time_t*) 0 ); + stats_connections = 0; + stats_bytes = 0; + stats_simultaneous = 0; + + /* If we're root, try to become someone else. */ + if ( getuid() == 0 ) + { + /* Set aux groups to null. */ + if ( setgroups( 0, (const gid_t*) 0 ) < 0 ) + { + syslog( LOG_CRIT, "setgroups - %m" ); + exit( 1 ); + } + /* Set primary group. */ + if ( setgid( gid ) < 0 ) + { + syslog( LOG_CRIT, "setgid - %m" ); + exit( 1 ); + } + /* Try setting aux groups correctly - not critical if this fails. */ + if ( initgroups( user, gid ) < 0 ) + syslog( LOG_WARNING, "initgroups - %m" ); +#ifdef HAVE_SETLOGIN + /* Set login name. */ + (void) setlogin( user ); +#endif /* HAVE_SETLOGIN */ + /* Set uid. */ + if ( setuid( uid ) < 0 ) + { + syslog( LOG_CRIT, "setuid - %m" ); + exit( 1 ); + } + /* Check for unnecessary security exposure. */ + if ( ! do_chroot ) + syslog( + LOG_WARNING, + "started as root without requesting chroot(), warning only" ); + } + + /* Initialize our connections table. */ + connects = NEW( connecttab, max_connects ); + if ( connects == (connecttab*) 0 ) + { + syslog( LOG_CRIT, "out of memory allocating a connecttab" ); + exit( 1 ); + } + for ( cnum = 0; cnum < max_connects; ++cnum ) + { + connects[cnum].conn_state = CNST_FREE; + connects[cnum].next_free_connect = cnum + 1; + connects[cnum].hc = (httpd_conn*) 0; + } + connects[max_connects - 1].next_free_connect = -1; /* end of link list */ + first_free_connect = 0; + num_connects = 0; + httpd_conn_count = 0; + + if ( hs != (httpd_server*) 0 ) + { + if ( hs->listen4_fd != -1 ) + fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ ); + if ( hs->listen6_fd != -1 ) + fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ ); + } + + /* Main loop. */ + (void) gettimeofday( &tv, (struct timezone*) 0 ); + while ( ( ! terminate ) || num_connects > 0 ) + { + /* Do we need to re-open the log file? */ + if ( got_hup ) + { + re_open_logfile(); + got_hup = 0; + } + + /* Do the fd watch. */ + num_ready = fdwatch( tmr_mstimeout( &tv ) ); + if ( num_ready < 0 ) + { + if ( errno == EINTR || errno == EAGAIN ) + continue; /* try again */ + syslog( LOG_ERR, "fdwatch - %m" ); + exit( 1 ); + } + (void) gettimeofday( &tv, (struct timezone*) 0 ); + + if ( num_ready == 0 ) + { + /* No fd's are ready - run the timers. */ + tmr_run( &tv ); + continue; + } + + /* Is it a new connection? */ + if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 && + fdwatch_check_fd( hs->listen6_fd ) ) + { + if ( handle_newconnect( &tv, hs->listen6_fd ) ) + /* Go around the loop and do another fdwatch, rather than + ** dropping through and processing existing connections. + ** New connections always get priority. + */ + continue; + } + if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 && + fdwatch_check_fd( hs->listen4_fd ) ) + { + if ( handle_newconnect( &tv, hs->listen4_fd ) ) + /* Go around the loop and do another fdwatch, rather than + ** dropping through and processing existing connections. + ** New connections always get priority. + */ + continue; + } + + /* Find the connections that need servicing. */ + while ( ( c = (connecttab*) fdwatch_get_next_client_data() ) != (connecttab*) -1 ) + { + if ( c == (connecttab*) 0 ) + continue; + hc = c->hc; + if ( ! fdwatch_check_fd( hc->conn_fd ) ) + /* Something went wrong. */ + clear_connection( c, &tv ); + else + switch ( c->conn_state ) + { + case CNST_READING: handle_read( c, &tv ); break; + case CNST_SENDING: handle_send( c, &tv ); break; + case CNST_LINGERING: handle_linger( c, &tv ); break; + } + } + tmr_run( &tv ); + + if ( got_usr1 && ! terminate ) + { + terminate = 1; + if ( hs != (httpd_server*) 0 ) + { + if ( hs->listen4_fd != -1 ) + fdwatch_del_fd( hs->listen4_fd ); + if ( hs->listen6_fd != -1 ) + fdwatch_del_fd( hs->listen6_fd ); + httpd_unlisten( hs ); + } + } + } + + /* The main loop terminated. */ + shut_down(); + syslog( LOG_NOTICE, "exiting" ); + closelog(); + exit( 0 ); + } + + +static void +parse_args( int argc, char** argv ) + { + int argn; + + debug = 0; + port = DEFAULT_PORT; + dir = (char*) 0; + data_dir = (char*) 0; +#ifdef ALWAYS_CHROOT + do_chroot = 1; +#else /* ALWAYS_CHROOT */ + do_chroot = 0; +#endif /* ALWAYS_CHROOT */ + no_log = 0; + no_symlink_check = do_chroot; +#ifdef ALWAYS_VHOST + do_vhost = 1; +#else /* ALWAYS_VHOST */ + do_vhost = 0; +#endif /* ALWAYS_VHOST */ +#ifdef ALWAYS_GLOBAL_PASSWD + do_global_passwd = 1; +#else /* ALWAYS_GLOBAL_PASSWD */ + do_global_passwd = 0; +#endif /* ALWAYS_GLOBAL_PASSWD */ +#ifdef CGI_PATTERN + cgi_pattern = CGI_PATTERN; +#else /* CGI_PATTERN */ + cgi_pattern = (char*) 0; +#endif /* CGI_PATTERN */ +#ifdef CGI_LIMIT + cgi_limit = CGI_LIMIT; +#else /* CGI_LIMIT */ + cgi_limit = 0; +#endif /* CGI_LIMIT */ + url_pattern = (char*) 0; + no_empty_referrers = 0; + local_pattern = (char*) 0; + throttlefile = (char*) 0; + hostname = (char*) 0; + logfile = (char*) 0; + pidfile = (char*) 0; + user = DEFAULT_USER; + charset = DEFAULT_CHARSET; + p3p = ""; + max_age = -1; + argn = 1; + while ( argn < argc && argv[argn][0] == '-' ) + { + if ( strcmp( argv[argn], "-V" ) == 0 ) + { + (void) printf( "%s\n", SERVER_SOFTWARE ); + exit( 0 ); + } + else if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc ) + { + ++argn; + read_config( argv[argn] ); + } + else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc ) + { + ++argn; + port = (unsigned short) atoi( argv[argn] ); + } + else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc ) + { + ++argn; + dir = argv[argn]; + } + else if ( strcmp( argv[argn], "-r" ) == 0 ) + { + do_chroot = 1; + no_symlink_check = 1; + } + else if ( strcmp( argv[argn], "-nor" ) == 0 ) + { + do_chroot = 0; + no_symlink_check = 0; + } + else if ( strcmp( argv[argn], "-dd" ) == 0 && argn + 1 < argc ) + { + ++argn; + data_dir = argv[argn]; + } + else if ( strcmp( argv[argn], "-s" ) == 0 ) + no_symlink_check = 0; + else if ( strcmp( argv[argn], "-nos" ) == 0 ) + no_symlink_check = 1; + else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc ) + { + ++argn; + user = argv[argn]; + } + else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc ) + { + ++argn; + cgi_pattern = argv[argn]; + } + else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc ) + { + ++argn; + throttlefile = argv[argn]; + } + else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc ) + { + ++argn; + hostname = argv[argn]; + } + else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc ) + { + ++argn; + logfile = argv[argn]; + } + else if ( strcmp( argv[argn], "-v" ) == 0 ) + do_vhost = 1; + else if ( strcmp( argv[argn], "-nov" ) == 0 ) + do_vhost = 0; + else if ( strcmp( argv[argn], "-g" ) == 0 ) + do_global_passwd = 1; + else if ( strcmp( argv[argn], "-nog" ) == 0 ) + do_global_passwd = 0; + else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc ) + { + ++argn; + pidfile = argv[argn]; + } + else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc ) + { + ++argn; + charset = argv[argn]; + } + else if ( strcmp( argv[argn], "-P" ) == 0 && argn + 1 < argc ) + { + ++argn; + p3p = argv[argn]; + } + else if ( strcmp( argv[argn], "-M" ) == 0 && argn + 1 < argc ) + { + ++argn; + max_age = atoi( argv[argn] ); + } + else if ( strcmp( argv[argn], "-D" ) == 0 ) + debug = 1; + else + usage(); + ++argn; + } + if ( argn != argc ) + usage(); + } + + +static void +usage( void ) + { + (void) fprintf( stderr, +"usage: %s [-C configfile] [-p port] [-d dir] [-r|-nor] [-dd data_dir] [-s|-nos] [-v|-nov] [-g|-nog] [-u user] [-c cgipat] [-t throttles] [-h host] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage] [-V] [-D]\n", + argv0 ); + exit( 1 ); + } + + +static void +read_config( char* filename ) + { + FILE* fp; + char line[10000]; + char* cp; + char* cp2; + char* name; + char* value; + + fp = fopen( filename, "r" ); + if ( fp == (FILE*) 0 ) + { + perror( filename ); + exit( 1 ); + } + + while ( fgets( line, sizeof(line), fp ) != (char*) 0 ) + { + /* Trim comments. */ + if ( ( cp = strchr( line, '#' ) ) != (char*) 0 ) + *cp = '\0'; + + /* Skip leading whitespace. */ + cp = line; + cp += strspn( cp, " \t\n\r" ); + + /* Split line into words. */ + while ( *cp != '\0' ) + { + /* Find next whitespace. */ + cp2 = cp + strcspn( cp, " \t\n\r" ); + /* Insert EOS and advance next-word pointer. */ + while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r' ) + *cp2++ = '\0'; + /* Split into name and value. */ + name = cp; + value = strchr( name, '=' ); + if ( value != (char*) 0 ) + *value++ = '\0'; + /* Interpret. */ + if ( strcasecmp( name, "debug" ) == 0 ) + { + no_value_required( name, value ); + debug = 1; + } + else if ( strcasecmp( name, "port" ) == 0 ) + { + value_required( name, value ); + port = (unsigned short) atoi( value ); + } + else if ( strcasecmp( name, "dir" ) == 0 ) + { + value_required( name, value ); + dir = e_strdup( value ); + } + else if ( strcasecmp( name, "chroot" ) == 0 ) + { + no_value_required( name, value ); + do_chroot = 1; + no_symlink_check = 1; + } + else if ( strcasecmp( name, "nochroot" ) == 0 ) + { + no_value_required( name, value ); + do_chroot = 0; + no_symlink_check = 0; + } + else if ( strcasecmp( name, "data_dir" ) == 0 ) + { + value_required( name, value ); + data_dir = e_strdup( value ); + } + else if ( strcasecmp( name, "nosymlinkcheck" ) == 0 ) + { + no_value_required( name, value ); + no_symlink_check = 1; + } + else if ( strcasecmp( name, "symlinkcheck" ) == 0 ) + { + no_value_required( name, value ); + no_symlink_check = 0; + } + else if ( strcasecmp( name, "user" ) == 0 ) + { + value_required( name, value ); + user = e_strdup( value ); + } + else if ( strcasecmp( name, "cgipat" ) == 0 ) + { + value_required( name, value ); + cgi_pattern = e_strdup( value ); + } + else if ( strcasecmp( name, "cgilimit" ) == 0 ) + { + value_required( name, value ); + cgi_limit = atoi( value ); + } + else if ( strcasecmp( name, "urlpat" ) == 0 ) + { + value_required( name, value ); + url_pattern = e_strdup( value ); + } + else if ( strcasecmp( name, "noemptyreferers" ) == 0 || + strcasecmp( name, "noemptyreferrers" ) == 0 ) + { + no_value_required( name, value ); + no_empty_referrers = 1; + } + else if ( strcasecmp( name, "localpat" ) == 0 ) + { + value_required( name, value ); + local_pattern = e_strdup( value ); + } + else if ( strcasecmp( name, "throttles" ) == 0 ) + { + value_required( name, value ); + throttlefile = e_strdup( value ); + } + else if ( strcasecmp( name, "host" ) == 0 ) + { + value_required( name, value ); + hostname = e_strdup( value ); + } + else if ( strcasecmp( name, "logfile" ) == 0 ) + { + value_required( name, value ); + logfile = e_strdup( value ); + } + else if ( strcasecmp( name, "vhost" ) == 0 ) + { + no_value_required( name, value ); + do_vhost = 1; + } + else if ( strcasecmp( name, "novhost" ) == 0 ) + { + no_value_required( name, value ); + do_vhost = 0; + } + else if ( strcasecmp( name, "globalpasswd" ) == 0 ) + { + no_value_required( name, value ); + do_global_passwd = 1; + } + else if ( strcasecmp( name, "noglobalpasswd" ) == 0 ) + { + no_value_required( name, value ); + do_global_passwd = 0; + } + else if ( strcasecmp( name, "pidfile" ) == 0 ) + { + value_required( name, value ); + pidfile = e_strdup( value ); + } + else if ( strcasecmp( name, "charset" ) == 0 ) + { + value_required( name, value ); + charset = e_strdup( value ); + } + else if ( strcasecmp( name, "p3p" ) == 0 ) + { + value_required( name, value ); + p3p = e_strdup( value ); + } + else if ( strcasecmp( name, "max_age" ) == 0 ) + { + value_required( name, value ); + max_age = atoi( value ); + } + else + { + (void) fprintf( + stderr, "%s: unknown config option '%s'\n", argv0, name ); + exit( 1 ); + } + + /* Advance to next word. */ + cp = cp2; + cp += strspn( cp, " \t\n\r" ); + } + } + + (void) fclose( fp ); + } + + +static void +value_required( char* name, char* value ) + { + if ( value == (char*) 0 ) + { + (void) fprintf( + stderr, "%s: value required for %s option\n", argv0, name ); + exit( 1 ); + } + } + + +static void +no_value_required( char* name, char* value ) + { + if ( value != (char*) 0 ) + { + (void) fprintf( + stderr, "%s: no value required for %s option\n", + argv0, name ); + exit( 1 ); + } + } + + +static char* +e_strdup( char* oldstr ) + { + char* newstr; + + newstr = strdup( oldstr ); + if ( newstr == (char*) 0 ) + { + syslog( LOG_CRIT, "out of memory copying a string" ); + (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 ); + exit( 1 ); + } + return newstr; + } + + +static void +lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P ) + { +#ifdef USE_IPV6 + + struct addrinfo hints; + char portstr[10]; + int gaierr; + struct addrinfo* ai; + struct addrinfo* ai2; + struct addrinfo* aiv6; + struct addrinfo* aiv4; + + (void) memset( &hints, 0, sizeof(hints) ); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + (void) snprintf( portstr, sizeof(portstr), "%d", (int) port ); + if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 ) + { + syslog( + LOG_CRIT, "getaddrinfo %.80s - %.80s", + hostname, gai_strerror( gaierr ) ); + (void) fprintf( + stderr, "%s: getaddrinfo %s - %s\n", + argv0, hostname, gai_strerror( gaierr ) ); + exit( 1 ); + } + + /* Find the first IPv6 and IPv4 entries. */ + aiv6 = (struct addrinfo*) 0; + aiv4 = (struct addrinfo*) 0; + for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next ) + { + switch ( ai2->ai_family ) + { + case AF_INET6: + if ( aiv6 == (struct addrinfo*) 0 ) + aiv6 = ai2; + break; + case AF_INET: + if ( aiv4 == (struct addrinfo*) 0 ) + aiv4 = ai2; + break; + } + } + + if ( aiv6 == (struct addrinfo*) 0 ) + *gotv6P = 0; + else + { + if ( sa6_len < aiv6->ai_addrlen ) + { + syslog( + LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", + hostname, (unsigned long) sa6_len, + (unsigned long) aiv6->ai_addrlen ); + exit( 1 ); + } + (void) memset( sa6P, 0, sa6_len ); + (void) memmove( sa6P, aiv6->ai_addr, aiv6->ai_addrlen ); + *gotv6P = 1; + } + + if ( aiv4 == (struct addrinfo*) 0 ) + *gotv4P = 0; + else + { + if ( sa4_len < aiv4->ai_addrlen ) + { + syslog( + LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", + hostname, (unsigned long) sa4_len, + (unsigned long) aiv4->ai_addrlen ); + exit( 1 ); + } + (void) memset( sa4P, 0, sa4_len ); + (void) memmove( sa4P, aiv4->ai_addr, aiv4->ai_addrlen ); + *gotv4P = 1; + } + + freeaddrinfo( ai ); + +#else /* USE_IPV6 */ + + struct hostent* he; + + *gotv6P = 0; + + (void) memset( sa4P, 0, sa4_len ); + sa4P->sa.sa_family = AF_INET; + if ( hostname == (char*) 0 ) + sa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY ); + else + { + sa4P->sa_in.sin_addr.s_addr = inet_addr( hostname ); + if ( (int) sa4P->sa_in.sin_addr.s_addr == -1 ) + { + he = gethostbyname( hostname ); + if ( he == (struct hostent*) 0 ) + { +#ifdef HAVE_HSTRERROR + syslog( + LOG_CRIT, "gethostbyname %.80s - %.80s", + hostname, hstrerror( h_errno ) ); + (void) fprintf( + stderr, "%s: gethostbyname %s - %s\n", + argv0, hostname, hstrerror( h_errno ) ); +#else /* HAVE_HSTRERROR */ + syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname ); + (void) fprintf( + stderr, "%s: gethostbyname %s failed\n", argv0, hostname ); +#endif /* HAVE_HSTRERROR */ + exit( 1 ); + } + if ( he->h_addrtype != AF_INET ) + { + syslog( LOG_CRIT, "%.80s - non-IP network address", hostname ); + (void) fprintf( + stderr, "%s: %s - non-IP network address\n", + argv0, hostname ); + exit( 1 ); + } + (void) memmove( + &sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length ); + } + } + sa4P->sa_in.sin_port = htons( port ); + *gotv4P = 1; + +#endif /* USE_IPV6 */ + } + + +static void +read_throttlefile( char* tf ) + { + FILE* fp; + char buf[5000]; + char* cp; + int len; + char pattern[5000]; + long max_limit, min_limit; + struct timeval tv; + + fp = fopen( tf, "r" ); + if ( fp == (FILE*) 0 ) + { + syslog( LOG_CRIT, "%.80s - %m", tf ); + perror( tf ); + exit( 1 ); + } + + (void) gettimeofday( &tv, (struct timezone*) 0 ); + + while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 ) + { + /* Nuke comments. */ + cp = strchr( buf, '#' ); + if ( cp != (char*) 0 ) + *cp = '\0'; + + /* Nuke trailing whitespace. */ + len = strlen( buf ); + while ( len > 0 && + ( buf[len-1] == ' ' || buf[len-1] == '\t' || + buf[len-1] == '\n' || buf[len-1] == '\r' ) ) + buf[--len] = '\0'; + + /* Ignore empty lines. */ + if ( len == 0 ) + continue; + + /* Parse line. */ + if ( sscanf( buf, " %4900[^ \t] %ld-%ld", pattern, &min_limit, &max_limit ) == 3 ) + {} + else if ( sscanf( buf, " %4900[^ \t] %ld", pattern, &max_limit ) == 2 ) + min_limit = 0; + else + { + syslog( LOG_CRIT, + "unparsable line in %.80s - %.80s", tf, buf ); + (void) fprintf( stderr, + "%s: unparsable line in %.80s - %.80s\n", + argv0, tf, buf ); + continue; + } + + /* Nuke any leading slashes in pattern. */ + if ( pattern[0] == '/' ) + (void) ol_strcpy( pattern, &pattern[1] ); + while ( ( cp = strstr( pattern, "|/" ) ) != (char*) 0 ) + (void) ol_strcpy( cp + 1, cp + 2 ); + + /* Check for room in throttles. */ + if ( numthrottles >= maxthrottles ) + { + if ( maxthrottles == 0 ) + { + maxthrottles = 100; /* arbitrary */ + throttles = NEW( throttletab, maxthrottles ); + } + else + { + maxthrottles *= 2; + throttles = RENEW( throttles, throttletab, maxthrottles ); + } + if ( throttles == (throttletab*) 0 ) + { + syslog( LOG_CRIT, "out of memory allocating a throttletab" ); + (void) fprintf( + stderr, "%s: out of memory allocating a throttletab\n", + argv0 ); + exit( 1 ); + } + } + + /* Add to table. */ + throttles[numthrottles].pattern = e_strdup( pattern ); + throttles[numthrottles].max_limit = max_limit; + throttles[numthrottles].min_limit = min_limit; + throttles[numthrottles].rate = 0; + throttles[numthrottles].bytes_since_avg = 0; + throttles[numthrottles].num_sending = 0; + + ++numthrottles; + } + (void) fclose( fp ); + } + + +static void +shut_down( void ) + { + int cnum; + struct timeval tv; + + (void) gettimeofday( &tv, (struct timezone*) 0 ); + logstats( &tv ); + for ( cnum = 0; cnum < max_connects; ++cnum ) + { + if ( connects[cnum].conn_state != CNST_FREE ) + httpd_close_conn( connects[cnum].hc, &tv ); + if ( connects[cnum].hc != (httpd_conn*) 0 ) + { + httpd_destroy_conn( connects[cnum].hc ); + free( (void*) connects[cnum].hc ); + --httpd_conn_count; + connects[cnum].hc = (httpd_conn*) 0; + } + } + if ( hs != (httpd_server*) 0 ) + { + httpd_server* ths = hs; + hs = (httpd_server*) 0; + if ( ths->listen4_fd != -1 ) + fdwatch_del_fd( ths->listen4_fd ); + if ( ths->listen6_fd != -1 ) + fdwatch_del_fd( ths->listen6_fd ); + httpd_terminate( ths ); + } + mmc_term(); + tmr_term(); + free( (void*) connects ); + if ( throttles != (throttletab*) 0 ) + free( (void*) throttles ); + } + + +static int +handle_newconnect( struct timeval* tvP, int listen_fd ) + { + connecttab* c; + ClientData client_data; + + /* This loops until the accept() fails, trying to start new + ** connections as fast as possible so we don't overrun the + ** listen queue. + */ + for (;;) + { + /* Is there room in the connection table? */ + if ( num_connects >= max_connects ) + { + /* Out of connection slots. Run the timers, then the + ** existing connections, and maybe we'll free up a slot + ** by the time we get back here. + */ + syslog( LOG_WARNING, "too many connections!" ); + tmr_run( tvP ); + return 0; + } + /* Get the first free connection entry off the free list. */ + if ( first_free_connect == -1 || connects[first_free_connect].conn_state != CNST_FREE ) + { + syslog( LOG_CRIT, "the connects free list is messed up" ); + exit( 1 ); + } + c = &connects[first_free_connect]; + /* Make the httpd_conn if necessary. */ + if ( c->hc == (httpd_conn*) 0 ) + { + c->hc = NEW( httpd_conn, 1 ); + if ( c->hc == (httpd_conn*) 0 ) + { + syslog( LOG_CRIT, "out of memory allocating an httpd_conn" ); + exit( 1 ); + } + c->hc->initialized = 0; + ++httpd_conn_count; + } + + /* Get the connection. */ + switch ( httpd_get_conn( hs, listen_fd, c->hc ) ) + { + /* Some error happened. Run the timers, then the + ** existing connections. Maybe the error will clear. + */ + case GC_FAIL: + tmr_run( tvP ); + return 0; + + /* No more connections to accept for now. */ + case GC_NO_MORE: + return 1; + } + c->conn_state = CNST_READING; + /* Pop it off the free list. */ + first_free_connect = c->next_free_connect; + c->next_free_connect = -1; + ++num_connects; + client_data.p = c; + c->active_at = tvP->tv_sec; + c->wakeup_timer = (Timer*) 0; + c->linger_timer = (Timer*) 0; + c->next_byte_index = 0; + c->numtnums = 0; + + /* Set the connection file descriptor to no-delay mode. */ + httpd_set_ndelay( c->hc->conn_fd ); + + fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ ); + + ++stats_connections; + if ( num_connects > stats_simultaneous ) + stats_simultaneous = num_connects; + } + } + + +static void +handle_read( connecttab* c, struct timeval* tvP ) + { + int sz; + ClientData client_data; + httpd_conn* hc = c->hc; + + /* Is there room in our buffer to read more bytes? */ + if ( hc->read_idx >= hc->read_size ) + { + if ( hc->read_size > 5000 ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + finish_connection( c, tvP ); + return; + } + httpd_realloc_str( + &hc->read_buf, &hc->read_size, hc->read_size + 1000 ); + } + + /* Read some more bytes. */ + sz = read( + hc->conn_fd, &(hc->read_buf[hc->read_idx]), + hc->read_size - hc->read_idx ); + if ( sz == 0 ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + finish_connection( c, tvP ); + return; + } + if ( sz < 0 ) + { + /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. At first glance + ** you would think that connections returned by fdwatch as readable + ** should never give an EWOULDBLOCK; however, this apparently can + ** happen if a packet gets garbled. + */ + if ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK ) + return; + httpd_send_err( + hc, 400, httpd_err400title, "", httpd_err400form, "" ); + finish_connection( c, tvP ); + return; + } + hc->read_idx += sz; + c->active_at = tvP->tv_sec; + + /* Do we have a complete request yet? */ + switch ( httpd_got_request( hc ) ) + { + case GR_NO_REQUEST: + return; + case GR_BAD_REQUEST: + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + finish_connection( c, tvP ); + return; + } + + /* Yes. Try parsing and resolving it. */ + if ( httpd_parse_request( hc ) < 0 ) + { + finish_connection( c, tvP ); + return; + } + + /* Check the throttle table */ + if ( ! check_throttles( c ) ) + { + httpd_send_err( + hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl ); + finish_connection( c, tvP ); + return; + } + + /* Start the connection going. */ + if ( httpd_start_request( hc, tvP ) < 0 ) + { + /* Something went wrong. Close down the connection. */ + finish_connection( c, tvP ); + return; + } + + /* Fill in end_byte_index. */ + if ( hc->got_range ) + { + c->next_byte_index = hc->first_byte_index; + c->end_byte_index = hc->last_byte_index + 1; + } + else if ( hc->bytes_to_send < 0 ) + c->end_byte_index = 0; + else + c->end_byte_index = hc->bytes_to_send; + + /* Check if it's already handled. */ + if ( hc->file_address == (char*) 0 ) + { + /* No file address means someone else is handling it. */ + int tind; + for ( tind = 0; tind < c->numtnums; ++tind ) + throttles[c->tnums[tind]].bytes_since_avg += hc->bytes_sent; + c->next_byte_index = hc->bytes_sent; + finish_connection( c, tvP ); + return; + } + if ( c->next_byte_index >= c->end_byte_index ) + { + /* There's nothing to send. */ + finish_connection( c, tvP ); + return; + } + + /* Cool, we have a valid connection and a file to send to it. */ + c->conn_state = CNST_SENDING; + c->started_at = tvP->tv_sec; + c->wouldblock_delay = 0; + client_data.p = c; + + fdwatch_del_fd( hc->conn_fd ); + fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE ); + } + + +static void +handle_send( connecttab* c, struct timeval* tvP ) + { + size_t max_bytes; + int sz, coast; + ClientData client_data; + time_t elapsed; + httpd_conn* hc = c->hc; + int tind; + + if ( c->max_limit == THROTTLE_NOLIMIT ) + max_bytes = 1000000000L; + else + max_bytes = c->max_limit / 4; /* send at most 1/4 seconds worth */ + + /* Do we need to write the headers first? */ + if ( hc->responselen == 0 ) + { + /* No, just write the file. */ + sz = write( + hc->conn_fd, &(hc->file_address[c->next_byte_index]), + MIN( c->end_byte_index - c->next_byte_index, max_bytes ) ); + } + else + { + /* Yes. We'll combine headers and file into a single writev(), + ** hoping that this generates a single packet. + */ + struct iovec iv[2]; + + iv[0].iov_base = hc->response; + iv[0].iov_len = hc->responselen; + iv[1].iov_base = &(hc->file_address[c->next_byte_index]); + iv[1].iov_len = MIN( c->end_byte_index - c->next_byte_index, max_bytes ); + sz = writev( hc->conn_fd, iv, 2 ); + } + + if ( sz < 0 && errno == EINTR ) + return; + + if ( sz == 0 || + ( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) ) + { + /* This shouldn't happen, but some kernels, e.g. + ** SunOS 4.1.x, are broken and select() says that + ** O_NDELAY sockets are always writable even when + ** they're actually not. + ** + ** Current workaround is to block sending on this + ** socket for a brief adaptively-tuned period. + ** Fortunately we already have all the necessary + ** blocking code, for use with throttling. + */ + c->wouldblock_delay += MIN_WOULDBLOCK_DELAY; + c->conn_state = CNST_PAUSING; + fdwatch_del_fd( hc->conn_fd ); + client_data.p = c; + if ( c->wakeup_timer != (Timer*) 0 ) + syslog( LOG_ERR, "replacing non-null wakeup_timer!" ); + c->wakeup_timer = tmr_create( + tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 ); + if ( c->wakeup_timer == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" ); + exit( 1 ); + } + return; + } + + if ( sz < 0 ) + { + /* Something went wrong, close this connection. + ** + ** If it's just an EPIPE, don't bother logging, that + ** just means the client hung up on us. + ** + ** On some systems, write() occasionally gives an EINVAL. + ** Dunno why, something to do with the socket going + ** bad. Anyway, we don't log those either. + ** + ** And ECONNRESET isn't interesting either. + */ + if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET ) + syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl ); + clear_connection( c, tvP ); + return; + } + + /* Ok, we wrote something. */ + c->active_at = tvP->tv_sec; + /* Was this a headers + file writev()? */ + if ( hc->responselen > 0 ) + { + /* Yes; did we write only part of the headers? */ + if ( sz < hc->responselen ) + { + /* Yes; move the unwritten part to the front of the buffer. */ + int newlen = hc->responselen - sz; + (void) memmove( hc->response, &(hc->response[sz]), newlen ); + hc->responselen = newlen; + sz = 0; + } + else + { + /* Nope, we wrote the full headers, so adjust accordingly. */ + sz -= hc->responselen; + hc->responselen = 0; + } + } + /* And update how much of the file we wrote. */ + c->next_byte_index += sz; + c->hc->bytes_sent += sz; + for ( tind = 0; tind < c->numtnums; ++tind ) + throttles[c->tnums[tind]].bytes_since_avg += sz; + + /* Are we done? */ + if ( c->next_byte_index >= c->end_byte_index ) + { + /* This connection is finished! */ + finish_connection( c, tvP ); + return; + } + + /* Tune the (blockheaded) wouldblock delay. */ + if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY ) + c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY; + + /* If we're throttling, check if we're sending too fast. */ + if ( c->max_limit != THROTTLE_NOLIMIT ) + { + elapsed = tvP->tv_sec - c->started_at; + if ( elapsed == 0 ) + elapsed = 1; /* count at least one second */ + if ( c->hc->bytes_sent / elapsed > c->max_limit ) + { + c->conn_state = CNST_PAUSING; + fdwatch_del_fd( hc->conn_fd ); + /* How long should we wait to get back on schedule? If less + ** than a second (integer math rounding), use 1/2 second. + */ + coast = c->hc->bytes_sent / c->max_limit - elapsed; + client_data.p = c; + if ( c->wakeup_timer != (Timer*) 0 ) + syslog( LOG_ERR, "replacing non-null wakeup_timer!" ); + c->wakeup_timer = tmr_create( + tvP, wakeup_connection, client_data, + coast > 0 ? ( coast * 1000L ) : 500L, 0 ); + if ( c->wakeup_timer == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" ); + exit( 1 ); + } + } + } + /* (No check on min_limit here, that only controls connection startups.) */ + } + + +static void +handle_linger( connecttab* c, struct timeval* tvP ) + { + char buf[4096]; + int r; + + /* In lingering-close mode we just read and ignore bytes. An error + ** or EOF ends things, otherwise we go until a timeout. + */ + r = read( c->hc->conn_fd, buf, sizeof(buf) ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + return; + if ( r <= 0 ) + really_clear_connection( c, tvP ); + } + + +static int +check_throttles( connecttab* c ) + { + int tnum; + long l; + + c->numtnums = 0; + c->max_limit = c->min_limit = THROTTLE_NOLIMIT; + for ( tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS; + ++tnum ) + if ( match( throttles[tnum].pattern, c->hc->expnfilename ) ) + { + /* If we're way over the limit, don't even start. */ + if ( throttles[tnum].rate > throttles[tnum].max_limit * 2 ) + return 0; + /* Also don't start if we're under the minimum. */ + if ( throttles[tnum].rate < throttles[tnum].min_limit ) + return 0; + if ( throttles[tnum].num_sending < 0 ) + { + syslog( LOG_ERR, "throttle sending count was negative - shouldn't happen!" ); + throttles[tnum].num_sending = 0; + } + c->tnums[c->numtnums++] = tnum; + ++throttles[tnum].num_sending; + l = throttles[tnum].max_limit / throttles[tnum].num_sending; + if ( c->max_limit == THROTTLE_NOLIMIT ) + c->max_limit = l; + else + c->max_limit = MIN( c->max_limit, l ); + l = throttles[tnum].min_limit; + if ( c->min_limit == THROTTLE_NOLIMIT ) + c->min_limit = l; + else + c->min_limit = MAX( c->min_limit, l ); + } + return 1; + } + + +static void +clear_throttles( connecttab* c, struct timeval* tvP ) + { + int tind; + + for ( tind = 0; tind < c->numtnums; ++tind ) + --throttles[c->tnums[tind]].num_sending; + } + + +static void +update_throttles( ClientData client_data, struct timeval* nowP ) + { + int tnum, tind; + int cnum; + connecttab* c; + long l; + + /* Update the average sending rate for each throttle. This is only used + ** when new connections start up. + */ + for ( tnum = 0; tnum < numthrottles; ++tnum ) + { + throttles[tnum].rate = ( 2 * throttles[tnum].rate + throttles[tnum].bytes_since_avg / THROTTLE_TIME ) / 3; + throttles[tnum].bytes_since_avg = 0; + /* Log a warning message if necessary. */ + if ( throttles[tnum].rate > throttles[tnum].max_limit && throttles[tnum].num_sending != 0 ) + { + if ( throttles[tnum].rate > throttles[tnum].max_limit * 2 ) + syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld greatly exceeding limit %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].max_limit, throttles[tnum].num_sending ); + else + syslog( LOG_INFO, "throttle #%d '%.80s' rate %ld exceeding limit %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].max_limit, throttles[tnum].num_sending ); + } + if ( throttles[tnum].rate < throttles[tnum].min_limit && throttles[tnum].num_sending != 0 ) + { + syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld lower than minimum %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].min_limit, throttles[tnum].num_sending ); + } + } + + /* Now update the sending rate on all the currently-sending connections, + ** redistributing it evenly. + */ + for ( cnum = 0; cnum < max_connects; ++cnum ) + { + c = &connects[cnum]; + if ( c->conn_state == CNST_SENDING || c->conn_state == CNST_PAUSING ) + { + c->max_limit = THROTTLE_NOLIMIT; + for ( tind = 0; tind < c->numtnums; ++tind ) + { + tnum = c->tnums[tind]; + l = throttles[tnum].max_limit / throttles[tnum].num_sending; + if ( c->max_limit == THROTTLE_NOLIMIT ) + c->max_limit = l; + else + c->max_limit = MIN( c->max_limit, l ); + } + } + } + } + + +static void +finish_connection( connecttab* c, struct timeval* tvP ) + { + /* If we haven't actually sent the buffered response yet, do so now. */ + httpd_write_response( c->hc ); + + /* And clear. */ + clear_connection( c, tvP ); + } + + +static void +clear_connection( connecttab* c, struct timeval* tvP ) + { + ClientData client_data; + + if ( c->wakeup_timer != (Timer*) 0 ) + { + tmr_cancel( c->wakeup_timer ); + c->wakeup_timer = 0; + } + + /* This is our version of Apache's lingering_close() routine, which is + ** their version of the often-broken SO_LINGER socket option. For why + ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html + ** What we do is delay the actual closing for a few seconds, while reading + ** any bytes that come over the connection. However, we don't want to do + ** this unless it's necessary, because it ties up a connection slot and + ** file descriptor which means our maximum connection-handling rate + ** is lower. So, elsewhere we set a flag when we detect the few + ** circumstances that make a lingering close necessary. If the flag + ** isn't set we do the real close now. + */ + if ( c->conn_state == CNST_LINGERING ) + { + /* If we were already lingering, shut down for real. */ + tmr_cancel( c->linger_timer ); + c->linger_timer = (Timer*) 0; + c->hc->should_linger = 0; + } + if ( c->hc->should_linger ) + { + if ( c->conn_state != CNST_PAUSING ) + fdwatch_del_fd( c->hc->conn_fd ); + c->conn_state = CNST_LINGERING; + shutdown( c->hc->conn_fd, SHUT_WR ); + fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ ); + client_data.p = c; + if ( c->linger_timer != (Timer*) 0 ) + syslog( LOG_ERR, "replacing non-null linger_timer!" ); + c->linger_timer = tmr_create( + tvP, linger_clear_connection, client_data, LINGER_TIME, 0 ); + if ( c->linger_timer == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" ); + exit( 1 ); + } + } + else + really_clear_connection( c, tvP ); + } + + +static void +really_clear_connection( connecttab* c, struct timeval* tvP ) + { + stats_bytes += c->hc->bytes_sent; + if ( c->conn_state != CNST_PAUSING ) + fdwatch_del_fd( c->hc->conn_fd ); + httpd_close_conn( c->hc, tvP ); + clear_throttles( c, tvP ); + if ( c->linger_timer != (Timer*) 0 ) + { + tmr_cancel( c->linger_timer ); + c->linger_timer = 0; + } + c->conn_state = CNST_FREE; + c->next_free_connect = first_free_connect; + first_free_connect = c - connects; /* division by sizeof is implied */ + --num_connects; + } + + +static void +idle( ClientData client_data, struct timeval* nowP ) + { + int cnum; + connecttab* c; + + for ( cnum = 0; cnum < max_connects; ++cnum ) + { + c = &connects[cnum]; + switch ( c->conn_state ) + { + case CNST_READING: + if ( nowP->tv_sec - c->active_at >= IDLE_READ_TIMELIMIT ) + { + syslog( LOG_INFO, + "%.80s connection timed out reading", + httpd_ntoa( &c->hc->client_addr ) ); + httpd_send_err( + c->hc, 408, httpd_err408title, "", httpd_err408form, "" ); + finish_connection( c, nowP ); + } + break; + case CNST_SENDING: + case CNST_PAUSING: + if ( nowP->tv_sec - c->active_at >= IDLE_SEND_TIMELIMIT ) + { + syslog( LOG_INFO, + "%.80s connection timed out sending", + httpd_ntoa( &c->hc->client_addr ) ); + clear_connection( c, nowP ); + } + break; + } + } + } + + +static void +wakeup_connection( ClientData client_data, struct timeval* nowP ) + { + connecttab* c; + + c = (connecttab*) client_data.p; + c->wakeup_timer = (Timer*) 0; + if ( c->conn_state == CNST_PAUSING ) + { + c->conn_state = CNST_SENDING; + fdwatch_add_fd( c->hc->conn_fd, c, FDW_WRITE ); + } + } + +static void +linger_clear_connection( ClientData client_data, struct timeval* nowP ) + { + connecttab* c; + + c = (connecttab*) client_data.p; + c->linger_timer = (Timer*) 0; + really_clear_connection( c, nowP ); + } + + +static void +occasional( ClientData client_data, struct timeval* nowP ) + { + mmc_cleanup( nowP ); + tmr_cleanup(); + watchdog_flag = 1; /* let the watchdog know that we are alive */ + } + + +#ifdef STATS_TIME +static void +show_stats( ClientData client_data, struct timeval* nowP ) + { + logstats( nowP ); + } +#endif /* STATS_TIME */ + + +/* Generate debugging statistics syslog messages for all packages. */ +static void +logstats( struct timeval* nowP ) + { + struct timeval tv; + time_t now; + long up_secs, stats_secs; + + if ( nowP == (struct timeval*) 0 ) + { + (void) gettimeofday( &tv, (struct timezone*) 0 ); + nowP = &tv; + } + now = nowP->tv_sec; + up_secs = now - start_time; + stats_secs = now - stats_time; + if ( stats_secs == 0 ) + stats_secs = 1; /* fudge */ + stats_time = now; + syslog( LOG_NOTICE, + "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs ); + + thttpd_logstats( stats_secs ); + httpd_logstats( stats_secs ); + mmc_logstats( stats_secs ); + fdwatch_logstats( stats_secs ); + tmr_logstats( stats_secs ); + } + + +/* Generate debugging statistics syslog message. */ +static void +thttpd_logstats( long secs ) + { + if ( secs > 0 ) + syslog( LOG_NOTICE, + " thttpd - %ld connections (%g/sec), %d max simultaneous, %lld bytes (%g/sec), %d httpd_conns allocated", + stats_connections, (float) stats_connections / secs, + stats_simultaneous, (long long) stats_bytes, + (float) stats_bytes / secs, httpd_conn_count ); + stats_connections = 0; + stats_bytes = 0; + stats_simultaneous = 0; + } diff --git a/timers.c b/timers.c new file mode 100644 index 0000000..908304a --- /dev/null +++ b/timers.c @@ -0,0 +1,351 @@ +/* timers.c - simple timer routines +** +** Copyright © 1995,1998,2000,2014 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include + +#include +#include +#include + +#include "timers.h" + + +#define HASH_SIZE 67 +static Timer* timers[HASH_SIZE]; +static Timer* free_timers; +static int alloc_count, active_count, free_count; + +ClientData JunkClientData; + + + +static unsigned int +hash( Timer* t ) + { + /* We can hash on the trigger time, even though it can change over + ** the life of a timer via either the periodic bit or the tmr_reset() + ** call. This is because both of those guys call l_resort(), which + ** recomputes the hash and moves the timer to the appropriate list. + */ + return ( + (unsigned int) t->time.tv_sec ^ + (unsigned int) t->time.tv_usec ) % HASH_SIZE; + } + + +static void +l_add( Timer* t ) + { + int h = t->hash; + Timer* t2; + Timer* t2prev; + + t2 = timers[h]; + if ( t2 == (Timer*) 0 ) + { + /* The list is empty. */ + timers[h] = t; + t->prev = t->next = (Timer*) 0; + } + else + { + if ( t->time.tv_sec < t2->time.tv_sec || + ( t->time.tv_sec == t2->time.tv_sec && + t->time.tv_usec <= t2->time.tv_usec ) ) + { + /* The new timer goes at the head of the list. */ + timers[h] = t; + t->prev = (Timer*) 0; + t->next = t2; + t2->prev = t; + } + else + { + /* Walk the list to find the insertion point. */ + for ( t2prev = t2, t2 = t2->next; t2 != (Timer*) 0; + t2prev = t2, t2 = t2->next ) + { + if ( t->time.tv_sec < t2->time.tv_sec || + ( t->time.tv_sec == t2->time.tv_sec && + t->time.tv_usec <= t2->time.tv_usec ) ) + { + /* Found it. */ + t2prev->next = t; + t->prev = t2prev; + t->next = t2; + t2->prev = t; + return; + } + } + /* Oops, got to the end of the list. Add to tail. */ + t2prev->next = t; + t->prev = t2prev; + t->next = (Timer*) 0; + } + } + } + + +static void +l_remove( Timer* t ) + { + int h = t->hash; + + if ( t->prev == (Timer*) 0 ) + timers[h] = t->next; + else + t->prev->next = t->next; + if ( t->next != (Timer*) 0 ) + t->next->prev = t->prev; + } + + +static void +l_resort( Timer* t ) + { + /* Remove the timer from its old list. */ + l_remove( t ); + /* Recompute the hash. */ + t->hash = hash( t ); + /* And add it back in to its new list, sorted correctly. */ + l_add( t ); + } + + +void +tmr_init( void ) + { + int h; + + for ( h = 0; h < HASH_SIZE; ++h ) + timers[h] = (Timer*) 0; + free_timers = (Timer*) 0; + alloc_count = active_count = free_count = 0; + } + + +Timer* +tmr_create( + struct timeval* nowP, TimerProc* timer_proc, ClientData client_data, + long msecs, int periodic ) + { + Timer* t; + + if ( free_timers != (Timer*) 0 ) + { + t = free_timers; + free_timers = t->next; + --free_count; + } + else + { + t = (Timer*) malloc( sizeof(Timer) ); + if ( t == (Timer*) 0 ) + return (Timer*) 0; + ++alloc_count; + } + + t->timer_proc = timer_proc; + t->client_data = client_data; + t->msecs = msecs; + t->periodic = periodic; + if ( nowP != (struct timeval*) 0 ) + t->time = *nowP; + else + (void) gettimeofday( &t->time, (struct timezone*) 0 ); + t->time.tv_sec += msecs / 1000L; + t->time.tv_usec += ( msecs % 1000L ) * 1000L; + if ( t->time.tv_usec >= 1000000L ) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + t->hash = hash( t ); + /* Add the new timer to the proper active list. */ + l_add( t ); + ++active_count; + + return t; + } + + +struct timeval* +tmr_timeout( struct timeval* nowP ) + { + long msecs; + static struct timeval timeout; + + msecs = tmr_mstimeout( nowP ); + if ( msecs == INFTIM ) + return (struct timeval*) 0; + timeout.tv_sec = msecs / 1000L; + timeout.tv_usec = ( msecs % 1000L ) * 1000L; + return &timeout; + } + + +long +tmr_mstimeout( struct timeval* nowP ) + { + int h; + int gotone; + long msecs, m; + Timer* t; + + gotone = 0; + msecs = 0; /* make lint happy */ + /* Since the lists are sorted, we only need to look at the + ** first timer on each one. + */ + for ( h = 0; h < HASH_SIZE; ++h ) + { + t = timers[h]; + if ( t != (Timer*) 0 ) + { + m = ( t->time.tv_sec - nowP->tv_sec ) * 1000L + + ( t->time.tv_usec - nowP->tv_usec ) / 1000L; + if ( ! gotone ) + { + msecs = m; + gotone = 1; + } + else if ( m < msecs ) + msecs = m; + } + } + if ( ! gotone ) + return INFTIM; + if ( msecs <= 0 ) + msecs = 0; + return msecs; + } + + +void +tmr_run( struct timeval* nowP ) + { + int h; + Timer* t; + Timer* next; + + for ( h = 0; h < HASH_SIZE; ++h ) + for ( t = timers[h]; t != (Timer*) 0; t = next ) + { + next = t->next; + /* Since the lists are sorted, as soon as we find a timer + ** that isn't ready yet, we can go on to the next list. + */ + if ( t->time.tv_sec > nowP->tv_sec || + ( t->time.tv_sec == nowP->tv_sec && + t->time.tv_usec > nowP->tv_usec ) ) + break; + (t->timer_proc)( t->client_data, nowP ); + if ( t->periodic ) + { + /* Reschedule. */ + t->time.tv_sec += t->msecs / 1000L; + t->time.tv_usec += ( t->msecs % 1000L ) * 1000L; + if ( t->time.tv_usec >= 1000000L ) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + l_resort( t ); + } + else + tmr_cancel( t ); + } + } + + +void +tmr_reset( struct timeval* nowP, Timer* t ) + { + t->time = *nowP; + t->time.tv_sec += t->msecs / 1000L; + t->time.tv_usec += ( t->msecs % 1000L ) * 1000L; + if ( t->time.tv_usec >= 1000000L ) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + l_resort( t ); + } + + +void +tmr_cancel( Timer* t ) + { + /* Remove it from its active list. */ + l_remove( t ); + --active_count; + /* And put it on the free list. */ + t->next = free_timers; + free_timers = t; + ++free_count; + t->prev = (Timer*) 0; + } + + +void +tmr_cleanup( void ) + { + Timer* t; + + while ( free_timers != (Timer*) 0 ) + { + t = free_timers; + free_timers = t->next; + --free_count; + free( (void*) t ); + --alloc_count; + } + } + + +void +tmr_term( void ) + { + int h; + + for ( h = 0; h < HASH_SIZE; ++h ) + while ( timers[h] != (Timer*) 0 ) + tmr_cancel( timers[h] ); + tmr_cleanup(); + } + + +/* Generate debugging statistics syslog message. */ +void +tmr_logstats( long secs ) + { + syslog( + LOG_NOTICE, " timers - %d allocated, %d active, %d free", + alloc_count, active_count, free_count ); + if ( active_count + free_count != alloc_count ) + syslog( LOG_ERR, "timer counts don't add up!" ); + } diff --git a/timers.h b/timers.h new file mode 100644 index 0000000..aaa06c6 --- /dev/null +++ b/timers.h @@ -0,0 +1,109 @@ +/* timers.h - header file for timers package +** +** Copyright © 1995,1998,1999,2000,2014 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _TIMERS_H_ +#define _TIMERS_H_ + +#include + +#ifndef INFTIM +#define INFTIM -1 +#endif /* INFTIM */ + +/* ClientData is a random value that tags along with a timer. The client +** can use it for whatever, and it gets passed to the callback when the +** timer triggers. +*/ +typedef union { + void* p; + int i; + long l; + } ClientData; + +extern ClientData JunkClientData; /* for use when you don't care */ + +/* The TimerProc gets called when the timer expires. It gets passed +** the ClientData associated with the timer, and a timeval in case +** it wants to schedule another timer. +*/ +typedef void TimerProc( ClientData client_data, struct timeval* nowP ); + +/* The Timer struct. */ +typedef struct TimerStruct { + TimerProc* timer_proc; + ClientData client_data; + long msecs; + int periodic; + struct timeval time; + struct TimerStruct* prev; + struct TimerStruct* next; + int hash; + } Timer; + +/* Initialize the timer package. */ +void tmr_init( void ); + +/* Set up a timer, either periodic or one-shot. Returns (Timer*) 0 on errors. */ +Timer* tmr_create( + struct timeval* nowP, TimerProc* timer_proc, ClientData client_data, + long msecs, int periodic ); + +/* Returns a timeout indicating how long until the next timer triggers. You +** can just put the call to this routine right in your select(). Returns +** (struct timeval*) 0 if no timers are pending. +*/ +struct timeval* tmr_timeout( struct timeval* nowP ); + +/* Returns a timeout in milliseconds indicating how long until the next timer +** triggers. You can just put the call to this routine right in your poll(). +** Returns INFTIM (-1) if no timers are pending. +*/ +long tmr_mstimeout( struct timeval* nowP ); + +/* Run the list of timers. Your main program needs to call this every so often, +** or as indicated by tmr_timeout(). +*/ +void tmr_run( struct timeval* nowP ); + +/* Reset the clock on a timer, to current time plus the original timeout. */ +void tmr_reset( struct timeval* nowP, Timer* timer ); + +/* Deschedule a timer. Note that non-periodic timers are automatically +** descheduled when they run, so you don't have to call this on them. +*/ +void tmr_cancel( Timer* timer ); + +/* Clean up the timers package, freeing any unused storage. */ +void tmr_cleanup( void ); + +/* Cancel all timers and free storage, usually in preparation for exiting. */ +void tmr_term( void ); + +/* Generate debugging statistics syslog message. */ +void tmr_logstats( long secs ); + +#endif /* _TIMERS_H_ */ diff --git a/version.h b/version.h new file mode 100644 index 0000000..8d841b9 --- /dev/null +++ b/version.h @@ -0,0 +1,9 @@ +/* version.h - version defines for thttpd and libhttpd */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define SERVER_SOFTWARE "thttpd/2.29 23May2018" +#define SERVER_ADDRESS "http://www.acme.com/software/thttpd/" + +#endif /* _VERSION_H_ */