Updated the included http-parser

This commit is contained in:
Nikos Mavrogiannopoulos
2014-03-11 13:19:06 +01:00
parent 8a1ab0e32d
commit 4a2a80dc7d
2 changed files with 98 additions and 25 deletions

View File

@@ -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;
}

View File

@@ -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 <sys/types.h>
#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);