Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/libexec/httpd/bozohttpd.c,v rcsdiff: /ftp/cvs/cvsroot/src/libexec/httpd/bozohttpd.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.32.2.1 retrieving revision 1.32.2.2 diff -u -p -r1.32.2.1 -r1.32.2.2 --- src/libexec/httpd/bozohttpd.c 2013/06/23 06:28:49 1.32.2.1 +++ src/libexec/httpd/bozohttpd.c 2014/08/20 00:02:22 1.32.2.2 @@ -1,9 +1,9 @@ -/* $NetBSD: bozohttpd.c,v 1.32.2.1 2013/06/23 06:28:49 tls Exp $ */ +/* $NetBSD: bozohttpd.c,v 1.32.2.2 2014/08/20 00:02:22 tls Exp $ */ /* $eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $ */ /* - * Copyright (c) 1997-2011 Matthew R. Green + * Copyright (c) 1997-2014 Matthew R. Green * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,17 +55,17 @@ /* * requirements for minimal http/1.1 (at least, as documented in - * which expired may 18, 1999): + * RFC 2616 (HTTP/1.1): * - * - 14.15: content-encoding handling. [1] + * - 14.11: content-encoding handling. [1] * - * - 14.16: content-length handling. this is only a SHOULD header + * - 14.13: content-length handling. this is only a SHOULD header * thus we could just not send it ever. [1] * * - 14.17: content-type handling. [1] * - * - 14.25/28: if-{,un}modified-since handling. maybe do this, but - * i really don't want to have to parse 3 differnet date formats + * - 14.28: if-unmodified-since handling. if-modified-since is + * done since, shouldn't be too hard for this one. * * [1] need to revisit to ensure proper behaviour * @@ -88,28 +88,28 @@ * * - 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.. + * - 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.5/14.16/14.35: only support simple ranges: %d- and %d-%d + * would be nice to support more. * * - 14.9: we aren't a cache. * - * - 14.15: content-md5 would be nice... - * - * - 14.24/14.26/14.27: be nice to support this... + * - 14.15: content-md5 would be nice. * - * - 14.44: not sure about this Vary: header. ignore it for now. + * - 14.24/14.26/14.27: if-match, if-none-match, if-range. be + * nice to support this. + * + * - 14.44: Vary: seems unneeded. ignore it for now. */ #ifndef INDEX_HTML #define INDEX_HTML "index.html" #endif #ifndef SERVER_SOFTWARE -#define SERVER_SOFTWARE "bozohttpd/20111118" +#define SERVER_SOFTWARE "bozohttpd/20140717" #endif #ifndef DIRECT_ACCESS_FILE #define DIRECT_ACCESS_FILE ".bzdirect" @@ -333,25 +333,23 @@ bozo_clean_request(bozo_httpreq_t *reque 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 + free(request->hr_remotehost); + free(request->hr_remoteaddr); + free(request->hr_serverport); + free(request->hr_virthostname); + free(request->hr_file); + free(request->hr_oldfile); + free(request->hr_query); + free(request->hr_host); bozo_auth_cleanup(request); for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr; hdr = SIMPLEQ_NEXT(hdr, h_next)) { free(hdr->h_value); free(hdr->h_header); - if (ohdr) - free(ohdr); + free(ohdr); ohdr = hdr; } - if (ohdr) - free(ohdr); + free(ohdr); free(request); } @@ -529,7 +527,8 @@ bozo_read_request(bozohttpd_t *httpd) */ if (bozo_daemon_fork(httpd)) return NULL; - bozo_ssl_accept(httpd); + if (bozo_ssl_accept(httpd)) + return NULL; request = bozomalloc(httpd, sizeof(*request)); memset(request, 0, sizeof(*request)); @@ -539,6 +538,7 @@ bozo_read_request(bozohttpd_t *httpd) request->hr_range = NULL; request->hr_last_byte_pos = -1; request->hr_if_modified_since = NULL; + request->hr_virthostname = NULL; request->hr_file = NULL; request->hr_oldfile = NULL; @@ -681,8 +681,8 @@ bozo_read_request(bozohttpd_t *httpd) 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 */ + request->hr_host = bozostrdup(httpd, hdr->h_value); + /* RFC 2616 (HTTP/1.1): 14.20 */ else if (strcasecmp(hdr->h_header, "expect") == 0) { (void)bozo_http_error(httpd, 417, request, "we don't support Expect:"); @@ -719,8 +719,9 @@ next_header: goto cleanup; } - /* HTTP/1.1 draft rev-06, 14.23 & 19.6.1.1 */ + /* RFC 2616 (HTTP/1.1), 14.23 & 19.6.1.1 */ if (request->hr_proto == httpd->consts.http_11 && + /*(strncasecmp(request->hr_file, "http://", 7) != 0) &&*/ request->hr_host == NULL) { (void)bozo_http_error(httpd, 400, request, "missing Host header"); @@ -852,7 +853,7 @@ parse_http_date(const char *val, time_t * to be updated for any sort of parallel processing. */ char * -escape_rfc3986(bozohttpd_t *httpd, const char *url) +bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url) { static char *buf; static size_t buflen = 0; @@ -865,7 +866,7 @@ escape_rfc3986(bozohttpd_t *httpd, const buflen = len * 3 + 1; buf = bozorealloc(httpd, buf, buflen); } - + if (url == NULL) { buf[0] = 0; return buf; @@ -922,7 +923,7 @@ check_direct_access(bozo_httpreq_t *requ 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)); + debug((request->hr_httpd, DEBUG_FAT, "check_direct_access: dir %s", dir)); basename = strrchr(dir, '/'); if ((!basename || basename[1] != '\0') && @@ -935,7 +936,12 @@ check_direct_access(bozo_httpreq_t *requ bozo_check_special_files(request, basename); } - snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, DIRECT_ACCESS_FILE); + if ((size_t)snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, + DIRECT_ACCESS_FILE) >= sizeof(dirfile)) { + bozo_http_error(request->hr_httpd, 404, request, + "directfile path too long"); + return 0; + } if (stat(dirfile, &sb) < 0 || (fp = fopen(dirfile, "r")) == NULL) return 0; @@ -954,15 +960,16 @@ handle_redirect(bozo_httpreq_t *request, bozohttpd_t *httpd = request->hr_httpd; char *urlbuf; char portbuf[20]; + const char *hostname = BOZOHOST(httpd, request); int query = 0; - + if (url == NULL) { if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0) bozo_err(httpd, 1, "asprintf"); url = urlbuf; } else urlbuf = NULL; - url = escape_rfc3986(request->hr_httpd, url); + url = bozo_escape_rfc3986(request->hr_httpd, url); if (request->hr_query && strlen(request->hr_query)) query = 1; @@ -972,15 +979,18 @@ handle_redirect(bozo_httpreq_t *request, request->hr_serverport); else portbuf[0] = '\0'; - bozo_warn(httpd, "redirecting %s%s%s", httpd->virthostname, portbuf, url); + if (absolute) + bozo_warn(httpd, "redirecting %s", url); + else + bozo_warn(httpd, "redirecting %s%s%s", hostname, portbuf, url); debug((httpd, DEBUG_FAT, "redirecting %s", url)); bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto); - if (request->hr_proto != httpd->consts.http_09) + 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); + bozo_printf(httpd, "%s%s", hostname, portbuf); if (query) { bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query); } else { @@ -997,21 +1007,20 @@ handle_redirect(bozo_httpreq_t *request, if (absolute) bozo_printf(httpd, "%s?%s", url, request->hr_query); else - bozo_printf(httpd, "%s%s%s?%s", httpd->virthostname, + bozo_printf(httpd, "%s%s%s?%s", hostname, portbuf, url, request->hr_query); } else { if (absolute) bozo_printf(httpd, "%s", url); else - bozo_printf(httpd, "%s%s%s", httpd->virthostname, + bozo_printf(httpd, "%s%s%s", hostname, portbuf, url); } bozo_printf(httpd, "\">here\n"); bozo_printf(httpd, "\n"); head: bozo_flush(httpd, stdout); - if (urlbuf) - free(urlbuf); + free(urlbuf); } /* @@ -1039,9 +1048,13 @@ check_virtual(bozo_httpreq_t *request) if (strncasecmp(file, "http://", 7) == 0) { /* we would do virtual hosting here? */ file += 7; + /* RFC 2616 (HTTP/1.1), 5.2: URI takes precedence over Host: */ + free(request->hr_host); + request->hr_host = bozostrdup(request->hr_httpd, file); + if ((s = strchr(request->hr_host, '/')) != NULL) + *s = '\0'; s = strchr(file, '/'); - /* HTTP/1.1 draft rev-06, 5.2: URI takes precedence over Host: */ - request->hr_host = file; + free(request->hr_file); request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/"); debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''", request->hr_host, request->hr_file)); @@ -1049,12 +1062,20 @@ check_virtual(bozo_httpreq_t *request) goto use_slashdir; /* - * ok, we have a virtual host, use scandir(3) to find a case + * canonicalise hr_host - that is, remove any :80. + */ + len = strlen(request->hr_host); + if (len > 3 && strcmp(request->hr_host + len - 3, ":80") == 0) { + request->hr_host[len - 3] = '\0'; + len = strlen(request->hr_host); + } + + /* + * ok, we have a virtual host, use opendir(3) to find a case * insensitive match for the virtual host we are asked for. * note that if the virtual host is the same as the master, * we don't need to do anything special. */ - len = strlen(request->hr_host); debug((httpd, DEBUG_OBESE, "check_virtual: checking host `%s' under httpd->virtbase `%s' " "for file `%s'", @@ -1076,9 +1097,10 @@ check_virtual(bozo_httpreq_t *request) len) == 0) { /* found it, punch it */ debug((httpd, DEBUG_OBESE, "found it punch it")); - httpd->virthostname = d->d_name; + request->hr_virthostname = + bozostrdup(httpd, d->d_name); if (asprintf(&s, "%s/%s", httpd->virtbase, - httpd->virthostname) < 0) + request->hr_virthostname) < 0) bozo_err(httpd, 1, "asprintf"); break; } @@ -1110,13 +1132,15 @@ use_slashdir: /* * checks to see if this request has a valid .bzredirect file. returns - * 0 on failure and 1 on success. + * 0 when no redirection happend, or 1 when handle_redirect() has been + * called, -1 on error. */ -static void +static int check_bzredirect(bozo_httpreq_t *request) { struct stat sb; - char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1]; + char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1], + path[MAXPATHLEN]; char *basename, *finalredir; int rv, absolute; @@ -1124,7 +1148,12 @@ check_bzredirect(bozo_httpreq_t *request * 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); + if((size_t)snprintf(dir, sizeof(dir), "%s", request->hr_file + 1) >= + sizeof(dir)) { + bozo_http_error(request->hr_httpd, 404, request, + "file path too long"); + return -1; + } debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); basename = strrchr(dir, '/'); @@ -1138,15 +1167,25 @@ check_bzredirect(bozo_httpreq_t *request bozo_check_special_files(request, basename); } - snprintf(redir, sizeof(redir), "%s/%s", dir, REDIRECT_FILE); + if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir, + REDIRECT_FILE) >= sizeof(redir)) { + bozo_http_error(request->hr_httpd, 404, request, + "redirectfile path too long"); + return -1; + } if (lstat(redir, &sb) == 0) { if (!S_ISLNK(sb.st_mode)) - return; + return 0; absolute = 0; } else { - snprintf(redir, sizeof(redir), "%s/%s", dir, ABSREDIRECT_FILE); + if((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir, + ABSREDIRECT_FILE) >= sizeof(redir)) { + bozo_http_error(request->hr_httpd, 404, request, + "redirectfile path too long"); + return -1; + } if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode)) - return; + return 0; absolute = 1; } debug((request->hr_httpd, DEBUG_FAT, @@ -1154,26 +1193,37 @@ check_bzredirect(bozo_httpreq_t *request rv = readlink(redir, redirpath, sizeof redirpath - 1); if (rv == -1 || rv == 0) { debug((request->hr_httpd, DEBUG_FAT, "readlink failed")); - return; + return 0; } redirpath[rv] = '\0'; debug((request->hr_httpd, DEBUG_FAT, "readlink returned \"%s\"", redirpath)); - + + /* check if we need authentication */ + snprintf(path, sizeof(path), "%s/", dir); + if (bozo_auth_check(request, path)) + return 1; + /* now we have the link pointer, redirect to the real place */ if (absolute) finalredir = redirpath; - else - snprintf(finalredir = redir, sizeof(redir), "/%s/%s", dir, - redirpath); + else { + if ((size_t)snprintf(finalredir = redir, sizeof(redir), "/%s/%s", + dir, redirpath) >= sizeof(redir)) { + bozo_http_error(request->hr_httpd, 404, request, + "redirect path too long"); + return -1; + } + } debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: new redir %s", finalredir)); handle_redirect(request, finalredir, absolute); + return 1; } /* this fixes the %HH hack that RFC2396 requires. */ -static void +static int fix_url_percent(bozo_httpreq_t *request) { bozohttpd_t *httpd = request->hr_httpd; @@ -1186,7 +1236,7 @@ fix_url_percent(bozo_httpreq_t *request) /* fast forward to the first % */ if ((s = strchr(url, '%')) == NULL) - return; + return 0; t = s; do { @@ -1203,19 +1253,19 @@ fix_url_percent(bozo_httpreq_t *request) if (s[1] == '\0' || s[2] == '\0') { (void)bozo_http_error(httpd, 400, request, "percent hack missing two chars afterwards"); - goto copy_rest; + return 1; } if (s[1] == '0' && s[2] == '0') { (void)bozo_http_error(httpd, 404, request, "percent hack was %00"); - goto copy_rest; + return 1; } if (s[1] == '2' && s[2] == 'f') { (void)bozo_http_error(httpd, 404, request, "percent hack was %2f (/)"); - goto copy_rest; + return 1; } - + buf[0] = *++s; buf[1] = *++s; buf[2] = '\0'; @@ -1226,7 +1276,7 @@ fix_url_percent(bozo_httpreq_t *request) if (*t++ == '\0') { (void)bozo_http_error(httpd, 400, request, "percent hack got a 0 back"); - goto copy_rest; + return 1; } while (*s && *s != '%') { @@ -1235,15 +1285,12 @@ fix_url_percent(bozo_httpreq_t *request) *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)); + + return 0; } /* @@ -1252,7 +1299,7 @@ copy_rest: * - punt if it doesn't start with / * - check httpd->untrustedref / referrer * - look for "http://myname/" and deal with it. - * - maybe call bozo_process_cgi() + * - 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 @@ -1268,11 +1315,14 @@ transform_request(bozo_httpreq_t *reques bozohttpd_t *httpd = request->hr_httpd; char *file, *newfile = NULL; size_t len; + const char *hostname = BOZOHOST(httpd, request); file = NULL; *isindex = 0; debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file)); - fix_url_percent(request); + if (fix_url_percent(request)) { + goto bad_done; + } if (check_virtual(request)) { goto bad_done; } @@ -1283,15 +1333,20 @@ transform_request(bozo_httpreq_t *reques goto bad_done; } - check_bzredirect(request); + switch(check_bzredirect(request)) { + case -1: + goto bad_done; + case 1: + return 0; + } if (httpd->untrustedref) { int to_indexhtml = 0; #define TOP_PAGE(x) (strcmp((x), "/") == 0 || \ strcmp((x) + 1, httpd->index_html) == 0 || \ - strcmp((x) + 1, "favicon.ico") == 0) - + strcmp((x) + 1, "favicon.ico") == 0) + debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref")); /* * first check that this path isn't allowed via .bzdirect file, @@ -1306,10 +1361,10 @@ transform_request(bozo_httpreq_t *reques debug((httpd, DEBUG_FAT, "checking referrer \"%s\" vs virthostname %s", - r, httpd->virthostname)); + r, hostname)); if (strncmp(r, "http://", 7) != 0 || - (strncasecmp(r + 7, httpd->virthostname, - strlen(httpd->virthostname)) != 0 && + (strncasecmp(r + 7, hostname, + strlen(hostname)) != 0 && !TOP_PAGE(file))) to_indexhtml = 1; } else { @@ -1318,8 +1373,8 @@ transform_request(bozo_httpreq_t *reques 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)) + (h && strncasecmp(h, hostname, + strlen(hostname)) != 0)) to_indexhtml = 1; } @@ -1386,7 +1441,7 @@ transform_request(bozo_httpreq_t *reques */ /* - * stop traversing outside our domain + * 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. @@ -1408,12 +1463,14 @@ transform_request(bozo_httpreq_t *reques if (bozo_process_cgi(request)) return 0; + if (bozo_process_lua(request)) + return 0; + debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); return 1; bad_done: debug((httpd, DEBUG_FAT, "transform_request returning: 0")); - if (newfile) - free(newfile); + free(newfile); return 0; } @@ -1506,15 +1563,21 @@ bozo_process_request(bozo_httpreq_t *req if (fd < 0) { debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); - if (errno == EPERM) + switch(errno) { + case EPERM: (void)bozo_http_error(httpd, 403, request, "no permission to open file"); - else if (errno == ENOENT) { - if (!bozo_dir_index(request, file, isindex)) + break; + case ENAMETOOLONG: + /*FALLTHROUGH*/ + case ENOENT: + if (!bozo_dir_index(request, file, isindex)) (void)bozo_http_error(httpd, 404, request, "no file"); - } else + break; + default: (void)bozo_http_error(httpd, 500, request, "open file"); + } goto cleanup_nofd; } if (fstat(fd, &sb) < 0) { @@ -1673,7 +1736,7 @@ debug__(bozohttpd_t *httpd, int level, c { va_list ap; int savederrno; - + /* only log if the level is low enough */ if (httpd->debug < level) return; @@ -1722,12 +1785,20 @@ bozo_err(bozohttpd_t *httpd, int code, c exit(code); } -/* this escape HTML tags */ -static void -escape_html(bozo_httpreq_t *request) +/* + * this escapes HTML tags. returns allocated escaped + * string if needed, or NULL on allocation failure or + * lack of escape need. + * call with NULL httpd in error paths, to avoid recursive + * malloc failure. call with valid httpd in normal paths + * to get automatic allocation failure handling. + */ +char * +bozo_escape_html(bozohttpd_t *httpd, const char *url) { int i, j; - char *url = request->hr_file, *tmp; + char *tmp; + size_t len; for (i = 0, j = 0; url[i]; i++) { switch (url[i]) { @@ -1742,16 +1813,17 @@ escape_html(bozo_httpreq_t *request) } if (j == 0) - return; + return NULL; - 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; + /* + * we need to handle being called from different + * pathnames. + */ + len = strlen(url) + j; + if (httpd) + tmp = bozomalloc(httpd, len); + else if ((tmp = malloc(len)) == 0) + return NULL; for (i = 0, j = 0; url[i]; i++) { switch (url[i]) { @@ -1773,8 +1845,7 @@ escape_html(bozo_httpreq_t *request) } tmp[j] = 0; - free(request->hr_file); - request->hr_file = tmp; + return tmp; } /* short map between error code, and short/long messages */ @@ -1847,15 +1918,21 @@ bozo_http_error(bozohttpd_t *httpd, int portbuf[0] = '\0'; if (request && request->hr_file) { - escape_html(request); + char *file = NULL; + const char *hostname = BOZOHOST(httpd, request); + + /* bozo_escape_html() failure here is just too bad. */ + file = bozo_escape_html(NULL, request->hr_file); + if (file == NULL) + file = request->hr_file; size = snprintf(httpd->errorbuf, BUFSIZ, "%s\n" "

%s

\n" "%s:
%s
\n" "
%s%s
\n" "\n", - header, header, request->hr_file, reason, - httpd->virthostname, portbuf, httpd->virthostname, portbuf); + header, header, file, reason, + hostname, portbuf, hostname, portbuf); if (size >= (int)BUFSIZ) { bozo_warn(httpd, "bozo_http_error buffer too small, truncated"); @@ -1874,7 +1951,9 @@ bozo_http_error(bozohttpd_t *httpd, int if (request && request->hr_allow) bozo_printf(httpd, "Allow: %s\r\n", request->hr_allow); bozo_printf(httpd, "\r\n"); - if (size) + /* According to the RFC 2616 sec. 9.4 HEAD method MUST NOT return a + * message-body in the response */ + if (size && request && request->hr_method != HTTP_HEAD) bozo_printf(httpd, "%s", httpd->errorbuf); bozo_flush(httpd, stdout); @@ -2055,6 +2134,9 @@ bozo_init_httpd(bozohttpd_t *httpd) "bozohttpd: memory_allocation failure\n"); return 0; } +#ifndef NO_LUA_SUPPORT + SIMPLEQ_INIT(&httpd->lua_states); +#endif return 1; }