version 1.3, 2007/10/17 18:48:00 |
version 1.3.2.3, 2008/03/23 00:41:24 |
|
|
/* $NetBSD$ */ |
/* bozohttpd.c,v 1.3.2.2 2007/11/06 23:12:02 matt Exp */ |
|
|
/* $eterna: bozohttpd.c,v 1.137 2006/05/17 08:37:36 mrg Exp $ */ |
/* $eterna: bozohttpd.c,v 1.142 2008/03/03 03:36:11 mrg Exp $ */ |
|
|
/* |
/* |
* Copyright (c) 1997-2006 Matthew R. Green |
* Copyright (c) 1997-2008 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 |
|
|
* |
* |
* - 14.5/14.16/14.35: we don't do ranges. from section 14.35.2 |
* - 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. |
* `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.9: we aren't a cache. |
* |
* |
|
|
#define INDEX_HTML "index.html" |
#define INDEX_HTML "index.html" |
#endif |
#endif |
#ifndef SERVER_SOFTWARE |
#ifndef SERVER_SOFTWARE |
#define SERVER_SOFTWARE "bozohttpd/20060517" |
#define SERVER_SOFTWARE "bozohttpd/20080303" |
#endif |
#endif |
#ifndef DIRECT_ACCESS_FILE |
#ifndef DIRECT_ACCESS_FILE |
#define DIRECT_ACCESS_FILE ".bzdirect" |
#define DIRECT_ACCESS_FILE ".bzdirect" |
Line 374 main(int argc, char **argv) |
|
Line 375 main(int argc, char **argv) |
|
break; |
break; |
#else /* NO_DAEMON_MODE */ |
#else /* NO_DAEMON_MODE */ |
case 'b': |
case 'b': |
|
case 'e': |
|
case 'f': |
case 'i': |
case 'i': |
case 'I': |
case 'I': |
error(1, "Daemon mode is not enabled"); |
error(1, "Daemon mode is not enabled"); |
Line 425 main(int argc, char **argv) |
|
Line 428 main(int argc, char **argv) |
|
uflag = 1; |
uflag = 1; |
break; |
break; |
#else |
#else |
case 'u': |
|
case 'p': |
case 'p': |
|
case 't': |
|
case 'u': |
error(1, "User support is not enabled"); |
error(1, "User support is not enabled"); |
/* NOTREACHED */ |
/* NOTREACHED */ |
#endif /* NO_USER_SUPPORT */ |
#endif /* NO_USER_SUPPORT */ |
Line 563 parse_request(char *in, char **method, c |
|
Line 567 parse_request(char *in, char **method, c |
|
*method = *url = *proto = NULL; /* set them up */ |
*method = *url = *proto = NULL; /* set them up */ |
|
|
len = (ssize_t)strlen(in); |
len = (ssize_t)strlen(in); |
val = strnsep(&in, " \t\n\r", &len); |
val = bozostrnsep(&in, " \t\n\r", &len); |
if (len < 1 || val == NULL) |
if (len < 1 || val == NULL) |
return; |
return; |
*method = val; |
*method = val; |
while (*in == ' ' || *in == '\t') |
while (*in == ' ' || *in == '\t') |
in++; |
in++; |
val = strnsep(&in, " \t\n\r", &len); |
val = bozostrnsep(&in, " \t\n\r", &len); |
if (len < 1) { |
if (len < 1) { |
if (len == 0) |
if (len == 0) |
*url = val; |
*url = val; |
Line 625 read_request(void) |
|
Line 629 read_request(void) |
|
memset(request, 0, sizeof *request); |
memset(request, 0, sizeof *request); |
request->hr_allow = request->hr_host = NULL; |
request->hr_allow = request->hr_host = NULL; |
request->hr_content_type = request->hr_content_length = NULL; |
request->hr_content_type = request->hr_content_length = NULL; |
|
request->hr_range = NULL; |
|
request->hr_last_byte_pos = -1; |
|
|
slen = sizeof(ss); |
slen = sizeof(ss); |
if (getpeername(0, (struct sockaddr *)&ss, &slen) < 0) |
if (getpeername(0, (struct sockaddr *)&ss, &slen) < 0) |
Line 668 read_request(void) |
|
Line 674 read_request(void) |
|
sigaction(SIGALRM, &sa, NULL); /* XXX */ |
sigaction(SIGALRM, &sa, NULL); /* XXX */ |
|
|
alarm(MAX_WAIT_TIME); |
alarm(MAX_WAIT_TIME); |
while ((str = dgetln(STDIN_FILENO, &len, bozoread)) != NULL) { |
while ((str = bozodgetln(STDIN_FILENO, &len, bozoread)) != NULL) { |
alarm(0); |
alarm(0); |
if (alarmhit) |
if (alarmhit) |
http_error(408, NULL, "request timed out"); |
http_error(408, NULL, "request timed out"); |
Line 709 read_request(void) |
|
Line 715 read_request(void) |
|
if (*str == '\0') |
if (*str == '\0') |
break; |
break; |
|
|
val = strnsep(&str, ":", &len); |
val = bozostrnsep(&str, ":", &len); |
debug((DEBUG_EXPLODING, |
debug((DEBUG_EXPLODING, |
"read_req2: after strnsep: str ``%s'' val ``%s''", |
"read_req2: after bozostrnsep: str ``%s'' val ``%s''", |
str, val)); |
str, val)); |
if (val == NULL || len == -1) |
if (val == NULL || len == -1) |
http_error(404, request, "no header"); |
http_error(404, request, "no header"); |
while (*str == ' ' || *str == '\t') |
while (*str == ' ' || *str == '\t') |
len--, str++; |
len--, str++; |
|
while (*val == ' ' || *val == '\t') |
|
val++; |
|
|
if (auth_check_headers(request, val, str, len)) |
if (auth_check_headers(request, val, str, len)) |
goto next_header; |
goto next_header; |
Line 735 read_request(void) |
|
Line 743 read_request(void) |
|
else if (strcasecmp(hdr->h_header, "referrer") == 0 || |
else if (strcasecmp(hdr->h_header, "referrer") == 0 || |
strcasecmp(hdr->h_header, "referer") == 0) |
strcasecmp(hdr->h_header, "referer") == 0) |
request->hr_referrer = hdr->h_value; |
request->hr_referrer = hdr->h_value; |
|
else if (strcasecmp(hdr->h_header, "range") == 0) |
|
request->hr_range = hdr->h_value; |
|
|
debug((DEBUG_FAT, "adding header %s: %s", |
debug((DEBUG_FAT, "adding header %s: %s", |
hdr->h_header, hdr->h_value)); |
hdr->h_header, hdr->h_value)); |
|
|
if (request->hr_proto == http_11 && request->hr_host == NULL) |
if (request->hr_proto == http_11 && request->hr_host == NULL) |
http_error(400, request, "missing Host header"); |
http_error(400, request, "missing Host header"); |
|
|
|
if (request->hr_range != NULL) { |
|
debug((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((DEBUG_FAT, "read_request returns url %s in request", request->hr_url)); |
debug((DEBUG_FAT, "read_request returns url %s in request", request->hr_url)); |
return (request); |
return (request); |
} |
} |
Line 847 process_request(http_req *request) |
|
Line 885 process_request(http_req *request) |
|
/* NOTREACHED */ |
/* NOTREACHED */ |
/* XXX RFC1945 10.9 If-Modified-Since (http code 304) */ |
/* XXX RFC1945 10.9 If-Modified-Since (http code 304) */ |
|
|
bozoprintf("%s 200 OK\r\n", request->hr_proto); |
/* 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((DEBUG_FAT, "have_range %d first_pos %qd last_pos %qd", |
|
request->hr_have_range, |
|
request->hr_first_byte_pos, request->hr_last_byte_pos)); |
|
if (request->hr_have_range) |
|
bozoprintf("%s 206 Partial Content\r\n", request->hr_proto); |
|
else |
|
bozoprintf("%s 200 OK\r\n", request->hr_proto); |
|
|
if (request->hr_proto != http_09) { |
if (request->hr_proto != http_09) { |
type = content_type(request, file); |
type = content_type(request, file); |
Line 861 process_request(http_req *request) |
|
Line 915 process_request(http_req *request) |
|
if (request->hr_method != HTTP_HEAD) { |
if (request->hr_method != HTTP_HEAD) { |
char *addr; |
char *addr; |
void *oaddr; |
void *oaddr; |
off_t sz = sb.st_size; |
size_t mappedsz; |
|
size_t sz; |
|
|
oaddr = addr = mmap(0, (size_t)sz, PROT_READ, |
sz = mappedsz = request->hr_last_byte_pos - request->hr_first_byte_pos + 1; |
MAP_SHARED, fd, 0); |
oaddr = addr = mmap(0, mappedsz, PROT_READ, |
|
MAP_SHARED, fd, request->hr_first_byte_pos); |
if (addr == (char *)-1) |
if (addr == (char *)-1) |
error(1, "mmap failed: %s", strerror(errno)); |
error(1, "mmap failed: %s", strerror(errno)); |
|
|
Line 874 process_request(http_req *request) |
|
Line 930 process_request(http_req *request) |
|
while (sz > WRSZ) { |
while (sz > WRSZ) { |
if (bozowrite(STDOUT_FILENO, addr, WRSZ) != WRSZ) |
if (bozowrite(STDOUT_FILENO, addr, WRSZ) != WRSZ) |
error(1, "write failed: %s", strerror(errno)); |
error(1, "write failed: %s", strerror(errno)); |
|
debug((DEBUG_OBESE, "wrote %d bytes", WRSZ)); |
sz -= WRSZ; |
sz -= WRSZ; |
addr += WRSZ; |
addr += WRSZ; |
} |
} |
if (sz && bozowrite(STDOUT_FILENO, addr, sz) != sz) |
if (sz && bozowrite(STDOUT_FILENO, addr, sz) != sz) |
error(1, "final write failed: %s", strerror(errno)); |
error(1, "final write failed: %s", strerror(errno)); |
if (munmap(oaddr, (size_t)sb.st_size) < 0) |
debug((DEBUG_OBESE, "wrote %d bytes", (int)sz)); |
|
if (munmap(oaddr, mappedsz) < 0) |
warning("munmap failed"); |
warning("munmap failed"); |
} |
} |
/* If SSL enabled cleanup SSL structure. */ |
/* If SSL enabled cleanup SSL structure. */ |
|
|
print_header(http_req *request, struct stat *sbp, const char *type, |
print_header(http_req *request, struct stat *sbp, const char *type, |
const char *encoding) |
const char *encoding) |
{ |
{ |
|
off_t len; |
|
|
bozoprintf("Date: %s\r\n", http_date()); |
bozoprintf("Date: %s\r\n", http_date()); |
bozoprintf("Server: %s\r\n", server_software); |
bozoprintf("Server: %s\r\n", server_software); |
|
bozoprintf("Accept-Ranges: bytes\r\n"); |
if (sbp) { |
if (sbp) { |
char filedate[40]; |
char filedate[40]; |
struct tm *tm; |
struct tm *tm; |
Line 1275 print_header(http_req *request, struct s |
|
Line 1336 print_header(http_req *request, struct s |
|
bozoprintf("Content-Type: %s\r\n", type); |
bozoprintf("Content-Type: %s\r\n", type); |
if (encoding && *encoding) |
if (encoding && *encoding) |
bozoprintf("Content-Encoding: %s\r\n", encoding); |
bozoprintf("Content-Encoding: %s\r\n", encoding); |
if (sbp) |
if (sbp) { |
bozoprintf("Content-Length: %qd\r\n", (long long)sbp->st_size); |
if (request->hr_have_range) { |
|
len = request->hr_last_byte_pos - request->hr_first_byte_pos +1; |
|
bozoprintf("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; |
|
bozoprintf("Content-Length: %qd\r\n", (long long)len); |
|
} |
if (request && request->hr_proto == http_11) |
if (request && request->hr_proto == http_11) |
bozoprintf("Connection: close\r\n"); |
bozoprintf("Connection: close\r\n"); |
bozoflush(stdout); |
bozoflush(stdout); |
Line 1334 escape_html(http_req *request) |
|
Line 1405 escape_html(http_req *request) |
|
tmp[j] = 0; |
tmp[j] = 0; |
|
|
/* |
/* |
* XXX original "url" is a substring of an allocation, so we |
* original "url" is a substring of an allocation, so we |
* can't touch it. so, ignore it and replace the request. |
* can't touch it. so, ignore it and replace the request. |
*/ |
*/ |
request->hr_url = tmp; |
request->hr_url = tmp; |
Line 1379 fix_url_percent(http_req *request) |
|
Line 1450 fix_url_percent(http_req *request) |
|
buf[2] = '\0'; |
buf[2] = '\0'; |
s++; |
s++; |
*t = (char)strtol(buf, NULL, 16); |
*t = (char)strtol(buf, NULL, 16); |
debug((DEBUG_EXPLODING, "fu_%%: strtol put %c into *t", *t)); |
debug((DEBUG_EXPLODING, "fu_%%: strtol put '%c' into *t", *t)); |
if (*t++ == '\0') |
if (*t++ == '\0') |
http_error(400, request, "percent hack got a 0 back"); |
http_error(400, request, "percent hack got a 0 back"); |
|
|
while (*s && *s != '%') { |
while (*s && *s != '%') { |
if (s >= end) |
if (end && s >= end) |
break; |
break; |
*t++ = *s++; |
*t++ = *s++; |
} |
} |
Line 1553 http_error(int code, http_req *request, |
|
Line 1624 http_error(int code, http_req *request, |
|
error(1, "http_error() failed (short = %p, long = %p)", |
error(1, "http_error() failed (short = %p, long = %p)", |
header, reason); |
header, reason); |
|
|
if (request && request->hr_serverport && strcmp(request->hr_serverport, "80") != 0) |
if (request && request->hr_serverport && |
|
strcmp(request->hr_serverport, "80") != 0) |
snprintf(portbuf, sizeof(portbuf), ":%s", request->hr_serverport); |
snprintf(portbuf, sizeof(portbuf), ":%s", request->hr_serverport); |
else |
else |
portbuf[0] = '\0'; |
portbuf[0] = '\0'; |
Line 1597 static struct errors_map { |
|
Line 1669 static struct errors_map { |
|
} errors_map[] = { |
} errors_map[] = { |
{ 400, "400 Bad Request", "The request was not valid", }, |
{ 400, "400 Bad Request", "The request was not valid", }, |
{ 401, "401 Unauthorized", "No authorization", }, |
{ 401, "401 Unauthorized", "No authorization", }, |
{ 403, "403 Forbidden", "Access to this item has been denied", }, |
{ 403, "403 Forbidden", "Access to this item has been denied",}, |
{ 404, "404 Not Found", "This item has not been found", }, |
{ 404, "404 Not Found", "This item has not been found", }, |
{ 408, "408 Request Timeout", "This request took too long", }, |
{ 408, "408 Request Timeout", "This request took too long", }, |
{ 417, "417 Expectation Failed","Expectations not available", }, |
{ 417, "417 Expectation Failed","Expectations not available", }, |
Line 1638 http_errors_long(int code) |
|
Line 1710 http_errors_long(int code) |
|
* correctly passed in. |
* correctly passed in. |
*/ |
*/ |
char * |
char * |
strnsep(char **strp, const char *delim, ssize_t *lenp) |
bozostrnsep(char **strp, const char *delim, ssize_t *lenp) |
{ |
{ |
char *s; |
char *s; |
const char *spanp; |
const char *spanp; |
Line 1672 strnsep(char **strp, const char *delim, |
|
Line 1744 strnsep(char **strp, const char *delim, |
|
* terminate the string. |
* terminate the string. |
*/ |
*/ |
char * |
char * |
dgetln(int fd, ssize_t *lenp, ssize_t (*readfn)(int, void *, size_t)) |
bozodgetln(int fd, ssize_t *lenp, ssize_t (*readfn)(int, void *, size_t)) |
{ |
{ |
static char *buffer; |
static char *buffer; |
static ssize_t buflen = 0; |
static ssize_t buflen = 0; |
Line 1698 dgetln(int fd, ssize_t *lenp, ssize_t (* |
|
Line 1770 dgetln(int fd, ssize_t *lenp, ssize_t (* |
|
* the program |
* the program |
*/ |
*/ |
for (; readfn(fd, &c, 1) == 1; ) { |
for (; readfn(fd, &c, 1) == 1; ) { |
debug((DEBUG_EXPLODING, "dgetln read %c", c)); |
debug((DEBUG_EXPLODING, "bozodgetln read %c", c)); |
|
|
if (len >= buflen - 1) { |
if (len >= buflen - 1) { |
buflen *= 2; |
buflen *= 2; |
debug((DEBUG_EXPLODING, |
debug((DEBUG_EXPLODING, "bozodgetln: " |
"dgetln: reallocating buffer to buflen %d", buflen)); |
"reallocating buffer to buflen %zu", buflen)); |
nbuffer = realloc(buffer, buflen); |
nbuffer = realloc(buffer, buflen); |
if (nbuffer == NULL) { |
if (nbuffer == NULL) { |
free(buffer); |
free(buffer); |
Line 1735 dgetln(int fd, ssize_t *lenp, ssize_t (* |
|
Line 1807 dgetln(int fd, ssize_t *lenp, ssize_t (* |
|
|
|
} |
} |
buffer[len] = '\0'; |
buffer[len] = '\0'; |
debug((DEBUG_OBESE, "dgetln returns: ``%s'' with len %d", buffer, len)); |
debug((DEBUG_OBESE, "bozodgetln returns: ``%s'' with len %d", |
|
buffer, len)); |
*lenp = len; |
*lenp = len; |
return (buffer); |
return (buffer); |
} |
} |