Also known as bozohttpd(8).
Change-Id: I40e955b5654674f2c708b10e5e403ca9cbc92534
./usr/libexec minix-base
./usr/libexec/atf-check minix-base atf
./usr/libexec/blacklistd-helper minix-base
+./usr/libexec/bozohttpd minix-base
./usr/libexec/fingerd minix-base
./usr/libexec/ftpd minix-base
./usr/libexec/getty minix-base
+./usr/libexec/httpd minix-base
./usr/libexec/kyua-atf-tester minix-base kyua
./usr/libexec/kyua-plain-tester minix-base kyua
./usr/libexec/ld.elf_so minix-base
./var/spool/ftp/etc minix-base
./var/spool/ftp/hidden minix-base
./var/tmp minix-base
+./var/www minix-base
./usr/libdata/debug/usr/lib/libz.so.1.0.debug minix-debug debug
./usr/libdata/debug/usr/libexec minix-debug
./usr/libdata/debug/usr/libexec/atf-check.debug minix-debug debug
+./usr/libdata/debug/usr/libexec/bozohttpd.debug minix-debug debug
./usr/libdata/debug/usr/libexec/cc1.debug minix-debug gcc=5,gcccmds,debug
./usr/libdata/debug/usr/libexec/cc1obj.debug minix-debug gcc=5,gcccmds,debug
./usr/libdata/debug/usr/libexec/cc1plus.debug minix-debug gcc=5,gcccmds,debug
./usr/libdata/debug/usr/libexec/fingerd.debug minix-debug debug
./usr/libdata/debug/usr/libexec/ftpd.debug minix-debug debug
./usr/libdata/debug/usr/libexec/getty.debug minix-debug debug
+./usr/libdata/debug/usr/libexec/httpd.debug minix-debug debug
./usr/libdata/debug/usr/libexec/kyua-atf-tester.debug minix-debug debug
./usr/libdata/debug/usr/libexec/kyua-plain-tester.debug minix-debug debug
./usr/libdata/debug/usr/libexec/ld.elf_so.debug minix-debug debug
./usr/man/man8/blacklistctl.8 minix-man
./usr/man/man8/blacklistd.8 minix-man
./usr/man/man8/boot.8 minix-man
+./usr/man/man8/bozohttpd.8 minix-man
./usr/man/man8/btrace.8 minix-man
./usr/man/man8/cdprobe.8 minix-man
./usr/man/man8/chown.8 minix-man
./usr/man/man8/groupinfo.8 minix-man
./usr/man/man8/groupmod.8 minix-man
./usr/man/man8/halt.8 minix-man
-./usr/man/man8/httpd.8 minix-man obsolete
+./usr/man/man8/httpd.8 minix-man
./usr/man/man8/i2cscan.8 minix-man
./usr/man/man8/ifconfig.8 minix-man
./usr/man/man8/in.httpd.8 minix-man obsolete
./var/spool/ftp/bin
./var/spool/ftp/etc
./var/spool/ftp/hidden #breaks cd image generation with non-root users: mode=0111
+./var/www
# Directories with special access rights
/set type=dir uid=0 gid=0 mode=1777
.include <bsd.own.mk>
SUBDIR= \
- fingerd ftpd getty \
+ fingerd ftpd getty httpd \
ld.elf_so \
rshd \
telnetd
--- /dev/null
+$eterna: CHANGES,v 1.78 2011/11/18 01:25:11 mrg Exp $
+
+changes in bozohttpd 20150320:
+ o fix redirection handling
+ o support transport stream (.ts) and video object (.vob) files
+ o directory listings show correct file sizes for large files
+
+changes in bozohttpd 20140717:
+ o properly handle SSL errors
+
+changes in bozohttpd 20140708:
+ o fixes for virtual host support, from rajeev_v_pillai@yahoo.com
+ o avoid printing double errors, from shm@netbsd.org
+ o fix a security issue in basic HTTP authentication which would allow
+ authentication to be bypassed, from shm@netbsd.org
+
+changes in bozohttpd 20140201:
+ o support .svg files
+ o fix a core dump when requests timeout
+
+changes in bozohttpd 20140102:
+ o update a few content types
+ o add support for directly calling lua scripts to handle
+ processes, from mbalmer@netbsd.org
+ o properly escape generated HTML
+ o add authentication for redirections, from martin@netbsd.org
+ o handle chained ssl certifications, from elric@netbsd.org
+ o add basic support for gzipped files, from elric@netbsd.org
+ o properly escape generated URIs
+
+changes in bozohttpd 20111118:
+ o add -P <pidfile> option, from jmmv@netbsd.org
+ o avoid crashes with http basic auth, from pooka@netbsd.org
+ o add support for REDIRECT_STATUS variable, from tls@netbsd.org
+ o support .mp4 files in the default map
+ o directory indexes with files with : are now displayed properly, from
+ reed@netbsd.org
+ o allow -I option to be useful in non-inetd mode as well
+
+changes in bozohttpd 20100920:
+ o properly fully disable multi-file mode for now
+ o fix the -t and -U options when used without the -e option, broken since
+ the library-ifcation
+ o be explicit that logs go to the FTP facility in syslog
+ o use scandir() with alphasort() for sorted directory lists, from moof
+ o fix a serious error in vhost handling; "Host:.." would allow access to
+ the next level directory from the virtual root directory, from seanb
+ o fix some various non standard compile time errors, from rudolf
+ o fix dynamic CGI content maps, from rudolf
+
+changes in bozohttpd 20100617:
+ o fix some compile issues
+ o fix SSL mode. from rtr
+ o fix some cgi-bin issues, as seen with cvsweb
+ o disable multi-file daemon mode for now, it breaks
+ o return 404's instead of 403's when chdir of ~user dirs fail
+ o remove "noreturn" attribute from bozo_http_error() that was
+ causing incorrect runtime behaviour
+
+changes in bozohttpd 20100509:
+ o major rework and clean up of internal interfaces. move the main
+ program into main.c, the remaining parts are useable as library.
+ add bindings for lua. by Alistair G. Crooks <agc@netbsd.org>
+ o fix http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=566325
+
+changes in bozohttpd 20090522:
+ o avoid dying in daemon mode for some uncommon, but recoverable, errors
+ o close leaking file descriptors for CGI and daemon mode
+ o handle poll errors properly
+ o don't try to handle more than one request per process yet
+ o add subdirs for build "debug" and "small" versions
+ o clean up a bad merge / duplicate code
+ o make mmap() usage portable, fixes linux & ranges: support
+ o document the -f option
+ o daemon mode now serves 6 files per child
+
+changes in bozohttpd 20090417:
+ o make bozohttpd internally more modular, preparing the way
+ to handle more than one request per process
+ o fix http-auth, set $REMOTE_USER not $REMOTEUSER. also fix
+ cgi-bin with cvsweb, from Holger Weiss <holger@CIS.FU-Berlin.DE>
+ o fix an uninitialised variable use in daemon mode
+ o fix ssl mode with newer OpenSSL
+ o mmap large files in manageable sizes so we can serve any size file
+ o refactor url processing to handle query strings correctly for CGI
+ from Sergey Katsev at Coyote Point
+ o add If-Modified-Since support, from Joerg Sonnenberger
+ <joerg@netbsd.org>
+ o many more manual fixes, from NetBSD
+
+changes in bozohttpd 20080303:
+ o fix some cgi header processing, from <thelsdj@gmail.com>
+ o add simple Range: header processing, from <bad@bsd.de>
+ o man page fixes, from NetBSD
+ o clean up various parts, from NetBSD
+
+changes in bozohttpd 20060710:
+ o prefix some function names with "bozo"
+ o align directory indexing <hr> markers
+ o clean up some code GCC4 grumbled about
+
+changes in bozohttpd 20060517:
+ o don't allow "/.." or "../" files
+ o don't write ":80" into urls for the http port
+ o fix a fd leak when fork() fails
+ o make directory indexing mode not look so ugly
+ o build a text version of the manual page
+ o make "make clean" work properly
+
+changes in bozohttpd 20050410:
+ o fix some off-by-one errors from <roland.illig@gmx.de>
+ o properly support nph- CGI
+ o make content maps case insensitive
+ o fix proto header merging to include the missing comma
+ o major source reorganisation; most features are in separate files now
+ o new -V flag that makes unknown virtualhosts use slashdir
+ from <rumble@ephemeral.org>
+ o HTTP/1.x protocol headers are now properly merged for CGI
+
+changes in bozohttpd 20040808:
+ o CGI status is now properly handled (-a flag has been removed)
+ o CGI file upload support works
+ o %xy translations are no longer ever applied after the first '?',
+ ala RFC2396. from lukem
+ o daemon mode (-b) should no longer hang spinning forever if it
+ sees no children. from lukem
+ o new .bzabsredirect file support. from <martin@netbsd.org>
+ o return a 404 error if we see %00 or %2f (/)
+ o don't print 2 "200" headers for CGI
+ o support .torrent files
+
+changes in bozohttpd 20040218:
+ o new .bzredirect file support for sane directory redirection
+ o new -Z option that enables SSL mode, from <rtr@eterna.com.au>
+ o the -C option has been changed to take two explicit options, rather
+ than a single option with a space separating the suffix and the
+ interpreter. ``-C ".foo /path/to/bar"'' should now be written
+ as ``-C .foo /path/to/bar''
+ o the -M option has been changed like -C and no longer requires or
+ supports a single argument with space-separated options
+ o with -a, still print the 200 OK. from <rtr@eterna.com.au>
+ o with -r, if a .bzdirect file appears in a directory, allow direct
+ access to this directory
+
+changes in bozohttpd 20031005:
+ o fixes for basic authorisation. from <ecu@ipv42.net>
+ o always display file size in directory index mode
+ o add .xbel, .xml & .xsl -> text/xml mappings. from
+ <wiz@danbala.ifoer.tuwien.ac.at>
+
+changes in bozohttpd 20030626:
+ o fix a recent core dump when given no input
+ o add new -r flag that ensures referrer is set to this host
+ o fix several compile time errors with -DNO_CGIBIN_SUPPORT
+ o fix some man page details. from lukem@wasabisystems.com
+ o re-add a missing memset(), fixing a core dump. from lukem
+ o support HTTP basic authorisation, disabled by default. from lukem
+ o print the port number in redirects and errors. from lukem
+ o only syslog the basename of the program. from lukem
+ o add __attribute__() format checking. from lukem
+ o fix cgibin SCRIPT_NAME to have a leading /. from zakj@nox.cx
+ o simplify some code in -C to avoid a core dump. from lukem
+ o add a .css -> css/text entry to the content_map[]. from zakj@nox.cx
+
+changes in bozohttpd 20030409:
+ o -d without DEBUG enabled only prints one warning and continues
+ o one can now define the C macro SERVER_SOFTWARE when building to
+ change the Server: header and CGI variable of the same name
+ o add new -s flag the force logging output to stderr. from zakj@nox.cx
+ o add new -a flag for CGI bin that stops bozohttpd from outputting
+ any HTTP reply, the CGI program must output these. from zakj@nox.cx
+ o new REQUEST_URI and DATE_GMT environment variables for CGI. from
+ zakj@nox.cx
+ o add a "Makefile.boot" that should work with any make program
+ o build on linux again
+ o fix core dumps when using -C
+
+changes in bozohttpd 20030313:
+ o deprecate -r flag; make this the default and silently ignore -r now
+ o add support for file extentions to call CGI programs (from lukem)
+ o add dynamic support to add new content map entries, allowing both
+ new file types and non /cgi-bin CGI programs to be run with the
+ new -C "suffix cgihandler" and -M "suffix type encoding encoding11"
+ options
+ o in -b mode, set the http date after accept() returns, not before we
+ call accept()
+ o in -b mode, bind all addresses found not just the first one
+ o unsupport old hostname API
+ o in -b mode, set the SO_REUSEADDR socket option (lukem)
+ o allow -x (index.html) mode to work with CGI handlers
+
+changes in bozohttpd 20021106:
+ o add .bz2 support
+ o properly escape <, > and & in error messages, partly from
+ Nicolas Jombart <ecu@mariejeanne.net>
+ o new -H flag to hide .* files in directory index mode
+ o fix buffer reallocation when parsing a request, to avoid
+ overflowing the buffer with carriage returns (\r)
+ o do not decode "%XY"-style cgi-bin data beyond the "?"
+
+changes in bozohttpd 5.15 (20020913):
+ o add .ogg support -> `application/x-ogg'
+ o fix CGI requests with "/" in the query part
+
+changes in bozohttpd 5.14 (20020823):
+ o allow -X mode to work for "/"
+ o work on systems without MADV_SEQUENTIAL
+ o make a local cut-down copy of "queue.h" (fixes linux & solaris
+ support at the very least)
+ o portability fixes for pre-ipv6 socket api systems (eg, solaris 7)
+ o portability fixes for missing _PATH_DEFPATH, LOG_FTP and __progname
+ o better documentation on virtual host support
+
+changes in bozohttpd 5.13 (20020804):
+ o support .mp3 files (type audio/mpeg)
+ o use stat() to find out if something is a directory, for -X mode
+
+changes in bozohttpd 5.12 (20020803):
+ o constification
+ o fixes & enhancements for directory index mode (-X)
+
+changes in bozohttpd 5.11 (20020730):
+ o more man page fixes from Thomas Klausner
+ <wiz@danbala.ifoer.tuwien.ac.at>
+ o de-K&R C-ification
+ o fix Date: header for daemon mode
+ o fix core dump when asking for /cgi-bin/ when CGI isn't configured
+ o use a valid Server: header
+
+changes in bozohttpd 5.10 (20020710):
+ - add freebsd support
+ - fix a couple of header typos
+ - many cgi-bin fixes from lukem@netbsd.org
+ - add -T chrootdir and -U user, plus several minor other cleanups
+ with signals and return values. from xs@kittenz.org
+ - add -e that does not clear the environment for -T/-U
+ - fix a formatting error noticed by ISIHARA Takanori <ishit@oak.dti.ne.jp>
+
+changes in bozohttpd 5.09 (20010922):
+ - add a daemon mode
+ - document how to use bozohttpd in netbsd inetd with more than 40
+ connections per minute and also with cgibin
+ - man page fixes from wiz@netbsd.org
+
+changes in bozohttpd 5.08 (20010812):
+ - add directory index generation support (-X) from ad@netbsd.org
+ - add .pa as an alias for .pac
+ - make server software version configurable (RFC)
+
+changes in bozohttpd 5.07 (20010610):
+ - add .png support
+ - new "-x index.html" flag to change default file
+ - new "-p public_html" flag to change default ~user directory
+ - fixes cgi-bin support and more from chuck@research.att.com
+ - add many new content-types, now support most common ones
+
+changes in bozohttpd 5.06 (20000825):
+ - add IPv6 suppor from itojun@iijlab.net
+ - man page fixes from jlam@netbsd.org
+
+changes in bozohttpd 5.05 (20000815):
+ - fix a virtual host bug, from kleink@netbsd.org
+
+changes in bozohttpd 5.04 (20000427):
+ - fix virtual host support; URI takes precedence over Host:
+
+changes in bozohttpd 5.03 (20000427):
+ - fix a bug with chdir()
+
+changes in bozohttpd 5.02 (20000426):
+ - .pac spport from simonb
+
+changes in bozohttpd 5.01 (20000421):
+ - .swf support
+ - virtual hosting support
--- /dev/null
+# $NetBSD: Makefile,v 1.24 2015/08/05 06:50:44 mrg Exp $
+#
+# $eterna: Makefile,v 1.30 2010/07/11 00:34:27 mrg Exp $
+#
+# berkeley (netbsd) makefile. see Makefile.boot for other systems.
+
+# compile-time options are:
+# NO_DEBUG /* don't include debugging support */
+# NO_USER_SUPPORT /* don't support /~user requests */
+# NO_CGIBIN_SUPPORT /* don't support cgi-bin requests */
+# NO_DIRINDEX_SUPPORT /* don't support directory indexing */
+# NO_DAEMON_MODE /* don't support daemon mode */
+# NO_DYNAMIC_CONTENT /* don't support dynamic content updates */
+# NO_SSL_SUPPORT /* don't support ssl (https) */
+# DO_HTPASSWD /* support .htpasswd files */
+# NO_LUA_SUPPORT /* don't support Lua for dynamic content */
+#
+# other system specific defines:
+# HAVE_NBUTIL_H /* netbsd compat is in <nbutil.h>
+# (don't forget to also enable -lnbutil)
+#
+# these are usually set via the "COPTS" variable, or some other method
+# for setting CFLAGS relevant to your make, eg
+# % make COPTS="-DDO_HTPASSWD"
+
+COPTS+= -DDO_HTPASSWD
+PROG= bozohttpd
+LINKS= ${BINDIR}/bozohttpd ${BINDIR}/httpd
+MAN= bozohttpd.8
+MLINKS+=bozohttpd.8 httpd.8
+SRCS= bozohttpd.c ssl-bozo.c auth-bozo.c cgi-bozo.c daemon-bozo.c \
+ tilde-luzah-bozo.c dir-index-bozo.c content-bozo.c lua-bozo.c
+SRCS+= main.c
+
+LDADD= -lcrypt -llua -lm
+DPADD= ${LIBCRYPT} ${LIBLUA} ${LIBM}
+
+WARNS?= 4
+
+.if defined(.OS.MAKE)
+OPSYS= ${.OS.MAKE}
+.else
+OPSYS:= ${:!uname -s!:S/-//g:S/\///g}
+.endif
+
+.if ${OPSYS} == "QNX"
+CPPFLAGS+= -DHAVE_NBUTIL_H
+LDADD+= -lnbutil
+.endif
+
+.include <bsd.own.mk>
+
+.if ${MKCRYPTO} != "no"
+
+LDADD+= -lssl -lcrypto
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+
+.else
+
+COPTS+= -DNO_SSL_SUPPORT
+
+.endif
+
+#
+# Build release things.
+#
+NROFF?= nroff
+
+PREHTMLFROB= sed \
+ -e 's/&/\&/' \
+ -e 's/</\</' \
+ -e 's/>/\>/'
+
+HTMLFROB= sed \
+ -e 's/\([MC] "[^"]*\)<dd>$$/\1<b>"<\/b><dd>/' \
+ -e 's/'"''"'/\”/' \
+ -e 's/""/\“/' \
+ -e 's/<a href="\.\.\/html[^>]*>\(.*\)<\/a>/\1/'
+
+TXTFROB= col -b
+
+bozohttpd.8.html: bozohttpd.8
+ $(PREHTMLFROB) $> | $(NROFF) -mdoc2html | $(HTMLFROB) > $@
+
+bozohttpd.8.txt: bozohttpd.8
+ $(NROFF) -mdoc -Tascii $> | $(TXTFROB) > $@
+
+CLEANFILES+= bozohttpd.8.html bozohttpd.8.txt
+
+# Create a distfile: uses /tmp
+BASE=bozohttpd-${BOZOVER}
+TAR=${BASE}.tar
+export-distfile:
+ dir=`mktemp -d /tmp/bozo-export-XXXXXX`; \
+ cd "$${dir}" || exit; \
+ mkdir ${BASE}; \
+ ( cd ${BASE} || exit; \
+ cp -r "${.CURDIR}/." "."; \
+ find . -name .CVS | xargs rm -r; \
+ ); \
+ pax -wf ${TAR} ${BASE}; \
+ gzip -nc9 ${TAR} > ${TAR}.gz; \
+ bzip2 -9 ${TAR}; \
+ echo "Exported two files in $${dir}:"; \
+ echo ${TAR}.gz; \
+ echo ${TAR}.bz2
+
+.include <bsd.prog.mk>
--- /dev/null
+# $eterna: Makefile.boot,v 1.9 2010/05/10 04:57:50 mrg Exp $
+#
+# very simple makefile to compile bozohttpd, should work with every make.
+# see Makefile for a list of compile options that may be placed in CFLAGS.
+
+CC= cc
+OPT= -O
+LARGE_CFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+LOCAL_CFLAGS= -DNO_LUA_SUPPORT
+CFLAGS= $(OPT) $(LARGE_CFLAGS) $(LOCAL_CFLAGS)
+
+GROFF= groff -Tascii
+CRYPTOLIBDIR= # -L/usr/local/lib
+CRYPTOLIBS= $(CRYPTOLIBDIR) -lcrypto -lssl
+
+FILES= bozohttpd.c auth-bozo.c cgi-bozo.c content-bozo.c daemon-bozo.c \
+ dir-index-bozo.c lua-bozo.c ssl-bozo.c tilde-luzah-bozo.c main.c
+
+all:
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o bozohttpd $(FILES) $(CRYPTOLIBS)
+
+man:
+ $(GROFF) -mandoc bozohttpd.8 > bozohttpd.cat8
+
+clean:
+ rm -f bozohttpd bozohttpd.cat8 *.o
--- /dev/null
+/* $NetBSD: auth-bozo.c,v 1.16 2014/12/26 19:52:00 mrg Exp $ */
+
+/* $eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2014 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements "http basic authorisation" for bozohttpd */
+
+#ifdef DO_HTPASSWD
+
+#include <sys/param.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "bozohttpd.h"
+
+#ifndef AUTH_FILE
+#define AUTH_FILE ".htpasswd"
+#endif
+
+static ssize_t base64_decode(const unsigned char *, size_t,
+ unsigned char *, size_t);
+
+/*
+ * Check if HTTP authentication is required
+ */
+int
+bozo_auth_check(bozo_httpreq_t *request, const char *file)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ struct stat sb;
+ char dir[MAXPATHLEN], authfile[MAXPATHLEN], *basename;
+ char user[BUFSIZ], *pass;
+ FILE *fp;
+ int len;
+
+ /* get dir=dirname(file) */
+ snprintf(dir, sizeof(dir), "%s", file);
+ if ((basename = strrchr(dir, '/')) == NULL)
+ strcpy(dir, ".");
+ else {
+ *basename++ = '\0';
+ /* ensure basename(file) != AUTH_FILE */
+ if (bozo_check_special_files(request, basename))
+ return 1;
+ }
+ request->hr_authrealm = bozostrdup(httpd, dir);
+
+ if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE) >=
+ sizeof(authfile)) {
+ return bozo_http_error(httpd, 404, request,
+ "authfile path too long");
+ }
+ if (stat(authfile, &sb) < 0) {
+ debug((httpd, DEBUG_NORMAL,
+ "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing",
+ dir, file, authfile));
+ return 0;
+ }
+ if ((fp = fopen(authfile, "r")) == NULL)
+ return bozo_http_error(httpd, 403, request,
+ "no permission to open authfile");
+ debug((httpd, DEBUG_NORMAL,
+ "bozo_auth_check realm `%s' dir `%s' authfile `%s' open",
+ dir, file, authfile));
+ if (request->hr_authuser && request->hr_authpass) {
+ while (fgets(user, sizeof(user), fp) != NULL) {
+ len = strlen(user);
+ if (len > 0 && user[len-1] == '\n')
+ user[--len] = '\0';
+ if ((pass = strchr(user, ':')) == NULL)
+ continue;
+ *pass++ = '\0';
+ debug((httpd, DEBUG_NORMAL,
+ "bozo_auth_check authfile `%s':`%s' "
+ "client `%s':`%s'",
+ user, pass, request->hr_authuser,
+ request->hr_authpass));
+ if (strcmp(request->hr_authuser, user) != 0)
+ continue;
+ if (strcmp(crypt(request->hr_authpass, pass),
+ pass) != 0)
+ break;
+ fclose(fp);
+ return 0;
+ }
+ }
+ fclose(fp);
+ return bozo_http_error(httpd, 401, request, "bad auth");
+}
+
+void
+bozo_auth_init(bozo_httpreq_t *request)
+{
+ request->hr_authuser = NULL;
+ request->hr_authpass = NULL;
+}
+
+void
+bozo_auth_cleanup(bozo_httpreq_t *request)
+{
+
+ if (request == NULL)
+ return;
+ free(request->hr_authuser);
+ free(request->hr_authpass);
+ free(request->hr_authrealm);
+}
+
+int
+bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, ssize_t len)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+
+ if (strcasecmp(val, "authorization") == 0 &&
+ strncasecmp(str, "Basic ", 6) == 0) {
+ char authbuf[BUFSIZ];
+ char *pass = NULL;
+ ssize_t alen;
+
+ alen = base64_decode((unsigned char *)str + 6,
+ (size_t)(len - 6),
+ (unsigned char *)authbuf,
+ sizeof(authbuf) - 1);
+ if (alen != -1)
+ authbuf[alen] = '\0';
+ if (alen == -1 ||
+ (pass = strchr(authbuf, ':')) == NULL)
+ return bozo_http_error(httpd, 400, request,
+ "bad authorization field");
+ *pass++ = '\0';
+ free(request->hr_authuser);
+ free(request->hr_authpass);
+ request->hr_authuser = bozostrdup(httpd, authbuf);
+ request->hr_authpass = bozostrdup(httpd, pass);
+ debug((httpd, DEBUG_FAT,
+ "decoded authorization `%s' as `%s':`%s'",
+ str, request->hr_authuser, request->hr_authpass));
+ /* don't store in request->headers */
+ return 1;
+ }
+ return 0;
+}
+
+int
+bozo_auth_check_special_files(bozo_httpreq_t *request,
+ const char *name)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+
+ if (strcmp(name, AUTH_FILE) == 0)
+ return bozo_http_error(httpd, 403, request,
+ "no permission to open authfile");
+ return 0;
+}
+
+void
+bozo_auth_check_401(bozo_httpreq_t *request, int code)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+
+ if (code == 401)
+ bozo_printf(httpd,
+ "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+ (request && request->hr_authrealm) ?
+ request->hr_authrealm : "default realm");
+}
+
+#ifndef NO_CGIBIN_SUPPORT
+void
+bozo_auth_cgi_setenv(bozo_httpreq_t *request,
+ char ***curenvpp)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+
+ if (request->hr_authuser && *request->hr_authuser) {
+ bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++);
+ bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser,
+ (*curenvpp)++);
+ }
+}
+
+int
+bozo_auth_cgi_count(bozo_httpreq_t *request)
+{
+ return (request->hr_authuser && *request->hr_authuser) ? 2 : 0;
+}
+#endif /* NO_CGIBIN_SUPPORT */
+
+/*
+ * Decode len bytes starting at in using base64 encoding into out.
+ * Result is *not* NUL terminated.
+ * Written by Luke Mewburn <lukem@NetBSD.org>
+ */
+const unsigned char decodetable[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
+};
+
+static ssize_t
+base64_decode(const unsigned char *in, size_t ilen, unsigned char *out,
+ size_t olen)
+{
+ unsigned char *cp;
+ size_t i;
+
+ if (ilen == 0) {
+ if (olen)
+ *out = '\0';
+ return 0;
+ }
+
+ cp = out;
+ for (i = 0; i < ilen; i += 4) {
+ if (cp + 3 > out + olen)
+ return (-1);
+#define IN_CHECK(x) \
+ if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
+ return(-1)
+
+ IN_CHECK(in[i + 0]);
+ /*LINTED*/
+ *(cp++) = decodetable[in[i + 0]] << 2
+ | decodetable[in[i + 1]] >> 4;
+ IN_CHECK(in[i + 1]);
+ /*LINTED*/
+ *(cp++) = decodetable[in[i + 1]] << 4
+ | decodetable[in[i + 2]] >> 2;
+ IN_CHECK(in[i + 2]);
+ *(cp++) = decodetable[in[i + 2]] << 6
+ | decodetable[in[i + 3]];
+#undef IN_CHECK
+ }
+ while (i > 0 && in[i - 1] == '=')
+ cp--,i--;
+ return (cp - out);
+}
+#endif /* DO_HTPASSWD */
--- /dev/null
+.\" $NetBSD: bozohttpd.8,v 1.53 2015/08/13 12:30:08 wiz Exp $
+.\"
+.\" $eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $
+.\"
+.\" Copyright (c) 1997-2015 Matthew R. Green
+.\" 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 ``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 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.
+.\"
+.Dd May 1, 2015
+.Dt BOZOHTTPD 8
+.Os
+.Sh NAME
+.Nm bozohttpd
+.Nd hyper text transfer protocol version 1.1 daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl CIMPSZciptvx
+.Op Fl C Ar suffix cgihandler
+.Op Fl I Ar port
+.Op Fl L Ar prefix script
+.Op Fl M Ar suffix type encoding encoding11
+.Op Fl P Ar pidfile
+.Op Fl S Ar server_software
+.Op Fl Z Ar cert privkey
+.Op Fl c Ar cgibin
+.Op Fl i Ar address
+.Op Fl p Ar pubdir
+.Op Fl t Ar chrootdir
+.Op Fl v Ar virtualroot
+.Op Fl x Ar index
+.Ar slashdir
+.Op Ar myname
+.Sh DESCRIPTION
+The
+.Nm
+program reads a
+.Em HTTP
+request from the standard input, and sends a reply to the standard output.
+Besides ~user translation and virtual hosting support (see below), all file
+requests are from
+.Ar slashdir
+directory.
+The server uses
+.Ar myname
+as its name, which defaults to the local hostname, obtained from
+.Xr gethostname 3
+(but see the
+.Fl v
+option for virtual hosting.)
+.Nm
+writes logs to
+.Xr syslog 3
+using the ftp facility (but see the
+.Fl s
+option for testing.)
+.Nm
+is designed to be small, simple and relatively featureless,
+hopefully increasing its security.
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width xxxcgibin
+.It Fl b
+Enables daemon mode, where
+.Nm
+detaches from the current terminal, running in the background and
+servicing HTTP requests.
+.It Fl C Ar suffix cgihandler
+Adds a new CGI handler program for a particular file type.
+The
+.Ar suffix
+should be any normal file suffix, and the
+.Ar cgihandler
+should be a full path to an interpreter.
+This option is the only way to enable CGI programs that exist
+outside of the cgibin directory to be executed.
+Multiple
+.Fl C
+options may be passed.
+.It Fl c Ar cgibin
+Enables the CGI/1.1 interface.
+The
+.Ar cgibin
+directory is expected to contain the CGI programs to be used.
+.Nm
+looks for URL's in the form of
+.Em /cgi-bin/\*[Lt]scriptname\*[Gt]
+where
+.Aq scriptname
+is a valid CGI program in the
+.Ar cgibin
+directory.
+In other words, all CGI URL's must begin with
+.Em \%/cgi-bin/ .
+Note that the CGI/1.1 interface is not available with
+.Em ~user
+translation.
+.It Fl e
+Causes
+.Nm
+to not clear the environment when used with either the
+.Fl t
+or
+.Fl U
+options.
+.It Fl f
+Stops the
+.Fl b
+flag from
+.Nm
+detaching from the tty and going into the background.
+.It Fl H
+Causes directory index mode to hide files and directories
+that start with a period, except for
+.Pa .. .
+Also see
+.Fl X .
+.It Fl I Ar port
+Causes
+.Nm
+to use
+.Ar port
+instead of the default
+.Dq http
+port.
+When used with the
+.Fl b
+option, it changes the bound port.
+Otherwise it forces redirections to use this port instead of the
+value obtained via
+.Xr getsockname 2 .
+.It Fl i Ar address
+Causes
+.Ar address
+to be used as the address to bind daemon mode.
+If otherwise unspecified, the address used to bind is derived from the
+.Ar myname ,
+which defaults to the name returned by
+.Xr gethostname 3 .
+Only the last
+.Fl i
+option is used.
+This option is only valid with the
+.Fl b
+option.
+.It Fl L Ar prefix script
+Adds a new Lua script for a particular prefix.
+The
+.Ar prefix
+should be an arbitrary text, and the
+.Ar script
+should be a full path to a Lua script.
+Multiple
+.Fl L
+options may be passed.
+A separate Lua state is created for each prefix.
+The Lua script can register callbacks using the
+httpd.register_handler('<name>', function) Lua function,
+which will trigger the execution of the Lua function
+.Em function
+when a URL in the form
+.Em http://<hostname>/<prefix>/<name>
+is being accessed.
+The function is passed three tables as arguments, the server
+environment, the request headers, and the decoded query string
+plus any data that was send as application/x-www-form-urlencoded.
+.It Fl M Ar suffix type encoding encoding11
+Adds a new entry to the table that converts file suffixes to
+content type and encoding.
+This option takes four additional arguments containing
+the file prefix, its
+.Dq Content-Type ,
+.Dq Content-Encoding ,
+and
+.Dq Content-Encoding
+for HTTP/1.1 connections, respectively.
+If any of these are a single dash
+.Pq Dq - ,
+the empty string is used instead.
+Multiple
+.Fl M
+options may be passed.
+.It Fl n
+Stops
+.Nm
+from doing IP address to name resolution of hosts for setting the
+.Ev REMOTE_HOST
+variable before running a CGI program.
+This option has no effect without the
+.Fl c
+option.
+.It Fl P Ar pidfile
+Causes
+.Nm
+to create a pid file in
+.Ar pidfile
+when run in daemon mode with the
+.Fl b
+option.
+.It Fl p Ar pubdir
+Changes the default user directory for
+.Em /~user/
+translations from
+.Dq public_html
+to
+.Ar pubdir .
+.It Fl r
+Forces pages besides the
+.Dq index.html
+(see the
+.Fl X
+option) page to require that the Referrer: header be present and
+refer to this web server, otherwise a redirect to the
+.Dq index.html
+page will be returned instead.
+.It Fl S Ar server_software
+Sets the internal server version to
+.Ar server_software .
+.It Fl s
+Forces logging to be set to stderr always.
+.It Fl t Ar chrootdir
+Makes
+.Nm
+chroot to the specified directory
+before answering requests.
+Every other path should be specified relative
+to the new root, if this option is used.
+Note that the current environment
+is normally replaced with an empty environment with this option, unless the
+.Fl e
+option is also used.
+.It Fl U Ar username
+Causes
+.Nm
+to switch to the user and the groups of
+.Ar username
+after initialization.
+This option, like
+.Fl t
+above, causes
+.Nm
+to clear the environment unless the
+.Fl e
+option is given.
+.It Fl u
+Enables the transformation of Uniform Resource Locators of
+the form
+.Em /~user/
+into the directory
+.Pa ~user/public_html
+(but see the
+.Fl p
+option above).
+.It Fl V
+Sets the default virtual host directory to
+.Ar slashdir .
+If no directory exists in
+.Ar virtualroot
+for the request, then
+.Ar slashdir
+will be used.
+The default behaviour is to return 404 (Not Found.)
+.It Fl v Ar virtualroot
+Enables virtual hosting support.
+Directories in
+.Ar virtualroot
+will be searched for a matching virtual host name, when parsing
+the HTML request.
+If a matching name is found, it will be used
+as both the server's real name,
+.Op Ar myname ,
+and as the
+.Ar slashdir .
+See the
+.Sx EXAMPLES
+section for an example of using this option.
+.It Fl X
+Enables directory indexing.
+A directory index will be generated only when the default file (i.e.
+.Pa index.html
+normally) is not present.
+.It Fl x Ar index
+Changes the default file read for directories from
+.Dq index.html
+to
+.Ar index .
+.It Fl Z Ar certificate_path privatekey_path
+Sets the path to the server certificate file and the private key file
+in pem format.
+It also causes
+.Nm
+to start SSL mode.
+.El
+.Pp
+Note that in
+.Nm
+versions 20031005 and prior that supported the
+.Fl C
+and
+.Fl M
+options, they took a single space-separated argument that was parsed.
+since version 20040828, they take multiple options (2 in the case of
+.Fl C
+and 4 in the case of
+.Fl M . )
+.Ss INETD CONFIGURATION
+As
+.Nm
+uses
+.Xr inetd 8
+by default to process incoming TCP connections for HTTP requests
+(but see the
+.Fl b
+option),
+.Nm
+has little internal networking knowledge.
+(Indeed, you can run it on the command line with little change of functionality.)
+A typical
+.Xr inetd.conf 5
+entry would be:
+.Bd -literal
+http stream tcp nowait:600 _httpd /usr/libexec/httpd httpd /var/www
+http stream tcp6 nowait:600 _httpd /usr/libexec/httpd httpd /var/www
+.Ed
+.Pp
+This would serve web pages from
+.Pa /var/www
+on both IPv4 and IPv6 ports.
+The
+.Em :600
+changes the
+requests per minute to 600, up from the
+.Xr inetd 8
+default of 40.
+.Pp
+Using the
+.Nx
+.Xr inetd 8 ,
+you can provide multiple IP-address based HTTP servers by having multiple
+listening ports with different configurations.
+.Ss NOTES
+This server supports the
+.Em HTTP/0.9 ,
+.Em HTTP/1.0 ,
+and
+.Em HTTP/1.1
+standards.
+Support for these protocols is very minimal and many optional features are
+not supported.
+.Pp
+.Nm
+can be compiled without
+CGI support (NO_CGIBIN_SUPPORT),
+user transformations (NO_USER_SUPPORT),
+directory index support (NO_DIRINDEX_SUPPORT),
+daemon mode support (NO_DAEMON_MODE),
+dynamic MIME content (NO_DYNAMIC_CONTENT),
+Lua suport (NO_LUA_SUPPORT),
+and SSL support (NO_SSL_SUPPORT)
+by defining the listed macros when building
+.Nm .
+.Ss HTTP BASIC AUTHORISATION
+.Nm
+has support for HTTP Basic Authorisation.
+If a file named
+.Pa .htpasswd
+exists in the directory of the current request,
+.Nm
+will restrict access to documents in that directory
+using the RFC 2617 HTTP
+.Dq Basic
+authentication scheme.
+.Pp
+Note:
+This does not recursively protect any sub-directories.
+.Pp
+The
+.Pa .htpasswd
+file contains lines delimited with a colon containing
+usernames and passwords hashed with
+.Xr crypt 3 ,
+for example:
+.Bd -literal
+heather:$1$pZWI4tH/$DzDPl63i6VvVRv2lJNV7k1
+jeremy:A.xewbx2DpQ8I
+.Ed
+.Pp
+On
+.Nx ,
+the
+.Xr pwhash 1
+utility may be used to generate hashed passwords.
+.Pp
+While
+.Nm
+distributed with
+.Nx
+has support for HTTP Basic Authorisation enabled by default,
+in the portable distribution it is excluded.
+Compile
+.Nm
+with
+.Dq -DDO_HTPASSWD
+on the compiler command line to enable this support.
+It may require linking with the crypt library, using
+.Dq -lcrypt .
+.Ss SSL SUPPORT
+.Nm
+has support for SSLv2, SSLv3, and TLSv1 protocols that is included by
+default.
+It requires linking with the crypto and ssl library, using
+.Dq -lcrypto -lssl .
+To disable SSL SUPPORT compile
+.Nm
+with
+.Dq -DNO_SSL_SUPPORT
+on the compiler command line.
+.Ss COMPRESSION
+.Nm
+supports a very basic form compression.
+.Nm
+will serve the requested file postpended with
+.Dq Pa .gz
+if it exists, it is readable, the client requested gzip compression, and
+the client did not make a ranged request.
+.Sh FILES
+.Nm
+looks for a couple of special files in directories that allow certain features
+to be provided on a per-directory basis.
+In addition to the
+.Pa .htpasswd
+used by HTTP basic authorisation,
+if a
+.Pa .bzdirect
+file is found (contents are irrelevant)
+.Nm
+will allow direct access even with the
+.Fl r
+option.
+If a
+.Pa .bzredirect
+symbolic link is found,
+.Nm
+will perform a smart redirect to the target of this symlink.
+The target is assumed to live on the same server.
+If a
+.Pa .bzabsredirect
+symbolic link is found,
+.Nm
+will redirect to the absolute url pointed to by this symlink.
+This is useful to redirect to different servers.
+.Sh EXAMPLES
+To configure set of virtual hosts, one would use an
+.Xr inetd.conf 5
+entry like:
+.Bd -literal
+http stream tcp nowait:600 _httpd /usr/libexec/httpd httpd -v /var/vroot /var/www
+.Ed
+.Pp
+and inside
+.Pa /var/vroot
+create a directory (or a symlink to a directory) with the same name as
+the virtual host, for each virtual host.
+Lookups for these names are done in a case-insensitive manner, and may
+include the port number part of the request, allowing for distinct
+virtual hosts on the same name.
+.Pp
+To use
+.Nm
+with PHP, one must use the
+.Fl C
+option to specify a CGI handler for a particular file type.
+Typically this will be like:
+.Bd -literal
+httpd -C .php /usr/pkg/bin/php /var/www
+.Ed
+.Sh SEE ALSO
+.Xr inetd.conf 5 ,
+.Xr inetd 8
+.Sh HISTORY
+.Nm
+was first written in perl, based on another perl http server
+called
+.Dq tinyhttpd .
+It was then rewritten from scratch in perl, and then once again in C.
+From
+.Dq bozohttpd
+version 20060517, it has been integrated into
+.Nx .
+The focus has always been simplicity and security, with minimal features
+and regular code audits.
+This manual documents
+.Nm
+version 20150501.
+.Sh AUTHORS
+.An -nosplit
+.Nm
+was written by
+.An Matthew R. Green
+.Aq Mt mrg@eterna.com.au .
+.Pp
+The large list of contributors includes:
+.Bl -dash
+.It
+.An Marc Balmer
+.Aq Mt mbalmer@NetBSD.org
+added Lua support for dynamic content creation
+.It
+.An Christoph Badura
+.Aq Mt bad@bsd.de
+provided Range: header support
+.It
+.An Sean Boudreau
+.Aq Mt seanb@NetBSD.org
+provided a security fix for virtual hosting
+.It
+.An Julian Coleman
+.Aq Mt jdc@coris.org.uk
+provided an IPv6 bugfix
+.It
+.An Chuck Cranor
+.Aq Mt chuck@research.att.com
+provided cgi-bin support fixes, and more
+.It
+.An Alistair G. Crooks
+.Aq Mt agc@NetBSD.org
+cleaned up many internal interfaces, made
+.Nm
+linkable as a library and provided the Lua binding.
+.It
+.An DEGROOTE Arnaud
+.Aq Mt degroote@NetBSD.org
+provided a fix for daemon mode
+.It
+.An Andrew Doran
+.Aq Mt ad@NetBSD.org
+provided directory indexing support
+.It
+.An Per Ekman
+.Aq Mt pek@pdc.kth.se
+provided a fix for a minor (non-security) buffer overflow condition
+.It
+.An Roland Dowdeswell
+.Aq Mt elric@NetBSD.org
+added support for serving gzipped files and better SSL handling
+.It
+.An Jun-ichiro itojun Hagino, KAME
+.Aq Mt itojun@iijlab.net
+provided initial IPv6 support
+.It
+.An Martin Husemann
+.Aq Mt martin@NetBSD.org
+provided .bzabsredirect support, and fixed various redirection issues
+.It
+.An Arto Huusko
+.Aq Mt arto.huusko@pp2.inet.fi
+provided fixes cgi-bin
+.It
+.An Roland Illig
+.Aq Mt roland.illig@gmx.de
+provided some off-by-one fixes
+.It
+.An Zak Johnson
+.Aq Mt zakj@nox.cx
+provided cgi-bin enhancements
+.It
+.An Nicolas Jombart
+.Aq Mt ecu@ipv42.net
+provided fixes for HTTP basic authorisation support
+.It
+.An Antti Kantee
+.Aq Mt pooka@NetBSD.org
+provided fixes for HTTP basic authorisation support
+.It
+.An Thomas Klausner
+.Aq Mt wiz@NetBSD.org
+provided many fixes and enhancements for the man page
+.It
+.An Mateusz Kocielski
+.Aq Mt shm@NetBSD.org
+fixed memory leaks, various issues with userdir support,
+information disclosure issues, added support for using CGI handlers
+with directory indexing and provided various other fixes.
+.It
+.An Arnaud Lacombe
+.Aq Mt alc@NetBSD.org
+provided some clean up for memory leaks
+.It
+.An Johnny Lam
+.Aq Mt jlam@NetBSD.org
+provided man page fixes
+.It
+.An Julio Merino
+.Aq Mt jmmv@NetBSD.org
+Added the
+.Fl P
+option.
+.It
+.An Luke Mewburn
+.Aq Mt lukem@NetBSD.org
+provided many various fixes, including cgi-bin fixes and enhancements,
+HTTP basic authorisation support and much code clean up
+.It
+.An Rajeev V. Pillai
+.Aq Mt rajeev_v_pillai@yahoo.com
+provided several fixes for virtual hosting
+.It
+.An Jeremy C. Reed
+.Aq Mt reed@NetBSD.org
+provided several clean up fixes, and man page updates
+.It
+.An Scott Reynolds
+.Aq Mt scottr@NetBSD.org
+provided various fixes
+.It
+.An Tyler Retzlaff
+.Aq Mt rtr@eterna.com.au
+provided SSL support, cgi-bin fixes and much other random other stuff
+.It
+.An rudolf
+.Aq Mt netbsd@eq.cz
+provided minor compile fixes and a CGI content map fix
+.It
+.An Steve Rumble
+.Aq Mt rumble@ephemeral.org
+provided the
+.Fl V
+option.
+.It
+.An Thor Lancelot Simon
+.Aq Mt tls@NetBSD.org
+enhanced cgi-bin support.
+.It
+.An Joerg Sonnenberger
+.Aq Mt joerg@NetBSD.org
+implemented If-Modified-Since support
+.It
+.An ISIHARA Takanori
+.Aq Mt ishit@oak.dti.ne.jp
+provided a man page fix
+.It
+.An Holger Weiss
+.Aq Mt holger@CIS.FU-Berlin.DE
+provided http authorisation fixes
+.It
+.Aq Mt xs@kittenz.org
+provided chroot and change-to-user support, and other various fixes
+.It
+Coyote Point provided various CGI fixes.
+.It
+.An Julio Merino
+added pidfile support and provided some man page fixes.
+.El
+.Pp
+There are probably others I have forgotten (let me know if you care)
+.Pp
+Please send all updates to
+.Nm
+to
+.Aq Mt mrg@eterna.com.au
+for inclusion in future releases.
+.Sh BUGS
+.Nm
+does not handle HTTP/1.1 chunked input from the client yet.
--- /dev/null
+/* $NetBSD: bozohttpd.c,v 1.66 2015/07/16 12:19:23 shm Exp $ */
+
+/* $eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2015 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 program is dedicated to the Great God of Processed Cheese */
+
+/*
+ * bozohttpd.c: minimal httpd; provides only these features:
+ * - HTTP/0.9 (by virtue of ..)
+ * - HTTP/1.0
+ * - HTTP/1.1
+ * - CGI/1.1 this will only be provided for "system" scripts
+ * - automatic "missing trailing slash" redirections
+ * - configurable translation of /~user/ to ~user/public_html,
+ * however, this does not include cgi-bin support
+ * - access lists via libwrap via inetd/tcpd
+ * - virtual hosting
+ * - not that we do not even pretend to understand MIME, but
+ * rely only on the HTTP specification
+ * - ipv6 support
+ * - automatic `index.html' generation
+ * - configurable server name
+ * - directory index generation
+ * - daemon mode (lacks libwrap support)
+ * - .htpasswd support
+ */
+
+/*
+ * requirements for minimal http/1.1 (at least, as documented in
+ * RFC 2616 (HTTP/1.1):
+ *
+ * - 14.11: content-encoding handling. [1]
+ *
+ * - 14.13: content-length handling. this is only a SHOULD header
+ * thus we could just not send it ever. [1]
+ *
+ * - 14.17: content-type handling. [1]
+ *
+ * - 14.28: if-unmodified-since handling. if-modified-since is
+ * done since, shouldn't be too hard for this one.
+ *
+ * [1] need to revisit to ensure proper behaviour
+ *
+ * and the following is a list of features that we do not need
+ * to have due to other limits, or are too lazy. there are more
+ * of these than are listed, but these are of particular note,
+ * and could perhaps be implemented.
+ *
+ * - 3.5/3.6: content/transfer codings. probably can ignore
+ * this? we "SHOULD"n't. but 4.4 says we should ignore a
+ * `content-length' header upon reciept of a `transfer-encoding'
+ * header.
+ *
+ * - 5.1.1: request methods. only MUST support GET and HEAD,
+ * but there are new ones besides POST that are currently
+ * supported: OPTIONS PUT DELETE TRACE and CONNECT, plus
+ * extensions not yet known?
+ *
+ * - 10.1: we can ignore informational status codes
+ *
+ * - 10.3.3/10.3.4/10.3.8: just use '302' codes always.
+ *
+ * - 14.1/14.2/14.3/14.27: we do not support Accept: headers.
+ * just ignore them and send the request anyway. they are
+ * only SHOULD.
+ *
+ * - 14.5/14.16/14.35: only support simple ranges: %d- and %d-%d
+ * would be nice to support more.
+ *
+ * - 14.9: we aren't a cache.
+ *
+ * - 14.15: content-md5 would be nice.
+ *
+ * - 14.24/14.26/14.27: if-match, if-none-match, if-range. be
+ * nice to support this.
+ *
+ * - 14.44: Vary: seems unneeded. ignore it for now.
+ */
+
+#ifndef INDEX_HTML
+#define INDEX_HTML "index.html"
+#endif
+#ifndef SERVER_SOFTWARE
+#define SERVER_SOFTWARE "bozohttpd/20150501"
+#endif
+#ifndef DIRECT_ACCESS_FILE
+#define DIRECT_ACCESS_FILE ".bzdirect"
+#endif
+#ifndef REDIRECT_FILE
+#define REDIRECT_FILE ".bzredirect"
+#endif
+#ifndef ABSREDIRECT_FILE
+#define ABSREDIRECT_FILE ".bzabsredirect"
+#endif
+#ifndef PUBLIC_HTML
+#define PUBLIC_HTML "public_html"
+#endif
+
+#ifndef USE_ARG
+#define USE_ARG(x) /*LINTED*/(void)&(x)
+#endif
+
+/*
+ * And so it begins ..
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <grp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "bozohttpd.h"
+
+#ifndef MAX_WAIT_TIME
+#define MAX_WAIT_TIME 60 /* hang around for 60 seconds max */
+#endif
+
+/* variables and functions */
+#ifndef LOG_FTP
+#define LOG_FTP LOG_DAEMON
+#endif
+
+volatile sig_atomic_t alarmhit;
+
+/*
+ * check there's enough space in the prefs and names arrays.
+ */
+static int
+size_arrays(bozoprefs_t *bozoprefs, unsigned needed)
+{
+ char **temp;
+
+ if (bozoprefs->size == 0) {
+ /* only get here first time around */
+ bozoprefs->size = needed;
+ if ((bozoprefs->name = calloc(sizeof(char *), needed)) == NULL) {
+ (void) fprintf(stderr, "size_arrays: bad alloc\n");
+ return 0;
+ }
+ if ((bozoprefs->value = calloc(sizeof(char *), needed)) == NULL) {
+ free(bozoprefs->name);
+ (void) fprintf(stderr, "size_arrays: bad alloc\n");
+ return 0;
+ }
+ } else if (bozoprefs->c == bozoprefs->size) {
+ /* only uses 'needed' when filled array */
+ bozoprefs->size += needed;
+ temp = realloc(bozoprefs->name, sizeof(char *) * needed);
+ if (temp == NULL) {
+ (void) fprintf(stderr, "size_arrays: bad alloc\n");
+ return 0;
+ }
+ bozoprefs->name = temp;
+ temp = realloc(bozoprefs->value, sizeof(char *) * needed);
+ if (temp == NULL) {
+ (void) fprintf(stderr, "size_arrays: bad alloc\n");
+ return 0;
+ }
+ bozoprefs->value = temp;
+ }
+ return 1;
+}
+
+static int
+findvar(bozoprefs_t *bozoprefs, const char *name)
+{
+ unsigned i;
+
+ for (i = 0 ; i < bozoprefs->c && strcmp(bozoprefs->name[i], name) != 0; i++)
+ ;
+ return (i == bozoprefs->c) ? -1 : (int)i;
+}
+
+int
+bozo_set_pref(bozoprefs_t *bozoprefs, const char *name, const char *value)
+{
+ int i;
+
+ if ((i = findvar(bozoprefs, name)) < 0) {
+ /* add the element to the array */
+ if (size_arrays(bozoprefs, bozoprefs->size + 15)) {
+ bozoprefs->name[i = bozoprefs->c++] = strdup(name);
+ }
+ } else {
+ /* replace the element in the array */
+ if (bozoprefs->value[i]) {
+ free(bozoprefs->value[i]);
+ bozoprefs->value[i] = NULL;
+ }
+ }
+ /* sanity checks for range of values go here */
+ bozoprefs->value[i] = strdup(value);
+ return 1;
+}
+
+/*
+ * get a variable's value, or NULL
+ */
+char *
+bozo_get_pref(bozoprefs_t *bozoprefs, const char *name)
+{
+ int i;
+
+ return ((i = findvar(bozoprefs, name)) < 0) ? NULL :
+ bozoprefs->value[i];
+}
+
+char *
+bozo_http_date(char *date, size_t datelen)
+{
+ struct tm *tm;
+ time_t now;
+
+ /* Sun, 06 Nov 1994 08:49:37 GMT */
+ now = time(NULL);
+ tm = gmtime(&now); /* HTTP/1.1 spec rev 06 sez GMT only */
+ strftime(date, datelen, "%a, %d %b %Y %H:%M:%S GMT", tm);
+ return date;
+}
+
+/*
+ * convert "in" into the three parts of a request (first line).
+ * we allocate into file and query, but return pointers into
+ * "in" for proto and method.
+ */
+static void
+parse_request(bozohttpd_t *httpd, char *in, char **method, char **file,
+ char **query, char **proto)
+{
+ ssize_t len;
+ char *val;
+
+ USE_ARG(httpd);
+ debug((httpd, DEBUG_EXPLODING, "parse in: %s", in));
+ *method = *file = *query = *proto = NULL;
+
+ len = (ssize_t)strlen(in);
+ val = bozostrnsep(&in, " \t\n\r", &len);
+ if (len < 1 || val == NULL)
+ return;
+ *method = val;
+
+ while (*in == ' ' || *in == '\t')
+ in++;
+ val = bozostrnsep(&in, " \t\n\r", &len);
+ if (len < 1) {
+ if (len == 0)
+ *file = val;
+ else
+ *file = in;
+ } else {
+ *file = val;
+
+ *query = strchr(*file, '?');
+ if (*query)
+ *(*query)++ = '\0';
+
+ if (in) {
+ while (*in && (*in == ' ' || *in == '\t'))
+ in++;
+ if (*in)
+ *proto = in;
+ }
+ }
+
+ /* allocate private copies */
+ *file = bozostrdup(httpd, *file);
+ if (*query)
+ *query = bozostrdup(httpd, *query);
+
+ debug((httpd, DEBUG_FAT,
+ "url: method: \"%s\" file: \"%s\" query: \"%s\" proto: \"%s\"",
+ *method, *file, *query, *proto));
+}
+
+/*
+ * cleanup a bozo_httpreq_t after use
+ */
+void
+bozo_clean_request(bozo_httpreq_t *request)
+{
+ struct bozoheaders *hdr, *ohdr = NULL;
+
+ if (request == NULL)
+ return;
+
+ /* If SSL enabled cleanup SSL structure. */
+ bozo_ssl_destroy(request->hr_httpd);
+
+ /* clean up request */
+ free(request->hr_remotehost);
+ free(request->hr_remoteaddr);
+ free(request->hr_serverport);
+ free(request->hr_virthostname);
+ free(request->hr_file);
+ free(request->hr_oldfile);
+ free(request->hr_query);
+ free(request->hr_host);
+ bozo_auth_cleanup(request);
+ for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr;
+ hdr = SIMPLEQ_NEXT(hdr, h_next)) {
+ free(hdr->h_value);
+ free(hdr->h_header);
+ free(ohdr);
+ ohdr = hdr;
+ }
+ free(ohdr);
+
+ free(request);
+}
+
+/*
+ * send a HTTP/1.1 408 response if we timeout.
+ */
+/* ARGSUSED */
+static void
+alarmer(int sig)
+{
+ alarmhit = 1;
+}
+
+/*
+ * add or merge this header (val: str) into the requests list
+ */
+static bozoheaders_t *
+addmerge_header(bozo_httpreq_t *request, char *val,
+ char *str, ssize_t len)
+{
+ struct bozoheaders *hdr;
+
+ USE_ARG(len);
+ /* do we exist already? */
+ SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) {
+ if (strcasecmp(val, hdr->h_header) == 0)
+ break;
+ }
+
+ if (hdr) {
+ /* yup, merge it in */
+ char *nval;
+
+ if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1) {
+ (void)bozo_http_error(request->hr_httpd, 500, NULL,
+ "memory allocation failure");
+ return NULL;
+ }
+ free(hdr->h_value);
+ hdr->h_value = nval;
+ } else {
+ /* nope, create a new one */
+
+ hdr = bozomalloc(request->hr_httpd, sizeof *hdr);
+ hdr->h_header = bozostrdup(request->hr_httpd, val);
+ if (str && *str)
+ hdr->h_value = bozostrdup(request->hr_httpd, str);
+ else
+ hdr->h_value = bozostrdup(request->hr_httpd, " ");
+
+ SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next);
+ request->hr_nheaders++;
+ }
+
+ return hdr;
+}
+
+/*
+ * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent
+ * to "HTTP/001.01"), we MUST parse this.
+ */
+static int
+process_proto(bozo_httpreq_t *request, const char *proto)
+{
+ char majorstr[16], *minorstr;
+ int majorint, minorint;
+
+ if (proto == NULL) {
+got_proto_09:
+ request->hr_proto = request->hr_httpd->consts.http_09;
+ debug((request->hr_httpd, DEBUG_FAT, "request %s is http/0.9",
+ request->hr_file));
+ return 0;
+ }
+
+ if (strncasecmp(proto, "HTTP/", 5) != 0)
+ goto bad;
+ strncpy(majorstr, proto + 5, sizeof majorstr);
+ majorstr[sizeof(majorstr)-1] = 0;
+ minorstr = strchr(majorstr, '.');
+ if (minorstr == NULL)
+ goto bad;
+ *minorstr++ = 0;
+
+ majorint = atoi(majorstr);
+ minorint = atoi(minorstr);
+
+ switch (majorint) {
+ case 0:
+ if (minorint != 9)
+ break;
+ goto got_proto_09;
+ case 1:
+ if (minorint == 0)
+ request->hr_proto = request->hr_httpd->consts.http_10;
+ else if (minorint == 1)
+ request->hr_proto = request->hr_httpd->consts.http_11;
+ else
+ break;
+
+ debug((request->hr_httpd, DEBUG_FAT, "request %s is %s",
+ request->hr_file, request->hr_proto));
+ SIMPLEQ_INIT(&request->hr_headers);
+ request->hr_nheaders = 0;
+ return 0;
+ }
+bad:
+ return bozo_http_error(request->hr_httpd, 404, NULL, "unknown prototype");
+}
+
+/*
+ * process each type of HTTP method, setting this HTTP requests
+ # method type.
+ */
+static struct method_map {
+ const char *name;
+ int type;
+} method_map[] = {
+ { "GET", HTTP_GET, },
+ { "POST", HTTP_POST, },
+ { "HEAD", HTTP_HEAD, },
+#if 0 /* other non-required http/1.1 methods */
+ { "OPTIONS", HTTP_OPTIONS, },
+ { "PUT", HTTP_PUT, },
+ { "DELETE", HTTP_DELETE, },
+ { "TRACE", HTTP_TRACE, },
+ { "CONNECT", HTTP_CONNECT, },
+#endif
+ { NULL, 0, },
+};
+
+static int
+process_method(bozo_httpreq_t *request, const char *method)
+{
+ struct method_map *mmp;
+
+ if (request->hr_proto == request->hr_httpd->consts.http_11)
+ request->hr_allow = "GET, HEAD, POST";
+
+ for (mmp = method_map; mmp->name; mmp++)
+ if (strcasecmp(method, mmp->name) == 0) {
+ request->hr_method = mmp->type;
+ request->hr_methodstr = mmp->name;
+ return 0;
+ }
+
+ return bozo_http_error(request->hr_httpd, 404, request, "unknown method");
+}
+
+/*
+ * This function reads a http request from stdin, returning a pointer to a
+ * bozo_httpreq_t structure, describing the request.
+ */
+bozo_httpreq_t *
+bozo_read_request(bozohttpd_t *httpd)
+{
+ struct sigaction sa;
+ char *str, *val, *method, *file, *proto, *query;
+ char *host, *addr, *port;
+ char bufport[10];
+ char hbuf[NI_MAXHOST], abuf[NI_MAXHOST];
+ struct sockaddr_storage ss;
+ ssize_t len;
+ int line = 0;
+ socklen_t slen;
+ bozo_httpreq_t *request;
+
+ /*
+ * if we're in daemon mode, bozo_daemon_fork() will return here twice
+ * for each call. once in the child, returning 0, and once in the
+ * parent, returning 1. for each child, then we can setup SSL, and
+ * the parent can signal the caller there was no request to process
+ * and it will wait for another.
+ */
+ if (bozo_daemon_fork(httpd))
+ return NULL;
+ if (bozo_ssl_accept(httpd))
+ return NULL;
+
+ request = bozomalloc(httpd, sizeof(*request));
+ memset(request, 0, sizeof(*request));
+ request->hr_httpd = httpd;
+ request->hr_allow = request->hr_host = NULL;
+ request->hr_content_type = request->hr_content_length = NULL;
+ request->hr_range = NULL;
+ request->hr_last_byte_pos = -1;
+ request->hr_if_modified_since = NULL;
+ request->hr_virthostname = NULL;
+ request->hr_file = NULL;
+ request->hr_oldfile = NULL;
+ bozo_auth_init(request);
+
+ slen = sizeof(ss);
+ if (getpeername(0, (struct sockaddr *)(void *)&ss, &slen) < 0)
+ host = addr = NULL;
+ else {
+ if (getnameinfo((struct sockaddr *)(void *)&ss, slen,
+ abuf, sizeof abuf, NULL, 0, NI_NUMERICHOST) == 0)
+ addr = abuf;
+ else
+ addr = NULL;
+ if (httpd->numeric == 0 &&
+ getnameinfo((struct sockaddr *)(void *)&ss, slen,
+ hbuf, sizeof hbuf, NULL, 0, 0) == 0)
+ host = hbuf;
+ else
+ host = NULL;
+ }
+ if (host != NULL)
+ request->hr_remotehost = bozostrdup(request->hr_httpd, host);
+ if (addr != NULL)
+ request->hr_remoteaddr = bozostrdup(request->hr_httpd, addr);
+ slen = sizeof(ss);
+
+ /*
+ * Override the bound port from the request value, so it works even
+ * if passed through a proxy that doesn't rewrite the port.
+ */
+ if (httpd->bindport) {
+ if (strcmp(httpd->bindport, "80") != 0)
+ port = httpd->bindport;
+ else
+ port = NULL;
+ } else {
+ if (getsockname(0, (struct sockaddr *)(void *)&ss, &slen) < 0)
+ port = NULL;
+ else {
+ if (getnameinfo((struct sockaddr *)(void *)&ss, slen, NULL, 0,
+ bufport, sizeof bufport, NI_NUMERICSERV) == 0)
+ port = bufport;
+ else
+ port = NULL;
+ }
+ }
+ if (port != NULL)
+ request->hr_serverport = bozostrdup(request->hr_httpd, port);
+
+ /*
+ * setup a timer to make sure the request is not hung
+ */
+ sa.sa_handler = alarmer;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); /* XXX */
+
+ alarm(MAX_WAIT_TIME);
+ while ((str = bozodgetln(httpd, STDIN_FILENO, &len, bozo_read)) != NULL) {
+ alarm(0);
+ if (alarmhit) {
+ (void)bozo_http_error(httpd, 408, NULL,
+ "request timed out");
+ goto cleanup;
+ }
+ line++;
+
+ if (line == 1) {
+
+ if (len < 1) {
+ (void)bozo_http_error(httpd, 404, NULL,
+ "null method");
+ goto cleanup;
+ }
+
+ bozo_warn(httpd, "got request ``%s'' from host %s to port %s",
+ str,
+ host ? host : addr ? addr : "<local>",
+ port ? port : "<stdin>");
+
+ /* we allocate return space in file and query only */
+ parse_request(httpd, str, &method, &file, &query, &proto);
+ request->hr_file = file;
+ request->hr_query = query;
+ if (method == NULL) {
+ (void)bozo_http_error(httpd, 404, NULL,
+ "null method");
+ goto cleanup;
+ }
+ if (file == NULL) {
+ (void)bozo_http_error(httpd, 404, NULL,
+ "null file");
+ goto cleanup;
+ }
+
+ /*
+ * note that we parse the proto first, so that we
+ * can more properly parse the method and the url.
+ */
+
+ if (process_proto(request, proto) ||
+ process_method(request, method)) {
+ goto cleanup;
+ }
+
+ debug((httpd, DEBUG_FAT, "got file \"%s\" query \"%s\"",
+ request->hr_file,
+ request->hr_query ? request->hr_query : "<none>"));
+
+ /* http/0.9 has no header processing */
+ if (request->hr_proto == httpd->consts.http_09)
+ break;
+ } else { /* incoming headers */
+ bozoheaders_t *hdr;
+
+ if (*str == '\0')
+ break;
+
+ val = bozostrnsep(&str, ":", &len);
+ debug((httpd, DEBUG_EXPLODING,
+ "read_req2: after bozostrnsep: str ``%s'' val ``%s''",
+ str, val));
+ if (val == NULL || len == -1) {
+ (void)bozo_http_error(httpd, 404, request,
+ "no header");
+ goto cleanup;
+ }
+ while (*str == ' ' || *str == '\t')
+ len--, str++;
+ while (*val == ' ' || *val == '\t')
+ val++;
+
+ if (bozo_auth_check_headers(request, val, str, len))
+ goto next_header;
+
+ hdr = addmerge_header(request, val, str, len);
+
+ if (strcasecmp(hdr->h_header, "content-type") == 0)
+ request->hr_content_type = hdr->h_value;
+ else if (strcasecmp(hdr->h_header, "content-length") == 0)
+ request->hr_content_length = hdr->h_value;
+ else if (strcasecmp(hdr->h_header, "host") == 0)
+ request->hr_host = bozostrdup(httpd, hdr->h_value);
+ /* RFC 2616 (HTTP/1.1): 14.20 */
+ else if (strcasecmp(hdr->h_header, "expect") == 0) {
+ (void)bozo_http_error(httpd, 417, request,
+ "we don't support Expect:");
+ goto cleanup;
+ }
+ else if (strcasecmp(hdr->h_header, "referrer") == 0 ||
+ strcasecmp(hdr->h_header, "referer") == 0)
+ request->hr_referrer = hdr->h_value;
+ else if (strcasecmp(hdr->h_header, "range") == 0)
+ request->hr_range = hdr->h_value;
+ else if (strcasecmp(hdr->h_header,
+ "if-modified-since") == 0)
+ request->hr_if_modified_since = hdr->h_value;
+ else if (strcasecmp(hdr->h_header,
+ "accept-encoding") == 0)
+ request->hr_accept_encoding = hdr->h_value;
+
+ debug((httpd, DEBUG_FAT, "adding header %s: %s",
+ hdr->h_header, hdr->h_value));
+ }
+next_header:
+ alarm(MAX_WAIT_TIME);
+ }
+
+ /* now, clear it all out */
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ /* RFC1945, 8.3 */
+ if (request->hr_method == HTTP_POST &&
+ request->hr_content_length == NULL) {
+ (void)bozo_http_error(httpd, 400, request,
+ "missing content length");
+ goto cleanup;
+ }
+
+ /* RFC 2616 (HTTP/1.1), 14.23 & 19.6.1.1 */
+ if (request->hr_proto == httpd->consts.http_11 &&
+ /*(strncasecmp(request->hr_file, "http://", 7) != 0) &&*/
+ request->hr_host == NULL) {
+ (void)bozo_http_error(httpd, 400, request,
+ "missing Host header");
+ goto cleanup;
+ }
+
+ if (request->hr_range != NULL) {
+ debug((httpd, DEBUG_FAT, "hr_range: %s", request->hr_range));
+ /* support only simple ranges %d- and %d-%d */
+ if (strchr(request->hr_range, ',') == NULL) {
+ const char *rstart, *dash;
+
+ rstart = strchr(request->hr_range, '=');
+ if (rstart != NULL) {
+ rstart++;
+ dash = strchr(rstart, '-');
+ if (dash != NULL && dash != rstart) {
+ dash++;
+ request->hr_have_range = 1;
+ request->hr_first_byte_pos =
+ strtoll(rstart, NULL, 10);
+ if (request->hr_first_byte_pos < 0)
+ request->hr_first_byte_pos = 0;
+ if (*dash != '\0') {
+ request->hr_last_byte_pos =
+ strtoll(dash, NULL, 10);
+ if (request->hr_last_byte_pos < 0)
+ request->hr_last_byte_pos = -1;
+ }
+ }
+ }
+ }
+ }
+
+ debug((httpd, DEBUG_FAT, "bozo_read_request returns url %s in request",
+ request->hr_file));
+ return request;
+
+cleanup:
+ bozo_clean_request(request);
+
+ return NULL;
+}
+
+static int
+mmap_and_write_part(bozohttpd_t *httpd, int fd, off_t first_byte_pos, size_t sz)
+{
+ size_t mappedsz, wroffset;
+ off_t mappedoffset;
+ char *addr;
+ void *mappedaddr;
+
+ /*
+ * we need to ensure that both the size *and* offset arguments to
+ * mmap() are page-aligned. our formala for this is:
+ *
+ * input offset: first_byte_pos
+ * input size: sz
+ *
+ * mapped offset = page align truncate (input offset)
+ * mapped size =
+ * page align extend (input offset - mapped offset + input size)
+ * write offset = input offset - mapped offset
+ *
+ * we use the write offset in all writes
+ */
+ mappedoffset = first_byte_pos & ~(httpd->page_size - 1);
+ mappedsz = (size_t)
+ (first_byte_pos - mappedoffset + sz + httpd->page_size - 1) &
+ ~(httpd->page_size - 1);
+ wroffset = (size_t)(first_byte_pos - mappedoffset);
+
+ addr = mmap(0, mappedsz, PROT_READ, MAP_SHARED, fd, mappedoffset);
+ if (addr == (char *)-1) {
+ bozo_warn(httpd, "mmap failed: %s", strerror(errno));
+ return -1;
+ }
+ mappedaddr = addr;
+
+#ifdef MADV_SEQUENTIAL
+#ifndef __minix
+ (void)madvise(addr, sz, MADV_SEQUENTIAL);
+#endif /* !__minix */
+#endif
+ while (sz > BOZO_WRSZ) {
+ if (bozo_write(httpd, STDOUT_FILENO, addr + wroffset,
+ BOZO_WRSZ) != BOZO_WRSZ) {
+ bozo_warn(httpd, "write failed: %s", strerror(errno));
+ goto out;
+ }
+ debug((httpd, DEBUG_OBESE, "wrote %d bytes", BOZO_WRSZ));
+ sz -= BOZO_WRSZ;
+ addr += BOZO_WRSZ;
+ }
+ if (sz && (size_t)bozo_write(httpd, STDOUT_FILENO, addr + wroffset,
+ sz) != sz) {
+ bozo_warn(httpd, "final write failed: %s", strerror(errno));
+ goto out;
+ }
+ debug((httpd, DEBUG_OBESE, "wrote %d bytes", (int)sz));
+ out:
+ if (munmap(mappedaddr, mappedsz) < 0) {
+ bozo_warn(httpd, "munmap failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+parse_http_date(const char *val, time_t *timestamp)
+{
+ char *remainder;
+ struct tm tm;
+
+ if ((remainder = strptime(val, "%a, %d %b %Y %T GMT", &tm)) == NULL &&
+ (remainder = strptime(val, "%a, %d-%b-%y %T GMT", &tm)) == NULL &&
+ (remainder = strptime(val, "%a %b %d %T %Y", &tm)) == NULL)
+ return 0; /* Invalid HTTP date format */
+
+ if (*remainder)
+ return 0; /* No trailing garbage */
+
+ *timestamp = timegm(&tm);
+ return 1;
+}
+
+/*
+ * given an url, encode it ala rfc 3986. ie, escape ? and friends.
+ * note that this function returns a static buffer, and thus needs
+ * to be updated for any sort of parallel processing.
+ */
+char *
+bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url)
+{
+ static char *buf;
+ static size_t buflen = 0;
+ size_t len;
+ const char *s;
+ char *d;
+
+ len = strlen(url);
+ if (buflen < len * 3 + 1) {
+ buflen = len * 3 + 1;
+ buf = bozorealloc(httpd, buf, buflen);
+ }
+
+ if (url == NULL) {
+ buf[0] = 0;
+ return buf;
+ }
+
+ for (len = 0, s = url, d = buf; *s;) {
+ if (*s & 0x80)
+ goto encode_it;
+ switch (*s) {
+ case ':':
+ case '?':
+ case '#':
+ case '[':
+ case ']':
+ case '@':
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ case '%':
+ case '\n':
+ case '\r':
+ case ' ':
+ case '"':
+ encode_it:
+ snprintf(d, 4, "%%%02X", *s++);
+ d += 3;
+ len += 3;
+ break;
+ default:
+ *d++ = *s++;
+ len++;
+ break;
+ }
+ }
+ buf[len] = 0;
+
+ return buf;
+}
+
+/*
+ * checks to see if this request has a valid .bzdirect file. returns
+ * 0 on failure and 1 on success.
+ */
+static int
+check_direct_access(bozo_httpreq_t *request)
+{
+ FILE *fp;
+ struct stat sb;
+ char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename;
+
+ snprintf(dir, sizeof(dir), "%s", request->hr_file + 1);
+ debug((request->hr_httpd, DEBUG_FAT, "check_direct_access: dir %s", dir));
+ basename = strrchr(dir, '/');
+
+ if ((!basename || basename[1] != '\0') &&
+ lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
+ /* nothing */;
+ else if (basename == NULL)
+ strcpy(dir, ".");
+ else {
+ *basename++ = '\0';
+ bozo_check_special_files(request, basename);
+ }
+
+ if ((size_t)snprintf(dirfile, sizeof(dirfile), "%s/%s", dir,
+ DIRECT_ACCESS_FILE) >= sizeof(dirfile)) {
+ bozo_http_error(request->hr_httpd, 404, request,
+ "directfile path too long");
+ return 0;
+ }
+ if (stat(dirfile, &sb) < 0 ||
+ (fp = fopen(dirfile, "r")) == NULL)
+ return 0;
+ fclose(fp);
+ return 1;
+}
+
+/*
+ * do automatic redirection -- if there are query parameters for the URL
+ * we will tack these on to the new (redirected) URL.
+ */
+static void
+handle_redirect(bozo_httpreq_t *request,
+ const char *url, int absolute)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ char *urlbuf;
+ char portbuf[20];
+ const char *hostname = BOZOHOST(httpd, request);
+ int query = 0;
+
+ if (url == NULL) {
+ if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0)
+ bozo_err(httpd, 1, "asprintf");
+ url = urlbuf;
+ } else
+ urlbuf = NULL;
+ url = bozo_escape_rfc3986(request->hr_httpd, url);
+
+ if (request->hr_query && strlen(request->hr_query))
+ query = 1;
+
+ if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0)
+ snprintf(portbuf, sizeof(portbuf), ":%s",
+ request->hr_serverport);
+ else
+ portbuf[0] = '\0';
+ if (absolute)
+ bozo_warn(httpd, "redirecting %s", url);
+ else
+ bozo_warn(httpd, "redirecting %s%s%s", hostname, portbuf, url);
+ debug((httpd, DEBUG_FAT, "redirecting %s", url));
+ bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto);
+ if (request->hr_proto != httpd->consts.http_09)
+ bozo_print_header(request, NULL, "text/html", NULL);
+ if (request->hr_proto != httpd->consts.http_09) {
+ bozo_printf(httpd, "Location: http://");
+ if (absolute == 0)
+ bozo_printf(httpd, "%s%s", hostname, portbuf);
+ if (query) {
+ bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query);
+ } else {
+ bozo_printf(httpd, "%s\r\n", url);
+ }
+ }
+ bozo_printf(httpd, "\r\n");
+ if (request->hr_method == HTTP_HEAD)
+ goto head;
+ bozo_printf(httpd, "<html><head><title>Document Moved</title></head>\n");
+ bozo_printf(httpd, "<body><h1>Document Moved</h1>\n");
+ bozo_printf(httpd, "This document had moved <a href=\"http://");
+ if (query) {
+ if (absolute)
+ bozo_printf(httpd, "%s?%s", url, request->hr_query);
+ else
+ bozo_printf(httpd, "%s%s%s?%s", hostname,
+ portbuf, url, request->hr_query);
+ } else {
+ if (absolute)
+ bozo_printf(httpd, "%s", url);
+ else
+ bozo_printf(httpd, "%s%s%s", hostname,
+ portbuf, url);
+ }
+ bozo_printf(httpd, "\">here</a>\n");
+ bozo_printf(httpd, "</body></html>\n");
+head:
+ bozo_flush(httpd, stdout);
+ free(urlbuf);
+}
+
+/*
+ * deal with virtual host names; we do this:
+ * if we have a virtual path root (httpd->virtbase), and we are given a
+ * virtual host spec (Host: ho.st or http://ho.st/), see if this
+ * directory exists under httpd->virtbase. if it does, use this as the
+ # new slashdir.
+ */
+static int
+check_virtual(bozo_httpreq_t *request)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ char *file = request->hr_file, *s;
+ size_t len;
+
+ if (!httpd->virtbase)
+ goto use_slashdir;
+
+ /*
+ * convert http://virtual.host/ to request->hr_host
+ */
+ debug((httpd, DEBUG_OBESE, "checking for http:// virtual host in ``%s''",
+ file));
+ if (strncasecmp(file, "http://", 7) == 0) {
+ /* we would do virtual hosting here? */
+ file += 7;
+ /* RFC 2616 (HTTP/1.1), 5.2: URI takes precedence over Host: */
+ free(request->hr_host);
+ request->hr_host = bozostrdup(request->hr_httpd, file);
+ if ((s = strchr(request->hr_host, '/')) != NULL)
+ *s = '\0';
+ s = strchr(file, '/');
+ free(request->hr_file);
+ request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/");
+ debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''",
+ request->hr_host, request->hr_file));
+ } else if (!request->hr_host)
+ goto use_slashdir;
+
+ /*
+ * canonicalise hr_host - that is, remove any :80.
+ */
+ len = strlen(request->hr_host);
+ if (len > 3 && strcmp(request->hr_host + len - 3, ":80") == 0) {
+ request->hr_host[len - 3] = '\0';
+ len = strlen(request->hr_host);
+ }
+
+ /*
+ * ok, we have a virtual host, use opendir(3) to find a case
+ * insensitive match for the virtual host we are asked for.
+ * note that if the virtual host is the same as the master,
+ * we don't need to do anything special.
+ */
+ debug((httpd, DEBUG_OBESE,
+ "check_virtual: checking host `%s' under httpd->virtbase `%s' "
+ "for file `%s'",
+ request->hr_host, httpd->virtbase, request->hr_file));
+ if (strncasecmp(httpd->virthostname, request->hr_host, len) != 0) {
+ s = 0;
+ DIR *dirp;
+ struct dirent *d;
+
+ if ((dirp = opendir(httpd->virtbase)) != NULL) {
+ while ((d = readdir(dirp)) != NULL) {
+ if (strcmp(d->d_name, ".") == 0 ||
+ strcmp(d->d_name, "..") == 0) {
+ continue;
+ }
+ debug((httpd, DEBUG_OBESE, "looking at dir``%s''",
+ d->d_name));
+ if (strcmp(d->d_name, request->hr_host) == 0) {
+ /* found it, punch it */
+ debug((httpd, DEBUG_OBESE, "found it punch it"));
+ request->hr_virthostname =
+ bozostrdup(httpd, d->d_name);
+ if (asprintf(&s, "%s/%s", httpd->virtbase,
+ request->hr_virthostname) < 0)
+ bozo_err(httpd, 1, "asprintf");
+ break;
+ }
+ }
+ closedir(dirp);
+ }
+ else {
+ debug((httpd, DEBUG_FAT, "opendir %s failed: %s",
+ httpd->virtbase, strerror(errno)));
+ }
+ if (s == 0) {
+ if (httpd->unknown_slash)
+ goto use_slashdir;
+ return bozo_http_error(httpd, 404, request,
+ "unknown URL");
+ }
+ } else
+use_slashdir:
+ s = httpd->slashdir;
+
+ /*
+ * ok, nailed the correct slashdir, chdir to it
+ */
+ if (chdir(s) < 0)
+ return bozo_http_error(httpd, 404, request,
+ "can't chdir to slashdir");
+ return 0;
+}
+
+/*
+ * checks to see if this request has a valid .bzredirect file. returns
+ * 0 when no redirection happend, or 1 when handle_redirect() has been
+ * called, -1 on error.
+ */
+static int
+check_bzredirect(bozo_httpreq_t *request)
+{
+ struct stat sb;
+ char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1],
+ path[MAXPATHLEN];
+ char *basename, *finalredir;
+ int rv, absolute;
+
+ /*
+ * if this pathname is really a directory, but doesn't end in /,
+ * use it as the directory to look for the redir file.
+ */
+ if((size_t)snprintf(dir, sizeof(dir), "%s", request->hr_file + 1) >=
+ sizeof(dir)) {
+ bozo_http_error(request->hr_httpd, 404, request,
+ "file path too long");
+ return -1;
+ }
+ debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir));
+ basename = strrchr(dir, '/');
+
+ if ((!basename || basename[1] != '\0') &&
+ lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
+ /* nothing */;
+ else if (basename == NULL)
+ strcpy(dir, ".");
+ else {
+ *basename++ = '\0';
+ bozo_check_special_files(request, basename);
+ }
+
+ if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir,
+ REDIRECT_FILE) >= sizeof(redir)) {
+ bozo_http_error(request->hr_httpd, 404, request,
+ "redirectfile path too long");
+ return -1;
+ }
+ if (lstat(redir, &sb) == 0) {
+ if (!S_ISLNK(sb.st_mode))
+ return 0;
+ absolute = 0;
+ } else {
+ if((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir,
+ ABSREDIRECT_FILE) >= sizeof(redir)) {
+ bozo_http_error(request->hr_httpd, 404, request,
+ "redirectfile path too long");
+ return -1;
+ }
+ if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode))
+ return 0;
+ absolute = 1;
+ }
+ debug((request->hr_httpd, DEBUG_FAT,
+ "check_bzredirect: calling readlink"));
+ rv = readlink(redir, redirpath, sizeof redirpath - 1);
+ if (rv == -1 || rv == 0) {
+ debug((request->hr_httpd, DEBUG_FAT, "readlink failed"));
+ return 0;
+ }
+ redirpath[rv] = '\0';
+ debug((request->hr_httpd, DEBUG_FAT,
+ "readlink returned \"%s\"", redirpath));
+
+ /* check if we need authentication */
+ snprintf(path, sizeof(path), "%s/", dir);
+ if (bozo_auth_check(request, path))
+ return 1;
+
+ /* now we have the link pointer, redirect to the real place */
+ if (absolute)
+ finalredir = redirpath;
+ else {
+ if ((size_t)snprintf(finalredir = redir, sizeof(redir), "/%s/%s",
+ dir, redirpath) >= sizeof(redir)) {
+ bozo_http_error(request->hr_httpd, 404, request,
+ "redirect path too long");
+ return -1;
+ }
+ }
+
+ debug((request->hr_httpd, DEBUG_FAT,
+ "check_bzredirect: new redir %s", finalredir));
+ handle_redirect(request, finalredir, absolute);
+ return 1;
+}
+
+/* this fixes the %HH hack that RFC2396 requires. */
+static int
+fix_url_percent(bozo_httpreq_t *request)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ char *s, *t, buf[3], *url;
+ char *end; /* if end is not-zero, we don't translate beyond that */
+
+ url = request->hr_file;
+
+ end = url + strlen(url);
+
+ /* fast forward to the first % */
+ if ((s = strchr(url, '%')) == NULL)
+ return 0;
+
+ t = s;
+ do {
+ if (end && s >= end) {
+ debug((httpd, DEBUG_EXPLODING,
+ "fu_%%: past end, filling out.."));
+ while (*s)
+ *t++ = *s++;
+ break;
+ }
+ debug((httpd, DEBUG_EXPLODING,
+ "fu_%%: got s == %%, s[1]s[2] == %c%c",
+ s[1], s[2]));
+ if (s[1] == '\0' || s[2] == '\0') {
+ (void)bozo_http_error(httpd, 400, request,
+ "percent hack missing two chars afterwards");
+ return 1;
+ }
+ if (s[1] == '0' && s[2] == '0') {
+ (void)bozo_http_error(httpd, 404, request,
+ "percent hack was %00");
+ return 1;
+ }
+ if (s[1] == '2' && s[2] == 'f') {
+ (void)bozo_http_error(httpd, 404, request,
+ "percent hack was %2f (/)");
+ return 1;
+ }
+
+ buf[0] = *++s;
+ buf[1] = *++s;
+ buf[2] = '\0';
+ s++;
+ *t = (char)strtol(buf, NULL, 16);
+ debug((httpd, DEBUG_EXPLODING,
+ "fu_%%: strtol put '%02x' into *t", *t));
+ if (*t++ == '\0') {
+ (void)bozo_http_error(httpd, 400, request,
+ "percent hack got a 0 back");
+ return 1;
+ }
+
+ while (*s && *s != '%') {
+ if (end && s >= end)
+ break;
+ *t++ = *s++;
+ }
+ } while (*s);
+ *t = '\0';
+
+ debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url",
+ request->hr_file));
+
+ return 0;
+}
+
+/*
+ * transform_request does this:
+ * - ``expand'' %20 crapola
+ * - punt if it doesn't start with /
+ * - check httpd->untrustedref / referrer
+ * - look for "http://myname/" and deal with it.
+ * - maybe call bozo_process_cgi()
+ * - check for ~user and call bozo_user_transform() if so
+ * - if the length > 1, check for trailing slash. if so,
+ * add the index.html file
+ * - if the length is 1, return the index.html file
+ * - disallow anything ending up with a file starting
+ * at "/" or having ".." in it.
+ * - anything else is a really weird internal error
+ * - returns malloced file to serve, if unhandled
+ */
+static int
+transform_request(bozo_httpreq_t *request, int *isindex)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ char *file, *newfile = NULL;
+ size_t len;
+ const char *hostname = BOZOHOST(httpd, request);
+
+ file = NULL;
+ *isindex = 0;
+ debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file));
+ if (fix_url_percent(request)) {
+ goto bad_done;
+ }
+ if (check_virtual(request)) {
+ goto bad_done;
+ }
+ file = request->hr_file;
+
+ if (file[0] != '/') {
+ (void)bozo_http_error(httpd, 404, request, "unknown URL");
+ goto bad_done;
+ }
+
+ /* omit additional slashes at the beginning */
+ while (file[1] == '/')
+ file++;
+
+ switch(check_bzredirect(request)) {
+ case -1:
+ goto bad_done;
+ case 1:
+ return 0;
+ }
+
+ if (httpd->untrustedref) {
+ int to_indexhtml = 0;
+
+#define TOP_PAGE(x) (strcmp((x), "/") == 0 || \
+ strcmp((x) + 1, httpd->index_html) == 0 || \
+ strcmp((x) + 1, "favicon.ico") == 0)
+
+ debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref"));
+ /*
+ * first check that this path isn't allowed via .bzdirect file,
+ * and then check referrer; make sure that people come via the
+ * real name... otherwise if we aren't looking at / or
+ * /index.html, redirect... we also special case favicon.ico.
+ */
+ if (check_direct_access(request))
+ /* nothing */;
+ else if (request->hr_referrer) {
+ const char *r = request->hr_referrer;
+
+ debug((httpd, DEBUG_FAT,
+ "checking referrer \"%s\" vs virthostname %s",
+ r, hostname));
+ if (strncmp(r, "http://", 7) != 0 ||
+ (strncasecmp(r + 7, hostname,
+ strlen(hostname)) != 0 &&
+ !TOP_PAGE(file)))
+ to_indexhtml = 1;
+ } else {
+ const char *h = request->hr_host;
+
+ debug((httpd, DEBUG_FAT, "url has no referrer at all"));
+ /* if there's no referrer, let / or /index.html past */
+ if (!TOP_PAGE(file) ||
+ (h && strncasecmp(h, hostname,
+ strlen(hostname)) != 0))
+ to_indexhtml = 1;
+ }
+
+ if (to_indexhtml) {
+ char *slashindexhtml;
+
+ if (asprintf(&slashindexhtml, "/%s",
+ httpd->index_html) < 0)
+ bozo_err(httpd, 1, "asprintf");
+ debug((httpd, DEBUG_FAT,
+ "httpd->untrustedref: redirecting %s to %s",
+ file, slashindexhtml));
+ handle_redirect(request, slashindexhtml, 0);
+ free(slashindexhtml);
+ return 0;
+ }
+ }
+
+ len = strlen(file);
+ if (/*CONSTCOND*/0) {
+#ifndef NO_USER_SUPPORT
+ } else if (len > 1 && httpd->enable_users && file[1] == '~') {
+ if (file[2] == '\0') {
+ (void)bozo_http_error(httpd, 404, request,
+ "missing username");
+ goto bad_done;
+ }
+ if (strchr(file + 2, '/') == NULL) {
+ handle_redirect(request, NULL, 0);
+ return 0;
+ }
+ debug((httpd, DEBUG_FAT, "calling bozo_user_transform"));
+
+ return bozo_user_transform(request, isindex);
+#endif /* NO_USER_SUPPORT */
+ } else if (len > 1) {
+ debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1]));
+ if (file[len-1] == '/') { /* append index.html */
+ *isindex = 1;
+ debug((httpd, DEBUG_FAT, "appending index.html"));
+ newfile = bozomalloc(httpd,
+ len + strlen(httpd->index_html) + 1);
+ strcpy(newfile, file + 1);
+ strcat(newfile, httpd->index_html);
+ } else
+ newfile = bozostrdup(request->hr_httpd, file + 1);
+ } else if (len == 1) {
+ debug((httpd, DEBUG_EXPLODING, "tf_req: len == 1"));
+ newfile = bozostrdup(request->hr_httpd, httpd->index_html);
+ *isindex = 1;
+ } else { /* len == 0 ? */
+ (void)bozo_http_error(httpd, 500, request,
+ "request->hr_file is nul?");
+ goto bad_done;
+ }
+
+ if (newfile == NULL) {
+ (void)bozo_http_error(httpd, 500, request, "internal failure");
+ goto bad_done;
+ }
+
+ /*
+ * look for "http://myname/" and deal with it as necessary.
+ */
+
+ /*
+ * stop traversing outside our domain
+ *
+ * XXX true security only comes from our parent using chroot(2)
+ * before execve(2)'ing us. or our own built in chroot(2) support.
+ */
+ if (*newfile == '/' || strcmp(newfile, "..") == 0 ||
+ strstr(newfile, "/..") || strstr(newfile, "../")) {
+ (void)bozo_http_error(httpd, 403, request, "illegal request");
+ goto bad_done;
+ }
+
+ if (bozo_auth_check(request, newfile))
+ goto bad_done;
+
+ if (strlen(newfile)) {
+ request->hr_oldfile = request->hr_file;
+ request->hr_file = newfile;
+ }
+
+ if (bozo_process_cgi(request))
+ return 0;
+
+ if (bozo_process_lua(request))
+ return 0;
+
+ debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile));
+ return 1;
+bad_done:
+ debug((httpd, DEBUG_FAT, "transform_request returning: 0"));
+ free(newfile);
+ return 0;
+}
+
+/*
+ * can_gzip checks if the request supports and prefers gzip encoding.
+ *
+ * XXX: we do not consider the associated q with gzip in making our
+ * decision which is broken.
+ */
+
+static int
+can_gzip(bozo_httpreq_t *request)
+{
+ const char *pos;
+ const char *tmp;
+ size_t len;
+
+ /* First we decide if the request can be gzipped at all. */
+
+ /* not if we already are encoded... */
+ tmp = bozo_content_encoding(request, request->hr_file);
+ if (tmp && *tmp)
+ return 0;
+
+ /* not if we are not asking for the whole file... */
+ if (request->hr_last_byte_pos != -1 || request->hr_have_range)
+ return 0;
+
+ /* Then we determine if gzip is on the cards. */
+
+ for (pos = request->hr_accept_encoding; pos && *pos; pos += len) {
+ while (*pos == ' ')
+ pos++;
+
+ len = strcspn(pos, ";,");
+
+ if ((len == 4 && strncasecmp("gzip", pos, 4) == 0) ||
+ (len == 6 && strncasecmp("x-gzip", pos, 6) == 0))
+ return 1;
+
+ if (pos[len] == ';')
+ len += strcspn(&pos[len], ",");
+
+ if (pos[len])
+ len++;
+ }
+
+ return 0;
+}
+
+/*
+ * bozo_process_request does the following:
+ * - check the request is valid
+ * - process cgi-bin if necessary
+ * - transform a filename if necesarry
+ * - return the HTTP request
+ */
+void
+bozo_process_request(bozo_httpreq_t *request)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ struct stat sb;
+ time_t timestamp;
+ char *file;
+ const char *type, *encoding;
+ int fd, isindex;
+
+ /*
+ * note that transform_request chdir()'s if required. also note
+ * that cgi is handed here. if transform_request() returns 0
+ * then the request has been handled already.
+ */
+ if (transform_request(request, &isindex) == 0)
+ return;
+
+ fd = -1;
+ encoding = NULL;
+ if (can_gzip(request)) {
+ asprintf(&file, "%s.gz", request->hr_file);
+ fd = open(file, O_RDONLY);
+ if (fd >= 0)
+ encoding = "gzip";
+ free(file);
+ }
+
+ file = request->hr_file;
+
+ if (fd < 0)
+ fd = open(file, O_RDONLY);
+
+ if (fd < 0) {
+ debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno)));
+ switch(errno) {
+ case EPERM:
+ case EACCES:
+ (void)bozo_http_error(httpd, 403, request,
+ "no permission to open file");
+ break;
+ case ENAMETOOLONG:
+ /*FALLTHROUGH*/
+ case ENOENT:
+ if (!bozo_dir_index(request, file, isindex))
+ (void)bozo_http_error(httpd, 404, request,
+ "no file");
+ break;
+ default:
+ (void)bozo_http_error(httpd, 500, request, "open file");
+ }
+ goto cleanup_nofd;
+ }
+ if (fstat(fd, &sb) < 0) {
+ (void)bozo_http_error(httpd, 500, request, "can't fstat");
+ goto cleanup;
+ }
+ if (S_ISDIR(sb.st_mode)) {
+ handle_redirect(request, NULL, 0);
+ goto cleanup;
+ }
+
+ if (request->hr_if_modified_since &&
+ parse_http_date(request->hr_if_modified_since, ×tamp) &&
+ timestamp >= sb.st_mtime) {
+ /* XXX ignore subsecond of timestamp */
+ bozo_printf(httpd, "%s 304 Not Modified\r\n",
+ request->hr_proto);
+ bozo_printf(httpd, "\r\n");
+ bozo_flush(httpd, stdout);
+ goto cleanup;
+ }
+
+ /* validate requested range */
+ if (request->hr_last_byte_pos == -1 ||
+ request->hr_last_byte_pos >= sb.st_size)
+ request->hr_last_byte_pos = sb.st_size - 1;
+ if (request->hr_have_range &&
+ request->hr_first_byte_pos > request->hr_last_byte_pos) {
+ request->hr_have_range = 0; /* punt */
+ request->hr_first_byte_pos = 0;
+ request->hr_last_byte_pos = sb.st_size - 1;
+ }
+ debug((httpd, DEBUG_FAT, "have_range %d first_pos %lld last_pos %lld",
+ request->hr_have_range,
+ (long long)request->hr_first_byte_pos,
+ (long long)request->hr_last_byte_pos));
+ if (request->hr_have_range)
+ bozo_printf(httpd, "%s 206 Partial Content\r\n",
+ request->hr_proto);
+ else
+ bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto);
+
+ if (request->hr_proto != httpd->consts.http_09) {
+ type = bozo_content_type(request, file);
+ if (!encoding)
+ encoding = bozo_content_encoding(request, file);
+
+ bozo_print_header(request, &sb, type, encoding);
+ bozo_printf(httpd, "\r\n");
+ }
+ bozo_flush(httpd, stdout);
+
+ if (request->hr_method != HTTP_HEAD) {
+ off_t szleft, cur_byte_pos;
+
+ szleft =
+ request->hr_last_byte_pos - request->hr_first_byte_pos + 1;
+ cur_byte_pos = request->hr_first_byte_pos;
+
+ retry:
+ while (szleft) {
+ size_t sz;
+
+ /* This should take care of the first unaligned chunk */
+ if ((cur_byte_pos & (httpd->page_size - 1)) != 0)
+ sz = (size_t)(cur_byte_pos & ~httpd->page_size);
+ if ((off_t)httpd->mmapsz < szleft)
+ sz = httpd->mmapsz;
+ else
+ sz = (size_t)szleft;
+ if (mmap_and_write_part(httpd, fd, cur_byte_pos, sz)) {
+ if (errno == ENOMEM) {
+ httpd->mmapsz /= 2;
+ if (httpd->mmapsz >= httpd->page_size)
+ goto retry;
+ }
+ goto cleanup;
+ }
+ cur_byte_pos += sz;
+ szleft -= sz;
+ }
+ }
+ cleanup:
+ close(fd);
+ cleanup_nofd:
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ /*close(STDERR_FILENO);*/
+}
+
+/* make sure we're not trying to access special files */
+int
+bozo_check_special_files(bozo_httpreq_t *request, const char *name)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+
+ /* ensure basename(name) != special files */
+ if (strcmp(name, DIRECT_ACCESS_FILE) == 0)
+ return bozo_http_error(httpd, 403, request,
+ "no permission to open direct access file");
+ if (strcmp(name, REDIRECT_FILE) == 0)
+ return bozo_http_error(httpd, 403, request,
+ "no permission to open redirect file");
+ if (strcmp(name, ABSREDIRECT_FILE) == 0)
+ return bozo_http_error(httpd, 403, request,
+ "no permission to open redirect file");
+ return bozo_auth_check_special_files(request, name);
+}
+
+/* generic header printing routine */
+void
+bozo_print_header(bozo_httpreq_t *request,
+ struct stat *sbp, const char *type, const char *encoding)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ off_t len;
+ char date[40];
+
+ bozo_printf(httpd, "Date: %s\r\n", bozo_http_date(date, sizeof(date)));
+ bozo_printf(httpd, "Server: %s\r\n", httpd->server_software);
+ bozo_printf(httpd, "Accept-Ranges: bytes\r\n");
+ if (sbp) {
+ char filedate[40];
+ struct tm *tm;
+
+ tm = gmtime(&sbp->st_mtime);
+ strftime(filedate, sizeof filedate,
+ "%a, %d %b %Y %H:%M:%S GMT", tm);
+ bozo_printf(httpd, "Last-Modified: %s\r\n", filedate);
+ }
+ if (type && *type)
+ bozo_printf(httpd, "Content-Type: %s\r\n", type);
+ if (encoding && *encoding)
+ bozo_printf(httpd, "Content-Encoding: %s\r\n", encoding);
+ if (sbp) {
+ if (request->hr_have_range) {
+ len = request->hr_last_byte_pos -
+ request->hr_first_byte_pos +1;
+ bozo_printf(httpd,
+ "Content-Range: bytes %qd-%qd/%qd\r\n",
+ (long long) request->hr_first_byte_pos,
+ (long long) request->hr_last_byte_pos,
+ (long long) sbp->st_size);
+ } else
+ len = sbp->st_size;
+ bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len);
+ }
+ if (request && request->hr_proto == httpd->consts.http_11)
+ bozo_printf(httpd, "Connection: close\r\n");
+ bozo_flush(httpd, stdout);
+}
+
+#ifndef NO_DEBUG
+void
+debug__(bozohttpd_t *httpd, int level, const char *fmt, ...)
+{
+ va_list ap;
+ int savederrno;
+
+ /* only log if the level is low enough */
+ if (httpd->debug < level)
+ return;
+
+ savederrno = errno;
+ va_start(ap, fmt);
+ if (httpd->logstderr) {
+ vfprintf(stderr, fmt, ap);
+ fputs("\n", stderr);
+ } else
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+ errno = savederrno;
+}
+#endif /* NO_DEBUG */
+
+/* these are like warn() and err(), except for syslog not stderr */
+void
+bozo_warn(bozohttpd_t *httpd, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (httpd->logstderr || isatty(STDERR_FILENO)) {
+ //fputs("warning: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputs("\n", stderr);
+ } else
+ vsyslog(LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+void
+bozo_err(bozohttpd_t *httpd, int code, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (httpd->logstderr || isatty(STDERR_FILENO)) {
+ //fputs("error: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputs("\n", stderr);
+ } else
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(code);
+}
+
+/*
+ * this escapes HTML tags. returns allocated escaped
+ * string if needed, or NULL on allocation failure or
+ * lack of escape need.
+ * call with NULL httpd in error paths, to avoid recursive
+ * malloc failure. call with valid httpd in normal paths
+ * to get automatic allocation failure handling.
+ */
+char *
+bozo_escape_html(bozohttpd_t *httpd, const char *url)
+{
+ int i, j;
+ char *tmp;
+ size_t len;
+
+ for (i = 0, j = 0; url[i]; i++) {
+ switch (url[i]) {
+ case '<':
+ case '>':
+ j += 4;
+ break;
+ case '&':
+ j += 5;
+ break;
+ }
+ }
+
+ if (j == 0)
+ return NULL;
+
+ /*
+ * we need to handle being called from different
+ * pathnames.
+ */
+ len = strlen(url) + j;
+ if (httpd)
+ tmp = bozomalloc(httpd, len);
+ else if ((tmp = malloc(len)) == 0)
+ return NULL;
+
+ for (i = 0, j = 0; url[i]; i++) {
+ switch (url[i]) {
+ case '<':
+ memcpy(tmp + j, "<", 4);
+ j += 4;
+ break;
+ case '>':
+ memcpy(tmp + j, ">", 4);
+ j += 4;
+ break;
+ case '&':
+ memcpy(tmp + j, "&", 5);
+ j += 5;
+ break;
+ default:
+ tmp[j++] = url[i];
+ }
+ }
+ tmp[j] = 0;
+
+ return tmp;
+}
+
+/* short map between error code, and short/long messages */
+static struct errors_map {
+ int code; /* HTTP return code */
+ const char *shortmsg; /* short version of message */
+ const char *longmsg; /* long version of message */
+} errors_map[] = {
+ { 400, "400 Bad Request", "The request was not valid", },
+ { 401, "401 Unauthorized", "No authorization", },
+ { 403, "403 Forbidden", "Access to this item has been denied",},
+ { 404, "404 Not Found", "This item has not been found", },
+ { 408, "408 Request Timeout", "This request took too long", },
+ { 417, "417 Expectation Failed","Expectations not available", },
+ { 420, "420 Enhance Your Calm","Chill, Winston", },
+ { 500, "500 Internal Error", "An error occured on the server", },
+ { 501, "501 Not Implemented", "This request is not available", },
+ { 0, NULL, NULL, },
+};
+
+static const char *help = "DANGER! WILL ROBINSON! DANGER!";
+
+static const char *
+http_errors_short(int code)
+{
+ struct errors_map *ep;
+
+ for (ep = errors_map; ep->code; ep++)
+ if (ep->code == code)
+ return (ep->shortmsg);
+ return (help);
+}
+
+static const char *
+http_errors_long(int code)
+{
+ struct errors_map *ep;
+
+ for (ep = errors_map; ep->code; ep++)
+ if (ep->code == code)
+ return (ep->longmsg);
+ return (help);
+}
+
+/* the follow functions and variables are used in handling HTTP errors */
+/* ARGSUSED */
+int
+bozo_http_error(bozohttpd_t *httpd, int code, bozo_httpreq_t *request,
+ const char *msg)
+{
+ char portbuf[20];
+ const char *header = http_errors_short(code);
+ const char *reason = http_errors_long(code);
+ const char *proto = (request && request->hr_proto) ?
+ request->hr_proto : httpd->consts.http_11;
+ int size;
+
+ debug((httpd, DEBUG_FAT, "bozo_http_error %d: %s", code, msg));
+ if (header == NULL || reason == NULL) {
+ bozo_err(httpd, 1,
+ "bozo_http_error() failed (short = %p, long = %p)",
+ header, reason);
+ return code;
+ }
+
+ if (request && request->hr_serverport &&
+ strcmp(request->hr_serverport, "80") != 0)
+ snprintf(portbuf, sizeof(portbuf), ":%s",
+ request->hr_serverport);
+ else
+ portbuf[0] = '\0';
+
+ if (request && request->hr_file) {
+ char *file = NULL;
+ const char *hostname = BOZOHOST(httpd, request);
+
+ /* bozo_escape_html() failure here is just too bad. */
+ file = bozo_escape_html(NULL, request->hr_file);
+ if (file == NULL)
+ file = request->hr_file;
+ size = snprintf(httpd->errorbuf, BUFSIZ,
+ "<html><head><title>%s</title></head>\n"
+ "<body><h1>%s</h1>\n"
+ "%s: <pre>%s</pre>\n"
+ "<hr><address><a href=\"http://%s%s/\">%s%s</a></address>\n"
+ "</body></html>\n",
+ header, header, file, reason,
+ hostname, portbuf, hostname, portbuf);
+ if (size >= (int)BUFSIZ) {
+ bozo_warn(httpd,
+ "bozo_http_error buffer too small, truncated");
+ size = (int)BUFSIZ;
+ }
+ } else
+ size = 0;
+
+ bozo_printf(httpd, "%s %s\r\n", proto, header);
+ if (request)
+ bozo_auth_check_401(request, code);
+
+ bozo_printf(httpd, "Content-Type: text/html\r\n");
+ bozo_printf(httpd, "Content-Length: %d\r\n", size);
+ bozo_printf(httpd, "Server: %s\r\n", httpd->server_software);
+ if (request && request->hr_allow)
+ bozo_printf(httpd, "Allow: %s\r\n", request->hr_allow);
+ bozo_printf(httpd, "\r\n");
+ /* According to the RFC 2616 sec. 9.4 HEAD method MUST NOT return a
+ * message-body in the response */
+ if (size && request && request->hr_method != HTTP_HEAD)
+ bozo_printf(httpd, "%s", httpd->errorbuf);
+ bozo_flush(httpd, stdout);
+
+ return code;
+}
+
+/* Below are various modified libc functions */
+
+/*
+ * returns -1 in lenp if the string ran out before finding a delimiter,
+ * but is otherwise the same as strsep. Note that the length must be
+ * correctly passed in.
+ */
+char *
+bozostrnsep(char **strp, const char *delim, ssize_t *lenp)
+{
+ char *s;
+ const char *spanp;
+ int c, sc;
+ char *tok;
+
+ if ((s = *strp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ if (lenp && --(*lenp) == -1)
+ return (NULL);
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = '\0';
+ *strp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * inspired by fgetln(3), but works for fd's. should work identically
+ * except it, however, does *not* return the newline, and it does nul
+ * terminate the string.
+ */
+char *
+bozodgetln(bozohttpd_t *httpd, int fd, ssize_t *lenp,
+ ssize_t (*readfn)(bozohttpd_t *, int, void *, size_t))
+{
+ ssize_t len;
+ int got_cr = 0;
+ char c, *nbuffer;
+
+ /* initialise */
+ if (httpd->getln_buflen == 0) {
+ /* should be plenty for most requests */
+ httpd->getln_buflen = 128;
+ httpd->getln_buffer = malloc((size_t)httpd->getln_buflen);
+ if (httpd->getln_buffer == NULL) {
+ httpd->getln_buflen = 0;
+ return NULL;
+ }
+ }
+ len = 0;
+
+ /*
+ * we *have* to read one byte at a time, to not break cgi
+ * programs (for we pass stdin off to them). could fix this
+ * by becoming a fd-passing program instead of just exec'ing
+ * the program
+ *
+ * the above is no longer true, we are the fd-passing
+ * program already.
+ */
+ for (; readfn(httpd, fd, &c, 1) == 1; ) {
+ debug((httpd, DEBUG_EXPLODING, "bozodgetln read %c", c));
+
+ if (len >= httpd->getln_buflen - 1) {
+ httpd->getln_buflen *= 2;
+ debug((httpd, DEBUG_EXPLODING, "bozodgetln: "
+ "reallocating buffer to buflen %zu",
+ httpd->getln_buflen));
+ nbuffer = bozorealloc(httpd, httpd->getln_buffer,
+ (size_t)httpd->getln_buflen);
+ httpd->getln_buffer = nbuffer;
+ }
+
+ httpd->getln_buffer[len++] = c;
+ if (c == '\r') {
+ got_cr = 1;
+ continue;
+ } else if (c == '\n') {
+ /*
+ * HTTP/1.1 spec says to ignore CR and treat
+ * LF as the real line terminator. even though
+ * the same spec defines CRLF as the line
+ * terminator, it is recommended in section 19.3
+ * to do the LF trick for tolerance.
+ */
+ if (got_cr)
+ len -= 2;
+ else
+ len -= 1;
+ break;
+ }
+
+ }
+ httpd->getln_buffer[len] = '\0';
+ debug((httpd, DEBUG_OBESE, "bozodgetln returns: ``%s'' with len %zd",
+ httpd->getln_buffer, len));
+ *lenp = len;
+ return httpd->getln_buffer;
+}
+
+void *
+bozorealloc(bozohttpd_t *httpd, void *ptr, size_t size)
+{
+ void *p;
+
+ p = realloc(ptr, size);
+ if (p == NULL) {
+ (void)bozo_http_error(httpd, 500, NULL,
+ "memory allocation failure");
+ exit(1);
+ }
+ return (p);
+}
+
+void *
+bozomalloc(bozohttpd_t *httpd, size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL) {
+ (void)bozo_http_error(httpd, 500, NULL,
+ "memory allocation failure");
+ exit(1);
+ }
+ return (p);
+}
+
+char *
+bozostrdup(bozohttpd_t *httpd, const char *str)
+{
+ char *p;
+
+ p = strdup(str);
+ if (p == NULL) {
+ (void)bozo_http_error(httpd, 500, NULL,
+ "memory allocation failure");
+ exit(1);
+ }
+ return (p);
+}
+
+/* set default values in bozohttpd_t struct */
+int
+bozo_init_httpd(bozohttpd_t *httpd)
+{
+ /* make sure everything is clean */
+ (void) memset(httpd, 0x0, sizeof(*httpd));
+
+ /* constants */
+ httpd->consts.http_09 = "HTTP/0.9";
+ httpd->consts.http_10 = "HTTP/1.0";
+ httpd->consts.http_11 = "HTTP/1.1";
+ httpd->consts.text_plain = "text/plain";
+
+ /* mmap region size */
+ httpd->mmapsz = BOZO_MMAPSZ;
+
+ /* error buffer for bozo_http_error() */
+ if ((httpd->errorbuf = malloc(BUFSIZ)) == NULL) {
+ (void) fprintf(stderr,
+ "bozohttpd: memory_allocation failure\n");
+ return 0;
+ }
+#ifndef NO_LUA_SUPPORT
+ SIMPLEQ_INIT(&httpd->lua_states);
+#endif
+ return 1;
+}
+
+/* set default values in bozoprefs_t struct */
+int
+bozo_init_prefs(bozoprefs_t *prefs)
+{
+ /* make sure everything is clean */
+ (void) memset(prefs, 0x0, sizeof(*prefs));
+
+ /* set up default values */
+ bozo_set_pref(prefs, "server software", SERVER_SOFTWARE);
+ bozo_set_pref(prefs, "index.html", INDEX_HTML);
+ bozo_set_pref(prefs, "public_html", PUBLIC_HTML);
+
+ return 1;
+}
+
+/* set default values */
+int
+bozo_set_defaults(bozohttpd_t *httpd, bozoprefs_t *prefs)
+{
+ return bozo_init_httpd(httpd) && bozo_init_prefs(prefs);
+}
+
+/* set the virtual host name, port and root */
+int
+bozo_setup(bozohttpd_t *httpd, bozoprefs_t *prefs, const char *vhost,
+ const char *root)
+{
+ struct passwd *pw;
+ extern char **environ;
+ static char *cleanenv[1] = { NULL };
+ uid_t uid;
+ char *chrootdir;
+ char *username;
+ char *portnum;
+ char *cp;
+ int dirtyenv;
+
+ dirtyenv = 0;
+
+ if (vhost == NULL) {
+ httpd->virthostname = bozomalloc(httpd, MAXHOSTNAMELEN+1);
+ /* XXX we do not check for FQDN here */
+ if (gethostname(httpd->virthostname, MAXHOSTNAMELEN+1) < 0)
+ bozo_err(httpd, 1, "gethostname");
+ httpd->virthostname[MAXHOSTNAMELEN] = '\0';
+ } else {
+ httpd->virthostname = strdup(vhost);
+ }
+ httpd->slashdir = strdup(root);
+ if ((portnum = bozo_get_pref(prefs, "port number")) != NULL) {
+ httpd->bindport = strdup(portnum);
+ }
+
+ /* go over preferences now */
+ if ((cp = bozo_get_pref(prefs, "numeric")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->numeric = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "trusted referal")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->untrustedref = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "log to stderr")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->logstderr = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "bind address")) != NULL) {
+ httpd->bindaddress = strdup(cp);
+ }
+ if ((cp = bozo_get_pref(prefs, "background")) != NULL) {
+ httpd->background = atoi(cp);
+ }
+ if ((cp = bozo_get_pref(prefs, "foreground")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->foreground = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "pid file")) != NULL) {
+ httpd->pidfile = strdup(cp);
+ }
+ if ((cp = bozo_get_pref(prefs, "unknown slash")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->unknown_slash = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "virtual base")) != NULL) {
+ httpd->virtbase = strdup(cp);
+ }
+ if ((cp = bozo_get_pref(prefs, "enable users")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->enable_users = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "dirty environment")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ dirtyenv = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "hide dots")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->hide_dots = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "directory indexing")) != NULL &&
+ strcmp(cp, "true") == 0) {
+ httpd->dir_indexing = 1;
+ }
+ if ((cp = bozo_get_pref(prefs, "public_html")) != NULL) {
+ httpd->public_html = strdup(cp);
+ }
+ httpd->server_software =
+ strdup(bozo_get_pref(prefs, "server software"));
+ httpd->index_html = strdup(bozo_get_pref(prefs, "index.html"));
+
+ /*
+ * initialise ssl and daemon mode if necessary.
+ */
+ bozo_ssl_init(httpd);
+ bozo_daemon_init(httpd);
+
+ if ((username = bozo_get_pref(prefs, "username")) == NULL) {
+ if ((pw = getpwuid(uid = 0)) == NULL)
+ bozo_err(httpd, 1, "getpwuid(0): %s", strerror(errno));
+ httpd->username = strdup(pw->pw_name);
+ } else {
+ httpd->username = strdup(username);
+ if ((pw = getpwnam(httpd->username)) == NULL)
+ bozo_err(httpd, 1, "getpwnam(%s): %s", httpd->username,
+ strerror(errno));
+ if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+ bozo_err(httpd, 1, "initgroups: %s", strerror(errno));
+ if (setgid(pw->pw_gid) == -1)
+ bozo_err(httpd, 1, "setgid(%u): %s", pw->pw_gid,
+ strerror(errno));
+ uid = pw->pw_uid;
+ }
+ /*
+ * handle chroot.
+ */
+ if ((chrootdir = bozo_get_pref(prefs, "chroot dir")) != NULL) {
+ httpd->rootdir = strdup(chrootdir);
+ if (chdir(httpd->rootdir) == -1)
+ bozo_err(httpd, 1, "chdir(%s): %s", httpd->rootdir,
+ strerror(errno));
+ if (chroot(httpd->rootdir) == -1)
+ bozo_err(httpd, 1, "chroot(%s): %s", httpd->rootdir,
+ strerror(errno));
+ }
+
+ if (username != NULL)
+ if (setuid(uid) == -1)
+ bozo_err(httpd, 1, "setuid(%d): %s", uid,
+ strerror(errno));
+
+ /*
+ * prevent info leakage between different compartments.
+ * some PATH values in the environment would be invalided
+ * by chroot. cross-user settings might result in undesirable
+ * effects.
+ */
+ if ((chrootdir != NULL || username != NULL) && !dirtyenv)
+ environ = cleanenv;
+
+#ifdef _SC_PAGESIZE
+ httpd->page_size = (long)sysconf(_SC_PAGESIZE);
+#else
+ httpd->page_size = 4096;
+#endif
+ debug((httpd, DEBUG_OBESE, "myname is %s, slashdir is %s",
+ httpd->virthostname, httpd->slashdir));
+
+ return 1;
+}
--- /dev/null
+/* $NetBSD: bozohttpd.h,v 1.36 2015/08/05 06:50:44 mrg Exp $ */
+
+/* $eterna: bozohttpd.h,v 1.39 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2015 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 BOZOHTTOPD_H_
+#define BOZOHTTOPD_H_ 1
+
+#include "netbsd_queue.h"
+
+#include <sys/stat.h>
+
+#ifndef NO_LUA_SUPPORT
+#include <lua.h>
+#endif
+#include <stdio.h>
+
+/* QNX provides a lot of NetBSD things in nbutil.h */
+#ifdef USE_NBUTIL
+#include <nbutil.h>
+#endif
+
+/* lots of "const" but gets free()'ed etc at times, sigh */
+
+/* headers */
+typedef struct bozoheaders {
+ /*const*/ char *h_header;
+ /*const*/ char *h_value; /* this gets free()'ed etc at times */
+ SIMPLEQ_ENTRY(bozoheaders) h_next;
+} bozoheaders_t;
+
+#ifndef NO_LUA_SUPPORT
+typedef struct lua_handler {
+ const char *name;
+ int ref;
+ SIMPLEQ_ENTRY(lua_handler) h_next;
+} lua_handler_t;
+
+typedef struct lua_state_map {
+ const char *script;
+ const char *prefix;
+ lua_State *L;
+ SIMPLEQ_HEAD(, lua_handler) handlers;
+ SIMPLEQ_ENTRY(lua_state_map) s_next;
+} lua_state_map_t;
+#endif
+
+typedef struct bozo_content_map_t {
+ const char *name; /* postfix of file */
+ const char *type; /* matching content-type */
+ const char *encoding; /* matching content-encoding */
+ const char *encoding11; /* matching content-encoding (HTTP/1.1) */
+ const char *cgihandler; /* optional CGI handler */
+} bozo_content_map_t;
+
+/* this struct holds the bozo constants */
+typedef struct bozo_consts_t {
+ const char *http_09; /* "HTTP/0.9" */
+ const char *http_10; /* "HTTP/1.0" */
+ const char *http_11; /* "HTTP/1.1" */
+ const char *text_plain; /* "text/plain" */
+} bozo_consts_t;
+
+/* this structure encapsulates all the bozo flags and control vars */
+typedef struct bozohttpd_t {
+ char *rootdir; /* root directory */
+ char *username; /* username to switch to */
+ int numeric; /* avoid gethostby*() */
+ char *virtbase; /* virtual directory base */
+ int unknown_slash; /* unknown vhosts go to normal slashdir */
+ int untrustedref; /* make sure referrer = me unless url = / */
+ int logstderr; /* log to stderr (even if not tty) */
+ int background; /* drop into daemon mode */
+ int foreground; /* keep daemon mode in foreground */
+ char *pidfile; /* path to the pid file, if any */
+ size_t page_size; /* page size */
+ char *slashdir; /* www slash directory */
+ char *bindport; /* bind port; default "http" */
+ char *bindaddress; /* address for binding - INADDR_ANY */
+ int debug; /* debugging level */
+ char *virthostname; /* my name */
+ const char *server_software;/* our brand :-) */
+ const char *index_html; /* our home page */
+ const char *public_html; /* ~user/public_html page */
+ int enable_users; /* enable public_html */
+ int *sock; /* bound sockets */
+ int nsock; /* number of above */
+ struct pollfd *fds; /* current poll fd set */
+ int request_times; /* # times a request was processed */
+ int dir_indexing; /* handle directories */
+ int hide_dots; /* hide .* */
+ int process_cgi; /* use the cgi handler */
+ char *cgibin; /* cgi-bin directory */
+#ifndef NO_LUA_SUPPORT
+ int process_lua; /* use the Lua handler */
+ SIMPLEQ_HEAD(, lua_state_map) lua_states;
+#endif
+ void *sslinfo; /* pointer to ssl struct */
+ int dynamic_content_map_size;/* size of dyn cont map */
+ bozo_content_map_t *dynamic_content_map;/* dynamic content map */
+ size_t mmapsz; /* size of region to mmap */
+ char *getln_buffer; /* space for getln buffer */
+ ssize_t getln_buflen; /* length of allocated space */
+ char *errorbuf; /* no dynamic allocation allowed */
+ bozo_consts_t consts; /* various constants */
+} bozohttpd_t;
+
+/* bozo_httpreq_t */
+typedef struct bozo_httpreq_t {
+ bozohttpd_t *hr_httpd;
+ int hr_method;
+#define HTTP_GET 0x01
+#define HTTP_POST 0x02
+#define HTTP_HEAD 0x03
+#define HTTP_OPTIONS 0x04 /* not supported */
+#define HTTP_PUT 0x05 /* not supported */
+#define HTTP_DELETE 0x06 /* not supported */
+#define HTTP_TRACE 0x07 /* not supported */
+#define HTTP_CONNECT 0x08 /* not supported */
+ const char *hr_methodstr;
+ char *hr_virthostname; /* server name (if not identical
+ to hr_httpd->virthostname) */
+ char *hr_file;
+ char *hr_oldfile; /* if we added an index_html */
+ char *hr_query;
+ char *hr_host; /* HTTP/1.1 Host: or virtual hostname,
+ possibly including a port number */
+ const char *hr_proto;
+ const char *hr_content_type;
+ const char *hr_content_length;
+ const char *hr_allow;
+ const char *hr_referrer;
+ const char *hr_range;
+ const char *hr_if_modified_since;
+ const char *hr_accept_encoding;
+ int hr_have_range;
+ off_t hr_first_byte_pos;
+ off_t hr_last_byte_pos;
+ /*const*/ char *hr_remotehost;
+ /*const*/ char *hr_remoteaddr;
+ /*const*/ char *hr_serverport;
+#ifdef DO_HTPASSWD
+ /*const*/ char *hr_authrealm;
+ /*const*/ char *hr_authuser;
+ /*const*/ char *hr_authpass;
+#endif
+ SIMPLEQ_HEAD(, bozoheaders) hr_headers;
+ int hr_nheaders;
+} bozo_httpreq_t;
+
+/* helper to access the "active" host name from a httpd/request pair */
+#define BOZOHOST(HTTPD,REQUEST) ((REQUEST)->hr_virthostname ? \
+ (REQUEST)->hr_virthostname : \
+ (HTTPD)->virthostname)
+
+/* structure to hold string based (name, value) pairs with preferences */
+typedef struct bozoprefs_t {
+ unsigned size; /* size of the two arrays */
+ unsigned c; /* # of entries in arrays */
+ char **name; /* names of each entry */
+ char **value; /* values for the name entries */
+} bozoprefs_t;
+
+/* by default write in upto 64KiB chunks, and mmap in upto 64MiB chunks */
+#ifndef BOZO_WRSZ
+#define BOZO_WRSZ (64 * 1024)
+#endif
+#ifndef BOZO_MMAPSZ
+#define BOZO_MMAPSZ (BOZO_WRSZ * 1024)
+#endif
+
+/* debug flags */
+#define DEBUG_NORMAL 1
+#define DEBUG_FAT 2
+#define DEBUG_OBESE 3
+#define DEBUG_EXPLODING 4
+
+#define strornull(x) ((x) ? (x) : "<null>")
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define BOZO_PRINTFLIKE(x,y) __attribute__((__format__(__printf__, x,y)))
+#define BOZO_DEAD __attribute__((__noreturn__))
+#endif
+
+#ifndef NO_DEBUG
+void debug__(bozohttpd_t *, int, const char *, ...) BOZO_PRINTFLIKE(3, 4);
+#define debug(x) debug__ x
+#else
+#define debug(x)
+#endif /* NO_DEBUG */
+
+void bozo_warn(bozohttpd_t *, const char *, ...)
+ BOZO_PRINTFLIKE(2, 3);
+void bozo_err(bozohttpd_t *, int, const char *, ...)
+ BOZO_PRINTFLIKE(3, 4)
+ BOZO_DEAD;
+int bozo_http_error(bozohttpd_t *, int, bozo_httpreq_t *, const char *);
+
+int bozo_check_special_files(bozo_httpreq_t *, const char *);
+char *bozo_http_date(char *, size_t);
+void bozo_print_header(bozo_httpreq_t *, struct stat *, const char *, const char *);
+char *bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url);
+char *bozo_escape_html(bozohttpd_t *httpd, const char *url);
+
+char *bozodgetln(bozohttpd_t *, int, ssize_t *, ssize_t (*)(bozohttpd_t *, int, void *, size_t));
+char *bozostrnsep(char **, const char *, ssize_t *);
+
+void *bozomalloc(bozohttpd_t *, size_t);
+void *bozorealloc(bozohttpd_t *, void *, size_t);
+char *bozostrdup(bozohttpd_t *, const char *);
+
+/* ssl-bozo.c */
+#ifdef NO_SSL_SUPPORT
+#define bozo_ssl_set_opts(w, x, y) do { /* nothing */ } while (0)
+#define bozo_ssl_init(x) do { /* nothing */ } while (0)
+#define bozo_ssl_accept(x) (0)
+#define bozo_ssl_destroy(x) do { /* nothing */ } while (0)
+#else
+void bozo_ssl_set_opts(bozohttpd_t *, const char *, const char *);
+void bozo_ssl_init(bozohttpd_t *);
+int bozo_ssl_accept(bozohttpd_t *);
+void bozo_ssl_destroy(bozohttpd_t *);
+#endif
+
+
+/* auth-bozo.c */
+#ifdef DO_HTPASSWD
+void bozo_auth_init(bozo_httpreq_t *);
+int bozo_auth_check(bozo_httpreq_t *, const char *);
+void bozo_auth_cleanup(bozo_httpreq_t *);
+int bozo_auth_check_headers(bozo_httpreq_t *, char *, char *, ssize_t);
+int bozo_auth_check_special_files(bozo_httpreq_t *, const char *);
+void bozo_auth_check_401(bozo_httpreq_t *, int);
+void bozo_auth_cgi_setenv(bozo_httpreq_t *, char ***);
+int bozo_auth_cgi_count(bozo_httpreq_t *);
+#else
+#define bozo_auth_init(x) do { /* nothing */ } while (0)
+#define bozo_auth_check(x, y) 0
+#define bozo_auth_cleanup(x) do { /* nothing */ } while (0)
+#define bozo_auth_check_headers(y, z, a, b) 0
+#define bozo_auth_check_special_files(x, y) 0
+#define bozo_auth_check_401(x, y) do { /* nothing */ } while (0)
+#define bozo_auth_cgi_setenv(x, y) do { /* nothing */ } while (0)
+#define bozo_auth_cgi_count(x) 0
+#endif /* DO_HTPASSWD */
+
+
+/* cgi-bozo.c */
+#ifdef NO_CGIBIN_SUPPORT
+#define bozo_process_cgi(h) 0
+#else
+void bozo_cgi_setbin(bozohttpd_t *, const char *);
+void bozo_setenv(bozohttpd_t *, const char *, const char *, char **);
+int bozo_process_cgi(bozo_httpreq_t *);
+void bozo_add_content_map_cgi(bozohttpd_t *, const char *, const char *);
+#endif /* NO_CGIBIN_SUPPORT */
+
+
+/* lua-bozo.c */
+#ifdef NO_LUA_SUPPORT
+#define bozo_process_lua(h) 0
+#else
+void bozo_add_lua_map(bozohttpd_t *, const char *, const char *);
+int bozo_process_lua(bozo_httpreq_t *);
+#endif /* NO_LUA_SUPPORT */
+
+
+/* daemon-bozo.c */
+#ifdef NO_DAEMON_MODE
+#define bozo_daemon_init(x) do { /* nothing */ } while (0)
+#define bozo_daemon_fork(x) 0
+#define bozo_daemon_closefds(x) do { /* nothing */ } while (0)
+#else
+void bozo_daemon_init(bozohttpd_t *);
+int bozo_daemon_fork(bozohttpd_t *);
+void bozo_daemon_closefds(bozohttpd_t *);
+#endif /* NO_DAEMON_MODE */
+
+
+/* tilde-luzah-bozo.c */
+#ifdef NO_USER_SUPPORT
+#define bozo_user_transform(a, c) 0
+#else
+int bozo_user_transform(bozo_httpreq_t *, int *);
+#endif /* NO_USER_SUPPORT */
+
+
+/* dir-index-bozo.c */
+#ifdef NO_DIRINDEX_SUPPORT
+#define bozo_dir_index(a, b, c) 0
+#else
+int bozo_dir_index(bozo_httpreq_t *, const char *, int);
+#endif /* NO_DIRINDEX_SUPPORT */
+
+
+/* content-bozo.c */
+const char *bozo_content_type(bozo_httpreq_t *, const char *);
+const char *bozo_content_encoding(bozo_httpreq_t *, const char *);
+bozo_content_map_t *bozo_match_content_map(bozohttpd_t *, const char *, int);
+bozo_content_map_t *bozo_get_content_map(bozohttpd_t *, const char *);
+#ifndef NO_DYNAMIC_CONTENT
+void bozo_add_content_map_mime(bozohttpd_t *, const char *, const char *, const char *, const char *);
+#endif
+
+/* I/O */
+int bozo_printf(bozohttpd_t *, const char *, ...) BOZO_PRINTFLIKE(2, 3);;
+ssize_t bozo_read(bozohttpd_t *, int, void *, size_t);
+ssize_t bozo_write(bozohttpd_t *, int, const void *, size_t);
+int bozo_flush(bozohttpd_t *, FILE *);
+
+/* misc */
+int bozo_init_httpd(bozohttpd_t *);
+int bozo_init_prefs(bozoprefs_t *);
+int bozo_set_defaults(bozohttpd_t *, bozoprefs_t *);
+int bozo_setup(bozohttpd_t *, bozoprefs_t *, const char *, const char *);
+bozo_httpreq_t *bozo_read_request(bozohttpd_t *);
+void bozo_process_request(bozo_httpreq_t *);
+void bozo_clean_request(bozo_httpreq_t *);
+
+/* variables */
+int bozo_set_pref(bozoprefs_t *, const char *, const char *);
+char *bozo_get_pref(bozoprefs_t *, const char *);
+
+#endif /* BOZOHTTOPD_H_ */
--- /dev/null
+/* $NetBSD: cgi-bozo.c,v 1.27 2015/05/02 11:35:48 mrg Exp $ */
+
+/* $eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2015 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements CGI/1.2 for bozohttpd */
+
+#ifndef NO_CGIBIN_SUPPORT
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+
+#include "bozohttpd.h"
+
+#define CGIBIN_PREFIX "cgi-bin/"
+#define CGIBIN_PREFIX_LEN (sizeof(CGIBIN_PREFIX)-1)
+
+#ifndef USE_ARG
+#define USE_ARG(x) /*LINTED*/(void)&(x)
+#endif
+
+/*
+ * given the file name, return a CGI interpreter
+ */
+static const char *
+content_cgihandler(bozohttpd_t *httpd, bozo_httpreq_t *request,
+ const char *file)
+{
+ bozo_content_map_t *map;
+
+ USE_ARG(request);
+ debug((httpd, DEBUG_FAT, "content_cgihandler: trying file %s", file));
+ map = bozo_match_content_map(httpd, file, 0);
+ if (map)
+ return map->cgihandler;
+ return NULL;
+}
+
+static int
+parse_header(bozohttpd_t *httpd, const char *str, ssize_t len, char **hdr_str,
+ char **hdr_val)
+{
+ char *name, *value;
+
+ /* if the string passed is zero-length bail out */
+ if (*str == '\0')
+ return -1;
+
+ value = bozostrdup(httpd, str);
+
+ /* locate the ':' separator in the header/value */
+ name = bozostrnsep(&value, ":", &len);
+
+ if (NULL == name || -1 == len) {
+ free(name);
+ return -1;
+ }
+
+ /* skip leading space/tab */
+ while (*value == ' ' || *value == '\t')
+ len--, value++;
+
+ *hdr_str = name;
+ *hdr_val = value;
+
+ return 0;
+}
+
+/*
+ * handle parsing a CGI header output, transposing a Status: header
+ * into the HTTP reply (ie, instead of "200 OK").
+ */
+static void
+finish_cgi_output(bozohttpd_t *httpd, bozo_httpreq_t *request, int in, int nph)
+{
+ char buf[BOZO_WRSZ];
+ char *str;
+ ssize_t len;
+ ssize_t rbytes;
+ SIMPLEQ_HEAD(, bozoheaders) headers;
+ bozoheaders_t *hdr, *nhdr;
+ int write_header, nheaders = 0;
+
+ /* much of this code is like bozo_read_request()'s header loop. */
+ SIMPLEQ_INIT(&headers);
+ write_header = nph == 0;
+ /* was read(2) here - XXX - agc */
+ while (nph == 0 &&
+ (str = bozodgetln(httpd, in, &len, bozo_read)) != NULL) {
+ char *hdr_name, *hdr_value;
+
+ if (parse_header(httpd, str, len, &hdr_name, &hdr_value))
+ break;
+
+ /*
+ * The CGI 1.{1,2} spec both say that if the cgi program
+ * returns a `Status:' header field then the server MUST
+ * return it in the response. If the cgi program does
+ * not return any `Status:' header then the server should
+ * respond with 200 OK.
+ * XXX The CGI 1.1 and 1.2 specification differ slightly on
+ * this in that v1.2 says that the script MUST NOT return a
+ * `Status:' header if it is returning a `Location:' header.
+ * For compatibility we are going with the CGI 1.1 behavior.
+ */
+ if (strcasecmp(hdr_name, "status") == 0) {
+ debug((httpd, DEBUG_OBESE,
+ "bozo_process_cgi: writing HTTP header "
+ "from status %s ..", hdr_value));
+ bozo_printf(httpd, "%s %s\r\n", request->hr_proto,
+ hdr_value);
+ bozo_flush(httpd, stdout);
+ write_header = 0;
+ free(hdr_name);
+ break;
+ }
+
+ hdr = bozomalloc(httpd, sizeof *hdr);
+ hdr->h_header = hdr_name;
+ hdr->h_value = hdr_value;
+ SIMPLEQ_INSERT_TAIL(&headers, hdr, h_next);
+ nheaders++;
+ }
+
+ if (write_header) {
+ debug((httpd, DEBUG_OBESE,
+ "bozo_process_cgi: writing HTTP header .."));
+ bozo_printf(httpd,
+ "%s 200 OK\r\n", request->hr_proto);
+ bozo_flush(httpd, stdout);
+ }
+
+ if (nheaders) {
+ debug((httpd, DEBUG_OBESE,
+ "bozo_process_cgi: writing delayed HTTP headers .."));
+ SIMPLEQ_FOREACH_SAFE(hdr, &headers, h_next, nhdr) {
+ bozo_printf(httpd, "%s: %s\r\n", hdr->h_header,
+ hdr->h_value);
+ free(hdr->h_header);
+ free(hdr);
+ }
+ bozo_printf(httpd, "\r\n");
+ bozo_flush(httpd, stdout);
+ }
+
+ /* XXX we should have some goo that times us out
+ */
+ while ((rbytes = read(in, buf, sizeof buf)) > 0) {
+ ssize_t wbytes;
+ char *bp = buf;
+
+ while (rbytes) {
+ wbytes = bozo_write(httpd, STDOUT_FILENO, buf,
+ (size_t)rbytes);
+ if (wbytes > 0) {
+ rbytes -= wbytes;
+ bp += wbytes;
+ } else
+ bozo_err(httpd, 1,
+ "cgi output write failed: %s",
+ strerror(errno));
+ }
+ }
+}
+
+static void
+append_index_html(bozohttpd_t *httpd, char **url)
+{
+ *url = bozorealloc(httpd, *url,
+ strlen(*url) + strlen(httpd->index_html) + 1);
+ strcat(*url, httpd->index_html);
+ debug((httpd, DEBUG_NORMAL,
+ "append_index_html: url adjusted to `%s'", *url));
+}
+
+void
+bozo_cgi_setbin(bozohttpd_t *httpd, const char *path)
+{
+ httpd->cgibin = strdup(path);
+ debug((httpd, DEBUG_OBESE, "cgibin (cgi-bin directory) is %s",
+ httpd->cgibin));
+}
+
+/* help build up the environ pointer */
+void
+bozo_setenv(bozohttpd_t *httpd, const char *env, const char *val,
+ char **envp)
+{
+ char *s1 = bozomalloc(httpd, strlen(env) + strlen(val) + 2);
+
+ strcpy(s1, env);
+ strcat(s1, "=");
+ strcat(s1, val);
+ debug((httpd, DEBUG_OBESE, "bozo_setenv: %s", s1));
+ *envp = s1;
+}
+
+/*
+ * Checks if the request has asked for a cgi-bin. Should only be called if
+ * cgibin is set. If it starts CGIBIN_PREFIX or has a ncontent handler,
+ * process the cgi, otherwise just return. Returns 0 if it did not handle
+ * the request.
+ */
+int
+bozo_process_cgi(bozo_httpreq_t *request)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ char buf[BOZO_WRSZ];
+ char date[40];
+ bozoheaders_t *headp;
+ const char *type, *clen, *info, *cgihandler;
+ char *query, *s, *t, *path, *env, *file, *url;
+ char command[MAXPATHLEN];
+ char **envp, **curenvp, *argv[4];
+ char *uri;
+ size_t len;
+ ssize_t rbytes;
+ pid_t pid;
+ int envpsize, ix, nph;
+ int sv[2];
+
+ if (!httpd->cgibin && !httpd->process_cgi)
+ return 0;
+
+ if (request->hr_oldfile && strcmp(request->hr_oldfile, "/") != 0)
+ uri = request->hr_oldfile;
+ else
+ uri = request->hr_file;
+
+ if (uri[0] == '/')
+ file = bozostrdup(httpd, uri);
+ else
+ asprintf(&file, "/%s", uri);
+ if (file == NULL)
+ return 0;
+
+ if (request->hr_query && strlen(request->hr_query))
+ query = bozostrdup(httpd, request->hr_query);
+ else
+ query = NULL;
+
+ asprintf(&url, "%s%s%s", file, query ? "?" : "", query ? query : "");
+ if (url == NULL)
+ goto out;
+ debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: url `%s'", url));
+
+ path = NULL;
+ envp = NULL;
+ cgihandler = NULL;
+ info = NULL;
+
+ len = strlen(url);
+
+ if (bozo_auth_check(request, url + 1))
+ goto out;
+
+ if (!httpd->cgibin ||
+ strncmp(url + 1, CGIBIN_PREFIX, CGIBIN_PREFIX_LEN) != 0) {
+ cgihandler = content_cgihandler(httpd, request, file + 1);
+ if (cgihandler == NULL) {
+ debug((httpd, DEBUG_FAT,
+ "bozo_process_cgi: no handler, returning"));
+ goto out;
+ }
+ if (len == 0 || file[len - 1] == '/')
+ append_index_html(httpd, &file);
+ debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: cgihandler `%s'",
+ cgihandler));
+ } else if (len - 1 == CGIBIN_PREFIX_LEN) /* url is "/cgi-bin/" */
+ append_index_html(httpd, &file);
+
+ ix = 0;
+ if (cgihandler) {
+ snprintf(command, sizeof(command), "%s", file + 1);
+ path = bozostrdup(httpd, cgihandler);
+ argv[ix++] = path;
+ /* argv[] = [ path, command, query, NULL ] */
+ } else {
+ snprintf(command, sizeof(command), "%s",
+ file + CGIBIN_PREFIX_LEN + 1);
+ if ((s = strchr(command, '/')) != NULL) {
+ info = bozostrdup(httpd, s);
+ *s = '\0';
+ }
+ path = bozomalloc(httpd,
+ strlen(httpd->cgibin) + 1 + strlen(command) + 1);
+ strcpy(path, httpd->cgibin);
+ strcat(path, "/");
+ strcat(path, command);
+ /* argv[] = [ command, query, NULL ] */
+ }
+ argv[ix++] = command;
+ argv[ix++] = query;
+ argv[ix++] = NULL;
+
+ nph = strncmp(command, "nph-", 4) == 0;
+
+ type = request->hr_content_type;
+ clen = request->hr_content_length;
+
+ envpsize = 13 + request->hr_nheaders +
+ (info && *info ? 1 : 0) +
+ (query && *query ? 1 : 0) +
+ (type && *type ? 1 : 0) +
+ (clen && *clen ? 1 : 0) +
+ (request->hr_remotehost && *request->hr_remotehost ? 1 : 0) +
+ (request->hr_remoteaddr && *request->hr_remoteaddr ? 1 : 0) +
+ bozo_auth_cgi_count(request) +
+ (request->hr_serverport && *request->hr_serverport ? 1 : 0);
+
+ debug((httpd, DEBUG_FAT,
+ "bozo_process_cgi: path `%s', cmd `%s', info `%s', "
+ "query `%s', nph `%d', envpsize `%d'",
+ path, command, strornull(info),
+ strornull(query), nph, envpsize));
+
+ envp = bozomalloc(httpd, sizeof(*envp) * envpsize);
+ for (ix = 0; ix < envpsize; ix++)
+ envp[ix] = NULL;
+ curenvp = envp;
+
+ SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next) {
+ const char *s2;
+ env = bozomalloc(httpd, 6 + strlen(headp->h_header) + 1 +
+ strlen(headp->h_value));
+
+ t = env;
+ strcpy(t, "HTTP_");
+ t += strlen(t);
+ for (s2 = headp->h_header; *s2; t++, s2++)
+ if (islower((u_int)*s2))
+ *t = toupper((u_int)*s2);
+ else if (*s2 == '-')
+ *t = '_';
+ else
+ *t = *s2;
+ *t = '\0';
+ debug((httpd, DEBUG_OBESE, "setting header %s as %s = %s",
+ headp->h_header, env, headp->h_value));
+ bozo_setenv(httpd, env, headp->h_value, curenvp++);
+ free(env);
+ }
+
+#ifndef _PATH_DEFPATH
+#define _PATH_DEFPATH "/usr/bin:/bin"
+#endif
+
+ bozo_setenv(httpd, "PATH", _PATH_DEFPATH, curenvp++);
+ bozo_setenv(httpd, "IFS", " \t\n", curenvp++);
+ bozo_setenv(httpd, "SERVER_NAME", BOZOHOST(httpd,request), curenvp++);
+ bozo_setenv(httpd, "GATEWAY_INTERFACE", "CGI/1.1", curenvp++);
+ bozo_setenv(httpd, "SERVER_PROTOCOL", request->hr_proto, curenvp++);
+ bozo_setenv(httpd, "REQUEST_METHOD", request->hr_methodstr, curenvp++);
+ bozo_setenv(httpd, "SCRIPT_NAME", file, curenvp++);
+ bozo_setenv(httpd, "SCRIPT_FILENAME", file + 1, curenvp++);
+ bozo_setenv(httpd, "SERVER_SOFTWARE", httpd->server_software,
+ curenvp++);
+ bozo_setenv(httpd, "REQUEST_URI", uri, curenvp++);
+ bozo_setenv(httpd, "DATE_GMT", bozo_http_date(date, sizeof(date)),
+ curenvp++);
+ if (query && *query)
+ bozo_setenv(httpd, "QUERY_STRING", query, curenvp++);
+ if (info && *info)
+ bozo_setenv(httpd, "PATH_INFO", info, curenvp++);
+ if (type && *type)
+ bozo_setenv(httpd, "CONTENT_TYPE", type, curenvp++);
+ if (clen && *clen)
+ bozo_setenv(httpd, "CONTENT_LENGTH", clen, curenvp++);
+ if (request->hr_serverport && *request->hr_serverport)
+ bozo_setenv(httpd, "SERVER_PORT", request->hr_serverport,
+ curenvp++);
+ if (request->hr_remotehost && *request->hr_remotehost)
+ bozo_setenv(httpd, "REMOTE_HOST", request->hr_remotehost,
+ curenvp++);
+ if (request->hr_remoteaddr && *request->hr_remoteaddr)
+ bozo_setenv(httpd, "REMOTE_ADDR", request->hr_remoteaddr,
+ curenvp++);
+ /*
+ * XXX Apache does this when invoking content handlers, and PHP
+ * XXX 5.3 requires it as a "security" measure.
+ */
+ if (cgihandler)
+ bozo_setenv(httpd, "REDIRECT_STATUS", "200", curenvp++);
+ bozo_auth_cgi_setenv(request, &curenvp);
+
+ free(file);
+ free(url);
+
+ debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s, %s %s %s",
+ path, argv[0], strornull(argv[1]), strornull(argv[2])));
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) == -1)
+ bozo_err(httpd, 1, "child socketpair failed: %s",
+ strerror(errno));
+
+ /*
+ * We create 2 procs: one to become the CGI, one read from
+ * the CGI and output to the network, and this parent will
+ * continue reading from the network and writing to the
+ * CGI procsss.
+ */
+ switch (fork()) {
+ case -1: /* eep, failure */
+ bozo_err(httpd, 1, "child fork failed: %s", strerror(errno));
+ /*NOTREACHED*/
+ case 0:
+ close(sv[0]);
+ dup2(sv[1], STDIN_FILENO);
+ dup2(sv[1], STDOUT_FILENO);
+ close(2);
+ close(sv[1]);
+ closelog();
+ bozo_daemon_closefds(httpd);
+
+ if (-1 == execve(path, argv, envp))
+ bozo_err(httpd, 1, "child exec failed: %s: %s",
+ path, strerror(errno));
+ /* NOT REACHED */
+ bozo_err(httpd, 1, "child execve returned?!");
+ }
+
+ close(sv[1]);
+
+ /* parent: read from stdin (bozo_read()) write to sv[0] */
+ /* child: read from sv[0] (bozo_write()) write to stdout */
+ pid = fork();
+ if (pid == -1)
+ bozo_err(httpd, 1, "io child fork failed: %s", strerror(errno));
+ else if (pid == 0) {
+ /* child reader/writer */
+ close(STDIN_FILENO);
+ finish_cgi_output(httpd, request, sv[0], nph);
+ /* if we're done output, our parent is useless... */
+ kill(getppid(), SIGKILL);
+ debug((httpd, DEBUG_FAT, "done processing cgi output"));
+ _exit(0);
+ }
+ close(STDOUT_FILENO);
+
+ /* XXX we should have some goo that times us out
+ */
+ while ((rbytes = bozo_read(httpd, STDIN_FILENO, buf, sizeof buf)) > 0) {
+ ssize_t wbytes;
+ char *bp = buf;
+
+ while (rbytes) {
+ wbytes = write(sv[0], buf, (size_t)rbytes);
+ if (wbytes > 0) {
+ rbytes -= wbytes;
+ bp += wbytes;
+ } else
+ bozo_err(httpd, 1, "write failed: %s",
+ strerror(errno));
+ }
+ }
+ debug((httpd, DEBUG_FAT, "done processing cgi input"));
+ exit(0);
+
+ out:
+ free(query);
+ free(file);
+ free(url);
+ return 0;
+}
+
+#ifndef NO_DYNAMIC_CONTENT
+/* cgi maps are simple ".postfix /path/to/prog" */
+void
+bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg, const char *cgihandler)
+{
+ bozo_content_map_t *map;
+
+ debug((httpd, DEBUG_NORMAL, "bozo_add_content_map_cgi: name %s cgi %s",
+ arg, cgihandler));
+
+ httpd->process_cgi = 1;
+
+ map = bozo_get_content_map(httpd, arg);
+ map->name = arg;
+ map->type = map->encoding = map->encoding11 = NULL;
+ map->cgihandler = cgihandler;
+}
+#endif /* NO_DYNAMIC_CONTENT */
+
+#endif /* NO_CGIBIN_SUPPORT */
--- /dev/null
+/* $NetBSD: content-bozo.c,v 1.12 2015/05/02 11:35:48 mrg Exp $ */
+
+/* $eterna: content-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2015 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements content-type handling for bozohttpd */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "bozohttpd.h"
+
+/*
+ * this map and the functions below map between filenames and the
+ * content type and content encoding definitions. this should become
+ * a configuration file, perhaps like apache's mime.types (but that
+ * has less info per-entry).
+ */
+
+static bozo_content_map_t static_content_map[] = {
+ { ".html", "text/html", "", "", NULL },
+ { ".htm", "text/html", "", "", NULL },
+ { ".gif", "image/gif", "", "", NULL },
+ { ".jpeg", "image/jpeg", "", "", NULL },
+ { ".jpg", "image/jpeg", "", "", NULL },
+ { ".jpe", "image/jpeg", "", "", NULL },
+ { ".png", "image/png", "", "", NULL },
+ { ".mp3", "audio/mpeg", "", "", NULL },
+ { ".css", "text/css", "", "", NULL },
+ { ".txt", "text/plain", "", "", NULL },
+ { ".swf", "application/x-shockwave-flash","", "", NULL },
+ { ".dcr", "application/x-director", "", "", NULL },
+ { ".pac", "application/x-ns-proxy-autoconfig", "", "", NULL },
+ { ".pa", "application/x-ns-proxy-autoconfig", "", "", NULL },
+ { ".tar", "multipart/x-tar", "", "", NULL },
+ { ".gtar", "multipart/x-gtar", "", "", NULL },
+ { ".tar.Z", "multipart/x-tar", "x-compress", "compress", NULL },
+ { ".tar.gz", "multipart/x-tar", "x-gzip", "gzip", NULL },
+ { ".taz", "multipart/x-tar", "x-gzip", "gzip", NULL },
+ { ".tgz", "multipart/x-tar", "x-gzip", "gzip", NULL },
+ { ".tar.z", "multipart/x-tar", "x-pack", "x-pack", NULL },
+ { ".Z", "application/x-compress", "x-compress", "compress", NULL },
+ { ".gz", "application/x-gzip", "x-gzip", "gzip", NULL },
+ { ".z", "unknown", "x-pack", "x-pack", NULL },
+ { ".bz2", "application/x-bzip2", "x-bzip2", "x-bzip2", NULL },
+ { ".ogg", "application/x-ogg", "", "", NULL },
+ { ".mkv", "video/x-matroska", "", "", NULL },
+ { ".xbel", "text/xml", "", "", NULL },
+ { ".xml", "text/xml", "", "", NULL },
+ { ".xsl", "text/xml", "", "", NULL },
+ { ".hqx", "application/mac-binhex40", "", "", NULL },
+ { ".cpt", "application/mac-compactpro", "", "", NULL },
+ { ".doc", "application/msword", "", "", NULL },
+ { ".bin", "application/octet-stream", "", "", NULL },
+ { ".dms", "application/octet-stream", "", "", NULL },
+ { ".lha", "application/octet-stream", "", "", NULL },
+ { ".lzh", "application/octet-stream", "", "", NULL },
+ { ".exe", "application/octet-stream", "", "", NULL },
+ { ".class", "application/octet-stream", "", "", NULL },
+ { ".oda", "application/oda", "", "", NULL },
+ { ".pdf", "application/pdf", "", "", NULL },
+ { ".ai", "application/postscript", "", "", NULL },
+ { ".eps", "application/postscript", "", "", NULL },
+ { ".ps", "application/postscript", "", "", NULL },
+ { ".ppt", "application/powerpoint", "", "", NULL },
+ { ".rtf", "application/rtf", "", "", NULL },
+ { ".bcpio", "application/x-bcpio", "", "", NULL },
+ { ".torrent", "application/x-bittorrent", "", "", NULL },
+ { ".vcd", "application/x-cdlink", "", "", NULL },
+ { ".cpio", "application/x-cpio", "", "", NULL },
+ { ".csh", "application/x-csh", "", "", NULL },
+ { ".dir", "application/x-director", "", "", NULL },
+ { ".dxr", "application/x-director", "", "", NULL },
+ { ".dvi", "application/x-dvi", "", "", NULL },
+ { ".hdf", "application/x-hdf", "", "", NULL },
+ { ".cgi", "application/x-httpd-cgi", "", "", NULL },
+ { ".skp", "application/x-koan", "", "", NULL },
+ { ".skd", "application/x-koan", "", "", NULL },
+ { ".skt", "application/x-koan", "", "", NULL },
+ { ".skm", "application/x-koan", "", "", NULL },
+ { ".latex", "application/x-latex", "", "", NULL },
+ { ".mif", "application/x-mif", "", "", NULL },
+ { ".nc", "application/x-netcdf", "", "", NULL },
+ { ".cdf", "application/x-netcdf", "", "", NULL },
+ { ".patch", "application/x-patch", "", "", NULL },
+ { ".sh", "application/x-sh", "", "", NULL },
+ { ".shar", "application/x-shar", "", "", NULL },
+ { ".sit", "application/x-stuffit", "", "", NULL },
+ { ".sv4cpio", "application/x-sv4cpio", "", "", NULL },
+ { ".sv4crc", "application/x-sv4crc", "", "", NULL },
+ { ".tar", "application/x-tar", "", "", NULL },
+ { ".tcl", "application/x-tcl", "", "", NULL },
+ { ".tex", "application/x-tex", "", "", NULL },
+ { ".texinfo", "application/x-texinfo", "", "", NULL },
+ { ".texi", "application/x-texinfo", "", "", NULL },
+ { ".t", "application/x-troff", "", "", NULL },
+ { ".tr", "application/x-troff", "", "", NULL },
+ { ".roff", "application/x-troff", "", "", NULL },
+ { ".man", "application/x-troff-man", "", "", NULL },
+ { ".me", "application/x-troff-me", "", "", NULL },
+ { ".ms", "application/x-troff-ms", "", "", NULL },
+ { ".ustar", "application/x-ustar", "", "", NULL },
+ { ".src", "application/x-wais-source", "", "", NULL },
+ { ".zip", "application/zip", "", "", NULL },
+ { ".au", "audio/basic", "", "", NULL },
+ { ".snd", "audio/basic", "", "", NULL },
+ { ".mpga", "audio/mpeg", "", "", NULL },
+ { ".mp2", "audio/mpeg", "", "", NULL },
+ { ".aif", "audio/x-aiff", "", "", NULL },
+ { ".aiff", "audio/x-aiff", "", "", NULL },
+ { ".aifc", "audio/x-aiff", "", "", NULL },
+ { ".ram", "audio/x-pn-realaudio", "", "", NULL },
+ { ".rpm", "audio/x-pn-realaudio-plugin", "", "", NULL },
+ { ".ra", "audio/x-realaudio", "", "", NULL },
+ { ".wav", "audio/x-wav", "", "", NULL },
+ { ".pdb", "chemical/x-pdb", "", "", NULL },
+ { ".xyz", "chemical/x-pdb", "", "", NULL },
+ { ".ief", "image/ief", "", "", NULL },
+ { ".tiff", "image/tiff", "", "", NULL },
+ { ".tif", "image/tiff", "", "", NULL },
+ { ".ras", "image/x-cmu-raster", "", "", NULL },
+ { ".pnm", "image/x-portable-anymap", "", "", NULL },
+ { ".pbm", "image/x-portable-bitmap", "", "", NULL },
+ { ".pgm", "image/x-portable-graymap", "", "", NULL },
+ { ".ppm", "image/x-portable-pixmap", "", "", NULL },
+ { ".rgb", "image/x-rgb", "", "", NULL },
+ { ".xbm", "image/x-xbitmap", "", "", NULL },
+ { ".xpm", "image/x-xpixmap", "", "", NULL },
+ { ".xwd", "image/x-xwindowdump", "", "", NULL },
+ { ".rtx", "text/richtext", "", "", NULL },
+ { ".tsv", "text/tab-separated-values", "", "", NULL },
+ { ".etx", "text/x-setext", "", "", NULL },
+ { ".sgml", "text/x-sgml", "", "", NULL },
+ { ".sgm", "text/x-sgml", "", "", NULL },
+ { ".mpeg", "video/mpeg", "", "", NULL },
+ { ".mpg", "video/mpeg", "", "", NULL },
+ { ".mpe", "video/mpeg", "", "", NULL },
+ { ".ts", "video/mpeg", "", "", NULL },
+ { ".vob", "video/mpeg", "", "", NULL },
+ { ".mp4", "video/mp4", "", "", NULL },
+ { ".qt", "video/quicktime", "", "", NULL },
+ { ".mov", "video/quicktime", "", "", NULL },
+ { ".avi", "video/x-msvideo", "", "", NULL },
+ { ".movie", "video/x-sgi-movie", "", "", NULL },
+ { ".ice", "x-conference/x-cooltalk", "", "", NULL },
+ { ".wrl", "x-world/x-vrml", "", "", NULL },
+ { ".vrml", "x-world/x-vrml", "", "", NULL },
+ { ".svg", "image/svg+xml", "", "", NULL },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+static bozo_content_map_t *
+search_map(bozo_content_map_t *map, const char *name, size_t len)
+{
+ for ( ; map && map->name; map++) {
+ const size_t namelen = strlen(map->name);
+
+ if (namelen < len &&
+ strcasecmp(map->name, name + (len - namelen)) == 0)
+ return map;
+ }
+ return NULL;
+}
+
+/* match a suffix on a file - dynamiconly means no static content search */
+bozo_content_map_t *
+bozo_match_content_map(bozohttpd_t *httpd, const char *name,
+ const int dynamiconly)
+{
+ bozo_content_map_t *map;
+ size_t len;
+
+ len = strlen(name);
+ if ((map = search_map(httpd->dynamic_content_map, name, len)) != NULL) {
+ return map;
+ }
+ if (!dynamiconly) {
+ if ((map = search_map(static_content_map, name, len)) != NULL) {
+ return map;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * given the file name, return a valid Content-Type: value.
+ */
+/* ARGSUSED */
+const char *
+bozo_content_type(bozo_httpreq_t *request, const char *file)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ bozo_content_map_t *map;
+
+ map = bozo_match_content_map(httpd, file, 0);
+ if (map)
+ return map->type;
+ return httpd->consts.text_plain;
+}
+
+/*
+ * given the file name, return a valid Content-Encoding: value.
+ */
+const char *
+bozo_content_encoding(bozo_httpreq_t *request, const char *file)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ bozo_content_map_t *map;
+
+ map = bozo_match_content_map(httpd, file, 0);
+ if (map)
+ return (request->hr_proto == httpd->consts.http_11) ?
+ map->encoding11 : map->encoding;
+ return NULL;
+}
+
+#ifndef NO_DYNAMIC_CONTENT
+
+bozo_content_map_t *
+bozo_get_content_map(bozohttpd_t *httpd, const char *name)
+{
+ bozo_content_map_t *map;
+
+ if ((map = bozo_match_content_map(httpd, name, 1)) != NULL)
+ return map;
+
+ httpd->dynamic_content_map_size++;
+ httpd->dynamic_content_map = bozorealloc(httpd,
+ httpd->dynamic_content_map,
+ (httpd->dynamic_content_map_size + 1) * sizeof *map);
+ if (httpd->dynamic_content_map == NULL)
+ bozo_err(httpd, 1, "out of memory allocating content map");
+ map = &httpd->dynamic_content_map[httpd->dynamic_content_map_size];
+ map->name = map->type = map->encoding = map->encoding11 =
+ map->cgihandler = NULL;
+ map--;
+
+ return map;
+}
+
+/*
+ * mime content maps look like:
+ * ".name type encoding encoding11"
+ * where any of type, encoding or encoding11 a dash "-" means "".
+ * eg the .gtar, .tar.Z from above could be written like:
+ * ".gtar multipart/x-gtar - -"
+ * ".tar.Z multipart/x-tar x-compress compress"
+ * or
+ * ".gtar multipart/x-gtar"
+ * ".tar.Z multipart/x-tar x-compress compress"
+ * NOTE: we destroy 'arg'
+ */
+void
+bozo_add_content_map_mime(bozohttpd_t *httpd, const char *cmap0,
+ const char *cmap1, const char *cmap2, const char *cmap3)
+{
+ bozo_content_map_t *map;
+
+ debug((httpd, DEBUG_FAT,
+ "add_content_map: name %s type %s enc %s enc11 %s ",
+ cmap0, cmap1, cmap2, cmap3));
+
+ map = bozo_get_content_map(httpd, cmap0);
+#define CHECKMAP(s) (!s || ((s)[0] == '-' && (s)[1] == '\0') ? "" : (s))
+ map->name = CHECKMAP(cmap0);
+ map->type = CHECKMAP(cmap1);
+ map->encoding = CHECKMAP(cmap2);
+ map->encoding11 = CHECKMAP(cmap3);
+#undef CHECKMAP
+ map->cgihandler = NULL;
+}
+#endif /* NO_DYNAMIC_CONTENT */
--- /dev/null
+/* $NetBSD: daemon-bozo.c,v 1.16 2014/01/02 08:21:38 mrg Exp $ */
+
+/* $eterna: daemon-bozo.c,v 1.24 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2014 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements daemon mode for bozohttpd */
+
+#ifndef NO_DAEMON_MODE
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bozohttpd.h"
+
+static void sigchild(int); /* SIGCHLD handler */
+
+#ifndef POLLRDNORM
+#define POLLRDNORM 0
+#endif
+#ifndef POLLRDBAND
+#define POLLRDBAND 0
+#endif
+#ifndef INFTIM
+#define INFTIM -1
+#endif
+
+static const char* pidfile_path = NULL;
+static pid_t pidfile_pid = 0;
+
+/* ARGSUSED */
+static void
+sigchild(int signo)
+{
+ while (waitpid(-1, NULL, WNOHANG) > 0) {
+ }
+}
+
+/* Signal handler to exit in a controlled manner. This ensures that
+ * any atexit(3) handlers are properly executed. */
+/* ARGSUSED */
+BOZO_DEAD static void
+controlled_exit(int signo)
+{
+
+ exit(EXIT_SUCCESS);
+}
+
+static void
+remove_pidfile(void)
+{
+
+ if (pidfile_path != NULL && pidfile_pid == getpid()) {
+ (void)unlink(pidfile_path);
+ pidfile_path = NULL;
+ }
+}
+
+static void
+create_pidfile(bozohttpd_t *httpd)
+{
+ FILE *file;
+
+ assert(pidfile_path == NULL);
+
+ if (httpd->pidfile == NULL)
+ return;
+
+ if (atexit(remove_pidfile) == -1)
+ bozo_err(httpd, 1, "Failed to install pidfile handler");
+
+ if ((file = fopen(httpd->pidfile, "w")) == NULL)
+ bozo_err(httpd, 1, "Failed to create pidfile '%s'",
+ httpd->pidfile);
+ (void)fprintf(file, "%d\n", getpid());
+ (void)fclose(file);
+
+ pidfile_path = httpd->pidfile;
+ pidfile_pid = getpid();
+
+ debug((httpd, DEBUG_FAT, "Created pid file '%s' for pid %d",
+ pidfile_path, pidfile_pid));
+}
+
+void
+bozo_daemon_init(bozohttpd_t *httpd)
+{
+ struct addrinfo h, *r, *r0;
+ const char *portnum;
+ int e, i, on = 1;
+
+ if (!httpd->background)
+ return;
+
+ portnum = (httpd->bindport) ? httpd->bindport : "http";
+
+ memset(&h, 0, sizeof(h));
+ h.ai_family = PF_UNSPEC;
+ h.ai_socktype = SOCK_STREAM;
+ h.ai_flags = AI_PASSIVE;
+ e = getaddrinfo(httpd->bindaddress, portnum, &h, &r0);
+ if (e)
+ bozo_err(httpd, 1, "getaddrinfo([%s]:%s): %s",
+ httpd->bindaddress ? httpd->bindaddress : "*",
+ portnum, gai_strerror(e));
+ for (r = r0; r != NULL; r = r->ai_next)
+ httpd->nsock++;
+ httpd->sock = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->sock));
+ httpd->fds = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->fds));
+ for (i = 0, r = r0; r != NULL; r = r->ai_next) {
+ httpd->sock[i] = socket(r->ai_family, SOCK_STREAM, 0);
+ if (httpd->sock[i] == -1)
+ continue;
+ if (setsockopt(httpd->sock[i], SOL_SOCKET, SO_REUSEADDR, &on,
+ sizeof(on)) == -1)
+ bozo_warn(httpd, "setsockopt SO_REUSEADDR: %s",
+ strerror(errno));
+ if (bind(httpd->sock[i], r->ai_addr, r->ai_addrlen) == -1)
+ continue;
+ if (listen(httpd->sock[i], SOMAXCONN) == -1)
+ continue;
+ httpd->fds[i].events = POLLIN | POLLPRI | POLLRDNORM |
+ POLLRDBAND | POLLERR;
+ httpd->fds[i].fd = httpd->sock[i];
+ i++;
+ }
+ if (i == 0)
+ bozo_err(httpd, 1, "could not find any addresses to bind");
+ httpd->nsock = i;
+ freeaddrinfo(r0);
+
+ if (httpd->foreground == 0)
+ daemon(1, 0);
+
+ create_pidfile(httpd);
+
+ bozo_warn(httpd, "started in daemon mode as `%s' port `%s' root `%s'",
+ httpd->virthostname, portnum, httpd->slashdir);
+
+ signal(SIGHUP, controlled_exit);
+ signal(SIGINT, controlled_exit);
+ signal(SIGTERM, controlled_exit);
+
+ signal(SIGCHLD, sigchild);
+}
+
+void
+bozo_daemon_closefds(bozohttpd_t *httpd)
+{
+ int i;
+
+ for (i = 0; i < httpd->nsock; i++)
+ close(httpd->sock[i]);
+}
+
+static void
+daemon_runchild(bozohttpd_t *httpd, int fd)
+{
+ httpd->request_times++;
+
+ /* setup stdin/stdout/stderr */
+ dup2(fd, 0);
+ dup2(fd, 1);
+ /*dup2(fd, 2);*/
+ close(fd);
+}
+
+static int
+daemon_poll_err(bozohttpd_t *httpd, int fd, int idx)
+{
+ if ((httpd->fds[idx].revents & (POLLNVAL|POLLERR|POLLHUP)) == 0)
+ return 0;
+
+ bozo_warn(httpd, "poll on fd %d pid %d revents %d: %s",
+ httpd->fds[idx].fd, getpid(), httpd->fds[idx].revents,
+ strerror(errno));
+ bozo_warn(httpd, "nsock = %d", httpd->nsock);
+ close(httpd->sock[idx]);
+ httpd->nsock--;
+ bozo_warn(httpd, "nsock now = %d", httpd->nsock);
+ /* no sockets left */
+ if (httpd->nsock == 0)
+ exit(0);
+ /* last socket closed is the easy case */
+ if (httpd->nsock != idx) {
+ memmove(&httpd->fds[idx], &httpd->fds[idx+1],
+ (httpd->nsock - idx) * sizeof(*httpd->fds));
+ memmove(&httpd->sock[idx], &httpd->sock[idx+1],
+ (httpd->nsock - idx) * sizeof(*httpd->sock));
+ }
+
+ return 1;
+}
+
+/*
+ * the parent never returns from this function, only children that
+ * are ready to run... XXXMRG - still true in fork-lesser bozo?
+ */
+int
+bozo_daemon_fork(bozohttpd_t *httpd)
+{
+ int i;
+
+ debug((httpd, DEBUG_FAT, "%s: pid %u request_times %d",
+ __func__, getpid(),
+ httpd->request_times));
+ /* if we've handled 5 files, exit and let someone else work */
+ if (httpd->request_times > 5 ||
+ (httpd->background == 2 && httpd->request_times > 0))
+ _exit(0);
+
+#if 1
+ if (httpd->request_times > 0)
+ _exit(0);
+#endif
+
+ while (httpd->background) {
+ struct sockaddr_storage ss;
+ socklen_t slen;
+ int fd;
+
+ if (httpd->nsock == 0)
+ exit(0);
+
+ /*
+ * wait for a connection, then fork() and return NULL in
+ * the parent, who will come back here waiting for another
+ * connection. read the request in in the child, and return
+ * it, for processing.
+ */
+again:
+ if (poll(httpd->fds, (unsigned)httpd->nsock, INFTIM) == -1) {
+ /* fail on programmer errors */
+ if (errno == EFAULT ||
+ errno == EINVAL)
+ bozo_err(httpd, 1, "poll: %s",
+ strerror(errno));
+
+ /* sleep on some temporary kernel failures */
+ if (errno == ENOMEM ||
+ errno == EAGAIN)
+ sleep(1);
+
+ goto again;
+ }
+
+ for (i = 0; i < httpd->nsock; i++) {
+ if (daemon_poll_err(httpd, fd, i))
+ break;
+ if (httpd->fds[i].revents == 0)
+ continue;
+
+ slen = sizeof(ss);
+ fd = accept(httpd->fds[i].fd,
+ (struct sockaddr *)(void *)&ss, &slen);
+ if (fd == -1) {
+ if (errno == EFAULT ||
+ errno == EINVAL)
+ bozo_err(httpd, 1, "accept: %s",
+ strerror(errno));
+
+ if (errno == ENOMEM ||
+ errno == EAGAIN)
+ sleep(1);
+
+ continue;
+ }
+
+#if 0
+ /*
+ * This code doesn't work. It interacts very poorly
+ * with ~user translation and needs to be fixed.
+ */
+ if (httpd->request_times > 0) {
+ daemon_runchild(httpd, fd);
+ return 0;
+ }
+#endif
+
+ switch (fork()) {
+ case -1: /* eep, failure */
+ bozo_warn(httpd, "fork() failed, sleeping for "
+ "10 seconds: %s", strerror(errno));
+ close(fd);
+ sleep(10);
+ break;
+
+ case 0: /* child */
+ daemon_runchild(httpd, fd);
+ return 0;
+
+ default: /* parent */
+ close(fd);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+#endif /* NO_DAEMON_MODE */
--- /dev/null
+# $eterna: Makefile,v 1.1 2009/05/22 21:51:39 mrg Exp $
+
+# build a debug bozohttpd
+PROG= bozohttpd-debug
+COPTS+= -DDEBUG -I$(.CURDIR)/..
+
+.include "../Makefile"
+
+.PATH: $(.CURDIR)/..
--- /dev/null
+/* $NetBSD: dir-index-bozo.c,v 1.21 2015/08/27 17:12:18 mrg Exp $ */
+
+/* $eterna: dir-index-bozo.c,v 1.20 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2014 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements directory index generation for bozohttpd */
+
+#ifndef NO_DIRINDEX_SUPPORT
+
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <assert.h>
+
+#include "bozohttpd.h"
+
+static void
+directory_hr(bozohttpd_t *httpd)
+{
+
+ bozo_printf(httpd,
+ "<hr noshade align=\"left\" width=\"80%%\">\r\n\r\n");
+}
+
+/*
+ * output a directory index. return 1 if it actually did something..
+ */
+int
+bozo_dir_index(bozo_httpreq_t *request, const char *dirpath, int isindex)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ struct stat sb;
+ struct dirent **de, **deo;
+ struct tm *tm;
+ DIR *dp;
+ char buf[MAXPATHLEN];
+ char spacebuf[48];
+ char *file = NULL;
+ int l, k, j, i;
+
+ if (!isindex || !httpd->dir_indexing)
+ return 0;
+
+ if (strlen(dirpath) <= strlen(httpd->index_html))
+ dirpath = ".";
+ else {
+ file = bozostrdup(httpd, dirpath);
+
+ file[strlen(file) - strlen(httpd->index_html)] = '\0';
+ dirpath = file;
+ }
+ debug((httpd, DEBUG_FAT, "bozo_dir_index: dirpath ``%s''", dirpath));
+ if (stat(dirpath, &sb) < 0 ||
+ (dp = opendir(dirpath)) == NULL) {
+ if (errno == EPERM)
+ (void)bozo_http_error(httpd, 403, request,
+ "no permission to open directory");
+ else if (errno == ENOENT)
+ (void)bozo_http_error(httpd, 404, request, "no file");
+ else
+ (void)bozo_http_error(httpd, 500, request,
+ "open directory");
+ goto done;
+ /* NOTREACHED */
+ }
+
+ bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto);
+
+ if (request->hr_proto != httpd->consts.http_09) {
+ bozo_print_header(request, NULL, "text/html", "");
+ bozo_printf(httpd, "\r\n");
+ }
+ bozo_flush(httpd, stdout);
+
+ if (request->hr_method == HTTP_HEAD) {
+ closedir(dp);
+ goto done;
+ }
+
+ bozo_printf(httpd,
+ "<html><head><title>Index of %s</title></head>\r\n",
+ request->hr_file);
+ bozo_printf(httpd, "<body><h1>Index of %s</h1>\r\n",
+ request->hr_file);
+ bozo_printf(httpd, "<pre>\r\n");
+#define NAMELEN 40
+#define LMODLEN 19
+ bozo_printf(httpd, "Name "
+ "Last modified "
+ "Size\n");
+ bozo_printf(httpd, "</pre>");
+ directory_hr(httpd);
+ bozo_printf(httpd, "<pre>");
+
+ for (j = k = scandir(dirpath, &de, NULL, alphasort), deo = de;
+ j--; de++) {
+ int nostat = 0;
+ char *name = (*de)->d_name;
+ char *urlname, *htmlname;
+
+ if (strcmp(name, ".") == 0 ||
+ (strcmp(name, "..") != 0 &&
+ httpd->hide_dots && name[0] == '.'))
+ continue;
+
+ snprintf(buf, sizeof buf, "%s/%s", dirpath, name);
+ if (stat(buf, &sb))
+ nostat = 1;
+
+ l = 0;
+
+ urlname = bozo_escape_rfc3986(httpd, name);
+ htmlname = bozo_escape_html(httpd, name);
+ if (htmlname == NULL)
+ htmlname = name;
+ if (strcmp(name, "..") == 0) {
+ bozo_printf(httpd, "<a href=\"../\">");
+ l += bozo_printf(httpd, "Parent Directory");
+ } else if (S_ISDIR(sb.st_mode)) {
+ bozo_printf(httpd, "<a href=\"%s/\">", urlname);
+ l += bozo_printf(httpd, "%s/", htmlname);
+ } else if (strchr(name, ':') != NULL) {
+ /* RFC 3986 4.2 */
+ bozo_printf(httpd, "<a href=\"./%s\">", urlname);
+ l += bozo_printf(httpd, "%s", htmlname);
+ } else {
+ bozo_printf(httpd, "<a href=\"%s\">", urlname);
+ l += bozo_printf(httpd, "%s", htmlname);
+ }
+ if (htmlname != name)
+ free(htmlname);
+ bozo_printf(httpd, "</a>");
+
+ /* NAMELEN spaces */
+ /*LINTED*/
+ assert(/*CONSTCOND*/sizeof(spacebuf) > NAMELEN);
+ i = (l < NAMELEN) ? (NAMELEN - l) : 0;
+ i++;
+ memset(spacebuf, ' ', (size_t)i);
+ spacebuf[i] = '\0';
+ bozo_printf(httpd, "%s", spacebuf);
+ l += i;
+
+ if (nostat)
+ bozo_printf(httpd, "? ?");
+ else {
+ tm = gmtime(&sb.st_mtime);
+ strftime(buf, sizeof buf, "%d-%b-%Y %R", tm);
+ l += bozo_printf(httpd, "%s", buf);
+
+ /* LMODLEN spaces */
+ /*LINTED*/
+ assert(/*CONSTCOND*/sizeof(spacebuf) > LMODLEN);
+ i = (l < (LMODLEN+NAMELEN+1)) ?
+ ((LMODLEN+NAMELEN+1) - l) : 0;
+ i++;
+ memset(spacebuf, ' ', (size_t)i);
+ spacebuf[i] = '\0';
+ bozo_printf(httpd, "%s", spacebuf);
+
+ bozo_printf(httpd, "%12llukB",
+ (unsigned long long)sb.st_size >> 10);
+ }
+ bozo_printf(httpd, "\r\n");
+ }
+
+ closedir(dp);
+ while (k--)
+ free(deo[k]);
+ free(deo);
+ bozo_printf(httpd, "</pre>");
+ directory_hr(httpd);
+ bozo_printf(httpd, "</body></html>\r\n\r\n");
+ bozo_flush(httpd, stdout);
+
+done:
+ free(file);
+ return 1;
+}
+#endif /* NO_DIRINDEX_SUPPORT */
+
--- /dev/null
+# $eterna: Makefile,v 1.1 2010/05/10 02:24:31 mrg Exp $
+
+.PATH: $(.CURDIR)/..
+
+# build bozohttpd library
+LIB= bozohttpd
+COPTS+= -I$(.CURDIR)/..
+
+COPTS+= -DDO_HTPASSWD
+CPPFLAGS+= -DDO_HTPASSWD
+SRCS= bozohttpd.c ssl-bozo.c auth-bozo.c cgi-bozo.c daemon-bozo.c
+SRCS+= tilde-luzah-bozo.c dir-index-bozo.c content-bozo.c
+SRCS+= lua-bozo.c
+
+LDADD= -lcrypt
+DPADD= ${LIBCRYPT}
+
+MAN= libbozohttpd.3
+WARNS= 4
+
+INCS= bozohttpd.h
+INCSDIR= /usr/include
+
+.include <bsd.own.mk>
+
+.if ${MKCRYPTO} != "no"
+
+LDADD+= -lssl -lcrypto
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+
+.else
+
+COPTS+= -DNO_SSL_SUPPORT
+
+.endif
+
+.include <bsd.lib.mk>
--- /dev/null
+.\" $NetBSD: libbozohttpd.3,v 1.3 2014/03/18 18:20:38 riastradh Exp $
+.\"
+.\" $eterna: libbozohttpd.3,v 1.2 2010/05/10 02:48:23 mrg Exp $
+.\"
+.\" Copyright (c) 2009 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This manual page is derived from software contributed to The
+.\" NetBSD Foundation by Alistair Crooks (agc@NetBSD.org)
+.\"
+.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd November 5, 2009
+.Dt LIBBOZOHTTPD 3
+.Os
+.Sh NAME
+.Nm libbozohttpd
+.Nd embedded web server library
+.Sh LIBRARY
+.Lb libbozohttpd
+.Sh SYNOPSIS
+.In bozohttpd.h
+.Ft int
+.Fo bozo_set_pref
+.Fa "bozoprefs_t *prefs" "char *name" "char *value"
+.Fc
+.Ft char *
+.Fo bozo_get_pref
+.Fa "bozoprefs_t *prefs" "char *name"
+.Fc
+.Ft int
+.Fo bozo_set_defaults
+.Fa "bozohttpd_t *httpd" "bozoprefs_t *prefs"
+.Fc
+.Ft void
+.Fo bozo_setup
+.Fa "bozohttpd_t *httpd" "bozoprefs_t *prefs" "const char *vhost" "char *slash"
+.Fc
+.Ft bozo_httpreq_t *
+.Fo bozo_read_request
+.Fa "bozohttpd_t *httpd"
+.Fc
+.Ft void
+.Fo bozo_process_request
+.Fa "bozo_httpreq_t *"
+.Fc
+.Ft void
+.Fo bozo_clean_request
+.Fa "bozo_httpreq_t *"
+.Fc
+.Sh DESCRIPTION
+.Nm
+is a library interface to the
+.Xr bozohttpd 8
+web server.
+The
+.Nm
+library can be used to embed a webserver
+in your applications.
+.Pp
+Normal operation sees the
+.Nm
+process be initialised using the
+.Fn bozo_set_defaults
+function, which will set up the default port
+and other internal settings, allocating
+any necessary space as needed.
+The
+.Fn bozo_set_defaults
+function returns 1 on sucess, 0 on failure.
+.Pp
+The
+.Fn bozo_setup
+function is used to specify the virtual host name
+for the web server.
+A NULL host name will mean that
+.Nm
+will use the local value for the host name,
+as returned by
+.Xr gethostname 3 .
+This virtual hostname should be a fully qualified domain name.
+The final argument to
+.Fn bozo_setup
+is the name of the directory to serve as the root
+directory of the web server tree.
+.Pp
+Once the server has been set up, it serves
+requests by using the
+.Fn bozo_read_request
+function, which returns a pointer to a request structure,
+and
+.Fn bozo_process_request ,
+which deals with the request, and answers the client.
+The request space is de-allocated
+using the
+.Fn bozo_clean_request
+function.
+.Pp
+Preferences are set
+using the function
+.Fn bozo_set_pref
+function
+and queried using the two
+.Fn bozo_get_pref
+function.
+This is the main interface for selecting options, and for
+setting preferences.
+.Sh SEE ALSO
+.Xr gethostname 3 ,
+.Xr ssl 3 ,
+.Xr services 5 ,
+.Xr httpd 8
+.Sh HISTORY
+The
+.Nm
+library first appeared in
+.Nx 6.0 .
+.Sh AUTHORS
+.An Matthew R. Green Aq Mt mrg@eterna.com.au
+.An Alistair Crooks Aq Mt agc@NetBSD.org
+wrote this high-level interface.
+.Pp
+This manual page was written by
+.An Alistair Crooks .
--- /dev/null
+major=0
+minor=0
--- /dev/null
+/* $NetBSD: lua-bozo.c,v 1.12 2015/07/04 22:39:23 christos Exp $ */
+
+/*
+ * Copyright (c) 2013 Marc Balmer <marc@msys.ch>
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements dynamic content generation using Lua for bozohttpd */
+
+#ifndef NO_LUA_SUPPORT
+
+#include <sys/param.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bozohttpd.h"
+
+/* Lua binding for bozohttp */
+
+#if LUA_VERSION_NUM < 502
+#define LUA_HTTPDLIBNAME "httpd"
+#endif
+
+#define FORM "application/x-www-form-urlencoded"
+
+static int
+lua_flush(lua_State *L)
+{
+ bozohttpd_t *httpd;
+
+ lua_pushstring(L, "bozohttpd");
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ httpd = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ bozo_flush(httpd, stdout);
+ return 0;
+}
+
+static int
+lua_print(lua_State *L)
+{
+ bozohttpd_t *httpd;
+
+ lua_pushstring(L, "bozohttpd");
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ httpd = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ bozo_printf(httpd, "%s\r\n", lua_tostring(L, -1));
+ return 0;
+}
+
+static int
+lua_read(lua_State *L)
+{
+ bozohttpd_t *httpd;
+ int n, len;
+ char *data;
+
+ lua_pushstring(L, "bozohttpd");
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ httpd = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ len = luaL_checkinteger(L, -1);
+ data = bozomalloc(httpd, len + 1);
+ n = bozo_read(httpd, STDIN_FILENO, data, len);
+ if (n >= 0) {
+ data[n] = '\0';
+ lua_pushstring(L, data);
+ } else
+ lua_pushnil(L);
+ free(data);
+ return 1;
+}
+
+static int
+lua_register_handler(lua_State *L)
+{
+ lua_state_map_t *map;
+ lua_handler_t *handler;
+ bozohttpd_t *httpd;
+
+ lua_pushstring(L, "lua_state_map");
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ map = lua_touserdata(L, -1);
+ lua_pushstring(L, "bozohttpd");
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ httpd = lua_touserdata(L, -1);
+ lua_pop(L, 2);
+
+ luaL_checkstring(L, 1);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ handler = bozomalloc(httpd, sizeof(lua_handler_t));
+
+ handler->name = bozostrdup(httpd, lua_tostring(L, 1));
+ handler->ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ SIMPLEQ_INSERT_TAIL(&map->handlers, handler, h_next);
+ httpd->process_lua = 1;
+ return 0;
+}
+
+static int
+lua_write(lua_State *L)
+{
+ bozohttpd_t *httpd;
+ const char *data;
+
+ lua_pushstring(L, "bozohttpd");
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ httpd = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ data = luaL_checkstring(L, -1);
+ lua_pushinteger(L, bozo_write(httpd, STDIN_FILENO, data, strlen(data)));
+ return 1;
+}
+
+static int
+luaopen_httpd(lua_State *L)
+{
+ struct luaL_Reg functions[] = {
+ { "flush", lua_flush },
+ { "print", lua_print },
+ { "read", lua_read },
+ { "register_handler", lua_register_handler },
+ { "write", lua_write },
+ { NULL, NULL }
+ };
+#if LUA_VERSION_NUM >= 502
+ luaL_newlib(L, functions);
+#else
+ luaL_register(L, LUA_HTTPDLIBNAME, functions);
+#endif
+ lua_pushstring(L, "httpd 1.0.0");
+ lua_setfield(L, -2, "_VERSION");
+ return 1;
+}
+
+#if LUA_VERSION_NUM < 502
+static void
+lua_openlib(lua_State *L, const char *name, lua_CFunction fn)
+{
+ lua_pushcfunction(L, fn);
+ lua_pushstring(L, name);
+ lua_call(L, 1, 0);
+}
+#endif
+
+/* bozohttpd integration */
+void
+bozo_add_lua_map(bozohttpd_t *httpd, const char *prefix, const char *script)
+{
+ lua_state_map_t *map;
+
+ map = bozomalloc(httpd, sizeof(lua_state_map_t));
+ map->prefix = bozostrdup(httpd, prefix);
+ if (*script == '/')
+ map->script = bozostrdup(httpd, script);
+ else {
+ char cwd[MAXPATHLEN], *path;
+
+ getcwd(cwd, sizeof(cwd) - 1);
+ asprintf(&path, "%s/%s", cwd, script);
+ map->script = path;
+ }
+ map->L = luaL_newstate();
+ if (map->L == NULL)
+ bozo_err(httpd, 1, "can't create Lua state");
+ SIMPLEQ_INIT(&map->handlers);
+
+#if LUA_VERSION_NUM >= 502
+ luaL_openlibs(map->L);
+ lua_getglobal(map->L, "package");
+ lua_getfield(map->L, -1, "preload");
+ lua_pushcfunction(map->L, luaopen_httpd);
+ lua_setfield(map->L, -2, "httpd");
+ lua_pop(map->L, 2);
+#else
+ lua_openlib(map->L, "", luaopen_base);
+ lua_openlib(map->L, LUA_LOADLIBNAME, luaopen_package);
+ lua_openlib(map->L, LUA_TABLIBNAME, luaopen_table);
+ lua_openlib(map->L, LUA_STRLIBNAME, luaopen_string);
+ lua_openlib(map->L, LUA_MATHLIBNAME, luaopen_math);
+ lua_openlib(map->L, LUA_OSLIBNAME, luaopen_os);
+ lua_openlib(map->L, LUA_IOLIBNAME, luaopen_io);
+ lua_openlib(map->L, LUA_HTTPDLIBNAME, luaopen_httpd);
+#endif
+ lua_pushstring(map->L, "lua_state_map");
+ lua_pushlightuserdata(map->L, map);
+ lua_settable(map->L, LUA_REGISTRYINDEX);
+
+ lua_pushstring(map->L, "bozohttpd");
+ lua_pushlightuserdata(map->L, httpd);
+ lua_settable(map->L, LUA_REGISTRYINDEX);
+
+ if (luaL_loadfile(map->L, script))
+ bozo_err(httpd, 1, "failed to load script %s: %s", script,
+ lua_tostring(map->L, -1));
+ if (lua_pcall(map->L, 0, 0, 0))
+ bozo_err(httpd, 1, "failed to execute script %s: %s", script,
+ lua_tostring(map->L, -1));
+ SIMPLEQ_INSERT_TAIL(&httpd->lua_states, map, s_next);
+}
+
+static void
+lua_env(lua_State *L, const char *name, const char *value)
+{
+ lua_pushstring(L, value);
+ lua_setfield(L, -2, name);
+}
+
+/* decode query string */
+static void
+lua_url_decode(lua_State *L, char *s)
+{
+ char *v, *p, *val, *q;
+ char buf[3];
+ int c;
+
+ v = strchr(s, '=');
+ if (v == NULL)
+ return;
+ *v++ = '\0';
+ val = malloc(strlen(v) + 1);
+ if (val == NULL)
+ return;
+
+ for (p = v, q = val; *p; p++) {
+ switch (*p) {
+ case '%':
+ if (*(p + 1) == '\0' || *(p + 2) == '\0') {
+ free(val);
+ return;
+ }
+ buf[0] = *++p;
+ buf[1] = *++p;
+ buf[2] = '\0';
+ sscanf(buf, "%2x", &c);
+ *q++ = (char)c;
+ break;
+ case '+':
+ *q++ = ' ';
+ break;
+ default:
+ *q++ = *p;
+ }
+ }
+ *q = '\0';
+ lua_pushstring(L, val);
+ lua_setfield(L, -2, s);
+ free(val);
+}
+
+static void
+lua_decode_query(lua_State *L, char *query)
+{
+ char *s;
+
+ s = strtok(query, "&");
+ while (s) {
+ lua_url_decode(L, s);
+ s = strtok(NULL, "&");
+ }
+}
+
+int
+bozo_process_lua(bozo_httpreq_t *request)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ lua_state_map_t *map;
+ lua_handler_t *hndlr;
+ int n, ret, length;
+ char date[40];
+ bozoheaders_t *headp;
+ char *s, *query, *uri, *file, *command, *info, *content;
+ const char *type, *clen;
+ char *prefix, *handler, *p;
+ int rv = 0;
+
+ if (!httpd->process_lua)
+ return 0;
+
+ info = NULL;
+ query = NULL;
+ prefix = NULL;
+ uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file;
+
+ if (*uri == '/') {
+ file = bozostrdup(httpd, uri);
+ if (file == NULL)
+ goto out;
+ prefix = bozostrdup(httpd, &uri[1]);
+ } else {
+ if (asprintf(&file, "/%s", uri) < 0)
+ goto out;
+ prefix = bozostrdup(httpd, uri);
+ }
+ if (prefix == NULL)
+ goto out;
+
+ if (request->hr_query && request->hr_query[0])
+ query = bozostrdup(httpd, request->hr_query);
+
+ p = strchr(prefix, '/');
+ if (p == NULL)
+ goto out;
+ *p++ = '\0';
+ handler = p;
+ if (!*handler)
+ goto out;
+ p = strchr(handler, '/');
+ if (p != NULL)
+ *p++ = '\0';
+
+ command = file + 1;
+ if ((s = strchr(command, '/')) != NULL) {
+ info = bozostrdup(httpd, s);
+ *s = '\0';
+ }
+
+ type = request->hr_content_type;
+ clen = request->hr_content_length;
+
+ SIMPLEQ_FOREACH(map, &httpd->lua_states, s_next) {
+ if (strcmp(map->prefix, prefix))
+ continue;
+
+ SIMPLEQ_FOREACH(hndlr, &map->handlers, h_next) {
+ if (strcmp(hndlr->name, handler))
+ continue;
+
+ lua_rawgeti(map->L, LUA_REGISTRYINDEX, hndlr->ref);
+
+ /* Create the "environment" */
+ lua_newtable(map->L);
+ lua_env(map->L, "SERVER_NAME",
+ BOZOHOST(httpd, request));
+ lua_env(map->L, "GATEWAY_INTERFACE", "Luigi/1.0");
+ lua_env(map->L, "SERVER_PROTOCOL", request->hr_proto);
+ lua_env(map->L, "REQUEST_METHOD",
+ request->hr_methodstr);
+ lua_env(map->L, "SCRIPT_PREFIX", map->prefix);
+ lua_env(map->L, "SCRIPT_NAME", file);
+ lua_env(map->L, "HANDLER_NAME", hndlr->name);
+ lua_env(map->L, "SCRIPT_FILENAME", map->script);
+ lua_env(map->L, "SERVER_SOFTWARE",
+ httpd->server_software);
+ lua_env(map->L, "REQUEST_URI", uri);
+ lua_env(map->L, "DATE_GMT",
+ bozo_http_date(date, sizeof(date)));
+ if (query && *query)
+ lua_env(map->L, "QUERY_STRING", query);
+ if (info && *info)
+ lua_env(map->L, "PATH_INFO", info);
+ if (type && *type)
+ lua_env(map->L, "CONTENT_TYPE", type);
+ if (clen && *clen)
+ lua_env(map->L, "CONTENT_LENGTH", clen);
+ if (request->hr_serverport && *request->hr_serverport)
+ lua_env(map->L, "SERVER_PORT",
+ request->hr_serverport);
+ if (request->hr_remotehost && *request->hr_remotehost)
+ lua_env(map->L, "REMOTE_HOST",
+ request->hr_remotehost);
+ if (request->hr_remoteaddr && *request->hr_remoteaddr)
+ lua_env(map->L, "REMOTE_ADDR",
+ request->hr_remoteaddr);
+
+ /* Pass the headers in a separate table */
+ lua_newtable(map->L);
+ SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next)
+ lua_env(map->L, headp->h_header,
+ headp->h_value);
+
+ /* Pass the query variables */
+ if ((query && *query) ||
+ (type && *type && !strcmp(type, FORM))) {
+ lua_newtable(map->L);
+ if (query && *query)
+ lua_decode_query(map->L, query);
+ if (type && *type && !strcmp(type, FORM)) {
+ if (clen && *clen && atol(clen) > 0) {
+ length = atol(clen);
+ content = bozomalloc(httpd,
+ length + 1);
+ n = bozo_read(httpd,
+ STDIN_FILENO, content,
+ length);
+ if (n >= 0) {
+ content[n] = '\0';
+ lua_decode_query(map->L,
+ content);
+ } else {
+ lua_pop(map->L, 1);
+ lua_pushnil(map->L);
+ }
+ free(content);
+ }
+ }
+ } else
+ lua_pushnil(map->L);
+
+ ret = lua_pcall(map->L, 3, 0, 0);
+ if (ret)
+ printf("<br>Lua error: %s\n",
+ lua_tostring(map->L, -1));
+ bozo_flush(httpd, stdout);
+ rv = 1;
+ goto out;
+ }
+ }
+out:
+ free(prefix);
+ free(uri);
+ free(info);
+ free(query);
+ free(file);
+ return rv;
+}
+
+#endif /* NO_LUA_SUPPORT */
--- /dev/null
+#PREFIX=/Users/agcrooks
+PREFIX=/usr
+
+#LIBDIR=/usr/lib
+
+LIB=luabozohttpd
+SRCS=glue.c
+MKMAN=no
+CPPFLAGS+=-g -I${PREFIX}/pkg/include
+LDADD+= -lbozohttpd
+WARNS=4
+CLEANFILES+= a a.sig
+
+.include <bsd.lib.mk>
+.include <bsd.own.mk>
+
+LUABOZOOBJDIR != cd ${.CURDIR} && ${PRINTOBJDIR}
+
+OPSYS!= uname -s
+
+.if ${OPSYS} == "Darwin"
+.sinclude <bsd.warns.mk>
+
+lib${LIB}.dylib:
+ libtool -dynamic -o ${.TARGET} ${OBJS} ${PREFIX}/pkg/lib/liblua.dylib /usr/lib/libc.dylib ${PREFIX}/pkg/lib/libbozohttpd.dylib
+
+t: lib${LIB}.dylib
+ cp Makefile a
+ ./bozo.lua --sign --detached a
+ ./bozo.lua --verify a.sig
+
+.else
+t:
+ cp Makefile a
+ env LD_LIBRARY_PATH=${LUABOZOOBJDIR}:/lib:/usr/lib:${PREFIX}/lib \
+ ./bozo.lua --sign --detached a
+ env LD_LIBRARY_PATH=${LUABOZOOBJDIR}:/lib:/usr/lib:${PREFIX}/lib \
+ ./bozo.lua --verify a.sig
+.endif
--- /dev/null
+#! /usr/bin/env lua
+
+--
+-- Copyright (c) 2009 The NetBSD Foundation, Inc.
+-- All rights reserved.
+--
+-- This code is derived from software contributed to The NetBSD Foundation
+-- by Alistair Crooks (agc@netbsd.org)
+--
+-- 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+--
+
+-- command line args
+dofile "optparse.lua"
+
+opt = OptionParser{usage="%prog [options] root [vhost]", version="20091105"}
+
+opt.add_option{"-C", "--cgimap", action="store", dest="cgimap", help="--cgimap 's t'"}
+opt.add_option{"-H", "--hide-dots", action="store_true", dest="hidedots", help="--hide-dots"}
+opt.add_option{"-I", "--portnum", action="store", dest="portnum", help="--portnum number"}
+opt.add_option{"-M", "--dynamicmime", action="store", dest="dynmime", help="--dynamicmime 'suffix type a b'"}
+opt.add_option{"-S", "--server-software", action="store", dest="serversw", help="--server-software name"}
+opt.add_option{"-U", "--username", action="store", dest="username", help="--username name"}
+opt.add_option{"-V", "--unknown-slash", action="store_true", dest="unknown", help="--unknown-slash"}
+opt.add_option{"-X", "--dir-index", action="store_true", dest="dirindex", help="--dir-index"}
+opt.add_option{"-Z", "--ssl", action="store", dest="ssl", help="--ssl 'cert priv'"}
+opt.add_option{"-b", "--background", action="store", dest="background", help="--background count"}
+opt.add_option{"-c", "--cgibin", action="store", dest="cgibin", help="--cgibin bin"}
+opt.add_option{"-e", "--dirtyenv", action="store_true", dest="dirtyenv", help="--dirtyenv"}
+opt.add_option{"-f", "--foreground", action="store_true", dest="foreground", help="--foreground"}
+opt.add_option{"-i", "--bindaddr", action="store", dest="bindaddress", help="--bindaddr address"}
+opt.add_option{"-n", "--numeric", action="store_true", dest="numeric", help="--numeric"}
+opt.add_option{"-p", "--public-html", action="store", dest="public_html", help="--public-html dir"}
+opt.add_option{"-r", "--trusted-referal", action="store_true", dest="trustedref", help="trusted referal"}
+opt.add_option{"-s", "--logtostderr", action="store_true", dest="logstderr", help="log to stderr"}
+opt.add_option{"-t", "--chroot", action="store", dest="chroot", help="--chroot dir"}
+opt.add_option{"-u", "--enable-users", action="store_true", dest="enableusers", help="--enable-users"}
+opt.add_option{"-v", "--virtbase", action="store", dest="virtbase", help="virtual base location"}
+opt.add_option{"-x", "--index-html", action="store", dest="indexhtml", help="index.html name"}
+
+-- caller lua script
+local extension = ".so"
+f = io.open("libluabozohttpd.dylib", "r")
+if f then
+ extension = ".dylib"
+ io.close(f)
+end
+glupkg = package.loadlib("./" .. "libluabozohttpd" .. extension, "luaopen_bozohttpd")
+bozohttpd = glupkg()
+
+-- initialise
+httpd = bozohttpd.new()
+bozohttpd.init_httpd(httpd)
+prefs = bozohttpd.init_prefs()
+
+-- parse command line args
+options,args = opt.parse_args()
+if options.portnum then
+ bozohttpd.set_pref(prefs, "port number", options.portnum)
+end
+if options.background then
+ bozohttpd.set_pref(prefs, "background", options.background)
+end
+if options.numeric then
+ bozohttpd.set_pref(prefs, "numeric", "true")
+end
+if options.logstderr then
+ bozohttpd.set_pref(prefs, "log to stderr", "true")
+end
+if options.foreground then
+ bozohttpd.set_pref(prefs, "foreground", "true")
+end
+if options.trustedref then
+ bozohttpd.set_pref(prefs, "trusted referal", "true")
+end
+if options.dynmime then
+ suffix, type, s1, s2 = string.find(options.dynmime,
+ "(%S+)%s+(%S+)%s+(%S+)%s+(%S+)")
+ bozohttpd.dynamic_mime(httpd, suffix, type, s1, s2)
+end
+if options.serversw then
+ bozohttpd.set_pref(prefs, "server software", options.serversw)
+end
+if options.ssl then
+ cert, priv = string.find(options.ssl, "(%S+)%s+(%S+)")
+ bozohttpd.dynamic_mime(httpd, cert, priv)
+end
+if options.username then
+ bozohttpd.set_pref(prefs, "username", options.username)
+end
+if options.unknownslash then
+ bozohttpd.set_pref(prefs, "unknown slash", "true")
+end
+if options.virtbase then
+ bozohttpd.set_pref(prefs, "virtual base", options.virtbase)
+end
+if options.indexhtml then
+ bozohttpd.set_pref(prefs, "index.html", options.indexhtml)
+end
+if options.dirtyenv then
+ bozohttpd.set_pref(prefs, "dirty environment", "true")
+end
+if options.bindaddr then
+ bozohttpd.set_pref(prefs, "bind address", options.bindaddr)
+end
+if options.cgibin then
+ bozohttpd.cgi_setbin(httpd, options.cgibin)
+end
+if options.cgimap then
+ name, handler = string.find(options.cgimap, "(%S+)%s+(%S+)")
+ bozohttpd.cgi_map(httpd, name, handler)
+end
+if options.public_html then
+ bozohttpd.set_pref(prefs, "public_html", options.public_html)
+end
+if options.chroot then
+ bozohttpd.set_pref(prefs, "chroot dir", options.chroot)
+end
+if options.enableusers then
+ bozohttpd.set_pref(prefs, "enable users", "true")
+end
+if options.hidedots then
+ bozohttpd.set_pref(prefs, "hide dots", "true")
+end
+if options.dirindex then
+ bozohttpd.set_pref(prefs, "directory indexing", "true")
+end
+
+if #args < 1 then
+ print("At least one arg needed for root directory")
+else
+ -- set up connections
+ local vhost = args[2] or ""
+ bozohttpd.setup(httpd, prefs, vhost, args[1])
+
+ -- loop, serving requests
+ local numreps = options.background or 0
+ repeat
+ req = bozohttpd.read_request(httpd)
+ bozohttpd.process_request(httpd, req)
+ bozohttpd.clean_request(req)
+ until numreps == 0
+end
--- /dev/null
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@netbsd.org)
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <bozohttpd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define LUA_LIB
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif /* !__UNCONST */
+
+int luaopen_bozohttpd(lua_State *);
+
+#if 0
+typedef struct strarg_t {
+ const char *s; /* string */
+ const int n; /* corresponding int value */
+} strarg_t;
+
+/* map a string onto an int */
+static int
+findtype(strarg_t *strs, const char *s)
+{
+ strarg_t *sp;
+
+ for (sp = strs ; sp->s && strcasecmp(sp->s, s) != 0 ; sp++) {
+ }
+ return sp->n;
+}
+#endif
+
+/* init() */
+static int
+l_new(lua_State *L)
+{
+ bozohttpd_t *httpd;
+
+ httpd = lua_newuserdata(L, sizeof(*httpd));
+ (void) memset(httpd, 0x0, sizeof(*httpd));
+ return 1;
+}
+
+/* initialise(httpd) */
+static int
+l_init_httpd(lua_State *L)
+{
+ bozohttpd_t *httpd;
+
+ httpd = lua_touserdata(L, 1);
+ lua_pushnumber(L, bozo_init_httpd(httpd));
+ return 1;
+}
+
+/* initialise(prefs) */
+static int
+l_init_prefs(lua_State *L)
+{
+ bozoprefs_t *prefs;
+
+ prefs = lua_newuserdata(L, sizeof(*prefs));
+ (void) memset(prefs, 0x0, sizeof(*prefs));
+ (void) bozo_init_prefs(prefs);
+ return 1;
+}
+
+/* bozo_set_pref(prefs, name, value) */
+static int
+l_bozo_set_pref(lua_State *L)
+{
+ bozoprefs_t *prefs;
+ const char *name;
+ const char *value;
+
+ prefs = lua_touserdata(L, 1);
+ name = luaL_checkstring(L, 2);
+ value = luaL_checkstring(L, 3);
+ lua_pushnumber(L, bozo_set_pref(prefs, name, value));
+ return 1;
+}
+
+/* bozo_get_pref(prefs, name) */
+static int
+l_bozo_get_pref(lua_State *L)
+{
+ bozoprefs_t *prefs;
+ const char *name;
+
+ prefs = lua_touserdata(L, 1);
+ name = luaL_checkstring(L, 2);
+ lua_pushstring(L, bozo_get_pref(prefs, name));
+ return 1;
+}
+
+/* bozo_setup(httpd, prefs, host, root) */
+static int
+l_bozo_setup(lua_State *L)
+{
+ bozohttpd_t *httpd;
+ bozoprefs_t *prefs;
+ const char *vhost;
+ const char *root;
+
+ httpd = lua_touserdata(L, 1);
+ prefs = lua_touserdata(L, 2);
+ vhost = luaL_checkstring(L, 3);
+ if (vhost && *vhost == 0x0) {
+ vhost = NULL;
+ }
+ root = luaL_checkstring(L, 4);
+ lua_pushnumber(L, bozo_setup(httpd, prefs, vhost, root));
+ return 1;
+}
+
+/* bozo_read_request(httpd) */
+static int
+l_bozo_read_request(lua_State *L)
+{
+ bozo_httpreq_t *req;
+ bozohttpd_t *httpd;
+
+ httpd = lua_touserdata(L, 1);
+ req = bozo_read_request(httpd);
+ lua_pushlightuserdata(L, req);
+ return 1;
+}
+
+/* bozo_process_request(httpd, req) */
+static int
+l_bozo_process_request(lua_State *L)
+{
+ bozo_httpreq_t *req;
+ bozohttpd_t *httpd;
+
+ httpd = lua_touserdata(L, 1);
+ req = lua_touserdata(L, 2);
+ bozo_process_request(httpd, req);
+ lua_pushnumber(L, 1);
+ return 1;
+}
+
+/* bozo_clean_request(req) */
+static int
+l_bozo_clean_request(lua_State *L)
+{
+ bozo_httpreq_t *req;
+
+ req = lua_touserdata(L, 1);
+ bozo_clean_request(req);
+ lua_pushnumber(L, 1);
+ return 1;
+}
+
+/* dynamic_mime(httpd, one, two, three, four) */
+static int
+l_bozo_dynamic_mime(lua_State *L)
+{
+ bozohttpd_t *httpd;
+ const char *s[4];
+
+ httpd = lua_touserdata(L, 1);
+ s[0] = luaL_checkstring(L, 2);
+ s[1] = luaL_checkstring(L, 3);
+ s[2] = luaL_checkstring(L, 4);
+ s[3] = luaL_checkstring(L, 5);
+ bozo_add_content_map_mime(httpd, s[0], s[1], s[2], s[3]);
+ lua_pushnumber(L, 1);
+ return 1;
+}
+
+/* ssl_set_opts(httpd, one, two) */
+static int
+l_bozo_ssl_set_opts(lua_State *L)
+{
+ bozohttpd_t *httpd;
+ const char *s[2];
+
+ httpd = lua_touserdata(L, 1);
+ s[0] = luaL_checkstring(L, 2);
+ s[1] = luaL_checkstring(L, 3);
+ bozo_ssl_set_opts(httpd, s[0], s[1]);
+ lua_pushnumber(L, 1);
+ return 1;
+}
+
+/* cgi_setbin(httpd, bin) */
+static int
+l_bozo_cgi_setbin(lua_State *L)
+{
+ bozohttpd_t *httpd;
+ const char *bin;
+
+ httpd = lua_touserdata(L, 1);
+ bin = luaL_checkstring(L, 2);
+ bozo_cgi_setbin(httpd, bin);
+ lua_pushnumber(L, 1);
+ return 1;
+}
+
+/* cgi_map(httpd, 1, 2) */
+static int
+l_bozo_cgi_map(lua_State *L)
+{
+ bozohttpd_t *httpd;
+ const char *s[2];
+
+ httpd = lua_touserdata(L, 1);
+ s[0] = luaL_checkstring(L, 2);
+ s[1] = luaL_checkstring(L, 3);
+ bozo_add_content_map_cgi(httpd, s[0], s[1]);
+ lua_pushnumber(L, 1);
+ return 1;
+}
+
+const struct luaL_reg libluabozohttpd[] = {
+ { "new", l_new },
+ { "init_httpd", l_init_httpd },
+ { "init_prefs", l_init_prefs },
+
+ { "set_pref", l_bozo_set_pref },
+ { "get_pref", l_bozo_get_pref },
+ { "setup", l_bozo_setup },
+ { "dynamic_mime", l_bozo_dynamic_mime },
+ { "ssl_set_opts", l_bozo_ssl_set_opts },
+ { "cgi_setbin", l_bozo_cgi_setbin },
+ { "cgi_map", l_bozo_cgi_map },
+
+ { "read_request", l_bozo_read_request },
+ { "process_request", l_bozo_process_request },
+ { "clean_request", l_bozo_clean_request },
+
+ { NULL, NULL }
+};
+
+int
+luaopen_bozohttpd(lua_State *L)
+{
+ luaL_openlib(L, "bozohttpd", libluabozohttpd, 0);
+ return 1;
+}
--- /dev/null
+-- Lua command line option parser.
+-- Interface based on Pythons optparse.
+-- http://docs.python.org/lib/module-optparse.html
+-- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license)
+--
+-- To be used like this:
+-- t={usage="<some usage message>", version="<version string>"}
+-- op=OptionParser(t)
+-- op=add_option{"<opt>", action=<action>, dest=<dest>, help="<help message for this option>"}
+--
+-- with :
+-- <opt> the option string to be used (can be anything, if one letter opt, then should be -x val, more letters: -xy=val )
+-- <action> one of
+-- - store: store in options as key, val
+-- - store_true: stores key, true
+-- - store_false: stores key, false
+-- <dest> is the key under which the option is saved
+--
+-- options,args = op.parse_args()
+--
+-- now options is the table of options (key, val) and args is the table with non-option arguments.
+-- You can use op.fail(message) for failing and op.print_help() for printing the usage as you like.
+
+function OptionParser(t)
+ local usage = t.usage
+ local version = t.version
+
+ local o = {}
+ local option_descriptions = {}
+ local option_of = {}
+
+ function o.fail(s) -- extension
+ io.stderr:write(s .. '\n')
+ os.exit(1)
+ end
+
+ function o.add_option(optdesc)
+ option_descriptions[#option_descriptions+1] = optdesc
+ for _,v in ipairs(optdesc) do
+ option_of[v] = optdesc
+ end
+ end
+ function o.parse_args()
+ -- expand options (e.g. "--input=file" -> "--input", "file")
+ local arg = {unpack(arg)}
+ for i=#arg,1,-1 do local v = arg[i]
+ local flag, val = v:match('^(%-%-%w+)=(.*)')
+ if flag then
+ arg[i] = flag
+ table.insert(arg, i+1, val)
+ end
+ end
+
+ local options = {}
+ local args = {}
+ local i = 1
+ while i <= #arg do local v = arg[i]
+ local optdesc = option_of[v]
+ if optdesc then
+ local action = optdesc.action
+ local val
+ if action == 'store' or action == nil then
+ i = i + 1
+ val = arg[i]
+ if not val then o.fail('option requires an argument ' .. v) end
+ elseif action == 'store_true' then
+ val = true
+ elseif action == 'store_false' then
+ val = false
+ end
+ options[optdesc.dest] = val
+ else
+ if v:match('^%-') then o.fail('invalid option ' .. v) end
+ args[#args+1] = v
+ end
+ i = i + 1
+ end
+ if options.help then
+ o.print_help()
+ os.exit()
+ end
+ if options.version then
+ io.stdout:write(t.version .. "\n")
+ os.exit()
+ end
+ return options, args
+ end
+
+ local function flags_str(optdesc)
+ local sflags = {}
+ local action = optdesc.action
+ for _,flag in ipairs(optdesc) do
+ local sflagend
+ if action == nil or action == 'store' then
+ local metavar = optdesc.metavar or optdesc.dest:upper()
+ sflagend = #flag == 2 and ' ' .. metavar
+ or '=' .. metavar
+ else
+ sflagend = ''
+ end
+ sflags[#sflags+1] = flag .. sflagend
+ end
+ return table.concat(sflags, ', ')
+ end
+
+ function o.print_help()
+ io.stdout:write("Usage: " .. usage:gsub('%%prog', arg[0]) .. "\n")
+ io.stdout:write("\n")
+ io.stdout:write("Options:\n")
+ for _,optdesc in ipairs(option_descriptions) do
+ io.stdout:write(" " .. flags_str(optdesc) ..
+ " " .. optdesc.help .. "\n")
+ end
+ end
+ o.add_option{"--help", action="store_true", dest="help",
+ help="show this help message and exit"}
+ if t.version then
+ o.add_option{"--version", action="store_true", dest="version",
+ help="output version info."}
+ end
+ return o
+end
+
--- /dev/null
+major=0
+minor=0
--- /dev/null
+/* $NetBSD: main.c,v 1.8 2014/07/16 07:41:43 mrg Exp $ */
+
+/* $eterna: main.c,v 1.6 2011/11/18 09:21:15 mrg Exp $ */
+/* from: eterna: bozohttpd.c,v 1.159 2009/05/23 02:14:30 mrg Exp */
+
+/*
+ * Copyright (c) 1997-2014 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 program is dedicated to the Great God of Processed Cheese */
+
+/*
+ * main.c: C front end to bozohttpd
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "bozohttpd.h"
+
+/* variables and functions */
+#ifndef LOG_FTP
+#define LOG_FTP LOG_DAEMON
+#endif
+
+/* print a usage message, and then exit */
+BOZO_DEAD static void
+usage(bozohttpd_t *httpd, char *progname)
+{
+ bozo_warn(httpd, "usage: %s [options] slashdir [virtualhostname]",
+ progname);
+ bozo_warn(httpd, "options:");
+#ifndef NO_DEBUG
+ bozo_warn(httpd, " -d\t\t\tenable debug support");
+#endif
+ bozo_warn(httpd, " -s\t\t\talways log to stderr");
+#ifndef NO_USER_SUPPORT
+ bozo_warn(httpd, " -u\t\t\tenable ~user/public_html support");
+ bozo_warn(httpd, " -p dir\t\tchange `public_html' directory name]");
+#endif
+#ifndef NO_DYNAMIC_CONTENT
+ bozo_warn(httpd, " -M arg t c c11\tadd this mime extenstion");
+#endif
+#ifndef NO_CGIBIN_SUPPORT
+#ifndef NO_DYNAMIC_CONTENT
+ bozo_warn(httpd, " -C arg prog\t\tadd this CGI handler");
+#endif
+ bozo_warn(httpd,
+ " -c cgibin\t\tenable cgi-bin support in this directory");
+#endif
+#ifndef NO_LUA_SUPPORT
+ bozo_warn(httpd, " -L arg script\tadd this Lua script");
+#endif
+ bozo_warn(httpd, " -I port\t\tbind or use on this port");
+#ifndef NO_DAEMON_MODE
+ bozo_warn(httpd, " -b\t\t\tbackground and go into daemon mode");
+ bozo_warn(httpd, " -f\t\t\tkeep daemon mode in the foreground");
+ bozo_warn(httpd,
+ " -i address\t\tbind on this address (daemon mode only)");
+ bozo_warn(httpd, " -P pidfile\t\tpath to the pid file to create");
+#endif
+ bozo_warn(httpd, " -S version\t\tset server version string");
+ bozo_warn(httpd, " -t dir\t\tchroot to `dir'");
+ bozo_warn(httpd, " -U username\t\tchange user to `user'");
+ bozo_warn(httpd,
+ " -e\t\t\tdon't clean the environment (-t and -U only)");
+ bozo_warn(httpd,
+ " -v virtualroot\tenable virtual host support "
+ "in this directory");
+ bozo_warn(httpd,
+ " -r\t\t\tmake sure sub-pages come from "
+ "this host via referrer");
+#ifndef NO_DIRINDEX_SUPPORT
+ bozo_warn(httpd,
+ " -X\t\t\tenable automatic directory index support");
+ bozo_warn(httpd,
+ " -H\t\t\thide files starting with a period (.)"
+ " in index mode");
+#endif
+ bozo_warn(httpd,
+ " -x index\t\tchange default `index.html' file name");
+#ifndef NO_SSL_SUPPORT
+ bozo_warn(httpd,
+ " -Z cert privkey\tspecify path to server certificate"
+ " and private key file\n"
+ "\t\t\tin pem format and enable bozohttpd in SSL mode");
+#endif /* NO_SSL_SUPPORT */
+ bozo_err(httpd, 1, "%s failed to start", progname);
+}
+
+int
+main(int argc, char **argv)
+{
+ bozo_httpreq_t *request;
+ bozohttpd_t httpd;
+ bozoprefs_t prefs;
+ char *progname;
+ int c;
+
+ (void) memset(&httpd, 0x0, sizeof(httpd));
+ (void) memset(&prefs, 0x0, sizeof(prefs));
+
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ openlog(progname, LOG_PID|LOG_NDELAY, LOG_FTP);
+
+ bozo_set_defaults(&httpd, &prefs);
+
+ while ((c = getopt(argc, argv,
+ "C:HI:L:M:P:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) {
+ switch(c) {
+
+ case 'L':
+#ifdef NO_LUA_SUPPORT
+ bozo_err(&httpd, 1,
+ "Lua support is not enabled");
+ /* NOTREACHED */
+#else
+ /* make sure there's two argument */
+ if (argc - optind < 1)
+ usage(&httpd, progname);
+ bozo_add_lua_map(&httpd, optarg, argv[optind]);
+ optind++;
+ break;
+#endif /* NO_LUA_SUPPORT */
+ case 'M':
+#ifdef NO_DYNAMIC_CONTENT
+ bozo_err(&httpd, 1,
+ "dynamic mime content support is not enabled");
+ /* NOTREACHED */
+#else
+ /* make sure there's four arguments */
+ if (argc - optind < 3)
+ usage(&httpd, progname);
+ bozo_add_content_map_mime(&httpd, optarg, argv[optind],
+ argv[optind+1], argv[optind+2]);
+ optind += 3;
+ break;
+#endif /* NO_DYNAMIC_CONTENT */
+
+ case 'n':
+ bozo_set_pref(&prefs, "numeric", "true");
+ break;
+
+ case 'r':
+ bozo_set_pref(&prefs, "trusted referal", "true");
+ break;
+
+ case 's':
+ bozo_set_pref(&prefs, "log to stderr", "true");
+ break;
+
+ case 'S':
+ bozo_set_pref(&prefs, "server software", optarg);
+ break;
+ case 'Z':
+#ifdef NO_SSL_SUPPORT
+ bozo_err(&httpd, 1, "ssl support is not enabled");
+ /* NOT REACHED */
+#else
+ /* make sure there's two arguments */
+ if (argc - optind < 1)
+ usage(&httpd, progname);
+ bozo_ssl_set_opts(&httpd, optarg, argv[optind++]);
+ break;
+#endif /* NO_SSL_SUPPORT */
+ case 'U':
+ bozo_set_pref(&prefs, "username", optarg);
+ break;
+
+ case 'V':
+ bozo_set_pref(&prefs, "unknown slash", "true");
+ break;
+
+ case 'v':
+ bozo_set_pref(&prefs, "virtual base", optarg);
+ break;
+
+ case 'x':
+ bozo_set_pref(&prefs, "index.html", optarg);
+ break;
+
+ case 'I':
+ bozo_set_pref(&prefs, "port number", optarg);
+ break;
+
+#ifdef NO_DAEMON_MODE
+ case 'b':
+ case 'e':
+ case 'f':
+ case 'i':
+ case 'P':
+ bozo_err(&httpd, 1, "Daemon mode is not enabled");
+ /* NOTREACHED */
+#else
+ case 'b':
+ /*
+ * test suite support - undocumented
+ * background == 2 (aka, -b -b) means to
+ * only process 1 per kid
+ */
+ if (bozo_get_pref(&prefs, "background") == NULL) {
+ bozo_set_pref(&prefs, "background", "1");
+ } else {
+ bozo_set_pref(&prefs, "background", "2");
+ }
+ break;
+
+ case 'e':
+ bozo_set_pref(&prefs, "dirty environment", "true");
+ break;
+
+ case 'f':
+ bozo_set_pref(&prefs, "foreground", "true");
+ break;
+
+ case 'i':
+ bozo_set_pref(&prefs, "bind address", optarg);
+ break;
+
+ case 'P':
+ bozo_set_pref(&prefs, "pid file", optarg);
+ break;
+#endif /* NO_DAEMON_MODE */
+
+#ifdef NO_CGIBIN_SUPPORT
+ case 'c':
+ case 'C':
+ bozo_err(&httpd, 1, "CGI is not enabled");
+ /* NOTREACHED */
+#else
+ case 'c':
+ bozo_cgi_setbin(&httpd, optarg);
+ break;
+
+ case 'C':
+# ifdef NO_DYNAMIC_CONTENT
+ bozo_err(&httpd, 1,
+ "dynamic CGI handler support is not enabled");
+ /* NOTREACHED */
+# else
+ /* make sure there's two arguments */
+ if (argc - optind < 1)
+ usage(&httpd, progname);
+ bozo_add_content_map_cgi(&httpd, optarg,
+ argv[optind++]);
+ break;
+# endif /* NO_DYNAMIC_CONTENT */
+#endif /* NO_CGIBIN_SUPPORT */
+
+ case 'd':
+ httpd.debug++;
+#ifdef NO_DEBUG
+ if (httpd.debug == 1)
+ bozo_warn(&httpd, "Debugging is not enabled");
+#endif /* NO_DEBUG */
+ break;
+
+ case 't':
+ bozo_set_pref(&prefs, "chroot dir", optarg);
+ break;
+
+#ifdef NO_USER_SUPPORT
+ case 'p':
+ case 'u':
+ bozo_err(&httpd, 1, "User support is not enabled");
+ /* NOTREACHED */
+#else
+ case 'p':
+ bozo_set_pref(&prefs, "public_html", optarg);
+ break;
+
+ case 'u':
+ bozo_set_pref(&prefs, "enable users", "true");
+ break;
+#endif /* NO_USER_SUPPORT */
+
+#ifdef NO_DIRINDEX_SUPPORT
+ case 'H':
+ case 'X':
+ bozo_err(&httpd, 1,
+ "directory indexing is not enabled");
+ /* NOTREACHED */
+#else
+ case 'H':
+ bozo_set_pref(&prefs, "hide dots", "true");
+ break;
+
+ case 'X':
+ bozo_set_pref(&prefs, "directory indexing", "true");
+ break;
+
+#endif /* NO_DIRINDEX_SUPPORT */
+
+ default:
+ usage(&httpd, progname);
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0 || argc > 2) {
+ usage(&httpd, progname);
+ }
+
+ /* virtual host, and root of tree to serve */
+ bozo_setup(&httpd, &prefs, argv[1], argv[0]);
+
+ /*
+ * read and process the HTTP request.
+ */
+ do {
+ if ((request = bozo_read_request(&httpd)) != NULL) {
+ bozo_process_request(request);
+ bozo_clean_request(request);
+ }
+ } while (httpd.background);
+
+ return (0);
+}
--- /dev/null
+/* $eterna: queue.h,v 1.6 2009/04/18 08:36:03 mrg Exp $ */
+/* from: NetBSD: queue.h,v 1.51 2009/03/11 06:51:53 mrg Exp */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var); \
+ (var) = ((var)->field.sqe_next))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->sqh_first); \
+ (var) && ((next = ((var)->field.sqe_next)), 1); \
+ (var) = (next))
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#endif /* !_SYS_QUEUE_H_ */
--- /dev/null
+-- $NetBSD: printenv.lua,v 1.2 2014/01/02 08:21:38 mrg Exp $
+
+-- this small Lua script demonstrates the use of Lua in (bozo)httpd
+-- it will simply output the "environment"
+
+-- Keep in mind that bozohttpd forks for each request when started in
+-- daemon mode, you can set global veriables here, but they will have
+-- the same value on each invocation. You can not keep state between
+-- two calls.
+
+local httpd = require 'httpd'
+
+function printenv(env, headers, query)
+
+ -- we get the "environment" in the env table, the values are more
+ -- or less the same as the variable for a CGI program
+
+ if count == nil then
+ count = 1
+ end
+
+ -- output a header
+ print([[
+ <html>
+ <head>
+ <title>Bozotic Lua Environment</title>
+ </head>
+ <body>
+ <h1>Bozotic Lua Environment</h1>
+ ]])
+
+ print('module version: ' .. httpd._VERSION .. '<br>')
+
+ print('<h2>Server Environment</h2>')
+ -- print the list of "environment" variables
+ for k, v in pairs(env) do
+ print(k .. '=' .. v .. '<br/>')
+ end
+
+ print('<h2>Request Headers</h2>')
+ for k, v in pairs(headers) do
+ print(k .. '=' .. v .. '<br/>')
+ end
+
+ if query ~= nil then
+ print('<h2>Query Variables</h2>')
+ for k, v in pairs(query) do
+ print(k .. '=' .. v .. '<br/>')
+ end
+ end
+
+ print('<h2>Form Test</h2>')
+
+ print([[
+ <form method="POST" action="/rest/form?sender=me">
+ <input type="text" name="a_value">
+ <input type="submit">
+ </form>
+ ]])
+ -- output a footer
+ print([[
+ </body>
+ </html>
+ ]])
+end
+
+function form(env, header, query)
+ if query ~= nil then
+ print('<h2>Form Variables</h2>')
+
+ if env.CONTENT_TYPE ~= nil then
+ print('Content-type: ' .. env.CONTENT_TYPE .. '<br>')
+ end
+
+ for k, v in pairs(query) do
+ print(k .. '=' .. v .. '<br/>')
+ end
+ else
+ print('No values')
+ end
+end
+
+-- register this handler for http://<hostname>/<prefix>/printenv
+httpd.register_handler('printenv', printenv)
+httpd.register_handler('form', form)
--- /dev/null
+# $eterna: Makefile,v 1.1 2009/05/22 21:51:39 mrg Exp $
+
+# build a 100% lean bozohttpd-small.c
+PROG= bozohttpd-small
+NOMAN= # defined
+SRCS= bozohttpd-small.c content-bozo-small.c ssl-bozo.c main.c:
+
+LEAN_IFDEF_FLAGS= -UDEBUG -DNO_USER_SUPPORT \
+ -DNO_CGIBIN_SUPPORT -DNO_DIRINDEX_SUPPORT \
+ -DNO_DAEMON_MODE -DNO_DYNAMIC_CONTENT \
+ -DNO_SSL_SUPPORT -UDO_HTPASSWD \
+ -DNO_LUA_SUPPORT
+
+CFLAGS= -I$(.CURDIR)/.. ${LEAN_IFDEF_FLAGS}
+
+bozohttpd-small.c: bozohttpd.c
+ unifdef $(LEAN_IFDEF_FLAGS) < $> > $@.tmp ;\
+ if [ $$? -ne 1 ]; then echo "unifdef returned $?, expecting 1" 2>&1; false; fi
+ mv -f $@.tmp $@
+
+content-bozo-small.c: content-bozo.c
+ unifdef $(LEAN_IFDEF_FLAGS) < $> > $@.tmp ;\
+ if [ $$? -ne 1 ]; then echo "unifdef returned $?, expecting 1" 2>&1; false; fi
+ mv -f $@.tmp $@
+
+CLEANFILES+= content-bozo-small.c bozohttpd-small.c
+
+.PATH: $(.CURDIR)/..
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $NetBSD: ssl-bozo.c,v 1.18 2014/07/17 06:27:52 mrg Exp $ */
+
+/* $eterna: ssl-bozo.c,v 1.15 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2014 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements SSL and backend IO for bozohttpd */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "bozohttpd.h"
+
+#ifndef NO_SSL_SUPPORT
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#ifndef USE_ARG
+#define USE_ARG(x) /*LINTED*/(void)&(x)
+#endif
+
+/* this structure encapsulates the ssl info */
+typedef struct sslinfo_t {
+ SSL_CTX *ssl_context;
+ const SSL_METHOD *ssl_method;
+ SSL *bozossl;
+ char *certificate_file;
+ char *privatekey_file;
+} sslinfo_t;
+
+/*
+ * bozo_clear_ssl_queue: print the contents of the SSL error queue
+ */
+static void
+bozo_clear_ssl_queue(bozohttpd_t *httpd)
+{
+ unsigned long sslcode = ERR_get_error();
+
+ do {
+ static const char sslfmt[] = "SSL Error: %s:%s:%s";
+
+ if (httpd->logstderr || isatty(STDERR_FILENO)) {
+ fprintf(stderr, sslfmt,
+ ERR_lib_error_string(sslcode),
+ ERR_func_error_string(sslcode),
+ ERR_reason_error_string(sslcode));
+ } else {
+ syslog(LOG_ERR, sslfmt,
+ ERR_lib_error_string(sslcode),
+ ERR_func_error_string(sslcode),
+ ERR_reason_error_string(sslcode));
+ }
+ } while (0 != (sslcode = ERR_get_error()));
+}
+
+/*
+ * bozo_ssl_warn works just like bozo_warn, plus the SSL error queue
+ */
+BOZO_PRINTFLIKE(2, 3) static void
+bozo_ssl_warn(bozohttpd_t *httpd, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (httpd->logstderr || isatty(STDERR_FILENO)) {
+ vfprintf(stderr, fmt, ap);
+ fputs("\n", stderr);
+ } else
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+
+ bozo_clear_ssl_queue(httpd);
+}
+
+
+/*
+ * bozo_ssl_err works just like bozo_err, plus the SSL error queue
+ */
+BOZO_PRINTFLIKE(3, 4) BOZO_DEAD static void
+bozo_ssl_err(bozohttpd_t *httpd, int code, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (httpd->logstderr || isatty(STDERR_FILENO)) {
+ vfprintf(stderr, fmt, ap);
+ fputs("\n", stderr);
+ } else
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+
+ bozo_clear_ssl_queue(httpd);
+ exit(code);
+}
+
+/*
+ * bozo_check_error_queue: print warnings if the error isn't expected
+ */
+static void
+bozo_check_error_queue(bozohttpd_t *httpd, const char *tag, int ret)
+{
+ if (ret > 0)
+ return;
+
+ const sslinfo_t *sslinfo = httpd->sslinfo;
+ const int sslerr = SSL_get_error(sslinfo->bozossl, ret);
+
+ if (sslerr != SSL_ERROR_ZERO_RETURN &&
+ sslerr != SSL_ERROR_SYSCALL &&
+ sslerr != SSL_ERROR_NONE)
+ bozo_ssl_warn(httpd, "%s: SSL_ERROR %d", tag, sslerr);
+}
+
+static BOZO_PRINTFLIKE(2, 0) int
+bozo_ssl_printf(bozohttpd_t *httpd, const char * fmt, va_list ap)
+{
+ char *buf;
+ int nbytes;
+
+ if ((nbytes = vasprintf(&buf, fmt, ap)) != -1) {
+ const sslinfo_t *sslinfo = httpd->sslinfo;
+ int ret = SSL_write(sslinfo->bozossl, buf, nbytes);
+ bozo_check_error_queue(httpd, "write", ret);
+ }
+
+ free(buf);
+
+ return nbytes;
+}
+
+static ssize_t
+bozo_ssl_read(bozohttpd_t *httpd, int fd, void *buf, size_t nbytes)
+{
+ const sslinfo_t *sslinfo = httpd->sslinfo;
+ int ret;
+
+ USE_ARG(fd);
+ ret = SSL_read(sslinfo->bozossl, buf, (int)nbytes);
+ bozo_check_error_queue(httpd, "read", ret);
+
+ return (ssize_t)ret;
+}
+
+static ssize_t
+bozo_ssl_write(bozohttpd_t *httpd, int fd, const void *buf, size_t nbytes)
+{
+ const sslinfo_t *sslinfo = httpd->sslinfo;
+ int ret;
+
+ USE_ARG(fd);
+ ret = SSL_write(sslinfo->bozossl, buf, (int)nbytes);
+ bozo_check_error_queue(httpd, "write", ret);
+
+ return (ssize_t)ret;
+}
+
+void
+bozo_ssl_init(bozohttpd_t *httpd)
+{
+ sslinfo_t *sslinfo = httpd->sslinfo;
+
+ if (sslinfo == NULL || !sslinfo->certificate_file)
+ return;
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ sslinfo->ssl_method = SSLv23_server_method();
+ sslinfo->ssl_context = SSL_CTX_new(sslinfo->ssl_method);
+
+ if (NULL == sslinfo->ssl_context)
+ bozo_ssl_err(httpd, EXIT_FAILURE,
+ "SSL context creation failed");
+
+ if (1 != SSL_CTX_use_certificate_chain_file(sslinfo->ssl_context,
+ sslinfo->certificate_file))
+ bozo_ssl_err(httpd, EXIT_FAILURE,
+ "Unable to use certificate file '%s'",
+ sslinfo->certificate_file);
+
+ if (1 != SSL_CTX_use_PrivateKey_file(sslinfo->ssl_context,
+ sslinfo->privatekey_file, SSL_FILETYPE_PEM))
+ bozo_ssl_err(httpd, EXIT_FAILURE,
+ "Unable to use private key file '%s'",
+ sslinfo->privatekey_file);
+
+ /* check consistency of key vs certificate */
+ if (!SSL_CTX_check_private_key(sslinfo->ssl_context))
+ bozo_ssl_err(httpd, EXIT_FAILURE,
+ "Check private key failed");
+}
+
+/*
+ * returns non-zero for failure
+ */
+int
+bozo_ssl_accept(bozohttpd_t *httpd)
+{
+ sslinfo_t *sslinfo = httpd->sslinfo;
+
+ if (sslinfo == NULL || !sslinfo->ssl_context)
+ return 0;
+
+ sslinfo->bozossl = SSL_new(sslinfo->ssl_context);
+ if (sslinfo->bozossl == NULL)
+ bozo_err(httpd, 1, "SSL_new failed");
+
+ SSL_set_rfd(sslinfo->bozossl, 0);
+ SSL_set_wfd(sslinfo->bozossl, 1);
+
+ const int ret = SSL_accept(sslinfo->bozossl);
+ bozo_check_error_queue(httpd, "accept", ret);
+
+ return ret != 1;
+}
+
+void
+bozo_ssl_destroy(bozohttpd_t *httpd)
+{
+ const sslinfo_t *sslinfo = httpd->sslinfo;
+
+ if (sslinfo && sslinfo->bozossl)
+ SSL_free(sslinfo->bozossl);
+}
+
+void
+bozo_ssl_set_opts(bozohttpd_t *httpd, const char *cert, const char *priv)
+{
+ sslinfo_t *sslinfo = httpd->sslinfo;
+
+ if (sslinfo == NULL) {
+ sslinfo = bozomalloc(httpd, sizeof(*sslinfo));
+ if (sslinfo == NULL)
+ bozo_err(httpd, 1, "sslinfo allocation failed");
+ httpd->sslinfo = sslinfo;
+ }
+ sslinfo->certificate_file = strdup(cert);
+ sslinfo->privatekey_file = strdup(priv);
+ debug((httpd, DEBUG_NORMAL, "using cert/priv files: %s & %s",
+ sslinfo->certificate_file,
+ sslinfo->privatekey_file));
+ if (!httpd->bindport)
+ httpd->bindport = strdup("https");
+}
+
+#endif /* NO_SSL_SUPPORT */
+
+int
+bozo_printf(bozohttpd_t *httpd, const char *fmt, ...)
+{
+ va_list args;
+ int cc;
+
+ va_start(args, fmt);
+#ifndef NO_SSL_SUPPORT
+ if (httpd->sslinfo)
+ cc = bozo_ssl_printf(httpd, fmt, args);
+ else
+#endif
+ cc = vprintf(fmt, args);
+ va_end(args);
+ return cc;
+}
+
+ssize_t
+bozo_read(bozohttpd_t *httpd, int fd, void *buf, size_t len)
+{
+#ifndef NO_SSL_SUPPORT
+ if (httpd->sslinfo)
+ return bozo_ssl_read(httpd, fd, buf, len);
+#endif
+ return read(fd, buf, len);
+}
+
+ssize_t
+bozo_write(bozohttpd_t *httpd, int fd, const void *buf, size_t len)
+{
+#ifndef NO_SSL_SUPPORT
+ if (httpd->sslinfo)
+ return bozo_ssl_write(httpd, fd, buf, len);
+#endif
+ return write(fd, buf, len);
+}
+
+int
+bozo_flush(bozohttpd_t *httpd, FILE *fp)
+{
+#ifndef NO_SSL_SUPPORT
+ if (httpd->sslinfo)
+ return 0;
+#endif
+ return fflush(fp);
+}
--- /dev/null
+# $eterna: Makefile,v 1.14 2009/05/22 21:51:39 mrg Exp $
+
+SIMPLETESTS= t1 t2 t3 t4 t5 t6 t7 t8 t9 t10
+BIGFILETESTS= partial4000 partial8000
+
+BOZOHTTPD?= ../bozohttpd
+BOZOHTTPD?= ../debug/bozohttpd-debug
+WGET?= wget
+
+all:
+
+clean:
+ for a in $(SIMPLETESTS); do \
+ rm -f tmp.$$a.out; \
+ done
+
+check: check-simple check-bigfile
+
+check-simple:
+.for a in $(SIMPLETESTS)
+ echo "Running test $a"
+ $(BOZOHTTPD) ./data < $(.CURDIR)/$a.in > tmp.$a.out || true
+ $(.CURDIR)/html_cmp $(.CURDIR)/$a.out tmp.$a.out
+.endfor
+
+check-bigfile:
+.for a in $(BIGFILETESTS)
+ echo "Running test $a"
+ $(.CURDIR)/test-bigfile "$a" "${BOZOHTTPD}" "${WGET}" "./data"
+.endfor
+
+.include <bsd.obj.mk>
--- /dev/null
+this is the big data file. it has to be over 1 page size in length. 0123456789
+these lines are all 80 long. this is the second line. 012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+012345678901 this is the seventh line. 12345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+456789 this is the 13th line, and there 127 lines in total. 67890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+901234567890123456789012345 this is the 18th line. 456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+78901234567890123456 this is the 31st line. 345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+90123456789012345678901234567890123456789 this is the 38th line. 78901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+012345678901234567890123456 this is the 47th line. 4567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+789012345678901234567890123456789012345678901234567 50th 7890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+56789012 this is the 52nd line. 1234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012 54th 1234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345 this is the 60th line. 2345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+678901234 this is the 71st line. 12345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567 this is the 80th line. 567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456 this is the 93th line. 3456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456 this is the 101st line. 456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+123456789012345678901234567890123456789012345 this is the 106th line. 3456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+789012345678901234 110th 4567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+34567890123456789012345678 114th 9012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+67890123 this is the 121st line. 12345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+this is the last line. this is the end of the file. there is no more. good bye.
--- /dev/null
+this is the big data file. it has to be over 1 page size in length. 0123456789
+these lines are all 80 long. this is the second line. 012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+012345678901 this is the seventh line. 12345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+456789 this is the 13th line, and there 127 lines in total. 67890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+901234567890123456789012345 this is the 18th line. 456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+78901234567890123456 this is the 31st line. 345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+90123456789012345678901234567890123456789 this is the 38th line. 78901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+012345678901234567890123456 this is the 47th line. 4567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+789012345678901234567890123456789012345678901234567 50th 7890123456789012345
--- /dev/null
+this is the big data file. it has to be over 1 page size in length. 0123456789
+these lines are all 80 long. this is the second line. 012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+012345678901 this is the seventh line. 12345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+456789 this is the 13th line, and there 127 lines in total. 67890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+901234567890123456789012345 this is the 18th line. 456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+78901234567890123456 this is the 31st line. 345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+90123456789012345678901234567890123456789 this is the 38th line. 78901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+012345678901234567890123456 this is the 47th line. 4567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+789012345678901234567890123456789012345678901234567 50th 7890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+56789012 this is the 52nd line. 1234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012 54th 1234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345 this is the 60th line. 2345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+678901234 this is the 71st line. 12345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567 this is the 80th line. 567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456789012345678901234567890123456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
+6789012345678901234567890123456789012345678901234567890123456789012345678901234
+5678901234567890123456789012345678901234567890123456789012345678901234567890123
+4567890123456 this is the 93th line. 3456789012345678901234567890123456789012
+3456789012345678901234567890123456789012345678901234567890123456789012345678901
+2345678901234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012345678901234567890123456789
+0123456789012345678901234567890123456789012345678901234567890123456789012345678
+9012345678901234567890123456789012345678901234567890123456789012345678901234567
+8901234567890123456789012345678901234567890123456789012345678901234567890123456
+7890123456789012345678901234567890123456789012345678901234567890123456789012345
--- /dev/null
+123456781234567
+345678903456789
+234567892345678
+012345670123456
--- /dev/null
+this is the bozohttpd testsuite ./data/index.html file
--- /dev/null
+#! /bin/sh
+#
+# $eterna: html_cmp,v 1.9 2011/11/17 22:18:02 mrg Exp $
+#
+# like cmp(1) but compares to files after making their `Date: ' headers
+# the same, to allow `now' and `then' to work properly. it also tries
+# to find servername's that might be the local host and converts those
+# as well..
+#
+# it must be called like `cmp file1 file1' *only*.
+
+h=`hostname || uname -n`
+
+sedcmd="s/^Date: .*/Date: nowish/;
+ s/^Last-Modified: .*/Last-Modified: nowish/;
+ s/[a-zA-Z0-9-]*\.eterna\.com\.au/$h/g;
+ s/^Server: .*/^Server: bozotic HTTP server version 5.08/;
+ s/^Content-Length: .*/Content-Length: 223/;"
+
+sed -e "$sedcmd" < "$1" > "f1.tmp.$$"
+sed -e "$sedcmd" < "$2" > "f2.tmp.$$"
+
+cmp -s "f1.tmp.$$" "f2.tmp.$$"
+rv=$?
+rm -f "f1.tmp.$$" "f2.tmp.$$"
+
+exit $rv
--- /dev/null
+HTTP/0.9 200 OK\r
+this is the bozohttpd testsuite ./data/index.html file
--- /dev/null
+GET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0\r\r\r\r\r\r\r\r\r\r\r\r\r
--- /dev/null
+HTTP/1.0 404 Not Found\r
+Content-Type: text/html\r
+Content-Length: 1024\r
+Server: bozohttpd/20140708\r
+\r
+<html><head><title>404 Not Found</title></head>
+<body><h1>404 Not Found</h1>
+/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\ No newline at end of file
--- /dev/null
+GET / HTTP/1.0
--- /dev/null
+HTTP/1.0 200 OK\r
+Date: Tue, 10 Jul 2001 15:45:36 GMT\r
+Server: bozotic HTTP server version 5.08\r
+Accept-Ranges: bytes\r
+Last-Modified: Tue, 10 Jul 2001 15:50:43 GMT\r
+Content-Type: text/html\r
+Content-Length: 55\r
+\r
+this is the bozohttpd testsuite ./data/index.html file
--- /dev/null
+GET / HTTP/1.1
--- /dev/null
+HTTP/1.1 400 Bad Request\r
+Content-Type: text/html\r
+Content-Length: 229\r
+Server: bozotic HTTP server version 5.08\r
+Allow: GET, HEAD, POST\r
+\r
+<html><head><title>400 Bad Request</title></head>
+<body><h1>400 Bad Request</h1>
+/: <pre>The request was not valid</pre>
+<hr><address><a href="http://madrugada.eterna.com.au/">madrugada.eterna.com.au</a></address>
+</body></html>
--- /dev/null
+GET / HTTP/1.1
+Host:
--- /dev/null
+HTTP/1.1 200 OK\r
+Date: Tue, 10 Jul 2001 15:49:21 GMT\r
+Server: bozotic HTTP server version 5.08\r
+Accept-Ranges: bytes\r
+Last-Modified: Tue, 10 Jul 2001 15:34:33 GMT\r
+Content-Type: text/html\r
+Content-Length: 55\r
+Connection: close\r
+\r
+this is the bozohttpd testsuite ./data/index.html file
--- /dev/null
+GET /cgi-bin/..M-@M-/..M-@M-/..M-@M-/..M-@M-/..M-@M-/../winnt/system32/cmd.exe?/c+dir+c:\\ HTTP/1.0
+
--- /dev/null
+HTTP/1.0 403 Forbidden\r
+Content-Type: text/html\r
+Content-Length: 336\r
+Server: bozohttpd/20030206\r
+\r
+<html><head><title>403 Forbidden</title></head>
+<body><h1>403 Forbidden</h1>
+/cgi-bin/..M-@M-/..M-@M-/..M-@M-/..M-@M-/..M-@M-/../winnt/system32/cmd.exe: <pre>Access to this item has been denied</pre>
+<hr><address><a href="http://what-time-is-love.eterna.com.au/">what-time-is-love.eterna.com.au</a></address>
+</body></html>
--- /dev/null
+GET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0\r\r\r\r\r\r\r\r\r\r\r\r\r
+
--- /dev/null
+HTTP/1.0 404 Not Found\r
+Content-Type: text/html\r
+Content-Length: 335\r
+Server: bozohttpd/5.15\r
+\r
+<html><head><title>404 Not Found</title></head>
+<body><h1>404 Not Found</h1>
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: <pre>This item has not been found</pre>
+<hr><address><a href="http://splode.eterna.com.au/">splode.eterna.com.au</a></address>
+</body></html>
--- /dev/null
+GET /file HTTP/1.1
+Host:
+Range: bytes=0-63
+
--- /dev/null
+HTTP/1.1 206 Partial Content\r
+Date: Sun, 02 Mar 2008 08:52:03 GMT\r
+Server: bozohttpd/20060710\r
+Accept-Ranges: bytes\r
+Last-Modified: Sun, 02 Mar 2008 08:44:38 GMT\r
+Content-Type: text/plain\r
+Content-Range: bytes 0-63/64\r
+Content-Length: 64\r
+Connection: close\r
+\r
+123456781234567
+345678903456789
+234567892345678
+012345670123456
--- /dev/null
+GET /file HTTP/1.1
+Host:
+Range: bytes=0-31
+
--- /dev/null
+HTTP/1.1 206 Partial Content\r
+Date: Sun, 02 Mar 2008 08:52:03 GMT\r
+Server: bozohttpd/20060710\r
+Accept-Ranges: bytes\r
+Last-Modified: Sun, 02 Mar 2008 08:44:38 GMT\r
+Content-Type: text/plain\r
+Content-Range: bytes 0-31/64\r
+Content-Length: 32\r
+Connection: close\r
+\r
+123456781234567
+345678903456789
--- /dev/null
+GET /file HTTP/1.1
+Host:
+Range: bytes=32-63
+
--- /dev/null
+HTTP/1.1 206 Partial Content\r
+Date: Sun, 02 Mar 2008 08:52:03 GMT\r
+Server: bozohttpd/20060710\r
+Accept-Ranges: bytes\r
+Last-Modified: Sun, 02 Mar 2008 08:44:38 GMT\r
+Content-Type: text/plain\r
+Content-Range: bytes 32-63/64\r
+Content-Length: 32\r
+Connection: close\r
+\r
+234567892345678
+012345670123456
--- /dev/null
+#! /bin/sh
+
+test="$1" # partial4000 or partial8000
+bozohttpd="$2"
+wget="$3"
+datadir="$4"
+
+bozotestport=11111
+
+# copy beginning file
+cp ./data/bigfile.${test} ./bigfile
+
+# fire up bozohttpd
+${bozohttpd} -b -b -I ${bozotestport} -n -s -f ${datadir} &
+bozopid=$!
+
+${wget} -c http://localhost:${bozotestport}/bigfile
+
+kill -9 $bozopid
+
+if cmp ./bigfile ./data/bigfile; then
+ rm -f ./bigfile
+ exit 0
+else
+ rm -f ./bigfile
+ exit 1
+fi
--- /dev/null
+/* $NetBSD: tilde-luzah-bozo.c,v 1.11 2015/07/16 12:19:23 shm Exp $ */
+
+/* $eterna: tilde-luzah-bozo.c,v 1.16 2011/11/18 09:21:15 mrg Exp $ */
+
+/*
+ * Copyright (c) 1997-2014 Matthew R. Green
+ * 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 and
+ * dedication in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 code implements ~user support for bozohttpd */
+
+#ifndef NO_USER_SUPPORT
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bozohttpd.h"
+
+/*
+ * bozo_user_transform does this:
+ * - chdir's /~user/public_html
+ * - returns the rest of the file, index.html appended if required
+ * - returned malloced file to serve in request->hr_file,
+ * ala transform_request().
+ *
+ * transform_request() is supposed to check that we have user support
+ * enabled.
+ */
+int
+bozo_user_transform(bozo_httpreq_t *request, int *isindex)
+{
+ bozohttpd_t *httpd = request->hr_httpd;
+ char c, *s, *file = NULL, *user;
+ struct passwd *pw;
+
+ *isindex = 0;
+
+ /* find username */
+ user = strchr(request->hr_file + 2, '~');
+
+ /* this shouldn't happen, but "better paranoid than sorry" */
+ assert(user != NULL);
+
+ user++;
+
+ if ((s = strchr(user, '/')) != NULL) {
+ *s++ = '\0';
+ c = s[strlen(s)-1];
+ *isindex = (c == '/' || c == '\0');
+ }
+
+ debug((httpd, DEBUG_OBESE, "looking for user %s",
+ user));
+ pw = getpwnam(user);
+ /* fix this up immediately */
+ if (s)
+ s[-1] = '/';
+ if (pw == NULL) {
+ (void)bozo_http_error(httpd, 404, request, "no such user");
+ return 0;
+ }
+
+ debug((httpd, DEBUG_OBESE, "user %s dir %s/%s uid %d gid %d",
+ pw->pw_name, pw->pw_dir, httpd->public_html,
+ pw->pw_uid, pw->pw_gid));
+
+ if (chdir(pw->pw_dir) < 0) {
+ bozo_warn(httpd, "chdir1 error: %s: %s", pw->pw_dir,
+ strerror(errno));
+ (void)bozo_http_error(httpd, 404, request,
+ "can't chdir to homedir");
+ return 0;
+ }
+ if (chdir(httpd->public_html) < 0) {
+ bozo_warn(httpd, "chdir2 error: %s: %s", httpd->public_html,
+ strerror(errno));
+ (void)bozo_http_error(httpd, 404, request,
+ "can't chdir to public_html");
+ return 0;
+ }
+ if (s == NULL || *s == '\0') {
+ file = bozostrdup(httpd, httpd->index_html);
+ } else {
+ file = bozomalloc(httpd, strlen(s) +
+ (*isindex ? strlen(httpd->index_html) + 1 : 1));
+ strcpy(file, s);
+ if (*isindex)
+ strcat(file, httpd->index_html);
+ }
+
+ /* see transform_request() */
+ if (*file == '/' || strcmp(file, "..") == 0 ||
+ strstr(file, "/..") || strstr(file, "../")) {
+ (void)bozo_http_error(httpd, 403, request, "illegal request");
+ free(file);
+ return 0;
+ }
+
+ if (bozo_auth_check(request, file)) {
+ free(file);
+ return 0;
+ }
+
+ free(request->hr_file);
+ request->hr_file = file;
+
+ debug((httpd, DEBUG_FAT, "transform_user returning %s under %s", file,
+ pw->pw_dir));
+ return 1;
+}
+#endif /* NO_USER_SUPPORT */