diff --git a/configure.in b/configure.in index f7ee7894..11db62d6 100644 --- a/configure.in +++ b/configure.in @@ -380,6 +380,7 @@ AC_CONFIG_FILES([Makefile debian/Makefile src/Makefile doc/Makefile tests/Makefi tests/docroot/www/Makefile \ tests/docroot/www/go/Makefile \ tests/docroot/www/indexfile/Makefile \ + tests/docroot/www/expire/Makefile \ lighttpd.spec distribute.sh cygwin/Makefile cygwin/lighttpd.README openwrt/Makefile openwrt/control openwrt/lighttpd.mk]) AC_OUTPUT diff --git a/src/request.c b/src/request.c index 22bfff76..235c3f63 100644 --- a/src/request.c +++ b/src/request.c @@ -732,6 +732,20 @@ int http_request_parse(server *srv, connection *con) { unsigned long int r; size_t j; + if (con_length_set) { + con->http_status = 400; + con->keep_alive = 0; + + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Content-Length-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + if (ds->value->used == 0) SEGFAULT(); for (j = 0; j < ds->value->used - 1; j++) { @@ -764,7 +778,22 @@ int http_request_parse(server *srv, connection *con) { return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) { - con->request.http_content_type = ds->value->ptr; + /* if dup, only the first one will survive */ + if (!con->request.http_content_type) { + con->request.http_content_type = ds->value->ptr; + } else { + con->http_status = 400; + con->keep_alive = 0; + + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Content-Type-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) { /* HTTP 2616 8.2.3 * Expect: 100-continue @@ -781,18 +810,77 @@ int http_request_parse(server *srv, connection *con) { array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) { - con->request.http_host = ds->value; - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) { - con->request.http_if_modified_since = ds->value->ptr; - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) { - con->request.http_if_none_match = ds->value->ptr; - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) { - /* bytes=.*-.* */ - - if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) && - NULL != strchr(ds->value->ptr+6, '-')) { + if (!con->request.http_host) { + con->request.http_host = ds->value; + } else { + con->http_status = 400; + con->keep_alive = 0; - con->request.http_range = ds->value->ptr + 6; + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Host-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) { + /* if dup, only the first one will survive */ + if (!con->request.http_if_modified_since) { + con->request.http_if_modified_since = ds->value->ptr; + } else { + con->http_status = 400; + con->keep_alive = 0; + + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate If-Modified-Since header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) { + /* if dup, only the first one will survive */ + if (!con->request.http_if_none_match) { + con->request.http_if_none_match = ds->value->ptr; + } else { + con->http_status = 400; + con->keep_alive = 0; + + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate If-None-Match-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) { + if (!con->request.http_range) { + /* bytes=.*-.* */ + + if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) && + NULL != strchr(ds->value->ptr+6, '-')) { + + /* if dup, only the first one will survive */ + con->request.http_range = ds->value->ptr + 6; + } + } else { + con->http_status = 400; + con->keep_alive = 0; + + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Host-header -> 400"); + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; } } diff --git a/tests/run-tests.pl b/tests/run-tests.pl index ea427d4d..66e58236 100755 --- a/tests/run-tests.pl +++ b/tests/run-tests.pl @@ -2,7 +2,7 @@ use strict; use IO::Socket; -use Test::More tests => 114; +use Test::More tests => 120; my $testname; @@ -510,6 +510,12 @@ EOF ok(handle_http == 0, 'Host missing'); + + + + + + print "\nLow-Level Response-Header Parsing - Content-Length:\n"; @request = ( < 'HTTP/1.0', 'HTTP-Status' => 400 } ); +ok(handle_http == 0, 'Duplicate Host headers, Bug #25'); + + +@request = ( < 'HTTP/1.0', 'HTTP-Status' => 400 } ); +ok(handle_http == 0, 'Duplicate Content-Length headers'); + +@request = ( < 'HTTP/1.0', 'HTTP-Status' => 400 } ); +ok(handle_http == 0, 'Duplicate Content-Type headers'); + +@request = ( < 'HTTP/1.0', 'HTTP-Status' => 400 } ); +ok(handle_http == 0, 'Duplicate Range headers'); + +@request = ( < 'HTTP/1.0', 'HTTP-Status' => 400 } ); +ok(handle_http == 0, 'Duplicate If-None-Match headers'); + +@request = ( < 'HTTP/1.0', 'HTTP-Status' => 400 } ); +ok(handle_http == 0, 'Duplicate If-Modified-Since headers'); + +