From 4a2a80dc7d6c00a078f428d53537d77a6b67699b Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Tue, 11 Mar 2014 13:19:06 +0100 Subject: [PATCH] Updated the included http-parser --- src/http-parser/http_parser.c | 85 +++++++++++++++++++++++++++++------ src/http-parser/http_parser.h | 38 +++++++++++----- 2 files changed, 98 insertions(+), 25 deletions(-) diff --git a/src/http-parser/http_parser.c b/src/http-parser/http_parser.c index 47016d48..97dcbf21 100644 --- a/src/http-parser/http_parser.c +++ b/src/http-parser/http_parser.c @@ -249,6 +249,7 @@ enum state , s_res_http_minor , s_res_first_status_code , s_res_status_code + , s_res_status_start , s_res_status , s_res_line_almost_done @@ -582,6 +583,7 @@ size_t http_parser_execute (http_parser *parser, const char *header_value_mark = 0; const char *url_mark = 0; const char *body_mark = 0; + const char *status_mark = 0; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -628,6 +630,9 @@ size_t http_parser_execute (http_parser *parser, case s_req_fragment: url_mark = data; break; + case s_res_status: + status_mark = data; + break; } for (p=data; p != data + len; p++) { @@ -635,7 +640,17 @@ size_t http_parser_execute (http_parser *parser, if (PARSING_HEADER(parser->state)) { ++parser->nread; - /* Buffer overflow attack */ + /* Don't allow the total size of the HTTP headers (including the status + * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ if (parser->nread > HTTP_MAX_HEADER_SIZE) { SET_ERRNO(HPE_HEADER_OVERFLOW); goto error; @@ -824,7 +839,7 @@ size_t http_parser_execute (http_parser *parser, if (!IS_NUM(ch)) { switch (ch) { case ' ': - parser->state = s_res_status; + parser->state = s_res_status_start; break; case CR: parser->state = s_res_line_almost_done; @@ -850,9 +865,8 @@ size_t http_parser_execute (http_parser *parser, break; } - case s_res_status: - /* the human readable status. e.g. "NOT FOUND" - * we are not humans so just ignore this */ + case s_res_status_start: + { if (ch == CR) { parser->state = s_res_line_almost_done; break; @@ -862,12 +876,31 @@ size_t http_parser_execute (http_parser *parser, parser->state = s_header_field_start; break; } + + MARK(status); + parser->state = s_res_status; + parser->index = 0; + break; + } + + case s_res_status: + if (ch == CR) { + parser->state = s_res_line_almost_done; + CALLBACK_DATA(status); + break; + } + + if (ch == LF) { + parser->state = s_header_field_start; + CALLBACK_DATA(status); + break; + } + break; case s_res_line_almost_done: STRICT_CHECK(ch != LF); parser->state = s_header_field_start; - CALLBACK_NOTIFY(status_complete); break; case s_start_req: @@ -930,6 +963,7 @@ size_t http_parser_execute (http_parser *parser, } else if (parser->index == 2 && ch == 'P') { parser->method = HTTP_COPY; } else { + SET_ERRNO(HPE_INVALID_METHOD); goto error; } } else if (parser->method == HTTP_MKCOL) { @@ -942,12 +976,14 @@ size_t http_parser_execute (http_parser *parser, } else if (parser->index == 2 && ch == 'A') { parser->method = HTTP_MKACTIVITY; } else { + SET_ERRNO(HPE_INVALID_METHOD); goto error; } } else if (parser->method == HTTP_SUBSCRIBE) { if (parser->index == 1 && ch == 'E') { parser->method = HTTP_SEARCH; } else { + SET_ERRNO(HPE_INVALID_METHOD); goto error; } } else if (parser->index == 1 && parser->method == HTTP_POST) { @@ -958,13 +994,27 @@ size_t http_parser_execute (http_parser *parser, } else if (ch == 'A') { parser->method = HTTP_PATCH; } else { + SET_ERRNO(HPE_INVALID_METHOD); goto error; } } else if (parser->index == 2) { if (parser->method == HTTP_PUT) { - if (ch == 'R') parser->method = HTTP_PURGE; + if (ch == 'R') { + parser->method = HTTP_PURGE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } } else if (parser->method == HTTP_UNLOCK) { - if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE; + if (ch == 'S') { + parser->method = HTTP_UNSUBSCRIBE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; } } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { parser->method = HTTP_PROPPATCH; @@ -1460,8 +1510,8 @@ size_t http_parser_execute (http_parser *parser, t *= 10; t += ch - '0'; - /* Overflow? */ - if (t < parser->content_length || t == ULLONG_MAX) { + /* Overflow? Test against a conservative limit for simplicity. */ + if ((ULLONG_MAX - 10) / 10 < parser->content_length) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1733,8 +1783,8 @@ size_t http_parser_execute (http_parser *parser, t *= 16; t += unhex_val; - /* Overflow? */ - if (t < parser->content_length || t == ULLONG_MAX) { + /* Overflow? Test against a conservative limit for simplicity. */ + if ((ULLONG_MAX - 16) / 16 < parser->content_length) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1828,12 +1878,14 @@ size_t http_parser_execute (http_parser *parser, assert(((header_field_mark ? 1 : 0) + (header_value_mark ? 1 : 0) + (url_mark ? 1 : 0) + - (body_mark ? 1 : 0)) <= 1); + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); CALLBACK_DATA_NOADVANCE(header_field); CALLBACK_DATA_NOADVANCE(header_value); CALLBACK_DATA_NOADVANCE(url); CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); return len; @@ -2174,3 +2226,10 @@ int http_body_is_final(const struct http_parser *parser) { return parser->state == s_message_done; } + +unsigned long +http_parser_version(void) { + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; +} diff --git a/src/http-parser/http_parser.h b/src/http-parser/http_parser.h index a992c742..3e288163 100644 --- a/src/http-parser/http_parser.h +++ b/src/http-parser/http_parser.h @@ -24,8 +24,10 @@ extern "C" { #endif +/* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 0 +#define HTTP_PARSER_VERSION_MINOR 2 +#define HTTP_PARSER_VERSION_PATCH 1 #include #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) @@ -141,13 +143,13 @@ enum flags \ /* Callback-related errors */ \ XX(CB_message_begin, "the on_message_begin callback failed") \ - XX(CB_status_complete, "the on_status_complete callback failed") \ XX(CB_url, "the on_url callback failed") \ XX(CB_header_field, "the on_header_field callback failed") \ XX(CB_header_value, "the on_header_value callback failed") \ XX(CB_headers_complete, "the on_headers_complete callback failed") \ XX(CB_body, "the on_body callback failed") \ XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ \ /* Parsing-related errors */ \ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ @@ -191,11 +193,11 @@ enum http_errno { struct http_parser { /** PRIVATE **/ - unsigned char type : 2; /* enum http_parser_type */ - unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */ - unsigned char state; /* enum state from http_parser.c */ - unsigned char header_state; /* enum header_state from http_parser.c */ - unsigned char index; /* index into current matcher */ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 8; /* enum state from http_parser.c */ + unsigned int header_state : 8; /* enum header_state from http_parser.c */ + unsigned int index : 8; /* index into current matcher */ uint32_t nread; /* # bytes read in various scenarios */ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ @@ -203,16 +205,16 @@ struct http_parser { /** READ-ONLY **/ unsigned short http_major; unsigned short http_minor; - unsigned short status_code; /* responses only */ - unsigned char method; /* requests only */ - unsigned char http_errno : 7; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; /* 1 = Upgrade header was present and the parser has exited because of that. * 0 = No upgrade header present. * Should be checked when http_parser_execute() returns in addition to * error checking. */ - unsigned char upgrade : 1; + unsigned int upgrade : 1; /** PUBLIC **/ void *data; /* A pointer to get hook to the "connection" or "socket" object */ @@ -222,7 +224,7 @@ struct http_parser { struct http_parser_settings { http_cb on_message_begin; http_data_cb on_url; - http_cb on_status_complete; + http_data_cb on_status; http_data_cb on_header_field; http_data_cb on_header_value; http_cb on_headers_complete; @@ -261,6 +263,18 @@ struct http_parser_url { }; +/* Returns the library version. Bits 16-23 contain the major version number, + * bits 8-15 the minor version number and bits 0-7 the patch level. + * Usage example: + * + * unsigned long version = http_parser_version(); + * unsigned major = (version >> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, version); + */ +unsigned long http_parser_version(void); + void http_parser_init(http_parser *parser, enum http_parser_type type);