Browse Source

Upgrade http-parser

Fixes, among other things, a header overflow attack.
v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
ce4204a069
  1. 4
      deps/http_parser/Makefile
  2. 28
      deps/http_parser/http_parser.c
  3. 4
      deps/http_parser/http_parser.h
  4. 116
      deps/http_parser/test.c

4
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

28
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;

4
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;

116
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[] =
"<A HREF=\"http://www.google.com/\">here</A>.\r\n"
"</BODY></HTML>\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[] =
" </SOAP-ENV:Body>\n"
"</SOAP-ENV:Envelope>"
,.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);

Loading…
Cancel
Save