]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD httpd(8) 73/3473/1
authorDavid van Moolenbroek <david@minix3.org>
Sun, 5 Mar 2017 16:03:54 +0000 (16:03 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Tue, 21 Mar 2017 22:00:31 +0000 (22:00 +0000)
Also known as bozohttpd(8).

Change-Id: I40e955b5654674f2c708b10e5e403ca9cbc92534

60 files changed:
distrib/sets/lists/minix-base/mi
distrib/sets/lists/minix-debug/mi
distrib/sets/lists/minix-man/mi
etc/mtree/NetBSD.dist.base
libexec/Makefile
libexec/httpd/CHANGES [new file with mode: 0644]
libexec/httpd/Makefile [new file with mode: 0644]
libexec/httpd/Makefile.boot [new file with mode: 0644]
libexec/httpd/auth-bozo.c [new file with mode: 0644]
libexec/httpd/bozohttpd.8 [new file with mode: 0644]
libexec/httpd/bozohttpd.c [new file with mode: 0644]
libexec/httpd/bozohttpd.h [new file with mode: 0644]
libexec/httpd/cgi-bozo.c [new file with mode: 0644]
libexec/httpd/content-bozo.c [new file with mode: 0644]
libexec/httpd/daemon-bozo.c [new file with mode: 0644]
libexec/httpd/debug/Makefile [new file with mode: 0644]
libexec/httpd/dir-index-bozo.c [new file with mode: 0644]
libexec/httpd/libbozohttpd/Makefile [new file with mode: 0644]
libexec/httpd/libbozohttpd/libbozohttpd.3 [new file with mode: 0644]
libexec/httpd/libbozohttpd/shlib_version [new file with mode: 0644]
libexec/httpd/lua-bozo.c [new file with mode: 0644]
libexec/httpd/lua/Makefile [new file with mode: 0644]
libexec/httpd/lua/bozo.lua [new file with mode: 0755]
libexec/httpd/lua/glue.c [new file with mode: 0644]
libexec/httpd/lua/optparse.lua [new file with mode: 0644]
libexec/httpd/lua/shlib_version [new file with mode: 0644]
libexec/httpd/main.c [new file with mode: 0644]
libexec/httpd/netbsd_queue.h [new file with mode: 0644]
libexec/httpd/printenv.lua [new file with mode: 0644]
libexec/httpd/small/Makefile [new file with mode: 0644]
libexec/httpd/ssl-bozo.c [new file with mode: 0644]
libexec/httpd/testsuite/Makefile [new file with mode: 0644]
libexec/httpd/testsuite/data/bigfile [new file with mode: 0644]
libexec/httpd/testsuite/data/bigfile.partial4000 [new file with mode: 0644]
libexec/httpd/testsuite/data/bigfile.partial8000 [new file with mode: 0644]
libexec/httpd/testsuite/data/file [new file with mode: 0644]
libexec/httpd/testsuite/data/index.html [new file with mode: 0644]
libexec/httpd/testsuite/html_cmp [new file with mode: 0755]
libexec/httpd/testsuite/t1.in [new file with mode: 0644]
libexec/httpd/testsuite/t1.out [new file with mode: 0644]
libexec/httpd/testsuite/t10.in [new file with mode: 0644]
libexec/httpd/testsuite/t10.out [new file with mode: 0644]
libexec/httpd/testsuite/t2.in [new file with mode: 0644]
libexec/httpd/testsuite/t2.out [new file with mode: 0644]
libexec/httpd/testsuite/t3.in [new file with mode: 0644]
libexec/httpd/testsuite/t3.out [new file with mode: 0644]
libexec/httpd/testsuite/t4.in [new file with mode: 0644]
libexec/httpd/testsuite/t4.out [new file with mode: 0644]
libexec/httpd/testsuite/t5.in [new file with mode: 0644]
libexec/httpd/testsuite/t5.out [new file with mode: 0644]
libexec/httpd/testsuite/t6.in [new file with mode: 0644]
libexec/httpd/testsuite/t6.out [new file with mode: 0644]
libexec/httpd/testsuite/t7.in [new file with mode: 0644]
libexec/httpd/testsuite/t7.out [new file with mode: 0644]
libexec/httpd/testsuite/t8.in [new file with mode: 0644]
libexec/httpd/testsuite/t8.out [new file with mode: 0644]
libexec/httpd/testsuite/t9.in [new file with mode: 0644]
libexec/httpd/testsuite/t9.out [new file with mode: 0644]
libexec/httpd/testsuite/test-bigfile [new file with mode: 0755]
libexec/httpd/tilde-luzah-bozo.c [new file with mode: 0644]

index 377a5be8b867ce3a50939f98087e120e44f62525..d3e8604bb651143698b9e52581ba90aa29cfe1a3 100644 (file)
 ./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
index c038f3805fcf9c5bd61c6b19b70e69dc002810a1..cf038b14bfe3c7bb19f55f72bf69b54685cda5ab 100644 (file)
 ./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
index 8ff7ee84cb968997f5358701879aa4508a770110..ae94daf7709595aaaf358e742d56b2922dd4d9e9 100644 (file)
 ./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
index 66a52f826aa75a9c37062e048066467e5c08da2d..074ce209782c2999c80196eb05d65185fea6db4b 100644 (file)
 ./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
index 7e079ac51c67c5e7ea4df9740e5379b94de12061..7a530915d118512d109e7d9e4d4f9dd954475f3f 100644 (file)
@@ -4,7 +4,7 @@
 .include <bsd.own.mk>
 
 SUBDIR= \
-       fingerd ftpd getty \
+       fingerd ftpd getty httpd \
        ld.elf_so \
        rshd \
        telnetd
diff --git a/libexec/httpd/CHANGES b/libexec/httpd/CHANGES
new file mode 100644 (file)
index 0000000..8eb0da7
--- /dev/null
@@ -0,0 +1,275 @@
+$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 
diff --git a/libexec/httpd/Makefile b/libexec/httpd/Makefile
new file mode 100644 (file)
index 0000000..db2e3e4
--- /dev/null
@@ -0,0 +1,108 @@
+#      $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/&/\&amp;/' \
+                   -e 's/</\&lt;/' \
+                   -e 's/>/\&gt;/'
+
+HTMLFROB=      sed \
+                   -e 's/\([MC] "[^"]*\)<dd>$$/\1<b>"<\/b><dd>/' \
+                   -e 's/'"''"'/\&rdquo;/' \
+                   -e 's/""/\&ldquo;/' \
+                   -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>
diff --git a/libexec/httpd/Makefile.boot b/libexec/httpd/Makefile.boot
new file mode 100644 (file)
index 0000000..8a9aaf0
--- /dev/null
@@ -0,0 +1,26 @@
+# $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
diff --git a/libexec/httpd/auth-bozo.c b/libexec/httpd/auth-bozo.c
new file mode 100644 (file)
index 0000000..4a8dd66
--- /dev/null
@@ -0,0 +1,272 @@
+/*     $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 */
diff --git a/libexec/httpd/bozohttpd.8 b/libexec/httpd/bozohttpd.8
new file mode 100644 (file)
index 0000000..876ff6b
--- /dev/null
@@ -0,0 +1,683 @@
+.\"    $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.
diff --git a/libexec/httpd/bozohttpd.c b/libexec/httpd/bozohttpd.c
new file mode 100644 (file)
index 0000000..b717501
--- /dev/null
@@ -0,0 +1,2321 @@
+/*     $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, &timestamp) &&
+           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, "&lt;", 4);
+                       j += 4;
+                       break;
+               case '>':
+                       memcpy(tmp + j, "&gt;", 4);
+                       j += 4;
+                       break;
+               case '&':
+                       memcpy(tmp + j, "&amp;", 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;
+}
diff --git a/libexec/httpd/bozohttpd.h b/libexec/httpd/bozohttpd.h
new file mode 100644 (file)
index 0000000..861f88e
--- /dev/null
@@ -0,0 +1,350 @@
+/*     $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_ */
diff --git a/libexec/httpd/cgi-bozo.c b/libexec/httpd/cgi-bozo.c
new file mode 100644 (file)
index 0000000..e0bcc8c
--- /dev/null
@@ -0,0 +1,523 @@
+/*     $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 */
diff --git a/libexec/httpd/content-bozo.c b/libexec/httpd/content-bozo.c
new file mode 100644 (file)
index 0000000..4a2e5a2
--- /dev/null
@@ -0,0 +1,301 @@
+/*     $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 */
diff --git a/libexec/httpd/daemon-bozo.c b/libexec/httpd/daemon-bozo.c
new file mode 100644 (file)
index 0000000..ff3109a
--- /dev/null
@@ -0,0 +1,339 @@
+/*     $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 */
diff --git a/libexec/httpd/debug/Makefile b/libexec/httpd/debug/Makefile
new file mode 100644 (file)
index 0000000..971b605
--- /dev/null
@@ -0,0 +1,9 @@
+#      $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)/..
diff --git a/libexec/httpd/dir-index-bozo.c b/libexec/httpd/dir-index-bozo.c
new file mode 100644 (file)
index 0000000..49989bf
--- /dev/null
@@ -0,0 +1,212 @@
+/*     $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 */
+
diff --git a/libexec/httpd/libbozohttpd/Makefile b/libexec/httpd/libbozohttpd/Makefile
new file mode 100644 (file)
index 0000000..5a9ad18
--- /dev/null
@@ -0,0 +1,37 @@
+#      $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>
diff --git a/libexec/httpd/libbozohttpd/libbozohttpd.3 b/libexec/httpd/libbozohttpd/libbozohttpd.3
new file mode 100644 (file)
index 0000000..77c13f6
--- /dev/null
@@ -0,0 +1,143 @@
+.\" $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 .
diff --git a/libexec/httpd/libbozohttpd/shlib_version b/libexec/httpd/libbozohttpd/shlib_version
new file mode 100644 (file)
index 0000000..97c9f92
--- /dev/null
@@ -0,0 +1,2 @@
+major=0
+minor=0
diff --git a/libexec/httpd/lua-bozo.c b/libexec/httpd/lua-bozo.c
new file mode 100644 (file)
index 0000000..972c130
--- /dev/null
@@ -0,0 +1,452 @@
+/*     $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 */
diff --git a/libexec/httpd/lua/Makefile b/libexec/httpd/lua/Makefile
new file mode 100644 (file)
index 0000000..448fa71
--- /dev/null
@@ -0,0 +1,39 @@
+#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
diff --git a/libexec/httpd/lua/bozo.lua b/libexec/httpd/lua/bozo.lua
new file mode 100755 (executable)
index 0000000..9a30c9f
--- /dev/null
@@ -0,0 +1,162 @@
+#! /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
diff --git a/libexec/httpd/lua/glue.c b/libexec/httpd/lua/glue.c
new file mode 100644 (file)
index 0000000..cad53b8
--- /dev/null
@@ -0,0 +1,276 @@
+/*-
+ * 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;
+}
diff --git a/libexec/httpd/lua/optparse.lua b/libexec/httpd/lua/optparse.lua
new file mode 100644 (file)
index 0000000..da9b62a
--- /dev/null
@@ -0,0 +1,123 @@
+-- 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
+
diff --git a/libexec/httpd/lua/shlib_version b/libexec/httpd/lua/shlib_version
new file mode 100644 (file)
index 0000000..97c9f92
--- /dev/null
@@ -0,0 +1,2 @@
+major=0
+minor=0
diff --git a/libexec/httpd/main.c b/libexec/httpd/main.c
new file mode 100644 (file)
index 0000000..348628b
--- /dev/null
@@ -0,0 +1,356 @@
+/*     $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);
+}
diff --git a/libexec/httpd/netbsd_queue.h b/libexec/httpd/netbsd_queue.h
new file mode 100644 (file)
index 0000000..906f731
--- /dev/null
@@ -0,0 +1,82 @@
+/*     $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_ */
diff --git a/libexec/httpd/printenv.lua b/libexec/httpd/printenv.lua
new file mode 100644 (file)
index 0000000..5b0fdf9
--- /dev/null
@@ -0,0 +1,85 @@
+-- $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)
diff --git a/libexec/httpd/small/Makefile b/libexec/httpd/small/Makefile
new file mode 100644 (file)
index 0000000..3460e67
--- /dev/null
@@ -0,0 +1,30 @@
+#      $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>
diff --git a/libexec/httpd/ssl-bozo.c b/libexec/httpd/ssl-bozo.c
new file mode 100644 (file)
index 0000000..045a26e
--- /dev/null
@@ -0,0 +1,321 @@
+/*     $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);
+}
diff --git a/libexec/httpd/testsuite/Makefile b/libexec/httpd/testsuite/Makefile
new file mode 100644 (file)
index 0000000..1c64fa0
--- /dev/null
@@ -0,0 +1,32 @@
+#      $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>
diff --git a/libexec/httpd/testsuite/data/bigfile b/libexec/httpd/testsuite/data/bigfile
new file mode 100644 (file)
index 0000000..812c4c3
--- /dev/null
@@ -0,0 +1,127 @@
+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.
diff --git a/libexec/httpd/testsuite/data/bigfile.partial4000 b/libexec/httpd/testsuite/data/bigfile.partial4000
new file mode 100644 (file)
index 0000000..5534f68
--- /dev/null
@@ -0,0 +1,50 @@
+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
diff --git a/libexec/httpd/testsuite/data/bigfile.partial8000 b/libexec/httpd/testsuite/data/bigfile.partial8000
new file mode 100644 (file)
index 0000000..372d389
--- /dev/null
@@ -0,0 +1,100 @@
+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
diff --git a/libexec/httpd/testsuite/data/file b/libexec/httpd/testsuite/data/file
new file mode 100644 (file)
index 0000000..4e03184
--- /dev/null
@@ -0,0 +1,4 @@
+123456781234567
+345678903456789
+234567892345678
+012345670123456
diff --git a/libexec/httpd/testsuite/data/index.html b/libexec/httpd/testsuite/data/index.html
new file mode 100644 (file)
index 0000000..dc88dcb
--- /dev/null
@@ -0,0 +1 @@
+this is the bozohttpd testsuite ./data/index.html file
diff --git a/libexec/httpd/testsuite/html_cmp b/libexec/httpd/testsuite/html_cmp
new file mode 100755 (executable)
index 0000000..15931b0
--- /dev/null
@@ -0,0 +1,27 @@
+#! /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
diff --git a/libexec/httpd/testsuite/t1.in b/libexec/httpd/testsuite/t1.in
new file mode 100644 (file)
index 0000000..35dbed3
--- /dev/null
@@ -0,0 +1 @@
+get /
diff --git a/libexec/httpd/testsuite/t1.out b/libexec/httpd/testsuite/t1.out
new file mode 100644 (file)
index 0000000..135ba7a
--- /dev/null
@@ -0,0 +1,2 @@
+HTTP/0.9 200 OK\r
+this is the bozohttpd testsuite ./data/index.html file
diff --git a/libexec/httpd/testsuite/t10.in b/libexec/httpd/testsuite/t10.in
new file mode 100644 (file)
index 0000000..6015fe2
--- /dev/null
@@ -0,0 +1 @@
+GET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0\r\r\r\r\r\r\r\r\r\r\r\r\r
diff --git a/libexec/httpd/testsuite/t10.out b/libexec/httpd/testsuite/t10.out
new file mode 100644 (file)
index 0000000..cf41011
--- /dev/null
@@ -0,0 +1,8 @@
+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
diff --git a/libexec/httpd/testsuite/t2.in b/libexec/httpd/testsuite/t2.in
new file mode 100644 (file)
index 0000000..d7b06e0
--- /dev/null
@@ -0,0 +1 @@
+GET / HTTP/1.0
diff --git a/libexec/httpd/testsuite/t2.out b/libexec/httpd/testsuite/t2.out
new file mode 100644 (file)
index 0000000..68dcab4
--- /dev/null
@@ -0,0 +1,9 @@
+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
diff --git a/libexec/httpd/testsuite/t3.in b/libexec/httpd/testsuite/t3.in
new file mode 100644 (file)
index 0000000..e71aab4
--- /dev/null
@@ -0,0 +1 @@
+GET / HTTP/1.1
diff --git a/libexec/httpd/testsuite/t3.out b/libexec/httpd/testsuite/t3.out
new file mode 100644 (file)
index 0000000..21803ad
--- /dev/null
@@ -0,0 +1,11 @@
+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>
diff --git a/libexec/httpd/testsuite/t4.in b/libexec/httpd/testsuite/t4.in
new file mode 100644 (file)
index 0000000..86a52f3
--- /dev/null
@@ -0,0 +1,2 @@
+GET / HTTP/1.1
+Host:
diff --git a/libexec/httpd/testsuite/t4.out b/libexec/httpd/testsuite/t4.out
new file mode 100644 (file)
index 0000000..501e167
--- /dev/null
@@ -0,0 +1,10 @@
+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
diff --git a/libexec/httpd/testsuite/t5.in b/libexec/httpd/testsuite/t5.in
new file mode 100644 (file)
index 0000000..6422d98
--- /dev/null
@@ -0,0 +1,2 @@
+GET /cgi-bin/..M-@M-/..M-@M-/..M-@M-/..M-@M-/..M-@M-/../winnt/system32/cmd.exe?/c+dir+c:\\ HTTP/1.0
+
diff --git a/libexec/httpd/testsuite/t5.out b/libexec/httpd/testsuite/t5.out
new file mode 100644 (file)
index 0000000..3a50ccd
--- /dev/null
@@ -0,0 +1,10 @@
+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>
diff --git a/libexec/httpd/testsuite/t6.in b/libexec/httpd/testsuite/t6.in
new file mode 100644 (file)
index 0000000..3403286
--- /dev/null
@@ -0,0 +1,2 @@
+GET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0\r\r\r\r\r\r\r\r\r\r\r\r\r
+
diff --git a/libexec/httpd/testsuite/t6.out b/libexec/httpd/testsuite/t6.out
new file mode 100644 (file)
index 0000000..906b126
--- /dev/null
@@ -0,0 +1,10 @@
+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>
diff --git a/libexec/httpd/testsuite/t7.in b/libexec/httpd/testsuite/t7.in
new file mode 100644 (file)
index 0000000..eaaab74
--- /dev/null
@@ -0,0 +1,4 @@
+GET /file HTTP/1.1
+Host: 
+Range: bytes=0-63
+
diff --git a/libexec/httpd/testsuite/t7.out b/libexec/httpd/testsuite/t7.out
new file mode 100644 (file)
index 0000000..8695d97
--- /dev/null
@@ -0,0 +1,14 @@
+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
diff --git a/libexec/httpd/testsuite/t8.in b/libexec/httpd/testsuite/t8.in
new file mode 100644 (file)
index 0000000..45263b9
--- /dev/null
@@ -0,0 +1,4 @@
+GET /file HTTP/1.1
+Host: 
+Range: bytes=0-31
+
diff --git a/libexec/httpd/testsuite/t8.out b/libexec/httpd/testsuite/t8.out
new file mode 100644 (file)
index 0000000..d755f81
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/libexec/httpd/testsuite/t9.in b/libexec/httpd/testsuite/t9.in
new file mode 100644 (file)
index 0000000..22b1235
--- /dev/null
@@ -0,0 +1,4 @@
+GET /file HTTP/1.1
+Host: 
+Range: bytes=32-63
+
diff --git a/libexec/httpd/testsuite/t9.out b/libexec/httpd/testsuite/t9.out
new file mode 100644 (file)
index 0000000..15d692b
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/libexec/httpd/testsuite/test-bigfile b/libexec/httpd/testsuite/test-bigfile
new file mode 100755 (executable)
index 0000000..0f33dcf
--- /dev/null
@@ -0,0 +1,27 @@
+#! /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
diff --git a/libexec/httpd/tilde-luzah-bozo.c b/libexec/httpd/tilde-luzah-bozo.c
new file mode 100644 (file)
index 0000000..f7fd0c7
--- /dev/null
@@ -0,0 +1,140 @@
+/*     $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 */