bozohttpd-20111118/0000755000175000017500000000000011661422032014266 5ustar mnordstrmnordstrbozohttpd-20111118/lua/0000755000175000017500000000000011661422033015050 5ustar mnordstrmnordstrbozohttpd-20111118/lua/bozo.lua0000755000175000017500000001515511371667137016554 0ustar mnordstrmnordstr#! /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 bozohttpd-20111118/lua/Makefile0000644000175000017500000000145411371667137016532 0ustar mnordstrmnordstr#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 bozohttpd-20111118/lua/optparse.lua0000644000175000017500000000762411371667137017437 0ustar mnordstrmnordstr-- 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 bozohttpd-20111118/lua/glue.c0000644000175000017500000001426011371667137016171 0ustar mnordstrmnordstr/*- * 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; } bozohttpd-20111118/lua/shlib_version0000644000175000017500000000002011371667137017647 0ustar mnordstrmnordstrmajor=0 minor=0 bozohttpd-20111118/Makefile0000644000175000017500000000331711416210623015731 0ustar mnordstrmnordstr# $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 */ # # these are usually set via the "COPTS" variable, or some other method # for setting CFLAGS relevant to your make, eg # % make COPTS="-DDO_HTPASSWD" PROG?= bozohttpd MAN= bozohttpd.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 SRCS+= main.c WARNS?= 4 check: cd $(.CURDIR)/testsuite && $(MAKE) check .if ${PROG} == "bozohttpd" clean: cd $(.CURDIR)/testsuite && $(MAKE) clean .endif CRYPTOLIBS= -lcrypto -lssl CRYPTODEPS= $(LIBCRYPTO) $(LIBSSL) NROFF?= nroff # if not defining -DNO_SSL_SUPPORT LDADD= $(CRYPTOLIBS) DPADD= $(CRYPTODEPS) PREHTMLFROB= sed \ -e 's/&/\&/' \ -e 's//\>/' HTMLFROB= sed \ -e 's/\([MC] "[^"]*\)
$$/\1"<\/b>
/' \ -e 's/'"''"'/\”/' \ -e 's/""/\“/' \ -e 's/ 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 */ bozohttpd-20111118/debug/0000755000175000017500000000000011661422033015355 5ustar mnordstrmnordstrbozohttpd-20111118/debug/Makefile0000644000175000017500000000026711205617153017025 0ustar mnordstrmnordstr# $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)/.. bozohttpd-20111118/small/0000755000175000017500000000000011661422033015377 5ustar mnordstrmnordstrbozohttpd-20111118/small/Makefile0000644000175000017500000000152211205617153017042 0ustar mnordstrmnordstr# $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 LEAN_IFDEF_FLAGS= -UDEBUG -DNO_USER_SUPPORT \ -DNO_CGIBIN_SUPPORT -DNO_DIRINDEX_SUPPORT \ -DNO_DAEMON_MODE -DNO_DYNAMIC_CONTENT \ -DNO_SSL_SUPPORT -UDO_HTPASSWD 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 bozohttpd-20111118/queue.h0000644000175000017500000000564111172310363015571 0ustar mnordstrmnordstr/* $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_ */ bozohttpd-20111118/bozohttpd.80000644000175000017500000003376111661331767016424 0ustar mnordstrmnordstr.\" $eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $ .\" .\" Copyright (c) 1997-2010 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 November 17, 2011 .Dt BOZOHTTPD 8 .Os BOZOS .Sh NAME .Nm bozohttpd .Nd hyper text transfer protocol version 1.1 daemon .Sh SYNOPSIS .Nm .Op Fl HVXbefnrus .Op Fl C Ar suffix cgihandler .Op Fl I Ar port .Op Fl M Ar suffix type encoding encoding11 .Op Fl P Ar pidfile .Op Fl S Ar server_software .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 .Op Fl Z Ar cert privkey .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 facaility (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 .Ar port to use used as the port to bind daemon mode. The default is the .Dq http port. This option is only valid with the .Fl b option. .It Fl i Ar address Causes .Ar address to use 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 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 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/pkg/libexec/bozohttpd bozohttpd /var/www http stream tcp6 nowait:600 httpd /usr/pkg/libexec/bozohttpd bozohttpd /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), and dynamic MIME content (NO_DYNAMIC_CONTENT), 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. .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/pkg/libexec/bozohttpd bozohttpd -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. .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 bozohttpd -C .php /usr/pkg/bin/php /var/www .Ed .Sh SEE ALSO .Xr inetd.conf 5 , .Xr inetd 8 .Sh HISTORY The .Nm program 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. The focus has always been simplicity and security, with minimal features and regular code audits. This manual documents .Nm version 20100920. .Sh AUTHORS .Nm was written by Matthew R. Green .Aq mrg@eterna.com.au . .Pp The large list of contributors includes: .Bl -dash .It Arnaud Lacombe .Aq alc@netbsd.org provided some clean up for memory leaks .It Christoph Badura .Aq bad@bsd.de provided Range: header support .It Sean Boudreau .Aq seanb@NetBSD.org provided a security fix for virtual hosting .It Julian Coleman .Aq jdc@coris.org.uk provided an IPv6 bugfix .It Chuck Cranor .Aq chuck@research.att.com provided cgi-bin support fixes, and more .It DEGROOTE Arnaud .Aq degroote@netbsd.org provided a fix for daemon mode .It Andrew Doran .Aq ad@netbsd.org provided directory indexing support .It Per Ekman .Aq pek@pdc.kth.se provided a fix for a minor (non-security) buffer overflow condition .It Alistair G. Crooks .Aq agc@netbsd.org cleaned up many internal interfaces, made bozohttpd linkable as a library and provided the lua binding. .It Jun-ichiro itojun Hagino, KAME .Aq itojun@iijlab.net provided initial IPv6 support .It Martin Husemann .Aq martin@netbsd.org provided .bzabsredirect support .It Arto Huusko .Aq arto.huusko@pp2.inet.fi provided fixes cgi-bin .It Roland Illig .Aq roland.illig@gmx.de provided some off-by-one fixes .It Zak Johnson .Aq zakj@nox.cx provided cgi-bin enhancements .It Nicolas Jombart .Aq ecu@ipv42.net provided fixes for HTTP basic authorisation support .It Thomas Klausner .Aq wiz@danbala.ifoer.tuwien.ac.at provided many fixes and enhancements for the man page .It Johnny Lam .Aq jlam@netbsd.org provided man page fixes .It Luke Mewburn .Aq lukem@netbsd.org provided many various fixes, including cgi-bin fixes and enhancements, HTTP basic authorisation support and much code clean up .It Jeremy C. Reed .Aq reed@netbsd.org provided several clean up fixes, and man page updates .It Scott Reynolds .Aq scottr@netbsd.org provided various fixes .It Tyler Retzlaff .Aq rtr@eterna.com.au provided SSL support, cgi-bin fixes and much other random other stuff .It rudolf .Aq netbsd@eq.cz provided minor compile fixes and a CGI content map fix .It Steve Rumble .Aq rumble@ephemeral.org provided the .Fl V option. .It Joerg Sonnenberger .Aq joerg@netbsd.org implemented If-Modified-Since support .It ISIHARA Takanori .Aq ishit@oak.dti.ne.jp provided a man page fix .It Holger Weiss .Aq holger@CIS.FU-Berlin.DE provided http authorisation fixes .It .Aq xs@kittenz.org provided chroot and change-to-user support, and other various fixes .It Coyote Point provided various CGI fixes .It 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 mrg@eterna.com.au for inclusion in future releaases. .Sh BUGS .Nm does not handle HTTP/1.1 chunked input from the client yet. bozohttpd-20111118/bozohttpd.c0000644000175000017500000015344111661421613016463 0ustar mnordstrmnordstr/* $eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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 * which expired may 18, 1999): * * - 14.15: content-encoding handling. [1] * * - 14.16: 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.25/28: if-{,un}modified-since handling. maybe do this, but * i really don't want to have to parse 3 differnet date formats * * [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: we don't do ranges. from section 14.35.2 * `A server MAY ignore the Range header'. but it might be nice. * since 20080301 we support simple range headers. * * - 14.9: we aren't a cache. * * - 14.15: content-md5 would be nice... * * - 14.24/14.26/14.27: be nice to support this... * * - 14.44: not sure about this Vary: header. ignore it for now. */ #ifndef INDEX_HTML #define INDEX_HTML "index.html" #endif #ifndef SERVER_SOFTWARE #define SERVER_SOFTWARE "bozohttpd/20111118" #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 */ #define MF(x) if (request->x) free(request->x) MF(hr_remotehost); MF(hr_remoteaddr); MF(hr_serverport); MF(hr_file); MF(hr_oldfile); MF(hr_query); #undef MF 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); if (ohdr) free(ohdr); ohdr = hdr; } if (ohdr) 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; bozo_ssl_accept(httpd); 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_file = NULL; request->hr_oldfile = NULL; 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 = hdr->h_value; /* HTTP/1.1 rev06 draft spec: 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; 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; } /* HTTP/1.1 draft rev-06, 14.23 & 19.6.1.1 */ if (request->hr_proto == httpd->consts.http_11 && 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 (void)madvise(addr, sz, MADV_SEQUENTIAL); #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; } /* * 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_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); } snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, DIRECT_ACCESS_FILE); 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]; int query = 0; if (url == NULL) { if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0) bozo_err(httpd, 1, "asprintf"); url = urlbuf; } else urlbuf = NULL; 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'; bozo_warn(httpd, "redirecting %s%s%s", httpd->virthostname, 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", httpd->virthostname, 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", httpd->virthostname, portbuf, url, request->hr_query); } else { if (absolute) bozo_printf(httpd, "%s", url); else bozo_printf(httpd, "%s%s%s", httpd->virthostname, portbuf, url); } bozo_printf(httpd, "\">here\n"); bozo_printf(httpd, "\n"); head: bozo_flush(httpd, stdout); if (urlbuf) 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; s = strchr(file, '/'); /* HTTP/1.1 draft rev-06, 5.2: URI takes precedence over Host: */ request->hr_host = 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; /* * ok, we have a virtual host, use scandir(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. */ len = strlen(request->hr_host); 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 (strncasecmp(d->d_name, request->hr_host, len) == 0) { /* found it, punch it */ debug((httpd, DEBUG_OBESE, "found it punch it")); httpd->virthostname = d->d_name; if (asprintf(&s, "%s/%s", httpd->virtbase, httpd->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 on failure and 1 on success. */ static void check_bzredirect(bozo_httpreq_t *request) { struct stat sb; char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1]; 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. */ snprintf(dir, sizeof(dir), "%s", request->hr_file + 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); } snprintf(redir, sizeof(redir), "%s/%s", dir, REDIRECT_FILE); if (lstat(redir, &sb) == 0) { if (!S_ISLNK(sb.st_mode)) return; absolute = 0; } else { snprintf(redir, sizeof(redir), "%s/%s", dir, ABSREDIRECT_FILE); if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode)) return; 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; } redirpath[rv] = '\0'; debug((request->hr_httpd, DEBUG_FAT, "readlink returned \"%s\"", redirpath)); /* now we have the link pointer, redirect to the real place */ if (absolute) finalredir = redirpath; else snprintf(finalredir = redir, sizeof(redir), "/%s/%s", dir, redirpath); debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: new redir %s", finalredir)); handle_redirect(request, finalredir, absolute); } /* this fixes the %HH hack that RFC2396 requires. */ static void 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; 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"); goto copy_rest; } if (s[1] == '0' && s[2] == '0') { (void)bozo_http_error(httpd, 404, request, "percent hack was %00"); goto copy_rest; } if (s[1] == '2' && s[2] == 'f') { (void)bozo_http_error(httpd, 404, request, "percent hack was %2f (/)"); goto copy_rest; } 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"); goto copy_rest; } while (*s && *s != '%') { if (end && s >= end) break; *t++ = *s++; } } while (*s); copy_rest: while (*s) { if (s >= end) break; *t++ = *s++; } *t = '\0'; debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url", request->hr_file)); } /* * 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; file = NULL; *isindex = 0; debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file)); fix_url_percent(request); 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; } check_bzredirect(request); 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, httpd->virthostname)); if (strncmp(r, "http://", 7) != 0 || (strncasecmp(r + 7, httpd->virthostname, strlen(httpd->virthostname)) != 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, httpd->virthostname, strlen(httpd->virthostname)) != 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; debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); return 1; bad_done: debug((httpd, DEBUG_FAT, "transform_request returning: 0")); if (newfile) free(newfile); 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; file = request->hr_file; fd = open(file, O_RDONLY); if (fd < 0) { debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); if (errno == EPERM) (void)bozo_http_error(httpd, 403, request, "no permission to open file"); else if (errno == ENOENT) { if (!bozo_dir_index(request, file, isindex)) (void)bozo_http_error(httpd, 404, request, "no file"); } else (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); 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 escape HTML tags */ static void escape_html(bozo_httpreq_t *request) { int i, j; char *url = request->hr_file, *tmp; for (i = 0, j = 0; url[i]; i++) { switch (url[i]) { case '<': case '>': j += 4; break; case '&': j += 5; break; } } if (j == 0) return; if ((tmp = (char *) malloc(strlen(url) + j)) == 0) /* * ouch, but we are only called from an error context, and * most paths here come from malloc(3) failures anyway... * we could completely punt and just exit, but isn't returning * an not-quite-correct error better than nothing at all? */ return; 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; free(request->hr_file); request->hr_file = 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", }, { 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) { escape_html(request); size = snprintf(httpd->errorbuf, BUFSIZ, "%s\n" "

