From: David van Moolenbroek Date: Sun, 5 Mar 2017 16:03:54 +0000 (+0000) Subject: Import NetBSD httpd(8) X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zlib_tech.html?a=commitdiff_plain;h=refs%2Fchanges%2F73%2F3473%2F1;p=minix.git Import NetBSD httpd(8) Also known as bozohttpd(8). Change-Id: I40e955b5654674f2c708b10e5e403ca9cbc92534 --- diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index 377a5be8b..d3e8604bb 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -1058,9 +1058,11 @@ ./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 @@ -3965,3 +3967,4 @@ ./var/spool/ftp/etc minix-base ./var/spool/ftp/hidden minix-base ./var/tmp minix-base +./var/www minix-base diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index c038f3805..cf038b14b 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -600,12 +600,14 @@ ./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 diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index 8ff7ee84c..ae94daf77 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -3462,6 +3462,7 @@ ./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 @@ -3501,7 +3502,7 @@ ./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 diff --git a/etc/mtree/NetBSD.dist.base b/etc/mtree/NetBSD.dist.base index 66a52f826..074ce2097 100644 --- a/etc/mtree/NetBSD.dist.base +++ b/etc/mtree/NetBSD.dist.base @@ -735,6 +735,7 @@ ./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 diff --git a/libexec/Makefile b/libexec/Makefile index 7e079ac51..7a530915d 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -4,7 +4,7 @@ .include 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 index 000000000..8eb0da78b --- /dev/null +++ b/libexec/httpd/CHANGES @@ -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 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 + 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 + 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 + + o many more manual fixes, from NetBSD + +changes in bozohttpd 20080303: + o fix some cgi header processing, from + o add simple Range: header processing, from + 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
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 + 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 + 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 + 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 + 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 + 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 + o always display file size in directory index mode + o add .xbel, .xml & .xsl -> text/xml mappings. from + + +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 + 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 + + 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 + +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 index 000000000..db2e3e48e --- /dev/null +++ b/libexec/httpd/Makefile @@ -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 +# (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 + +.if ${MKCRYPTO} != "no" + +LDADD+= -lssl -lcrypto +DPADD+= ${LIBSSL} ${LIBCRYPTO} + +.else + +COPTS+= -DNO_SSL_SUPPORT + +.endif + +# +# Build release things. +# +NROFF?= nroff + +PREHTMLFROB= sed \ + -e 's/&/\&/' \ + -e 's//\>/' + +HTMLFROB= sed \ + -e 's/\([MC] "[^"]*\)
$$/\1"<\/b>
/' \ + -e 's/'"''"'/\”/' \ + -e 's/""/\“/' \ + -e 's/ ${TAR}.gz; \ + bzip2 -9 ${TAR}; \ + echo "Exported two files in $${dir}:"; \ + echo ${TAR}.gz; \ + echo ${TAR}.bz2 + +.include diff --git a/libexec/httpd/Makefile.boot b/libexec/httpd/Makefile.boot new file mode 100644 index 000000000..8a9aaf0f1 --- /dev/null +++ b/libexec/httpd/Makefile.boot @@ -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 index 000000000..4a8dd666e --- /dev/null +++ b/libexec/httpd/auth-bozo.c @@ -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 + +#include +#include +#include + +#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 + */ +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 index 000000000..876ff6bb3 --- /dev/null +++ b/libexec/httpd/bozohttpd.8 @@ -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('', function) Lua function, +which will trigger the execution of the Lua function +.Em function +when a URL in the form +.Em http://// +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 index 000000000..b717501c6 --- /dev/null +++ b/libexec/httpd/bozohttpd.c @@ -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 +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 : "", + port ? port : ""); + + /* 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 : "")); + + /* 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, "Document Moved\n"); + bozo_printf(httpd, "

Document Moved

