diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile index 0636bd0b28..42f643cc9e 100644 --- a/deps/http_parser/Makefile +++ b/deps/http_parser/Makefile @@ -1,5 +1,5 @@ -OPT_DEBUG=-O0 -g -Wall -Wextra -Werror -OPT_FAST=-O3 -DHTTP_PARSER_STRICT=0 +OPT_DEBUG=-O0 -g -Wall -Wextra -Werror -I. +OPT_FAST=-O3 -DHTTP_PARSER_STRICT=0 -I. test: test_debug diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c index 95afa18142..36257fd013 100644 --- a/deps/http_parser/http_parser.c +++ b/deps/http_parser/http_parser.c @@ -106,11 +106,10 @@ static inline int message_complete_callback (http_parser *parser) #define KEEP_ALIVE "keep-alive" #define CLOSE "close" - static const unsigned char lowcase[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" - "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" + "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_" "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" @@ -199,7 +198,10 @@ enum state , s_header_almost_done , s_headers_almost_done - + /* Important: 's_headers_almost_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ , s_chunk_size_start , s_chunk_size , s_chunk_size_almost_done @@ -212,6 +214,8 @@ enum state , s_body_identity_eof }; +#define PARSING_HEADER(state) (state <= s_headers_almost_done) + enum header_states { h_general = 0 , h_C @@ -262,12 +266,13 @@ size_t http_parser_execute (http_parser *parser, size_t len) { char c, ch; - const char *p, *pe; + const char *p = data, *pe; ssize_t to_read; enum state state = parser->state; enum header_states header_state = parser->header_state; size_t index = parser->index; + size_t nread = parser->nread; if (len == 0) { if (state == s_body_identity_eof) { @@ -285,6 +290,12 @@ size_t http_parser_execute (http_parser *parser, for (p=data, pe=data+len; p != pe; p++) { ch = *p; + + if (++nread > HTTP_MAX_HEADER_SIZE && PARSING_HEADER(state)) { + /* Buffer overflow attack */ + goto error; + } + switch (state) { case s_dead: @@ -442,6 +453,8 @@ size_t http_parser_execute (http_parser *parser, case s_start_req: { + if (ch == CR || ch == LF) + break; parser->flags = 0; parser->content_length = -1; @@ -739,6 +752,9 @@ size_t http_parser_execute (http_parser *parser, if (USUAL(ch)) break; switch (ch) { + case '?': + // allow extra '?' in query string + break; case ' ': CALLBACK(url); CALLBACK(query_string); @@ -1262,6 +1278,7 @@ size_t http_parser_execute (http_parser *parser, } parser->body_read = 0; + nread = 0; CALLBACK2(headers_complete); @@ -1421,6 +1438,7 @@ size_t http_parser_execute (http_parser *parser, parser->state = state; parser->header_state = header_state; parser->index = index; + parser->nread = nread; return len; @@ -1455,6 +1473,8 @@ http_parser_init (http_parser *parser, enum http_parser_type t) { parser->type = t; parser->state = (t == HTTP_REQUEST ? s_start_req : s_start_res); + parser->nread = 0; + parser->on_message_begin = NULL; parser->on_path = NULL; parser->on_query_string = NULL; diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h index a1439ea364..7fb00840fe 100644 --- a/deps/http_parser/http_parser.h +++ b/deps/http_parser/http_parser.h @@ -38,6 +38,9 @@ extern "C" { # define HTTP_PARSER_STRICT 0 #endif +/* Maximium header size allowed */ +#define HTTP_MAX_HEADER_SIZE (80*1024) + typedef struct http_parser http_parser; /* Callbacks should return non-zero to indicate an error. The parse will @@ -85,6 +88,7 @@ struct http_parser { char flags; + size_t nread; ssize_t body_read; ssize_t content_length; diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c index 07f0f8119e..63d4800186 100644 --- a/deps/http_parser/test.c +++ b/deps/http_parser/test.c @@ -58,7 +58,7 @@ struct message { int message_begin_cb_called; int headers_complete_cb_called; int message_complete_cb_called; - int eof_indicates_message_end; + int message_complete_on_eof; }; static int currently_parsing_eof; @@ -85,7 +85,7 @@ const struct message requests[] = "Accept: */*\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_GET @@ -116,7 +116,7 @@ const struct message requests[] = "Connection: keep-alive\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_GET @@ -145,7 +145,7 @@ const struct message requests[] = "aaaaaaaaaaaaa:++++++++++\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_GET @@ -166,7 +166,7 @@ const struct message requests[] = ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_GET @@ -185,7 +185,7 @@ const struct message requests[] = ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE /* would need Connection: close */ + ,.message_complete_on_eof= FALSE /* would need Connection: close */ ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_GET @@ -204,7 +204,7 @@ const struct message requests[] = "Accept: */*\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE /* would need Connection: close */ + ,.message_complete_on_eof= FALSE /* would need Connection: close */ ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_GET @@ -227,7 +227,7 @@ const struct message requests[] = "\r\n" "HELLO" ,.should_keep_alive= FALSE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 0 ,.method= HTTP_GET @@ -252,7 +252,7 @@ const struct message requests[] = "\r\n" "World" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_POST @@ -279,7 +279,7 @@ const struct message requests[] = "0\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_POST @@ -305,7 +305,7 @@ const struct message requests[] = "000\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_POST @@ -333,7 +333,7 @@ const struct message requests[] = "Content-Type: text/plain\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_POST @@ -361,7 +361,7 @@ const struct message requests[] = "0\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_POST @@ -381,7 +381,7 @@ const struct message requests[] = ,.type= HTTP_REQUEST ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.method= HTTP_GET @@ -397,7 +397,7 @@ const struct message requests[] = #define APACHEBENCH_GET 13 /* The server receiving this request SHOULD NOT wait for EOF * to know that content-length == 0. - * How to represent this in a unit test? eof_indicates_message_end + * How to represent this in a unit test? message_complete_on_eof * Compare with NO_CONTENT_LENGTH_RESPONSE. */ , {.name = "apachebench get" @@ -407,7 +407,7 @@ const struct message requests[] = "User-Agent: ApacheBench/2.3\r\n" "Accept: */*\r\n\r\n" ,.should_keep_alive= FALSE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 0 ,.method= HTTP_GET @@ -423,6 +423,47 @@ const struct message requests[] = ,.body= "" } +#define QUERY_URL_WITH_QUESTION_MARK_GET 14 +/* Some clients include '?' characters in query strings. + */ +, {.name = "query url with question mark" + ,.type= HTTP_REQUEST + ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "foo=bar?baz" + ,.fragment= "" + ,.request_path= "/test.cgi" + ,.request_url= "/test.cgi?foo=bar?baz" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define PREFIX_NEWLINE_GET 15 +/* Some clients, especially after a POST in a keep-alive connection, + * will send an extra CRLF before the next request + */ +, {.name = "newline prefix get" + ,.type= HTTP_REQUEST + ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; @@ -447,7 +488,7 @@ const struct message responses[] = "here.\r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 @@ -494,7 +535,7 @@ const struct message responses[] = " \n" "" ,.should_keep_alive= FALSE - ,.eof_indicates_message_end= TRUE + ,.message_complete_on_eof= TRUE ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 @@ -522,7 +563,7 @@ const struct message responses[] = ,.type= HTTP_RESPONSE ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.status_code= 404 @@ -536,7 +577,7 @@ const struct message responses[] = ,.type= HTTP_RESPONSE ,.raw= "HTTP/1.1 301\r\n\r\n" ,.should_keep_alive = TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 @@ -561,7 +602,7 @@ const struct message responses[] = "0 \r\n" "\r\n" ,.should_keep_alive= TRUE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 @@ -585,7 +626,7 @@ const struct message responses[] = "\n" "these headers are from http://news.ycombinator.com/" ,.should_keep_alive= FALSE - ,.eof_indicates_message_end= TRUE + ,.message_complete_on_eof= TRUE ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 @@ -608,7 +649,7 @@ const struct message responses[] = "\r\n" "hello world" ,.should_keep_alive= FALSE - ,.eof_indicates_message_end= FALSE + ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 @@ -622,6 +663,31 @@ const struct message responses[] = ,.body= "hello world" } +#define UNDERSTORE_HEADER_KEY 7 + // shown by + // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;" +, {.name="underscore header key" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Server: DCLK-AdSvr\r\n" + "Content-Type: text/xml\r\n" + "Content-Length: 0\r\n" + "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.num_headers= 4 + ,.headers= + { {"Server", "DCLK-AdSvr" } + , {"Content-Type", "text/xml" } + , {"Content-Length", "0" } + , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" } + } + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; @@ -730,7 +796,7 @@ message_complete_cb (http_parser *p) } messages[num_messages].message_complete_cb_called = TRUE; - messages[num_messages].eof_indicates_message_end = currently_parsing_eof; + messages[num_messages].message_complete_on_eof = currently_parsing_eof; num_messages++; return 0; @@ -820,7 +886,7 @@ message_eq (int index, const struct message *expected) } MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); - MESSAGE_CHECK_NUM_EQ(expected, m, eof_indicates_message_end); + MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof); assert(m->message_begin_cb_called); assert(m->headers_complete_cb_called);