summaryrefslogtreecommitdiff
path: root/deps/http_parser
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2010-02-02 16:40:54 -0800
committerRyan Dahl <ry@tinyclouds.org>2010-02-02 16:40:59 -0800
commitce4204a069047146b44e687b485bb91697268460 (patch)
tree074b5360a3d3e331c43f868fc096e27b6dd1629f /deps/http_parser
parent987441283b77aecea42338d9379a82e5ff53b1f1 (diff)
downloadandroid-node-v8-ce4204a069047146b44e687b485bb91697268460.tar.gz
android-node-v8-ce4204a069047146b44e687b485bb91697268460.tar.bz2
android-node-v8-ce4204a069047146b44e687b485bb91697268460.zip
Upgrade http-parser
Fixes, among other things, a header overflow attack.
Diffstat (limited to 'deps/http_parser')
-rw-r--r--deps/http_parser/Makefile4
-rw-r--r--deps/http_parser/http_parser.c28
-rw-r--r--deps/http_parser/http_parser.h4
-rw-r--r--deps/http_parser/test.c116
4 files changed, 121 insertions, 31 deletions
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[] =
"<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);