\n"); + bozo_printf(httpd, "This document had moved
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\n"); + bozo_printf(httpd, "\n"); +head: + bozo_flush(httpd, stdout); + free(urlbuf); +} + +/* + * deal with virtual host names; we do this: + * if we have a virtual path root (httpd->virtbase), and we are given a + * virtual host spec (Host: ho.st or http://ho.st/), see if this + * directory exists under httpd->virtbase. if it does, use this as the + # new slashdir. + */ +static int +check_virtual(bozo_httpreq_t *request) +{ + bozohttpd_t *httpd = request->hr_httpd; + char *file = request->hr_file, *s; + size_t len; + + if (!httpd->virtbase) + goto use_slashdir; + + /* + * convert http://virtual.host/ to request->hr_host + */ + debug((httpd, DEBUG_OBESE, "checking for http:// virtual host in ``%s''", + file)); + if (strncasecmp(file, "http://", 7) == 0) { + /* we would do virtual hosting here? */ + file += 7; + /* RFC 2616 (HTTP/1.1), 5.2: URI takes precedence over Host: */ + free(request->hr_host); + request->hr_host = bozostrdup(request->hr_httpd, file); + if ((s = strchr(request->hr_host, '/')) != NULL) + *s = '\0'; + s = strchr(file, '/'); + free(request->hr_file); + request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/"); + debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''", + request->hr_host, request->hr_file)); + } else if (!request->hr_host) + goto use_slashdir; + + /* + * canonicalise hr_host - that is, remove any :80. + */ + len = strlen(request->hr_host); + if (len > 3 && strcmp(request->hr_host + len - 3, ":80") == 0) { + request->hr_host[len - 3] = '\0'; + len = strlen(request->hr_host); + } + + /* + * ok, we have a virtual host, use opendir(3) to find a case + * insensitive match for the virtual host we are asked for. + * note that if the virtual host is the same as the master, + * we don't need to do anything special. + */ + debug((httpd, DEBUG_OBESE, + "check_virtual: checking host `%s' under httpd->virtbase `%s' " + "for file `%s'", + request->hr_host, httpd->virtbase, request->hr_file)); + if (strncasecmp(httpd->virthostname, request->hr_host, len) != 0) { + s = 0; + DIR *dirp; + struct dirent *d; + + if ((dirp = opendir(httpd->virtbase)) != NULL) { + while ((d = readdir(dirp)) != NULL) { + if (strcmp(d->d_name, ".") == 0 || + strcmp(d->d_name, "..") == 0) { + continue; + } + debug((httpd, DEBUG_OBESE, "looking at dir``%s''", + d->d_name)); + if (strcmp(d->d_name, request->hr_host) == 0) { + /* found it, punch it */ + debug((httpd, DEBUG_OBESE, "found it punch it")); + request->hr_virthostname = + bozostrdup(httpd, d->d_name); + if (asprintf(&s, "%s/%s", httpd->virtbase, + request->hr_virthostname) < 0) + bozo_err(httpd, 1, "asprintf"); + break; + } + } + closedir(dirp); + } + else { + debug((httpd, DEBUG_FAT, "opendir %s failed: %s", + httpd->virtbase, strerror(errno))); + } + if (s == 0) { + if (httpd->unknown_slash) + goto use_slashdir; + return bozo_http_error(httpd, 404, request, + "unknown URL"); + } + } else +use_slashdir: + s = httpd->slashdir; + + /* + * ok, nailed the correct slashdir, chdir to it + */ + if (chdir(s) < 0) + return bozo_http_error(httpd, 404, request, + "can't chdir to slashdir"); + return 0; +} + +/* + * checks to see if this request has a valid .bzredirect file. returns + * 0 when no redirection happend, or 1 when handle_redirect() has been + * called, -1 on error. + */ +static int +check_bzredirect(bozo_httpreq_t *request) +{ + struct stat sb; + char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1], + path[MAXPATHLEN]; + char *basename, *finalredir; + int rv, absolute; + + /* + * if this pathname is really a directory, but doesn't end in /, + * use it as the directory to look for the redir file. + */ + if((size_t)snprintf(dir, sizeof(dir), "%s", request->hr_file + 1) >= + sizeof(dir)) { + bozo_http_error(request->hr_httpd, 404, request, + "file path too long"); + return -1; + } + debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); + basename = strrchr(dir, '/'); + + if ((!basename || basename[1] != '\0') && + lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) + /* nothing */; + else if (basename == NULL) + strcpy(dir, "."); + else { + *basename++ = '\0'; + bozo_check_special_files(request, basename); + } + + if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir, + REDIRECT_FILE) >= sizeof(redir)) { + bozo_http_error(request->hr_httpd, 404, request, + "redirectfile path too long"); + return -1; + } + if (lstat(redir, &sb) == 0) { + if (!S_ISLNK(sb.st_mode)) + return 0; + absolute = 0; + } else { + if((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir, + ABSREDIRECT_FILE) >= sizeof(redir)) { + bozo_http_error(request->hr_httpd, 404, request, + "redirectfile path too long"); + return -1; + } + if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode)) + return 0; + absolute = 1; + } + debug((request->hr_httpd, DEBUG_FAT, + "check_bzredirect: calling readlink")); + rv = readlink(redir, redirpath, sizeof redirpath - 1); + if (rv == -1 || rv == 0) { + debug((request->hr_httpd, DEBUG_FAT, "readlink failed")); + return 0; + } + redirpath[rv] = '\0'; + debug((request->hr_httpd, DEBUG_FAT, + "readlink returned \"%s\"", redirpath)); + + /* check if we need authentication */ + snprintf(path, sizeof(path), "%s/", dir); + if (bozo_auth_check(request, path)) + return 1; + + /* now we have the link pointer, redirect to the real place */ + if (absolute) + finalredir = redirpath; + else { + if ((size_t)snprintf(finalredir = redir, sizeof(redir), "/%s/%s", + dir, redirpath) >= sizeof(redir)) { + bozo_http_error(request->hr_httpd, 404, request, + "redirect path too long"); + return -1; + } + } + + debug((request->hr_httpd, DEBUG_FAT, + "check_bzredirect: new redir %s", finalredir)); + handle_redirect(request, finalredir, absolute); + return 1; +} + +/* this fixes the %HH hack that RFC2396 requires. */ +static int +fix_url_percent(bozo_httpreq_t *request) +{ + bozohttpd_t *httpd = request->hr_httpd; + char *s, *t, buf[3], *url; + char *end; /* if end is not-zero, we don't translate beyond that */ + + url = request->hr_file; + + end = url + strlen(url); + + /* fast forward to the first % */ + if ((s = strchr(url, '%')) == NULL) + return 0; + + t = s; + do { + if (end && s >= end) { + debug((httpd, DEBUG_EXPLODING, + "fu_%%: past end, filling out..")); + while (*s) + *t++ = *s++; + break; + } + debug((httpd, DEBUG_EXPLODING, + "fu_%%: got s == %%, s[1]s[2] == %c%c", + s[1], s[2])); + if (s[1] == '\0' || s[2] == '\0') { + (void)bozo_http_error(httpd, 400, request, + "percent hack missing two chars afterwards"); + return 1; + } + if (s[1] == '0' && s[2] == '0') { + (void)bozo_http_error(httpd, 404, request, + "percent hack was %00"); + return 1; + } + if (s[1] == '2' && s[2] == 'f') { + (void)bozo_http_error(httpd, 404, request, + "percent hack was %2f (/)"); + return 1; + } + + buf[0] = *++s; + buf[1] = *++s; + buf[2] = '\0'; + s++; + *t = (char)strtol(buf, NULL, 16); + debug((httpd, DEBUG_EXPLODING, + "fu_%%: strtol put '%02x' into *t", *t)); + if (*t++ == '\0') { + (void)bozo_http_error(httpd, 400, request, + "percent hack got a 0 back"); + return 1; + } + + while (*s && *s != '%') { + if (end && s >= end) + break; + *t++ = *s++; + } + } while (*s); + *t = '\0'; + + debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url", + request->hr_file)); + + return 0; +} + +/* + * transform_request does this: + * - ``expand'' %20 crapola + * - punt if it doesn't start with / + * - check httpd->untrustedref / referrer + * - look for "http://myname/" and deal with it. + * - maybe call bozo_process_cgi() + * - check for ~user and call bozo_user_transform() if so + * - if the length > 1, check for trailing slash. if so, + * add the index.html file + * - if the length is 1, return the index.html file + * - disallow anything ending up with a file starting + * at "/" or having ".." in it. + * - anything else is a really weird internal error + * - returns malloced file to serve, if unhandled + */ +static int +transform_request(bozo_httpreq_t *request, int *isindex) +{ + bozohttpd_t *httpd = request->hr_httpd; + char *file, *newfile = NULL; + size_t len; + const char *hostname = BOZOHOST(httpd, request); + + file = NULL; + *isindex = 0; + debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file)); + if (fix_url_percent(request)) { + goto bad_done; + } + if (check_virtual(request)) { + goto bad_done; + } + file = request->hr_file; + + if (file[0] != '/') { + (void)bozo_http_error(httpd, 404, request, "unknown URL"); + goto bad_done; + } + + /* omit additional slashes at the beginning */ + while (file[1] == '/') + file++; + + switch(check_bzredirect(request)) { + case -1: + goto bad_done; + case 1: + return 0; + } + + if (httpd->untrustedref) { + int to_indexhtml = 0; + +#define TOP_PAGE(x) (strcmp((x), "/") == 0 || \ + strcmp((x) + 1, httpd->index_html) == 0 || \ + strcmp((x) + 1, "favicon.ico") == 0) + + debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref")); + /* + * first check that this path isn't allowed via .bzdirect file, + * and then check referrer; make sure that people come via the + * real name... otherwise if we aren't looking at / or + * /index.html, redirect... we also special case favicon.ico. + */ + if (check_direct_access(request)) + /* nothing */; + else if (request->hr_referrer) { + const char *r = request->hr_referrer; + + debug((httpd, DEBUG_FAT, + "checking referrer \"%s\" vs virthostname %s", + r, hostname)); + if (strncmp(r, "http://", 7) != 0 || + (strncasecmp(r + 7, hostname, + strlen(hostname)) != 0 && + !TOP_PAGE(file))) + to_indexhtml = 1; + } else { + const char *h = request->hr_host; + + debug((httpd, DEBUG_FAT, "url has no referrer at all")); + /* if there's no referrer, let / or /index.html past */ + if (!TOP_PAGE(file) || + (h && strncasecmp(h, hostname, + strlen(hostname)) != 0)) + to_indexhtml = 1; + } + + if (to_indexhtml) { + char *slashindexhtml; + + if (asprintf(&slashindexhtml, "/%s", + httpd->index_html) < 0) + bozo_err(httpd, 1, "asprintf"); + debug((httpd, DEBUG_FAT, + "httpd->untrustedref: redirecting %s to %s", + file, slashindexhtml)); + handle_redirect(request, slashindexhtml, 0); + free(slashindexhtml); + return 0; + } + } + + len = strlen(file); + if (/*CONSTCOND*/0) { +#ifndef NO_USER_SUPPORT + } else if (len > 1 && httpd->enable_users && file[1] == '~') { + if (file[2] == '\0') { + (void)bozo_http_error(httpd, 404, request, + "missing username"); + goto bad_done; + } + if (strchr(file + 2, '/') == NULL) { + handle_redirect(request, NULL, 0); + return 0; + } + debug((httpd, DEBUG_FAT, "calling bozo_user_transform")); + + return bozo_user_transform(request, isindex); +#endif /* NO_USER_SUPPORT */ + } else if (len > 1) { + debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1])); + if (file[len-1] == '/') { /* append index.html */ + *isindex = 1; + debug((httpd, DEBUG_FAT, "appending index.html")); + newfile = bozomalloc(httpd, + len + strlen(httpd->index_html) + 1); + strcpy(newfile, file + 1); + strcat(newfile, httpd->index_html); + } else + newfile = bozostrdup(request->hr_httpd, file + 1); + } else if (len == 1) { + debug((httpd, DEBUG_EXPLODING, "tf_req: len == 1")); + newfile = bozostrdup(request->hr_httpd, httpd->index_html); + *isindex = 1; + } else { /* len == 0 ? */ + (void)bozo_http_error(httpd, 500, request, + "request->hr_file is nul?"); + goto bad_done; + } + + if (newfile == NULL) { + (void)bozo_http_error(httpd, 500, request, "internal failure"); + goto bad_done; + } + + /* + * look for "http://myname/" and deal with it as necessary. + */ + + /* + * stop traversing outside our domain + * + * XXX true security only comes from our parent using chroot(2) + * before execve(2)'ing us. or our own built in chroot(2) support. + */ + if (*newfile == '/' || strcmp(newfile, "..") == 0 || + strstr(newfile, "/..") || strstr(newfile, "../")) { + (void)bozo_http_error(httpd, 403, request, "illegal request"); + goto bad_done; + } + + if (bozo_auth_check(request, newfile)) + goto bad_done; + + if (strlen(newfile)) { + request->hr_oldfile = request->hr_file; + request->hr_file = newfile; + } + + if (bozo_process_cgi(request)) + return 0; + + if (bozo_process_lua(request)) + return 0; + + debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); + return 1; +bad_done: + debug((httpd, DEBUG_FAT, "transform_request returning: 0")); + free(newfile); + return 0; +} + +/* + * can_gzip checks if the request supports and prefers gzip encoding. + * + * XXX: we do not consider the associated q with gzip in making our + * decision which is broken. + */ + +static int +can_gzip(bozo_httpreq_t *request) +{ + const char *pos; + const char *tmp; + size_t len; + + /* First we decide if the request can be gzipped at all. */ + + /* not if we already are encoded... */ + tmp = bozo_content_encoding(request, request->hr_file); + if (tmp && *tmp) + return 0; + + /* not if we are not asking for the whole file... */ + if (request->hr_last_byte_pos != -1 || request->hr_have_range) + return 0; + + /* Then we determine if gzip is on the cards. */ + + for (pos = request->hr_accept_encoding; pos && *pos; pos += len) { + while (*pos == ' ') + pos++; + + len = strcspn(pos, ";,"); + + if ((len == 4 && strncasecmp("gzip", pos, 4) == 0) || + (len == 6 && strncasecmp("x-gzip", pos, 6) == 0)) + return 1; + + if (pos[len] == ';') + len += strcspn(&pos[len], ","); + + if (pos[len]) + len++; + } + + return 0; +} + +/* + * bozo_process_request does the following: + * - check the request is valid + * - process cgi-bin if necessary + * - transform a filename if necesarry + * - return the HTTP request + */ +void +bozo_process_request(bozo_httpreq_t *request) +{ + bozohttpd_t *httpd = request->hr_httpd; + struct stat sb; + time_t timestamp; + char *file; + const char *type, *encoding; + int fd, isindex; + + /* + * note that transform_request chdir()'s if required. also note + * that cgi is handed here. if transform_request() returns 0 + * then the request has been handled already. + */ + if (transform_request(request, &isindex) == 0) + return; + + fd = -1; + encoding = NULL; + if (can_gzip(request)) { + asprintf(&file, "%s.gz", request->hr_file); + fd = open(file, O_RDONLY); + if (fd >= 0) + encoding = "gzip"; + free(file); + } + + file = request->hr_file; + + if (fd < 0) + fd = open(file, O_RDONLY); + + if (fd < 0) { + debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); + switch(errno) { + case EPERM: + case EACCES: + (void)bozo_http_error(httpd, 403, request, + "no permission to open file"); + break; + case ENAMETOOLONG: + /*FALLTHROUGH*/ + case ENOENT: + if (!bozo_dir_index(request, file, isindex)) + (void)bozo_http_error(httpd, 404, request, + "no file"); + break; + default: + (void)bozo_http_error(httpd, 500, request, "open file"); + } + goto cleanup_nofd; + } + if (fstat(fd, &sb) < 0) { + (void)bozo_http_error(httpd, 500, request, "can't fstat"); + goto cleanup; + } + if (S_ISDIR(sb.st_mode)) { + handle_redirect(request, NULL, 0); + goto cleanup; + } + + if (request->hr_if_modified_since && + parse_http_date(request->hr_if_modified_since, ×tamp) && + timestamp >= sb.st_mtime) { + /* XXX ignore subsecond of timestamp */ + bozo_printf(httpd, "%s 304 Not Modified\r\n", + request->hr_proto); + bozo_printf(httpd, "\r\n"); + bozo_flush(httpd, stdout); + goto cleanup; + } + + /* validate requested range */ + if (request->hr_last_byte_pos == -1 || + request->hr_last_byte_pos >= sb.st_size) + request->hr_last_byte_pos = sb.st_size - 1; + if (request->hr_have_range && + request->hr_first_byte_pos > request->hr_last_byte_pos) { + request->hr_have_range = 0; /* punt */ + request->hr_first_byte_pos = 0; + request->hr_last_byte_pos = sb.st_size - 1; + } + debug((httpd, DEBUG_FAT, "have_range %d first_pos %lld last_pos %lld", + request->hr_have_range, + (long long)request->hr_first_byte_pos, + (long long)request->hr_last_byte_pos)); + if (request->hr_have_range) + bozo_printf(httpd, "%s 206 Partial Content\r\n", + request->hr_proto); + else + bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto); + + if (request->hr_proto != httpd->consts.http_09) { + type = bozo_content_type(request, file); + if (!encoding) + encoding = bozo_content_encoding(request, file); + + bozo_print_header(request, &sb, type, encoding); + bozo_printf(httpd, "\r\n"); + } + bozo_flush(httpd, stdout); + + if (request->hr_method != HTTP_HEAD) { + off_t szleft, cur_byte_pos; + + szleft = + request->hr_last_byte_pos - request->hr_first_byte_pos + 1; + cur_byte_pos = request->hr_first_byte_pos; + + retry: + while (szleft) { + size_t sz; + + /* This should take care of the first unaligned chunk */ + if ((cur_byte_pos & (httpd->page_size - 1)) != 0) + sz = (size_t)(cur_byte_pos & ~httpd->page_size); + if ((off_t)httpd->mmapsz < szleft) + sz = httpd->mmapsz; + else + sz = (size_t)szleft; + if (mmap_and_write_part(httpd, fd, cur_byte_pos, sz)) { + if (errno == ENOMEM) { + httpd->mmapsz /= 2; + if (httpd->mmapsz >= httpd->page_size) + goto retry; + } + goto cleanup; + } + cur_byte_pos += sz; + szleft -= sz; + } + } + cleanup: + close(fd); + cleanup_nofd: + close(STDIN_FILENO); + close(STDOUT_FILENO); + /*close(STDERR_FILENO);*/ +} + +/* make sure we're not trying to access special files */ +int +bozo_check_special_files(bozo_httpreq_t *request, const char *name) +{ + bozohttpd_t *httpd = request->hr_httpd; + + /* ensure basename(name) != special files */ + if (strcmp(name, DIRECT_ACCESS_FILE) == 0) + return bozo_http_error(httpd, 403, request, + "no permission to open direct access file"); + if (strcmp(name, REDIRECT_FILE) == 0) + return bozo_http_error(httpd, 403, request, + "no permission to open redirect file"); + if (strcmp(name, ABSREDIRECT_FILE) == 0) + return bozo_http_error(httpd, 403, request, + "no permission to open redirect file"); + return bozo_auth_check_special_files(request, name); +} + +/* generic header printing routine */ +void +bozo_print_header(bozo_httpreq_t *request, + struct stat *sbp, const char *type, const char *encoding) +{ + bozohttpd_t *httpd = request->hr_httpd; + off_t len; + char date[40]; + + bozo_printf(httpd, "Date: %s\r\n", bozo_http_date(date, sizeof(date))); + bozo_printf(httpd, "Server: %s\r\n", httpd->server_software); + bozo_printf(httpd, "Accept-Ranges: bytes\r\n"); + if (sbp) { + char filedate[40]; + struct tm *tm; + + tm = gmtime(&sbp->st_mtime); + strftime(filedate, sizeof filedate, + "%a, %d %b %Y %H:%M:%S GMT", tm); + bozo_printf(httpd, "Last-Modified: %s\r\n", filedate); + } + if (type && *type) + bozo_printf(httpd, "Content-Type: %s\r\n", type); + if (encoding && *encoding) + bozo_printf(httpd, "Content-Encoding: %s\r\n", encoding); + if (sbp) { + if (request->hr_have_range) { + len = request->hr_last_byte_pos - + request->hr_first_byte_pos +1; + bozo_printf(httpd, + "Content-Range: bytes %qd-%qd/%qd\r\n", + (long long) request->hr_first_byte_pos, + (long long) request->hr_last_byte_pos, + (long long) sbp->st_size); + } else + len = sbp->st_size; + bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len); + } + if (request && request->hr_proto == httpd->consts.http_11) + bozo_printf(httpd, "Connection: close\r\n"); + bozo_flush(httpd, stdout); +} + +#ifndef NO_DEBUG +void +debug__(bozohttpd_t *httpd, int level, const char *fmt, ...) +{ + va_list ap; + int savederrno; + + /* only log if the level is low enough */ + if (httpd->debug < level) + return; + + savederrno = errno; + va_start(ap, fmt); + if (httpd->logstderr) { + vfprintf(stderr, fmt, ap); + fputs("\n", stderr); + } else + vsyslog(LOG_DEBUG, fmt, ap); + va_end(ap); + errno = savederrno; +} +#endif /* NO_DEBUG */ + +/* these are like warn() and err(), except for syslog not stderr */ +void +bozo_warn(bozohttpd_t *httpd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (httpd->logstderr || isatty(STDERR_FILENO)) { + //fputs("warning: ", stderr); + vfprintf(stderr, fmt, ap); + fputs("\n", stderr); + } else + vsyslog(LOG_INFO, fmt, ap); + va_end(ap); +} + +void +bozo_err(bozohttpd_t *httpd, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (httpd->logstderr || isatty(STDERR_FILENO)) { + //fputs("error: ", stderr); + vfprintf(stderr, fmt, ap); + fputs("\n", stderr); + } else + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); + exit(code); +} + +/* + * this escapes HTML tags. returns allocated escaped + * string if needed, or NULL on allocation failure or + * lack of escape need. + * call with NULL httpd in error paths, to avoid recursive + * malloc failure. call with valid httpd in normal paths + * to get automatic allocation failure handling. + */ +char * +bozo_escape_html(bozohttpd_t *httpd, const char *url) +{ + int i, j; + char *tmp; + size_t len; + + for (i = 0, j = 0; url[i]; i++) { + switch (url[i]) { + case '<': + case '>': + j += 4; + break; + case '&': + j += 5; + break; + } + } + + if (j == 0) + return NULL; + + /* + * we need to handle being called from different + * pathnames. + */ + len = strlen(url) + j; + if (httpd) + tmp = bozomalloc(httpd, len); + else if ((tmp = malloc(len)) == 0) + return NULL; + + for (i = 0, j = 0; url[i]; i++) { + switch (url[i]) { + case '<': + memcpy(tmp + j, "<", 4); + j += 4; + break; + case '>': + memcpy(tmp + j, ">", 4); + j += 4; + break; + case '&': + memcpy(tmp + j, "&", 5); + j += 5; + break; + default: + tmp[j++] = url[i]; + } + } + tmp[j] = 0; + + return tmp; +} + +/* short map between error code, and short/long messages */ +static struct errors_map { + int code; /* HTTP return code */ + const char *shortmsg; /* short version of message */ + const char *longmsg; /* long version of message */ +} errors_map[] = { + { 400, "400 Bad Request", "The request was not valid", }, + { 401, "401 Unauthorized", "No authorization", }, + { 403, "403 Forbidden", "Access to this item has been denied",}, + { 404, "404 Not Found", "This item has not been found", }, + { 408, "408 Request Timeout", "This request took too long", }, + { 417, "417 Expectation Failed","Expectations not available", }, + { 420, "420 Enhance Your Calm","Chill, Winston", }, + { 500, "500 Internal Error", "An error occured on the server", }, + { 501, "501 Not Implemented", "This request is not available", }, + { 0, NULL, NULL, }, +}; + +static const char *help = "DANGER! WILL ROBINSON! DANGER!"; + +static const char * +http_errors_short(int code) +{ + struct errors_map *ep; + + for (ep = errors_map; ep->code; ep++) + if (ep->code == code) + return (ep->shortmsg); + return (help); +} + +static const char * +http_errors_long(int code) +{ + struct errors_map *ep; + + for (ep = errors_map; ep->code; ep++) + if (ep->code == code) + return (ep->longmsg); + return (help); +} + +/* the follow functions and variables are used in handling HTTP errors */ +/* ARGSUSED */ +int +bozo_http_error(bozohttpd_t *httpd, int code, bozo_httpreq_t *request, + const char *msg) +{ + char portbuf[20]; + const char *header = http_errors_short(code); + const char *reason = http_errors_long(code); + const char *proto = (request && request->hr_proto) ? + request->hr_proto : httpd->consts.http_11; + int size; + + debug((httpd, DEBUG_FAT, "bozo_http_error %d: %s", code, msg)); + if (header == NULL || reason == NULL) { + bozo_err(httpd, 1, + "bozo_http_error() failed (short = %p, long = %p)", + header, reason); + return code; + } + + if (request && request->hr_serverport && + strcmp(request->hr_serverport, "80") != 0) + snprintf(portbuf, sizeof(portbuf), ":%s", + request->hr_serverport); + else + portbuf[0] = '\0'; + + if (request && request->hr_file) { + char *file = NULL; + const char *hostname = BOZOHOST(httpd, request); + + /* bozo_escape_html() failure here is just too bad. */ + file = bozo_escape_html(NULL, request->hr_file); + if (file == NULL) + file = request->hr_file; + size = snprintf(httpd->errorbuf, BUFSIZ, + "%s\n" + "