%s

\n" "%s:
%s
\n" "
%s%s
\n" "\n", header, header, request->hr_file, reason, httpd->virthostname, portbuf, httpd->virthostname, 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"); if (size) 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; } 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; } bozohttpd-20111118/bozohttpd.h0000644000175000017500000002446211661421613016470 0ustar mnordstrmnordstr/* $eterna: bozohttpd.h,v 1.39 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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 #include #include "queue.h" /* 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; typedef struct bozo_content_map_t { const char *name; /* postfix of file */ size_t namelen; /* length of postfix */ 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 */ 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_file; char *hr_oldfile; /* if we added an index_html */ char *hr_query; const char *hr_proto; const char *hr_content_type; const char *hr_content_length; const char *hr_allow; const char *hr_host; /* HTTP/1.1 Host: */ const char *hr_referrer; const char *hr_range; const char *hr_if_modified_since; 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; /* 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; /* write in upto 64KiB chunks, and mmap in upto 64MiB chunks */ #define BOZO_WRSZ (64 * 1024) #define BOZO_MMAPSZ (BOZO_WRSZ * 1024) /* debug flags */ #define DEBUG_NORMAL 1 #define DEBUG_FAT 2 #define DEBUG_OBESE 3 #define DEBUG_EXPLODING 4 #define strornull(x) ((x) ? (x) : "") #ifndef NO_DEBUG void debug__(bozohttpd_t *, int, const char *, ...) __attribute__((__format__(__printf__, 3, 4))); #define debug(x) debug__ x #else #define debug(x) #endif /* NO_DEBUG */ #if defined(__GNUC__) && __GNUC__ >= 3 #define BOZO_PRINTFLIKE(x,y) __attribute__((__format__(__printf__, x,y))) #define BOZO_DEAD __attribute__((__noreturn__)) #endif 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 *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) do { /* nothing */ } while (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 *); void bozo_ssl_accept(bozohttpd_t *); void bozo_ssl_destroy(bozohttpd_t *); #endif /* auth-bozo.c */ #ifdef DO_HTPASSWD 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_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 */ /* 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 *, ...); 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_ */ bozohttpd-20111118/ssl-bozo.c0000644000175000017500000001633111661421613016212 0ustar mnordstrmnordstr/* $eterna: ssl-bozo.c,v 1.15 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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 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_ssl_err * * bozo_ssl_err works just like bozo_err except in addition to printing * the error provided by the caller at the point of error it pops and * prints all errors from the SSL error queue. */ 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); unsigned int 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())); exit(code); } static int bozo_ssl_printf(bozohttpd_t *httpd, const char * fmt, va_list ap) { sslinfo_t *sslinfo; char *buf; int nbytes; sslinfo = httpd->sslinfo; /* XXX we need more elegant/proper handling of SSL_write return */ if ((nbytes = vasprintf(&buf, fmt, ap)) != -1) SSL_write(sslinfo->bozossl, buf, nbytes); free(buf); return nbytes; } static ssize_t bozo_ssl_read(bozohttpd_t *httpd, int fd, void *buf, size_t nbytes) { sslinfo_t *sslinfo; ssize_t rbytes; USE_ARG(fd); sslinfo = httpd->sslinfo; /* XXX we need elegant/proper handling of SSL_read return */ rbytes = (ssize_t)SSL_read(sslinfo->bozossl, buf, (int)nbytes); if (rbytes < 1) { if (SSL_get_error(sslinfo->bozossl, rbytes) == SSL_ERROR_WANT_READ) bozo_warn(httpd, "SSL_ERROR_WANT_READ"); else bozo_warn(httpd, "SSL_ERROR OTHER"); } return rbytes; } static ssize_t bozo_ssl_write(bozohttpd_t *httpd, int fd, const void *buf, size_t nbytes) { sslinfo_t *sslinfo; ssize_t wbytes; USE_ARG(fd); sslinfo = httpd->sslinfo; /* XXX we need elegant/proper handling of SSL_write return */ wbytes = (ssize_t)SSL_write(sslinfo->bozossl, buf, (int)nbytes); return wbytes; } static int bozo_ssl_flush(bozohttpd_t *httpd, FILE *fp) { USE_ARG(httpd); USE_ARG(fp); /* nothing to see here, move right along */ return 0; } void bozo_ssl_init(bozohttpd_t *httpd) { sslinfo_t *sslinfo; 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); /* XXX we need to learn how to check the SSL stack for more info */ if (NULL == sslinfo->ssl_context) bozo_ssl_err(httpd, EXIT_FAILURE, "SSL context creation failed"); if (1 != SSL_CTX_use_certificate_file(sslinfo->ssl_context, sslinfo->certificate_file, SSL_FILETYPE_PEM)) 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"); } void bozo_ssl_accept(bozohttpd_t *httpd) { sslinfo_t *sslinfo; sslinfo = httpd->sslinfo; if (sslinfo != NULL && sslinfo->ssl_context) { sslinfo->bozossl = SSL_new(sslinfo->ssl_context); SSL_set_rfd(sslinfo->bozossl, 0); SSL_set_wfd(sslinfo->bozossl, 1); SSL_accept(sslinfo->bozossl); } } void bozo_ssl_destroy(bozohttpd_t *httpd) { sslinfo_t *sslinfo; 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; if ((sslinfo = httpd->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); va_end(args); return cc; } #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 bozo_ssl_flush(httpd, fp); } #endif return fflush(fp); } bozohttpd-20111118/cgi-bozo.c0000644000175000017500000003411411661421613016152 0ustar mnordstrmnordstr/* $eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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, *command, *file, *url; 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; uri = request->hr_oldfile ? request->hr_oldfile : 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; command = 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) { command = file + 1; path = bozostrdup(httpd, cgihandler); argv[ix++] = path; /* argv[] = [ path, command, query, NULL ] */ } else { command = 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", httpd->virthostname, 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: if (query) free(query); if (file) free(file); if (url) 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->namelen = strlen(map->name); map->type = map->encoding = map->encoding11 = NULL; map->cgihandler = cgihandler; } #endif /* NO_DYNAMIC_CONTENT */ #endif /* NO_CGIBIN_SUPPORT */ bozohttpd-20111118/tilde-luzah-bozo.c0000644000175000017500000000737611661421613017644 0ustar mnordstrmnordstr/* $eterna: tilde-luzah-bozo.c,v 1.16 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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 "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; struct passwd *pw; *isindex = 0; if ((s = strchr(request->hr_file + 2, '/')) != NULL) { *s++ = '\0'; c = s[strlen(s)-1]; *isindex = (c == '/' || c == '\0'); } debug((httpd, DEBUG_OBESE, "looking for user %s", request->hr_file + 2)); pw = getpwnam(request->hr_file + 2); /* 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 */ bozohttpd-20111118/main.c0000644000175000017500000002076511661421613015374 0ustar mnordstrmnordstr/* $NetBSD: bozohttpd.c,v 1.15 2009/05/23 08:26:26 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-2011 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 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:M:P:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) { switch(c) { 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; #ifdef NO_USER_SUPPORT case 'p': case 't': case 'u': bozo_err(&httpd, 1, "User support is not enabled"); /* NOTREACHED */ #else case 'p': bozo_set_pref(&prefs, "public_html", optarg); break; case 't': bozo_set_pref(&prefs, "chroot dir", 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); } bozohttpd-20111118/content-bozo.c0000644000175000017500000002672011661421613017066 0ustar mnordstrmnordstr/* $eterna: content-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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", 5, "text/html", "", "", NULL }, { ".htm", 4, "text/html", "", "", NULL }, { ".gif", 4, "image/gif", "", "", NULL }, { ".jpeg", 5, "image/jpeg", "", "", NULL }, { ".jpg", 4, "image/jpeg", "", "", NULL }, { ".jpe", 4, "image/jpeg", "", "", NULL }, { ".png", 4, "image/png", "", "", NULL }, { ".mp3", 4, "audio/mpeg", "", "", NULL }, { ".css", 4, "text/css", "", "", NULL }, { ".txt", 4, "text/plain", "", "", NULL }, { ".swf", 4, "application/x-shockwave-flash","", "", NULL }, { ".dcr", 4, "application/x-director", "", "", NULL }, { ".pac", 4, "application/x-ns-proxy-autoconfig", "", "", NULL }, { ".pa", 3, "application/x-ns-proxy-autoconfig", "", "", NULL }, { ".tar", 4, "multipart/x-tar", "", "", NULL }, { ".gtar", 5, "multipart/x-gtar", "", "", NULL }, { ".tar.Z", 6, "multipart/x-tar", "x-compress", "compress", NULL }, { ".tar.gz", 7, "multipart/x-tar", "x-gzip", "gzip", NULL }, { ".taz", 4, "multipart/x-tar", "x-gzip", "gzip", NULL }, { ".tgz", 4, "multipart/x-tar", "x-gzip", "gzip", NULL }, { ".tar.z", 6, "multipart/x-tar", "x-pack", "x-pack", NULL }, { ".Z", 2, "application/x-compress", "x-compress", "compress", NULL }, { ".gz", 3, "application/x-gzip", "x-gzip", "gzip", NULL }, { ".z", 2, "unknown", "x-pack", "x-pack", NULL }, { ".bz2", 4, "application/x-bzip2", "x-bzip2", "x-bzip2", NULL }, { ".ogg", 4, "application/x-ogg", "", "", NULL }, { ".xbel", 5, "text/xml", "", "", NULL }, { ".xml", 4, "text/xml", "", "", NULL }, { ".xsl", 4, "text/xml", "", "", NULL }, { ".hqx", 4, "application/mac-binhex40", "", "", NULL }, { ".cpt", 4, "application/mac-compactpro","", "", NULL }, { ".doc", 4, "application/msword", "", "", NULL }, { ".bin", 4, "application/octet-stream", "", "", NULL }, { ".dms", 4, "application/octet-stream", "", "", NULL }, { ".lha", 4, "application/octet-stream", "", "", NULL }, { ".lzh", 4, "application/octet-stream", "", "", NULL }, { ".exe", 4, "application/octet-stream", "", "", NULL }, { ".class", 6, "application/octet-stream", "", "", NULL }, { ".oda", 4, "application/oda", "", "", NULL }, { ".pdf", 4, "application/pdf", "", "", NULL }, { ".ai", 3, "application/postscript", "", "", NULL }, { ".eps", 4, "application/postscript", "", "", NULL }, { ".ps", 3, "application/postscript", "", "", NULL }, { ".ppt", 4, "application/powerpoint", "", "", NULL }, { ".rtf", 4, "application/rtf", "", "", NULL }, { ".bcpio", 6, "application/x-bcpio", "", "", NULL }, { ".torrent", 8, "application/x-bittorrent", "", "", NULL }, { ".vcd", 4, "application/x-cdlink", "", "", NULL }, { ".cpio", 5, "application/x-cpio", "", "", NULL }, { ".csh", 4, "application/x-csh", "", "", NULL }, { ".dir", 4, "application/x-director", "", "", NULL }, { ".dxr", 4, "application/x-director", "", "", NULL }, { ".dvi", 4, "application/x-dvi", "", "", NULL }, { ".hdf", 4, "application/x-hdf", "", "", NULL }, { ".cgi", 4, "application/x-httpd-cgi", "", "", NULL }, { ".skp", 4, "application/x-koan", "", "", NULL }, { ".skd", 4, "application/x-koan", "", "", NULL }, { ".skt", 4, "application/x-koan", "", "", NULL }, { ".skm", 4, "application/x-koan", "", "", NULL }, { ".latex", 6, "application/x-latex", "", "", NULL }, { ".mif", 4, "application/x-mif", "", "", NULL }, { ".nc", 3, "application/x-netcdf", "", "", NULL }, { ".cdf", 4, "application/x-netcdf", "", "", NULL }, { ".patch", 6, "application/x-patch", "", "", NULL }, { ".sh", 3, "application/x-sh", "", "", NULL }, { ".shar", 5, "application/x-shar", "", "", NULL }, { ".sit", 4, "application/x-stuffit", "", "", NULL }, { ".sv4cpio", 8, "application/x-sv4cpio", "", "", NULL }, { ".sv4crc", 7, "application/x-sv4crc", "", "", NULL }, { ".tar", 4, "application/x-tar", "", "", NULL }, { ".tcl", 4, "application/x-tcl", "", "", NULL }, { ".tex", 4, "application/x-tex", "", "", NULL }, { ".texinfo", 8, "application/x-texinfo", "", "", NULL }, { ".texi", 5, "application/x-texinfo", "", "", NULL }, { ".t", 2, "application/x-troff", "", "", NULL }, { ".tr", 3, "application/x-troff", "", "", NULL }, { ".roff", 5, "application/x-troff", "", "", NULL }, { ".man", 4, "application/x-troff-man", "", "", NULL }, { ".me", 3, "application/x-troff-me", "", "", NULL }, { ".ms", 3, "application/x-troff-ms", "", "", NULL }, { ".ustar", 6, "application/x-ustar", "", "", NULL }, { ".src", 4, "application/x-wais-source", "", "", NULL }, { ".zip", 4, "application/zip", "", "", NULL }, { ".au", 3, "audio/basic", "", "", NULL }, { ".snd", 4, "audio/basic", "", "", NULL }, { ".mpga", 5, "audio/mpeg", "", "", NULL }, { ".mp2", 4, "audio/mpeg", "", "", NULL }, { ".aif", 4, "audio/x-aiff", "", "", NULL }, { ".aiff", 5, "audio/x-aiff", "", "", NULL }, { ".aifc", 5, "audio/x-aiff", "", "", NULL }, { ".ram", 4, "audio/x-pn-realaudio", "", "", NULL }, { ".rpm", 4, "audio/x-pn-realaudio-plugin","", "", NULL }, { ".ra", 3, "audio/x-realaudio", "", "", NULL }, { ".wav", 4, "audio/x-wav", "", "", NULL }, { ".pdb", 4, "chemical/x-pdb", "", "", NULL }, { ".xyz", 4, "chemical/x-pdb", "", "", NULL }, { ".ief", 4, "image/ief", "", "", NULL }, { ".tiff", 5, "image/tiff", "", "", NULL }, { ".tif", 4, "image/tiff", "", "", NULL }, { ".ras", 4, "image/x-cmu-raster", "", "", NULL }, { ".pnm", 4, "image/x-portable-anymap", "", "", NULL }, { ".pbm", 4, "image/x-portable-bitmap", "", "", NULL }, { ".pgm", 4, "image/x-portable-graymap", "", "", NULL }, { ".ppm", 4, "image/x-portable-pixmap", "", "", NULL }, { ".rgb", 4, "image/x-rgb", "", "", NULL }, { ".xbm", 4, "image/x-xbitmap", "", "", NULL }, { ".xpm", 4, "image/x-xpixmap", "", "", NULL }, { ".xwd", 4, "image/x-xwindowdump", "", "", NULL }, { ".rtx", 4, "text/richtext", "", "", NULL }, { ".tsv", 4, "text/tab-separated-values", "", "", NULL }, { ".etx", 4, "text/x-setext", "", "", NULL }, { ".sgml", 5, "text/x-sgml", "", "", NULL }, { ".sgm", 4, "text/x-sgml", "", "", NULL }, { ".mpeg", 5, "video/mpeg", "", "", NULL }, { ".mpg", 4, "video/mpeg", "", "", NULL }, { ".mpe", 4, "video/mpeg", "", "", NULL }, { ".mp4", 4, "video/mp4", "", "", NULL }, { ".qt", 3, "video/quicktime", "", "", NULL }, { ".mov", 4, "video/quicktime", "", "", NULL }, { ".avi", 4, "video/x-msvideo", "", "", NULL }, { ".movie", 6, "video/x-sgi-movie", "", "", NULL }, { ".ice", 4, "x-conference/x-cooltalk", "", "", NULL }, { ".wrl", 4, "x-world/x-vrml", "", "", NULL }, { ".vrml", 5, "x-world/x-vrml", "", "", NULL }, { NULL, 0, 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++) { if (map->namelen < len && strcasecmp(map->name, name + (len - map->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->namelen = 0; 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->namelen = strlen(map->name); map->type = CHECKMAP(cmap1); map->encoding = CHECKMAP(cmap2); map->encoding11 = CHECKMAP(cmap3); #undef CHECKMAP map->cgihandler = NULL; } #endif /* NO_DYNAMIC_CONTENT */ bozohttpd-20111118/auth-bozo.c0000644000175000017500000001630111661421613016347 0ustar mnordstrmnordstr/* $eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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); snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE); 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_cleanup(bozo_httpreq_t *request) { if (request == NULL) return; if (request->hr_authuser) free(request->hr_authuser); if (request->hr_authpass) free(request->hr_authpass); if (request->hr_authrealm) 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'; 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; 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 (in[i - 1] == '=') cp--,i--; return (cp - out); } #endif /* DO_HTPASSWD */ bozohttpd-20111118/dir-index-bozo.c0000644000175000017500000001270711661421613017277 0ustar mnordstrmnordstr/* $eterna: dir-index-bozo.c,v 1.20 2011/11/18 09:21:15 mrg Exp $ */ /* * Copyright (c) 1997-2011 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 *dirname, 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(dirname) <= strlen(httpd->index_html)) dirname = "."; else { file = bozostrdup(httpd, dirname); file[strlen(file) - strlen(httpd->index_html)] = '\0'; dirname = file; } debug((httpd, DEBUG_FAT, "bozo_dir_index: dirname ``%s''", dirname)); if (stat(dirname, &sb) < 0 || (dp = opendir(dirname)) == 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(dirname, &de, NULL, alphasort), deo = de;
	    j--; de++) {
		int nostat = 0;
		char *name = (*de)->d_name;

		if (strcmp(name, ".") == 0 ||
		    (strcmp(name, "..") != 0 &&
		     httpd->hide_dots && name[0] == '.'))
			continue;

		snprintf(buf, sizeof buf, "%s/%s", dirname, name);
		if (stat(buf, &sb))
			nostat = 1;

		l = 0;

		if (strcmp(name, "..") == 0) {
			bozo_printf(httpd, "");
			l += bozo_printf(httpd, "Parent Directory");
		} else if (S_ISDIR(sb.st_mode)) {
			bozo_printf(httpd, "", name);
			l += bozo_printf(httpd, "%s/", name);
		} else if (strchr(name, ':') != NULL) {
			/* RFC 3986 4.2 */
			bozo_printf(httpd, "", name);
			l += bozo_printf(httpd, "%s", name);
		} else {
			bozo_printf(httpd, "", name);
			l += bozo_printf(httpd, "%s", name);
		}
		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, 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, spacebuf);

			bozo_printf(httpd, "%7ukB",
			    ((unsigned)((unsigned)(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: if (file) free(file); return 1; } #endif /* NO_DIRINDEX_SUPPORT */ bozohttpd-20111118/CHANGES0000644000175000017500000002471211661331767015305 0ustar mnordstrmnordstr$eterna: CHANGES,v 1.78 2011/11/18 01:25:11 mrg Exp $ changes since bozohttpd 20100920: 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 since bozohttpd 20100617: 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 since bozohttpd 20100509: 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 since bozohttpd 20090522: 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 since bozohttpd 20090417: 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 since bozohttpd 20080303: 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 since bozohttpd 20060710: 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 since bozohttpd 20060517: o prefix some function names with "bozo" o align directory indexing
markers o clean up some code GCC4 grumbled about changes since bozohttpd 20050410: 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 since bozohttpd 20040808: 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 since bozohttpd 20040218: 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 since bozohttpd 20031005: 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 since bozohttpd 20030626: 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 since bozohttpd 20030409: 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 since bozohttpd 20030313: 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 since bozohttpd 20021106: 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 since bozohttpd 5.15 (20020913): 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 since bozohttpd 5.14 (20020823): o add .ogg support -> `application/x-ogg' o fix CGI requests with "/" in the query part changes since bozohttpd 5.13 (20020804): 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 since bozohttpd 5.12 (20020803): o support .mp3 files (type audio/mpeg) o use stat() to find out if something is a directory, for -X mode changes since bozohttpd 5.11 (20020730): o constification o fixes & enhancements for directory index mode (-X) changes since bozohttpd 5.10 (20020710): 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 since bozohttpd 5.09 (20010922): - 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 since bozohttpd 5.08 (20010812): - 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 since bozohttpd 5.07 (20010610): - add directory index generation support (-X) from ad@netbsd.org - add .pa as an alias for .pac - make server software version configurable (RFC) changes since bozohttpd 5.06 (20000825): - 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 since bozohttpd 5.05 (20000815): - add IPv6 suppor from itojun@iijlab.net - man page fixes from jlam@netbsd.org changes since bozohttpd 5.04 (20000427): - fix a virtual host bug, from kleink@netbsd.org changes since bozohttpd 5.03 (20000427): - fix virtual host support; URI takes precedence over Host: changes since bozohttpd 5.02 (20000426): - fix a bug with chdir() changes since bozohttpd 5.01 (20000421): - .pac spport from simonb changes since bozohttpd 5.00 (19990519): - .swf support - virtual hosting support bozohttpd-20111118/libbozohttpd/0000755000175000017500000000000011661422033016773 5ustar mnordstrmnordstrbozohttpd-20111118/libbozohttpd/Makefile0000644000175000017500000000111511371667137020447 0ustar mnordstrmnordstr# $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 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 bozohttpd-20111118/libbozohttpd/libbozohttpd.30000644000175000017500000000756511371671767021622 0ustar mnordstrmnordstr.\" $NetBSD$ .\" .\" $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 mrg@eterna.com.au .An Alistair Crooks Aq agc@NetBSD.org wrote this high-level interface. .Pp This manual page was written by .An Alistair Crooks . bozohttpd-20111118/libbozohttpd/shlib_version0000644000175000017500000000002011371667137021572 0ustar mnordstrmnordstrmajor=0 minor=0 bozohttpd-20111118/testsuite/0000755000175000017500000000000011661422033016320 5ustar mnordstrmnordstrbozohttpd-20111118/testsuite/data/0000755000175000017500000000000011661422033017231 5ustar mnordstrmnordstrbozohttpd-20111118/testsuite/data/file0000644000175000017500000000010010762471255020075 0ustar mnordstrmnordstr123456781234567 345678903456789 234567892345678 012345670123456 bozohttpd-20111118/testsuite/data/bigfile.partial40000000644000175000017500000000764011205617153022526 0ustar mnordstrmnordstrthis 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 bozohttpd-20111118/testsuite/data/bigfile.partial80000000644000175000017500000001750011205617153022526 0ustar mnordstrmnordstrthis 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 bozohttpd-20111118/testsuite/data/index.html0000644000175000017500000000006707322622123021232 0ustar mnordstrmnordstrthis is the bozohttpd testsuite ./data/index.html file bozohttpd-20111118/testsuite/data/bigfile0000644000175000017500000002366011205617153020567 0ustar mnordstrmnordstrthis 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. bozohttpd-20111118/testsuite/Makefile0000644000175000017500000000123511205617153017764 0ustar mnordstrmnordstr# $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 bozohttpd-20111118/testsuite/t1.in0000644000175000017500000000000607322622120017166 0ustar mnordstrmnordstrget / bozohttpd-20111118/testsuite/t2.in0000644000175000017500000000001707322622121017172 0ustar mnordstrmnordstrGET / HTTP/1.0 bozohttpd-20111118/testsuite/t3.in0000644000175000017500000000001707322622121017173 0ustar mnordstrmnordstrGET / HTTP/1.1 bozohttpd-20111118/testsuite/t4.in0000644000175000017500000000002507322622122017174 0ustar mnordstrmnordstrGET / HTTP/1.1 Host: bozohttpd-20111118/testsuite/t5.in0000644000175000017500000000014507521463634017214 0ustar mnordstrmnordstrGET /cgi-bin/..M-@M-/..M-@M-/..M-@M-/..M-@M-/..M-@M-/../winnt/system32/cmd.exe?/c+dir+c:\\ HTTP/1.0 bozohttpd-20111118/testsuite/t6.in0000644000175000017500000000021607546612425017215 0ustar mnordstrmnordstrGET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0 bozohttpd-20111118/testsuite/t7.in0000644000175000017500000000005510762471251017211 0ustar mnordstrmnordstrGET /file HTTP/1.1 Host: Range: bytes=0-63 bozohttpd-20111118/testsuite/t8.in0000644000175000017500000000005510762471251017212 0ustar mnordstrmnordstrGET /file HTTP/1.1 Host: Range: bytes=0-31 bozohttpd-20111118/testsuite/t9.in0000644000175000017500000000005610762471251017214 0ustar mnordstrmnordstrGET /file HTTP/1.1 Host: Range: bytes=32-63 bozohttpd-20111118/testsuite/html_cmp0000755000175000017500000000137011661304032020050 0ustar mnordstrmnordstr#! /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 bozohttpd-20111118/testsuite/test-bigfile0000755000175000017500000000065411205617153020634 0ustar mnordstrmnordstr#! /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 bozohttpd-20111118/testsuite/t10.in0000644000175000017500000002354510763152057017275 0ustar mnordstrmnordstrGET /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.0 bozohttpd-20111118/testsuite/t1.out0000644000175000017500000000011007322622120017363 0ustar mnordstrmnordstrHTTP/0.9 200 OK this is the bozohttpd testsuite ./data/index.html file bozohttpd-20111118/testsuite/t2.out0000644000175000017500000000041210762471251017402 0ustar mnordstrmnordstrHTTP/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 bozohttpd-20111118/testsuite/t3.out0000644000175000017500000000056110763152057017411 0ustar mnordstrmnordstrHTTP/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
bozohttpd-20111118/testsuite/t4.out0000644000175000017500000000043510762471251017411 0ustar mnordstrmnordstrHTTP/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 bozohttpd-20111118/testsuite/t5.out0000644000175000017500000000065011172230543017402 0ustar mnordstrmnordstrHTTP/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
bozohttpd-20111118/testsuite/t6.out0000644000175000017500000000065611172230543017411 0ustar mnordstrmnordstrHTTP/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
bozohttpd-20111118/testsuite/t7.out0000644000175000017500000000050410762471251017411 0ustar mnordstrmnordstrHTTP/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 bozohttpd-20111118/testsuite/t8.out0000644000175000017500000000044410762471251017415 0ustar mnordstrmnordstrHTTP/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 bozohttpd-20111118/testsuite/t9.out0000644000175000017500000000044510762471251017417 0ustar mnordstrmnordstrHTTP/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 bozohttpd-20111118/testsuite/t10.out0000644000175000017500000000215211172230543017455 0ustar mnordstrmnordstrHTTP/1.0 500 Internal Error Content-Type: text/html Content-Length: 10296 Server: bozohttpd/20090417 500 Internal Error

500 Internal Error

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx