version 1.32.2.1, 2013/06/23 06:28:49 |
version 1.32.2.2, 2014/08/20 00:02:22 |
|
|
/* $eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg 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. |
* All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
|
|
|
|
/* |
/* |
* requirements for minimal http/1.1 (at least, as documented in |
* requirements for minimal http/1.1 (at least, as documented in |
* <draft-ietf-http-v11-spec-rev-06> 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] |
* thus we could just not send it ever. [1] |
* |
* |
* - 14.17: content-type handling. [1] |
* - 14.17: content-type handling. [1] |
* |
* |
* - 14.25/28: if-{,un}modified-since handling. maybe do this, but |
* - 14.28: if-unmodified-since handling. if-modified-since is |
* i really don't want to have to parse 3 differnet date formats |
* done since, shouldn't be too hard for this one. |
* |
* |
* [1] need to revisit to ensure proper behaviour |
* [1] need to revisit to ensure proper behaviour |
* |
* |
|
|
* |
* |
* - 10.3.3/10.3.4/10.3.8: just use '302' codes always. |
* - 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 |
* just ignore them and send the request anyway. they are |
* only SHOULD. |
* only SHOULD. |
* |
* |
* - 14.5/14.16/14.35: we don't do ranges. from section 14.35.2 |
* - 14.5/14.16/14.35: only support simple ranges: %d- and %d-%d |
* `A server MAY ignore the Range header'. but it might be nice. |
* would be nice to support more. |
* since 20080301 we support simple range headers. |
|
* |
* |
* - 14.9: we aren't a cache. |
* - 14.9: we aren't a cache. |
* |
* |
* - 14.15: content-md5 would be nice... |
* - 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. |
* - 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 |
#ifndef INDEX_HTML |
#define INDEX_HTML "index.html" |
#define INDEX_HTML "index.html" |
#endif |
#endif |
#ifndef SERVER_SOFTWARE |
#ifndef SERVER_SOFTWARE |
#define SERVER_SOFTWARE "bozohttpd/20111118" |
#define SERVER_SOFTWARE "bozohttpd/20140717" |
#endif |
#endif |
#ifndef DIRECT_ACCESS_FILE |
#ifndef DIRECT_ACCESS_FILE |
#define DIRECT_ACCESS_FILE ".bzdirect" |
#define DIRECT_ACCESS_FILE ".bzdirect" |
Line 333 bozo_clean_request(bozo_httpreq_t *reque |
|
Line 333 bozo_clean_request(bozo_httpreq_t *reque |
|
bozo_ssl_destroy(request->hr_httpd); |
bozo_ssl_destroy(request->hr_httpd); |
|
|
/* clean up request */ |
/* clean up request */ |
#define MF(x) if (request->x) free(request->x) |
free(request->hr_remotehost); |
MF(hr_remotehost); |
free(request->hr_remoteaddr); |
MF(hr_remoteaddr); |
free(request->hr_serverport); |
MF(hr_serverport); |
free(request->hr_virthostname); |
MF(hr_file); |
free(request->hr_file); |
MF(hr_oldfile); |
free(request->hr_oldfile); |
MF(hr_query); |
free(request->hr_query); |
#undef MF |
free(request->hr_host); |
bozo_auth_cleanup(request); |
bozo_auth_cleanup(request); |
for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr; |
for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr; |
hdr = SIMPLEQ_NEXT(hdr, h_next)) { |
hdr = SIMPLEQ_NEXT(hdr, h_next)) { |
free(hdr->h_value); |
free(hdr->h_value); |
free(hdr->h_header); |
free(hdr->h_header); |
if (ohdr) |
free(ohdr); |
free(ohdr); |
|
ohdr = hdr; |
ohdr = hdr; |
} |
} |
if (ohdr) |
free(ohdr); |
free(ohdr); |
|
|
|
free(request); |
free(request); |
} |
} |
Line 529 bozo_read_request(bozohttpd_t *httpd) |
|
Line 527 bozo_read_request(bozohttpd_t *httpd) |
|
*/ |
*/ |
if (bozo_daemon_fork(httpd)) |
if (bozo_daemon_fork(httpd)) |
return NULL; |
return NULL; |
bozo_ssl_accept(httpd); |
if (bozo_ssl_accept(httpd)) |
|
return NULL; |
|
|
request = bozomalloc(httpd, sizeof(*request)); |
request = bozomalloc(httpd, sizeof(*request)); |
memset(request, 0, sizeof(*request)); |
memset(request, 0, sizeof(*request)); |
Line 539 bozo_read_request(bozohttpd_t *httpd) |
|
Line 538 bozo_read_request(bozohttpd_t *httpd) |
|
request->hr_range = NULL; |
request->hr_range = NULL; |
request->hr_last_byte_pos = -1; |
request->hr_last_byte_pos = -1; |
request->hr_if_modified_since = NULL; |
request->hr_if_modified_since = NULL; |
|
request->hr_virthostname = NULL; |
request->hr_file = NULL; |
request->hr_file = NULL; |
request->hr_oldfile = NULL; |
request->hr_oldfile = NULL; |
|
|
Line 681 bozo_read_request(bozohttpd_t *httpd) |
|
Line 681 bozo_read_request(bozohttpd_t *httpd) |
|
else if (strcasecmp(hdr->h_header, "content-length") == 0) |
else if (strcasecmp(hdr->h_header, "content-length") == 0) |
request->hr_content_length = hdr->h_value; |
request->hr_content_length = hdr->h_value; |
else if (strcasecmp(hdr->h_header, "host") == 0) |
else if (strcasecmp(hdr->h_header, "host") == 0) |
request->hr_host = hdr->h_value; |
request->hr_host = bozostrdup(httpd, hdr->h_value); |
/* HTTP/1.1 rev06 draft spec: 14.20 */ |
/* RFC 2616 (HTTP/1.1): 14.20 */ |
else if (strcasecmp(hdr->h_header, "expect") == 0) { |
else if (strcasecmp(hdr->h_header, "expect") == 0) { |
(void)bozo_http_error(httpd, 417, request, |
(void)bozo_http_error(httpd, 417, request, |
"we don't support Expect:"); |
"we don't support Expect:"); |
|
|
goto cleanup; |
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 && |
if (request->hr_proto == httpd->consts.http_11 && |
|
/*(strncasecmp(request->hr_file, "http://", 7) != 0) &&*/ |
request->hr_host == NULL) { |
request->hr_host == NULL) { |
(void)bozo_http_error(httpd, 400, request, |
(void)bozo_http_error(httpd, 400, request, |
"missing Host header"); |
"missing Host header"); |
Line 852 parse_http_date(const char *val, time_t |
|
Line 853 parse_http_date(const char *val, time_t |
|
* to be updated for any sort of parallel processing. |
* to be updated for any sort of parallel processing. |
*/ |
*/ |
char * |
char * |
escape_rfc3986(bozohttpd_t *httpd, const char *url) |
bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url) |
{ |
{ |
static char *buf; |
static char *buf; |
static size_t buflen = 0; |
static size_t buflen = 0; |
Line 865 escape_rfc3986(bozohttpd_t *httpd, const |
|
Line 866 escape_rfc3986(bozohttpd_t *httpd, const |
|
buflen = len * 3 + 1; |
buflen = len * 3 + 1; |
buf = bozorealloc(httpd, buf, buflen); |
buf = bozorealloc(httpd, buf, buflen); |
} |
} |
|
|
if (url == NULL) { |
if (url == NULL) { |
buf[0] = 0; |
buf[0] = 0; |
return buf; |
return buf; |
Line 922 check_direct_access(bozo_httpreq_t *requ |
|
Line 923 check_direct_access(bozo_httpreq_t *requ |
|
char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename; |
char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename; |
|
|
snprintf(dir, sizeof(dir), "%s", request->hr_file + 1); |
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, '/'); |
basename = strrchr(dir, '/'); |
|
|
if ((!basename || basename[1] != '\0') && |
if ((!basename || basename[1] != '\0') && |
Line 935 check_direct_access(bozo_httpreq_t *requ |
|
Line 936 check_direct_access(bozo_httpreq_t *requ |
|
bozo_check_special_files(request, basename); |
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 || |
if (stat(dirfile, &sb) < 0 || |
(fp = fopen(dirfile, "r")) == NULL) |
(fp = fopen(dirfile, "r")) == NULL) |
return 0; |
return 0; |
Line 954 handle_redirect(bozo_httpreq_t *request, |
|
Line 960 handle_redirect(bozo_httpreq_t *request, |
|
bozohttpd_t *httpd = request->hr_httpd; |
bozohttpd_t *httpd = request->hr_httpd; |
char *urlbuf; |
char *urlbuf; |
char portbuf[20]; |
char portbuf[20]; |
|
const char *hostname = BOZOHOST(httpd, request); |
int query = 0; |
int query = 0; |
|
|
if (url == NULL) { |
if (url == NULL) { |
if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0) |
if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0) |
bozo_err(httpd, 1, "asprintf"); |
bozo_err(httpd, 1, "asprintf"); |
url = urlbuf; |
url = urlbuf; |
} else |
} else |
urlbuf = NULL; |
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)) |
if (request->hr_query && strlen(request->hr_query)) |
query = 1; |
query = 1; |
Line 972 handle_redirect(bozo_httpreq_t *request, |
|
Line 979 handle_redirect(bozo_httpreq_t *request, |
|
request->hr_serverport); |
request->hr_serverport); |
else |
else |
portbuf[0] = '\0'; |
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)); |
debug((httpd, DEBUG_FAT, "redirecting %s", url)); |
bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto); |
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); |
bozo_print_header(request, NULL, "text/html", NULL); |
if (request->hr_proto != httpd->consts.http_09) { |
if (request->hr_proto != httpd->consts.http_09) { |
bozo_printf(httpd, "Location: http://"); |
bozo_printf(httpd, "Location: http://"); |
if (absolute == 0) |
if (absolute == 0) |
bozo_printf(httpd, "%s%s", httpd->virthostname, portbuf); |
bozo_printf(httpd, "%s%s", hostname, portbuf); |
if (query) { |
if (query) { |
bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query); |
bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query); |
} else { |
} else { |
Line 997 handle_redirect(bozo_httpreq_t *request, |
|
Line 1007 handle_redirect(bozo_httpreq_t *request, |
|
if (absolute) |
if (absolute) |
bozo_printf(httpd, "%s?%s", url, request->hr_query); |
bozo_printf(httpd, "%s?%s", url, request->hr_query); |
else |
else |
bozo_printf(httpd, "%s%s%s?%s", httpd->virthostname, |
bozo_printf(httpd, "%s%s%s?%s", hostname, |
portbuf, url, request->hr_query); |
portbuf, url, request->hr_query); |
} else { |
} else { |
if (absolute) |
if (absolute) |
bozo_printf(httpd, "%s", url); |
bozo_printf(httpd, "%s", url); |
else |
else |
bozo_printf(httpd, "%s%s%s", httpd->virthostname, |
bozo_printf(httpd, "%s%s%s", hostname, |
portbuf, url); |
portbuf, url); |
} |
} |
bozo_printf(httpd, "\">here</a>\n"); |
bozo_printf(httpd, "\">here</a>\n"); |
bozo_printf(httpd, "</body></html>\n"); |
bozo_printf(httpd, "</body></html>\n"); |
head: |
head: |
bozo_flush(httpd, stdout); |
bozo_flush(httpd, stdout); |
if (urlbuf) |
free(urlbuf); |
free(urlbuf); |
|
} |
} |
|
|
/* |
/* |
Line 1039 check_virtual(bozo_httpreq_t *request) |
|
Line 1048 check_virtual(bozo_httpreq_t *request) |
|
if (strncasecmp(file, "http://", 7) == 0) { |
if (strncasecmp(file, "http://", 7) == 0) { |
/* we would do virtual hosting here? */ |
/* we would do virtual hosting here? */ |
file += 7; |
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, '/'); |
s = strchr(file, '/'); |
/* HTTP/1.1 draft rev-06, 5.2: URI takes precedence over Host: */ |
free(request->hr_file); |
request->hr_host = file; |
|
request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/"); |
request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/"); |
debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''", |
debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''", |
request->hr_host, request->hr_file)); |
request->hr_host, request->hr_file)); |
Line 1049 check_virtual(bozo_httpreq_t *request) |
|
Line 1062 check_virtual(bozo_httpreq_t *request) |
|
goto use_slashdir; |
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. |
* insensitive match for the virtual host we are asked for. |
* note that if the virtual host is the same as the master, |
* note that if the virtual host is the same as the master, |
* we don't need to do anything special. |
* we don't need to do anything special. |
*/ |
*/ |
len = strlen(request->hr_host); |
|
debug((httpd, DEBUG_OBESE, |
debug((httpd, DEBUG_OBESE, |
"check_virtual: checking host `%s' under httpd->virtbase `%s' " |
"check_virtual: checking host `%s' under httpd->virtbase `%s' " |
"for file `%s'", |
"for file `%s'", |
Line 1076 check_virtual(bozo_httpreq_t *request) |
|
Line 1097 check_virtual(bozo_httpreq_t *request) |
|
len) == 0) { |
len) == 0) { |
/* found it, punch it */ |
/* found it, punch it */ |
debug((httpd, DEBUG_OBESE, "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, |
if (asprintf(&s, "%s/%s", httpd->virtbase, |
httpd->virthostname) < 0) |
request->hr_virthostname) < 0) |
bozo_err(httpd, 1, "asprintf"); |
bozo_err(httpd, 1, "asprintf"); |
break; |
break; |
} |
} |
|
|
|
|
/* |
/* |
* checks to see if this request has a valid .bzredirect file. returns |
* 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) |
check_bzredirect(bozo_httpreq_t *request) |
{ |
{ |
struct stat sb; |
struct stat sb; |
char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1]; |
char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1], |
|
path[MAXPATHLEN]; |
char *basename, *finalredir; |
char *basename, *finalredir; |
int rv, absolute; |
int rv, absolute; |
|
|
Line 1124 check_bzredirect(bozo_httpreq_t *request |
|
Line 1148 check_bzredirect(bozo_httpreq_t *request |
|
* if this pathname is really a directory, but doesn't end in /, |
* if this pathname is really a directory, but doesn't end in /, |
* use it as the directory to look for the redir file. |
* 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)); |
debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); |
basename = strrchr(dir, '/'); |
basename = strrchr(dir, '/'); |
|
|
Line 1138 check_bzredirect(bozo_httpreq_t *request |
|
Line 1167 check_bzredirect(bozo_httpreq_t *request |
|
bozo_check_special_files(request, basename); |
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 (lstat(redir, &sb) == 0) { |
if (!S_ISLNK(sb.st_mode)) |
if (!S_ISLNK(sb.st_mode)) |
return; |
return 0; |
absolute = 0; |
absolute = 0; |
} else { |
} 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)) |
if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode)) |
return; |
return 0; |
absolute = 1; |
absolute = 1; |
} |
} |
debug((request->hr_httpd, DEBUG_FAT, |
debug((request->hr_httpd, DEBUG_FAT, |
Line 1154 check_bzredirect(bozo_httpreq_t *request |
|
Line 1193 check_bzredirect(bozo_httpreq_t *request |
|
rv = readlink(redir, redirpath, sizeof redirpath - 1); |
rv = readlink(redir, redirpath, sizeof redirpath - 1); |
if (rv == -1 || rv == 0) { |
if (rv == -1 || rv == 0) { |
debug((request->hr_httpd, DEBUG_FAT, "readlink failed")); |
debug((request->hr_httpd, DEBUG_FAT, "readlink failed")); |
return; |
return 0; |
} |
} |
redirpath[rv] = '\0'; |
redirpath[rv] = '\0'; |
debug((request->hr_httpd, DEBUG_FAT, |
debug((request->hr_httpd, DEBUG_FAT, |
"readlink returned \"%s\"", redirpath)); |
"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 */ |
/* now we have the link pointer, redirect to the real place */ |
if (absolute) |
if (absolute) |
finalredir = redirpath; |
finalredir = redirpath; |
else |
else { |
snprintf(finalredir = redir, sizeof(redir), "/%s/%s", dir, |
if ((size_t)snprintf(finalredir = redir, sizeof(redir), "/%s/%s", |
redirpath); |
dir, redirpath) >= sizeof(redir)) { |
|
bozo_http_error(request->hr_httpd, 404, request, |
|
"redirect path too long"); |
|
return -1; |
|
} |
|
} |
|
|
debug((request->hr_httpd, DEBUG_FAT, |
debug((request->hr_httpd, DEBUG_FAT, |
"check_bzredirect: new redir %s", finalredir)); |
"check_bzredirect: new redir %s", finalredir)); |
handle_redirect(request, finalredir, absolute); |
handle_redirect(request, finalredir, absolute); |
|
return 1; |
} |
} |
|
|
/* this fixes the %HH hack that RFC2396 requires. */ |
/* this fixes the %HH hack that RFC2396 requires. */ |
static void |
static int |
fix_url_percent(bozo_httpreq_t *request) |
fix_url_percent(bozo_httpreq_t *request) |
{ |
{ |
bozohttpd_t *httpd = request->hr_httpd; |
bozohttpd_t *httpd = request->hr_httpd; |
Line 1186 fix_url_percent(bozo_httpreq_t *request) |
|
Line 1236 fix_url_percent(bozo_httpreq_t *request) |
|
|
|
/* fast forward to the first % */ |
/* fast forward to the first % */ |
if ((s = strchr(url, '%')) == NULL) |
if ((s = strchr(url, '%')) == NULL) |
return; |
return 0; |
|
|
t = s; |
t = s; |
do { |
do { |
Line 1203 fix_url_percent(bozo_httpreq_t *request) |
|
Line 1253 fix_url_percent(bozo_httpreq_t *request) |
|
if (s[1] == '\0' || s[2] == '\0') { |
if (s[1] == '\0' || s[2] == '\0') { |
(void)bozo_http_error(httpd, 400, request, |
(void)bozo_http_error(httpd, 400, request, |
"percent hack missing two chars afterwards"); |
"percent hack missing two chars afterwards"); |
goto copy_rest; |
return 1; |
} |
} |
if (s[1] == '0' && s[2] == '0') { |
if (s[1] == '0' && s[2] == '0') { |
(void)bozo_http_error(httpd, 404, request, |
(void)bozo_http_error(httpd, 404, request, |
"percent hack was %00"); |
"percent hack was %00"); |
goto copy_rest; |
return 1; |
} |
} |
if (s[1] == '2' && s[2] == 'f') { |
if (s[1] == '2' && s[2] == 'f') { |
(void)bozo_http_error(httpd, 404, request, |
(void)bozo_http_error(httpd, 404, request, |
"percent hack was %2f (/)"); |
"percent hack was %2f (/)"); |
goto copy_rest; |
return 1; |
} |
} |
|
|
buf[0] = *++s; |
buf[0] = *++s; |
buf[1] = *++s; |
buf[1] = *++s; |
buf[2] = '\0'; |
buf[2] = '\0'; |
Line 1226 fix_url_percent(bozo_httpreq_t *request) |
|
Line 1276 fix_url_percent(bozo_httpreq_t *request) |
|
if (*t++ == '\0') { |
if (*t++ == '\0') { |
(void)bozo_http_error(httpd, 400, request, |
(void)bozo_http_error(httpd, 400, request, |
"percent hack got a 0 back"); |
"percent hack got a 0 back"); |
goto copy_rest; |
return 1; |
} |
} |
|
|
while (*s && *s != '%') { |
while (*s && *s != '%') { |
Line 1235 fix_url_percent(bozo_httpreq_t *request) |
|
Line 1285 fix_url_percent(bozo_httpreq_t *request) |
|
*t++ = *s++; |
*t++ = *s++; |
} |
} |
} while (*s); |
} while (*s); |
copy_rest: |
|
while (*s) { |
|
if (s >= end) |
|
break; |
|
*t++ = *s++; |
|
} |
|
*t = '\0'; |
*t = '\0'; |
|
|
debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url", |
debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url", |
request->hr_file)); |
request->hr_file)); |
|
|
|
return 0; |
} |
} |
|
|
/* |
/* |
|
|
* - punt if it doesn't start with / |
* - punt if it doesn't start with / |
* - check httpd->untrustedref / referrer |
* - check httpd->untrustedref / referrer |
* - look for "http://myname/" and deal with it. |
* - 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 |
* - check for ~user and call bozo_user_transform() if so |
* - if the length > 1, check for trailing slash. if so, |
* - if the length > 1, check for trailing slash. if so, |
* add the index.html file |
* add the index.html file |
Line 1268 transform_request(bozo_httpreq_t *reques |
|
Line 1315 transform_request(bozo_httpreq_t *reques |
|
bozohttpd_t *httpd = request->hr_httpd; |
bozohttpd_t *httpd = request->hr_httpd; |
char *file, *newfile = NULL; |
char *file, *newfile = NULL; |
size_t len; |
size_t len; |
|
const char *hostname = BOZOHOST(httpd, request); |
|
|
file = NULL; |
file = NULL; |
*isindex = 0; |
*isindex = 0; |
debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file)); |
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)) { |
if (check_virtual(request)) { |
goto bad_done; |
goto bad_done; |
} |
} |
Line 1283 transform_request(bozo_httpreq_t *reques |
|
Line 1333 transform_request(bozo_httpreq_t *reques |
|
goto bad_done; |
goto bad_done; |
} |
} |
|
|
check_bzredirect(request); |
switch(check_bzredirect(request)) { |
|
case -1: |
|
goto bad_done; |
|
case 1: |
|
return 0; |
|
} |
|
|
if (httpd->untrustedref) { |
if (httpd->untrustedref) { |
int to_indexhtml = 0; |
int to_indexhtml = 0; |
|
|
#define TOP_PAGE(x) (strcmp((x), "/") == 0 || \ |
#define TOP_PAGE(x) (strcmp((x), "/") == 0 || \ |
strcmp((x) + 1, httpd->index_html) == 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")); |
debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref")); |
/* |
/* |
* first check that this path isn't allowed via .bzdirect file, |
* first check that this path isn't allowed via .bzdirect file, |
Line 1306 transform_request(bozo_httpreq_t *reques |
|
Line 1361 transform_request(bozo_httpreq_t *reques |
|
|
|
debug((httpd, DEBUG_FAT, |
debug((httpd, DEBUG_FAT, |
"checking referrer \"%s\" vs virthostname %s", |
"checking referrer \"%s\" vs virthostname %s", |
r, httpd->virthostname)); |
r, hostname)); |
if (strncmp(r, "http://", 7) != 0 || |
if (strncmp(r, "http://", 7) != 0 || |
(strncasecmp(r + 7, httpd->virthostname, |
(strncasecmp(r + 7, hostname, |
strlen(httpd->virthostname)) != 0 && |
strlen(hostname)) != 0 && |
!TOP_PAGE(file))) |
!TOP_PAGE(file))) |
to_indexhtml = 1; |
to_indexhtml = 1; |
} else { |
} else { |
Line 1318 transform_request(bozo_httpreq_t *reques |
|
Line 1373 transform_request(bozo_httpreq_t *reques |
|
debug((httpd, DEBUG_FAT, "url has no referrer at all")); |
debug((httpd, DEBUG_FAT, "url has no referrer at all")); |
/* if there's no referrer, let / or /index.html past */ |
/* if there's no referrer, let / or /index.html past */ |
if (!TOP_PAGE(file) || |
if (!TOP_PAGE(file) || |
(h && strncasecmp(h, httpd->virthostname, |
(h && strncasecmp(h, hostname, |
strlen(httpd->virthostname)) != 0)) |
strlen(hostname)) != 0)) |
to_indexhtml = 1; |
to_indexhtml = 1; |
} |
} |
|
|
Line 1386 transform_request(bozo_httpreq_t *reques |
|
Line 1441 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) |
* XXX true security only comes from our parent using chroot(2) |
* before execve(2)'ing us. or our own built in chroot(2) support. |
* before execve(2)'ing us. or our own built in chroot(2) support. |
Line 1408 transform_request(bozo_httpreq_t *reques |
|
Line 1463 transform_request(bozo_httpreq_t *reques |
|
if (bozo_process_cgi(request)) |
if (bozo_process_cgi(request)) |
return 0; |
return 0; |
|
|
|
if (bozo_process_lua(request)) |
|
return 0; |
|
|
debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); |
debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); |
return 1; |
return 1; |
bad_done: |
bad_done: |
debug((httpd, DEBUG_FAT, "transform_request returning: 0")); |
debug((httpd, DEBUG_FAT, "transform_request returning: 0")); |
if (newfile) |
free(newfile); |
free(newfile); |
|
return 0; |
return 0; |
} |
} |
|
|
Line 1506 bozo_process_request(bozo_httpreq_t *req |
|
Line 1563 bozo_process_request(bozo_httpreq_t *req |
|
|
|
if (fd < 0) { |
if (fd < 0) { |
debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); |
debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); |
if (errno == EPERM) |
switch(errno) { |
|
case EPERM: |
(void)bozo_http_error(httpd, 403, request, |
(void)bozo_http_error(httpd, 403, request, |
"no permission to open file"); |
"no permission to open file"); |
else if (errno == ENOENT) { |
break; |
if (!bozo_dir_index(request, file, isindex)) |
case ENAMETOOLONG: |
|
/*FALLTHROUGH*/ |
|
case ENOENT: |
|
if (!bozo_dir_index(request, file, isindex)) |
(void)bozo_http_error(httpd, 404, request, |
(void)bozo_http_error(httpd, 404, request, |
"no file"); |
"no file"); |
} else |
break; |
|
default: |
(void)bozo_http_error(httpd, 500, request, "open file"); |
(void)bozo_http_error(httpd, 500, request, "open file"); |
|
} |
goto cleanup_nofd; |
goto cleanup_nofd; |
} |
} |
if (fstat(fd, &sb) < 0) { |
if (fstat(fd, &sb) < 0) { |
Line 1673 debug__(bozohttpd_t *httpd, int level, c |
|
Line 1736 debug__(bozohttpd_t *httpd, int level, c |
|
{ |
{ |
va_list ap; |
va_list ap; |
int savederrno; |
int savederrno; |
|
|
/* only log if the level is low enough */ |
/* only log if the level is low enough */ |
if (httpd->debug < level) |
if (httpd->debug < level) |
return; |
return; |
Line 1722 bozo_err(bozohttpd_t *httpd, int code, c |
|
Line 1785 bozo_err(bozohttpd_t *httpd, int code, c |
|
exit(code); |
exit(code); |
} |
} |
|
|
/* this escape HTML tags */ |
/* |
static void |
* this escapes HTML tags. returns allocated escaped |
escape_html(bozo_httpreq_t *request) |
* 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; |
int i, j; |
char *url = request->hr_file, *tmp; |
char *tmp; |
|
size_t len; |
|
|
for (i = 0, j = 0; url[i]; i++) { |
for (i = 0, j = 0; url[i]; i++) { |
switch (url[i]) { |
switch (url[i]) { |
Line 1742 escape_html(bozo_httpreq_t *request) |
|
Line 1813 escape_html(bozo_httpreq_t *request) |
|
} |
} |
|
|
if (j == 0) |
if (j == 0) |
return; |
return NULL; |
|
|
if ((tmp = (char *) malloc(strlen(url) + j)) == 0) |
/* |
/* |
* we need to handle being called from different |
* ouch, but we are only called from an error context, and |
* pathnames. |
* most paths here come from malloc(3) failures anyway... |
*/ |
* we could completely punt and just exit, but isn't returning |
len = strlen(url) + j; |
* an not-quite-correct error better than nothing at all? |
if (httpd) |
*/ |
tmp = bozomalloc(httpd, len); |
return; |
else if ((tmp = malloc(len)) == 0) |
|
return NULL; |
|
|
for (i = 0, j = 0; url[i]; i++) { |
for (i = 0, j = 0; url[i]; i++) { |
switch (url[i]) { |
switch (url[i]) { |
Line 1773 escape_html(bozo_httpreq_t *request) |
|
Line 1845 escape_html(bozo_httpreq_t *request) |
|
} |
} |
tmp[j] = 0; |
tmp[j] = 0; |
|
|
free(request->hr_file); |
return tmp; |
request->hr_file = tmp; |
|
} |
} |
|
|
/* short map between error code, and short/long messages */ |
/* short map between error code, and short/long messages */ |
Line 1847 bozo_http_error(bozohttpd_t *httpd, int |
|
Line 1918 bozo_http_error(bozohttpd_t *httpd, int |
|
portbuf[0] = '\0'; |
portbuf[0] = '\0'; |
|
|
if (request && request->hr_file) { |
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, |
size = snprintf(httpd->errorbuf, BUFSIZ, |
"<html><head><title>%s</title></head>\n" |
"<html><head><title>%s</title></head>\n" |
"<body><h1>%s</h1>\n" |
"<body><h1>%s</h1>\n" |
"%s: <pre>%s</pre>\n" |
"%s: <pre>%s</pre>\n" |
"<hr><address><a href=\"http://%s%s/\">%s%s</a></address>\n" |
"<hr><address><a href=\"http://%s%s/\">%s%s</a></address>\n" |
"</body></html>\n", |
"</body></html>\n", |
header, header, request->hr_file, reason, |
header, header, file, reason, |
httpd->virthostname, portbuf, httpd->virthostname, portbuf); |
hostname, portbuf, hostname, portbuf); |
if (size >= (int)BUFSIZ) { |
if (size >= (int)BUFSIZ) { |
bozo_warn(httpd, |
bozo_warn(httpd, |
"bozo_http_error buffer too small, truncated"); |
"bozo_http_error buffer too small, truncated"); |
Line 1874 bozo_http_error(bozohttpd_t *httpd, int |
|
Line 1951 bozo_http_error(bozohttpd_t *httpd, int |
|
if (request && request->hr_allow) |
if (request && request->hr_allow) |
bozo_printf(httpd, "Allow: %s\r\n", request->hr_allow); |
bozo_printf(httpd, "Allow: %s\r\n", request->hr_allow); |
bozo_printf(httpd, "\r\n"); |
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_printf(httpd, "%s", httpd->errorbuf); |
bozo_flush(httpd, stdout); |
bozo_flush(httpd, stdout); |
|
|
Line 2055 bozo_init_httpd(bozohttpd_t *httpd) |
|
Line 2134 bozo_init_httpd(bozohttpd_t *httpd) |
|
"bozohttpd: memory_allocation failure\n"); |
"bozohttpd: memory_allocation failure\n"); |
return 0; |
return 0; |
} |
} |
|
#ifndef NO_LUA_SUPPORT |
|
SIMPLEQ_INIT(&httpd->lua_states); |
|
#endif |
return 1; |
return 1; |
} |
} |
|
|