%s

\n" + "%s:
%s
\n" + "
%s%s
\n" + "\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 index 000000000..861f88e6c --- /dev/null +++ b/libexec/httpd/bozohttpd.h @@ -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 + +#ifndef NO_LUA_SUPPORT +#include +#endif +#include + +/* QNX provides a lot of NetBSD things in nbutil.h */ +#ifdef USE_NBUTIL +#include +#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) : "") + +#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 index 000000000..e0bcc8ca7 --- /dev/null +++ b/libexec/httpd/cgi-bozo.c @@ -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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 index 000000000..4a2e5a285 --- /dev/null +++ b/libexec/httpd/content-bozo.c @@ -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 + +#include +#include + +#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 index 000000000..ff3109af3 --- /dev/null +++ b/libexec/httpd/daemon-bozo.c @@ -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 +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..971b605f1 --- /dev/null +++ b/libexec/httpd/debug/Makefile @@ -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 index 000000000..49989bfc8 --- /dev/null +++ b/libexec/httpd/dir-index-bozo.c @@ -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 + +#include +#include +#include +#include +#include +#include + +#include "bozohttpd.h" + +static void +directory_hr(bozohttpd_t *httpd) +{ + + bozo_printf(httpd, + "
\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, + "Index of %s\r\n", + request->hr_file); + bozo_printf(httpd, "

Index of %s

\r\n", + request->hr_file); + bozo_printf(httpd, "
\r\n");
+#define NAMELEN 40
+#define LMODLEN 19
+	bozo_printf(httpd, "Name                                     "
+	    "Last modified          "
+	    "Size\n");
+	bozo_printf(httpd, "
"); + directory_hr(httpd); + bozo_printf(httpd, "
");
+
+	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, "");
+			l += bozo_printf(httpd, "Parent Directory");
+		} else if (S_ISDIR(sb.st_mode)) {
+			bozo_printf(httpd, "", urlname);
+			l += bozo_printf(httpd, "%s/", htmlname);
+		} else if (strchr(name, ':') != NULL) {
+			/* RFC 3986 4.2 */
+			bozo_printf(httpd, "", urlname);
+			l += bozo_printf(httpd, "%s", htmlname);
+		} else {
+			bozo_printf(httpd, "", urlname);
+			l += bozo_printf(httpd, "%s", htmlname);
+		}
+		if (htmlname != name)
+			free(htmlname);
+		bozo_printf(httpd, "");
+
+		/* 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, "
"); + directory_hr(httpd); + bozo_printf(httpd, "\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 index 000000000..5a9ad1825 --- /dev/null +++ b/libexec/httpd/libbozohttpd/Makefile @@ -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 + +.if ${MKCRYPTO} != "no" + +LDADD+= -lssl -lcrypto +DPADD+= ${LIBSSL} ${LIBCRYPTO} + +.else + +COPTS+= -DNO_SSL_SUPPORT + +.endif + +.include diff --git a/libexec/httpd/libbozohttpd/libbozohttpd.3 b/libexec/httpd/libbozohttpd/libbozohttpd.3 new file mode 100644 index 000000000..77c13f6f0 --- /dev/null +++ b/libexec/httpd/libbozohttpd/libbozohttpd.3 @@ -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 index 000000000..97c9f92d6 --- /dev/null +++ b/libexec/httpd/libbozohttpd/shlib_version @@ -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 index 000000000..972c130bc --- /dev/null +++ b/libexec/httpd/lua-bozo.c @@ -0,0 +1,452 @@ +/* $NetBSD: lua-bozo.c,v 1.12 2015/07/04 22:39:23 christos Exp $ */ + +/* + * Copyright (c) 2013 Marc Balmer + * 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 + +#include +#include +#include +#include +#include +#include + +#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("
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 index 000000000..448fa7194 --- /dev/null +++ b/libexec/httpd/lua/Makefile @@ -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 +.include + +LUABOZOOBJDIR != cd ${.CURDIR} && ${PRINTOBJDIR} + +OPSYS!= uname -s + +.if ${OPSYS} == "Darwin" +.sinclude + +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 index 000000000..9a30c9f1f --- /dev/null +++ b/libexec/httpd/lua/bozo.lua @@ -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 index 000000000..cad53b8e3 --- /dev/null +++ b/libexec/httpd/lua/glue.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#define LUA_LIB +#include +#include +#include + +#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 index 000000000..da9b62a16 --- /dev/null +++ b/libexec/httpd/lua/optparse.lua @@ -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="", version=""} +-- op=OptionParser(t) +-- op=add_option{"", action=, dest=, help=""} +-- +-- with : +-- the option string to be used (can be anything, if one letter opt, then should be -x val, more letters: -xy=val ) +-- one of +-- - store: store in options as key, val +-- - store_true: stores key, true +-- - store_false: stores key, false +-- 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 index 000000000..97c9f92d6 --- /dev/null +++ b/libexec/httpd/lua/shlib_version @@ -0,0 +1,2 @@ +major=0 +minor=0 diff --git a/libexec/httpd/main.c b/libexec/httpd/main.c new file mode 100644 index 000000000..348628bf6 --- /dev/null +++ b/libexec/httpd/main.c @@ -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 +#include + +#include +#include +#include +#include +#include +#include + +#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 index 000000000..906f73141 --- /dev/null +++ b/libexec/httpd/netbsd_queue.h @@ -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 index 000000000..5b0fdf9c6 --- /dev/null +++ b/libexec/httpd/printenv.lua @@ -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([[ + + + Bozotic Lua Environment + + +

Bozotic Lua Environment

+ ]]) + + print('module version: ' .. httpd._VERSION .. '
') + + print('

Server Environment

') + -- print the list of "environment" variables + for k, v in pairs(env) do + print(k .. '=' .. v .. '
') + end + + print('

Request Headers

') + for k, v in pairs(headers) do + print(k .. '=' .. v .. '
') + end + + if query ~= nil then + print('

Query Variables

') + for k, v in pairs(query) do + print(k .. '=' .. v .. '
') + end + end + + print('

Form Test

') + + print([[ +
+ + +
+ ]]) + -- output a footer + print([[ + + + ]]) +end + +function form(env, header, query) + if query ~= nil then + print('

Form Variables

') + + if env.CONTENT_TYPE ~= nil then + print('Content-type: ' .. env.CONTENT_TYPE .. '
') + end + + for k, v in pairs(query) do + print(k .. '=' .. v .. '
') + end + else + print('No values') + end +end + +-- register this handler for http:////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 index 000000000..3460e67f6 --- /dev/null +++ b/libexec/httpd/small/Makefile @@ -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 diff --git a/libexec/httpd/ssl-bozo.c b/libexec/httpd/ssl-bozo.c new file mode 100644 index 000000000..045a26e6d --- /dev/null +++ b/libexec/httpd/ssl-bozo.c @@ -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 +#include +#include +#include + +#include "bozohttpd.h" + +#ifndef NO_SSL_SUPPORT + +#include +#include + +#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 index 000000000..1c64fa0b3 --- /dev/null +++ b/libexec/httpd/testsuite/Makefile @@ -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 diff --git a/libexec/httpd/testsuite/data/bigfile b/libexec/httpd/testsuite/data/bigfile new file mode 100644 index 000000000..812c4c3b3 --- /dev/null +++ b/libexec/httpd/testsuite/data/bigfile @@ -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 index 000000000..5534f688a --- /dev/null +++ b/libexec/httpd/testsuite/data/bigfile.partial4000 @@ -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 index 000000000..372d38907 --- /dev/null +++ b/libexec/httpd/testsuite/data/bigfile.partial8000 @@ -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 index 000000000..4e03184fb --- /dev/null +++ b/libexec/httpd/testsuite/data/file @@ -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 index 000000000..dc88dcb5a --- /dev/null +++ b/libexec/httpd/testsuite/data/index.html @@ -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 index 000000000..15931b017 --- /dev/null +++ b/libexec/httpd/testsuite/html_cmp @@ -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 index 000000000..35dbed365 --- /dev/null +++ b/libexec/httpd/testsuite/t1.in @@ -0,0 +1 @@ +get / diff --git a/libexec/httpd/testsuite/t1.out b/libexec/httpd/testsuite/t1.out new file mode 100644 index 000000000..135ba7ab5 --- /dev/null +++ b/libexec/httpd/testsuite/t1.out @@ -0,0 +1,2 @@ +HTTP/0.9 200 OK +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 index 000000000..6015fe237 --- /dev/null +++ b/libexec/httpd/testsuite/t10.in @@ -0,0 +1 @@ +GET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0 diff --git a/libexec/httpd/testsuite/t10.out b/libexec/httpd/testsuite/t10.out new file mode 100644 index 000000000..cf4101106 --- /dev/null +++ b/libexec/httpd/testsuite/t10.out @@ -0,0 +1,8 @@ +HTTP/1.0 404 Not Found +Content-Type: text/html +Content-Length: 1024 +Server: bozohttpd/20140708 + +404 Not Found +

404 Not Found

+/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 index 000000000..d7b06e0a4 --- /dev/null +++ b/libexec/httpd/testsuite/t2.in @@ -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 index 000000000..68dcab458 --- /dev/null +++ b/libexec/httpd/testsuite/t2.out @@ -0,0 +1,9 @@ +HTTP/1.0 200 OK +Date: Tue, 10 Jul 2001 15:45:36 GMT +Server: bozotic HTTP server version 5.08 +Accept-Ranges: bytes +Last-Modified: Tue, 10 Jul 2001 15:50:43 GMT +Content-Type: text/html +Content-Length: 55 + +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 index 000000000..e71aab4be --- /dev/null +++ b/libexec/httpd/testsuite/t3.in @@ -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 index 000000000..21803ad9e --- /dev/null +++ b/libexec/httpd/testsuite/t3.out @@ -0,0 +1,11 @@ +HTTP/1.1 400 Bad Request +Content-Type: text/html +Content-Length: 229 +Server: bozotic HTTP server version 5.08 +Allow: GET, HEAD, POST + +400 Bad Request +

400 Bad Request

+/:
The request was not valid
+
madrugada.eterna.com.au
+ diff --git a/libexec/httpd/testsuite/t4.in b/libexec/httpd/testsuite/t4.in new file mode 100644 index 000000000..86a52f319 --- /dev/null +++ b/libexec/httpd/testsuite/t4.in @@ -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 index 000000000..501e1673d --- /dev/null +++ b/libexec/httpd/testsuite/t4.out @@ -0,0 +1,10 @@ +HTTP/1.1 200 OK +Date: Tue, 10 Jul 2001 15:49:21 GMT +Server: bozotic HTTP server version 5.08 +Accept-Ranges: bytes +Last-Modified: Tue, 10 Jul 2001 15:34:33 GMT +Content-Type: text/html +Content-Length: 55 +Connection: close + +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 index 000000000..6422d98dd --- /dev/null +++ b/libexec/httpd/testsuite/t5.in @@ -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 index 000000000..3a50ccd01 --- /dev/null +++ b/libexec/httpd/testsuite/t5.out @@ -0,0 +1,10 @@ +HTTP/1.0 403 Forbidden +Content-Type: text/html +Content-Length: 336 +Server: bozohttpd/20030206 + +403 Forbidden +

403 Forbidden

+/cgi-bin/..M-@M-/..M-@M-/..M-@M-/..M-@M-/..M-@M-/../winnt/system32/cmd.exe:
Access to this item has been denied
+
what-time-is-love.eterna.com.au
+ diff --git a/libexec/httpd/testsuite/t6.in b/libexec/httpd/testsuite/t6.in new file mode 100644 index 000000000..3403286cf --- /dev/null +++ b/libexec/httpd/testsuite/t6.in @@ -0,0 +1,2 @@ +GET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0 + diff --git a/libexec/httpd/testsuite/t6.out b/libexec/httpd/testsuite/t6.out new file mode 100644 index 000000000..906b126b2 --- /dev/null +++ b/libexec/httpd/testsuite/t6.out @@ -0,0 +1,10 @@ +HTTP/1.0 404 Not Found +Content-Type: text/html +Content-Length: 335 +Server: bozohttpd/5.15 + +404 Not Found +

404 Not Found

+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:
This item has not been found
+
splode.eterna.com.au
+ diff --git a/libexec/httpd/testsuite/t7.in b/libexec/httpd/testsuite/t7.in new file mode 100644 index 000000000..eaaab74dc --- /dev/null +++ b/libexec/httpd/testsuite/t7.in @@ -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 index 000000000..8695d97f3 --- /dev/null +++ b/libexec/httpd/testsuite/t7.out @@ -0,0 +1,14 @@ +HTTP/1.1 206 Partial Content +Date: Sun, 02 Mar 2008 08:52:03 GMT +Server: bozohttpd/20060710 +Accept-Ranges: bytes +Last-Modified: Sun, 02 Mar 2008 08:44:38 GMT +Content-Type: text/plain +Content-Range: bytes 0-63/64 +Content-Length: 64 +Connection: close + +123456781234567 +345678903456789 +234567892345678 +012345670123456 diff --git a/libexec/httpd/testsuite/t8.in b/libexec/httpd/testsuite/t8.in new file mode 100644 index 000000000..45263b92c --- /dev/null +++ b/libexec/httpd/testsuite/t8.in @@ -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 index 000000000..d755f81ca --- /dev/null +++ b/libexec/httpd/testsuite/t8.out @@ -0,0 +1,12 @@ +HTTP/1.1 206 Partial Content +Date: Sun, 02 Mar 2008 08:52:03 GMT +Server: bozohttpd/20060710 +Accept-Ranges: bytes +Last-Modified: Sun, 02 Mar 2008 08:44:38 GMT +Content-Type: text/plain +Content-Range: bytes 0-31/64 +Content-Length: 32 +Connection: close + +123456781234567 +345678903456789 diff --git a/libexec/httpd/testsuite/t9.in b/libexec/httpd/testsuite/t9.in new file mode 100644 index 000000000..22b123534 --- /dev/null +++ b/libexec/httpd/testsuite/t9.in @@ -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 index 000000000..15d692b77 --- /dev/null +++ b/libexec/httpd/testsuite/t9.out @@ -0,0 +1,12 @@ +HTTP/1.1 206 Partial Content +Date: Sun, 02 Mar 2008 08:52:03 GMT +Server: bozohttpd/20060710 +Accept-Ranges: bytes +Last-Modified: Sun, 02 Mar 2008 08:44:38 GMT +Content-Type: text/plain +Content-Range: bytes 32-63/64 +Content-Length: 32 +Connection: close + +234567892345678 +012345670123456 diff --git a/libexec/httpd/testsuite/test-bigfile b/libexec/httpd/testsuite/test-bigfile new file mode 100755 index 000000000..0f33dcfc7 --- /dev/null +++ b/libexec/httpd/testsuite/test-bigfile @@ -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 index 000000000..f7fd0c7bd --- /dev/null +++ b/libexec/httpd/tilde-luzah-bozo.c @@ -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 + +#include +#include +#include +#include +#include +#include + +#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 */