[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[Libevent-users] Patch: add constraints on HTTP first line/headers/body size
- To: libevent-users@xxxxxxxxxxxxx
- Subject: [Libevent-users] Patch: add constraints on HTTP first line/headers/body size
- From: Constantine Verutin <cverutin@xxxxxxxxx>
- Date: Wed, 7 Oct 2009 20:50:25 +0300
- Delivered-to: archiver@xxxxxxxx
- Delivered-to: libevent-users-outgoing@xxxxxxxx
- Delivered-to: libevent-users@xxxxxxxx
- Delivery-date: Wed, 07 Oct 2009 13:56:24 -0400
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:date:message-id:subject :from:to:content-type; bh=8ijt6vNnjWyMXnSCSf+q7oGnqVSxAK3xnmN76jvKA6A=; b=PuQ6Ry4SOABei7hBWTbRUEQlKkBLO2rZgA5ENN4yHQmXqPwsIK6GquiFJpe15G/gUc cYGsbtdvRdxCcJRjTzC8tWauEbGl/spk81jag/Y6kpgSS+G+DIe7hcUfnxpqEqZlsb/m iELK2y4cjq2Pj9hK0VCQZHUbp6BnS6etdNESw=
- Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:content-type; b=AUjeP+dktIqKkf4b7A7K+DFEQgUaDLfHMhic6o1/fG+ppcE+cBK2ftGaGIEYSeUdf3 VHEF6rGwz2DZ5dlZBD4ysPVHslkyIgDTiEc/1fd5wKb/55Lgh6Z+FpkM5bNki14KKSgh TFuCXtkJez3RZTigUJVcT+n8zBftd1yxVNLZM=
- Reply-to: libevent-users@xxxxxxxxxxxxx
- Sender: owner-libevent-users@xxxxxxxxxxxxx
Hi,
Here's the patch which adds customizable limits on first line/header/body length and also on headers number. There's four functions for setting these limits on evhttp object:
evhttp_set_max_first_line_size(struct evhttp* http, int max_first_line_size);
evhttp_set_max_header_size(struct evhttp* http, int max_header_size);
evhttp_set_max_header_number(struct evhttp* http, int max_header_number);
evhttp_set_max_body_size(struct evhttp* http, int max_body_size);
Also, added testcase for this functionality.
--
WBR,
Constantine
diff -ur ./libevent-2.0.2-alpha/http.c ./libevent-2.0.2-alpha-patched/http.c
--- ./libevent-2.0.2-alpha/http.c 2009-05-15 09:40:58.000000000 +0300
+++ ./libevent-2.0.2-alpha-patched/http.c 2009-10-07 20:44:34.000000000 +0300
@@ -556,6 +556,35 @@
}
}
+
+void
+evhttp_connection_set_max_first_line_size(struct evhttp_connection *evcon,
+ size_t new_max_first_line_size)
+{
+ evcon->max_first_line_size = new_max_first_line_size;
+}
+
+void
+evhttp_connection_set_max_header_size(struct evhttp_connection *evcon,
+ size_t new_max_header_size)
+{
+ evcon->max_header_size = new_max_header_size;
+}
+
+void
+evhttp_connection_set_max_header_number(struct evhttp_connection* evcon,
+ size_t new_max_header_number)
+{
+ evcon->max_header_number = new_max_header_number;
+}
+
+void
+evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
+ size_t new_max_body_size)
+{
+ evcon->max_body_size = new_max_body_size;
+}
+
static int
evhttp_connection_incoming_fail(struct evhttp_request *req,
enum evhttp_connection_error error)
@@ -759,6 +788,13 @@
/* could not get chunk size */
return (DATA_CORRUPTED);
}
+ if (req->evcon->max_body_size != SIZE_UNLIMITED &&
+ req->body_size + ntoread > req->evcon->max_body_size) {
+ /* failed body length test */
+ event_debug(("%s\n", "Request body is too long"));
+ return (DATA_CORRUPTED);
+ }
+ req->body_size += ntoread;
req->ntoread = ntoread;
if (req->ntoread == 0) {
/* Last chunk */
@@ -839,14 +875,25 @@
} else if (req->ntoread < 0) {
/* Read until connection close. */
evbuffer_add_buffer(req->input_buffer, buf);
+ req->body_size += evbuffer_get_length(buf);
} else if (req->chunk_cb != NULL ||
evbuffer_get_length(buf) >= req->ntoread) {
/* We've postponed moving the data until now, but we're
* about to use it. */
req->ntoread -= evbuffer_get_length(buf);
+ req->body_size += evbuffer_get_length(buf);
evbuffer_add_buffer(req->input_buffer, buf);
}
+ if (req->evcon->max_body_size != SIZE_UNLIMITED &&
+ req->body_size > req->evcon->max_body_size) {
+ /* failed body length test */
+ event_debug(("%s\n", "Request body is too long"));
+ evhttp_connection_fail(evcon,
+ EVCON_HTTP_INVALID_HEADER);
+ return;
+ }
+
if (evbuffer_get_length(req->input_buffer) > 0 && req->chunk_cb != NULL) {
req->flags |= EVHTTP_REQ_DEFER_FREE;
(*req->chunk_cb)(req, req->cb_arg);
@@ -1451,10 +1498,24 @@
char *line;
enum message_read_status status = ALL_DATA_READ;
- line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_CRLF);
- if (line == NULL)
- return (MORE_DATA_EXPECTED);
+ size_t line_length;
+ line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF);
+ if (line == NULL) {
+ if (req->evcon != NULL &&
+ req->evcon->max_first_line_size != SIZE_UNLIMITED &&
+ evbuffer_get_length(buffer) > req->evcon->max_first_line_size)
+ return (DATA_CORRUPTED);
+ else
+ return (MORE_DATA_EXPECTED);
+ }
+ if (req->evcon != NULL &&
+ req->evcon->max_first_line_size != SIZE_UNLIMITED &&
+ line_length > req->evcon->max_first_line_size) {
+ mm_free(line);
+ return (DATA_CORRUPTED);
+ }
+
switch (req->kind) {
case EVHTTP_REQUEST:
if (evhttp_parse_request_line(req, line) == -1)
@@ -1502,10 +1563,18 @@
enum message_read_status status = MORE_DATA_EXPECTED;
struct evkeyvalq* headers = req->input_headers;
- while ((line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_CRLF))
+ size_t line_length;
+ while ((line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF))
!= NULL) {
char *skey, *svalue;
+ if (req->evcon != NULL &&
+ ((req->evcon->max_header_size != SIZE_UNLIMITED &&
+ req->last_header_size > req->evcon->max_header_size) ||
+ (req->evcon->max_header_number != SIZE_UNLIMITED &&
+ req->headers_number > req->evcon->max_header_number)))
+ goto error;
+
if (*line == '\0') { /* Last header - Done */
status = ALL_DATA_READ;
mm_free(line);
@@ -1514,12 +1583,15 @@
/* Check if this is a continuation line */
if (*line == ' ' || *line == '\t') {
+ req->last_header_size += line_length;
if (evhttp_append_to_last_header(headers, line) == -1)
goto error;
mm_free(line);
continue;
}
+ req->last_header_size = line_length;
+
/* Processing of header lines */
svalue = line;
skey = strsep(&svalue, ":");
@@ -1531,9 +1603,16 @@
if (evhttp_add_header(headers, skey, svalue) == -1)
goto error;
+ req->headers_number += 1;
mm_free(line);
}
+ if (status == MORE_DATA_EXPECTED) {
+ if (req->evcon->max_header_size != SIZE_UNLIMITED &&
+ evbuffer_get_length(buffer) > req->evcon->max_header_size)
+ return (DATA_CORRUPTED);
+ }
+
return (status);
error:
@@ -1713,6 +1792,11 @@
evcon->fd = -1;
evcon->port = port;
+ evcon->max_first_line_size = SIZE_UNLIMITED;
+ evcon->max_header_size = SIZE_UNLIMITED;
+ evcon->max_header_number = SIZE_UNLIMITED;
+ evcon->max_body_size = SIZE_UNLIMITED;
+
evcon->timeout = -1;
evcon->retry_cnt = evcon->retry_max = 0;
@@ -2444,7 +2528,11 @@
}
http->timeout = -1;
-
+ evhttp_set_max_first_line_size(http, SIZE_UNLIMITED);
+ evhttp_set_max_header_size(http, SIZE_UNLIMITED);
+ evhttp_set_max_header_number(http, SIZE_UNLIMITED);
+ evhttp_set_max_body_size(http, SIZE_UNLIMITED);
+
TAILQ_INIT(&http->sockets);
TAILQ_INIT(&http->callbacks);
TAILQ_INIT(&http->connections);
@@ -2561,6 +2649,22 @@
http->timeout = timeout_in_secs;
}
+void evhttp_set_max_first_line_size(struct evhttp* http, size_t max_first_line_size) {
+ http->default_max_first_line_size = max_first_line_size;
+}
+
+void evhttp_set_max_header_size(struct evhttp* http, size_t max_header_size) {
+ http->default_max_header_size = max_header_size;
+}
+
+void evhttp_set_max_header_number(struct evhttp* http, size_t max_header_number) {
+ http->default_max_header_number = max_header_number;
+}
+
+void evhttp_set_max_body_size(struct evhttp* http, size_t max_body_size) {
+ http->default_max_body_size = max_body_size;
+}
+
int
evhttp_set_cb(struct evhttp *http, const char *uri,
void (*cb)(struct evhttp_request *, void *), void *cbarg)
@@ -2626,6 +2730,10 @@
goto error;
}
+ req->last_header_size = 0;
+ req->headers_number = 0;
+ req->body_size = 0;
+
req->kind = EVHTTP_RESPONSE;
req->input_headers = mm_calloc(1, sizeof(struct evkeyvalq));
if (req->input_headers == NULL) {
@@ -2778,6 +2886,11 @@
if (evcon == NULL)
return (NULL);
+ evhttp_connection_set_max_first_line_size(evcon, http->default_max_first_line_size);
+ evhttp_connection_set_max_header_size(evcon, http->default_max_header_size);
+ evhttp_connection_set_max_header_number(evcon, http->default_max_header_number);
+ evhttp_connection_set_max_body_size(evcon, http->default_max_body_size);
+
evcon->flags |= EVHTTP_CON_INCOMING;
evcon->state = EVCON_READING_FIRSTLINE;
diff -ur ./libevent-2.0.2-alpha/http-internal.h ./libevent-2.0.2-alpha-patched/http-internal.h
--- ./libevent-2.0.2-alpha/http-internal.h 2009-07-25 06:42:30.000000000 +0300
+++ ./libevent-2.0.2-alpha-patched/http-internal.h 2009-09-09 16:07:56.000000000 +0300
@@ -20,6 +20,8 @@
#define HTTP_PREFIX "http://"
#define HTTP_DEFAULTPORT 80
+#define SIZE_UNLIMITED -1
+
enum message_read_status {
ALL_DATA_READ = 1,
MORE_DATA_EXPECTED = 0,
@@ -70,6 +72,11 @@
char *address; /* address to connect to */
u_short port;
+ size_t max_first_line_size;
+ size_t max_header_size;
+ size_t max_header_number;
+ size_t max_body_size;
+
int flags;
#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
@@ -127,8 +134,13 @@
/* NULL if this server is not a vhost */
char *vhost_pattern;
- int timeout;
+ int timeout;
+ size_t default_max_first_line_size;
+ size_t default_max_header_size;
+ size_t default_max_header_number;
+ size_t default_max_body_size;
+
void (*gencb)(struct evhttp_request *req, void *);
void *gencbarg;
diff -ur ./libevent-2.0.2-alpha/include/event2/http.h ./libevent-2.0.2-alpha-patched/include/event2/http.h
--- ./libevent-2.0.2-alpha/include/event2/http.h 2009-07-25 06:42:30.000000000 +0300
+++ ./libevent-2.0.2-alpha-patched/include/event2/http.h 2009-10-07 20:46:02.000000000 +0300
@@ -119,6 +119,11 @@
*/
void evhttp_free(struct evhttp* http);
+void evhttp_set_max_first_line_size(struct evhttp* http, size_t max_first_line_size);
+void evhttp_set_max_header_size(struct evhttp* http, size_t max_header_size);
+void evhttp_set_max_header_number(struct evhttp* http, size_t max_header_number);
+void evhttp_set_max_body_size(struct evhttp* http, size_t max_body_size);
+
/**
Set a callback for a specified URI
@@ -313,6 +318,18 @@
/** Returns 1 if the request is owned by the user */
int evhttp_request_is_owned(struct evhttp_request *req);
+void evhttp_connection_set_max_first_line_size(struct evhttp_connection *evcon,
+ size_t new_max_first_line_size);
+
+void evhttp_connection_set_max_header_size(struct evhttp_connection *evcon,
+ size_t new_max_header_size);
+
+void evhttp_connection_set_max_header_number(struct evhttp_connection* evcon,
+ size_t new_max_header_number);
+
+void evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
+ size_t new_max_body_size);
+
/** Frees an http connection */
void evhttp_connection_free(struct evhttp_connection *evcon);
diff -ur ./libevent-2.0.2-alpha/include/event2/http_struct.h ./libevent-2.0.2-alpha-patched/include/event2/http_struct.h
--- ./libevent-2.0.2-alpha/include/event2/http_struct.h 2009-05-29 01:50:37.000000000 +0300
+++ ./libevent-2.0.2-alpha-patched/include/event2/http_struct.h 2009-10-07 20:45:37.000000000 +0300
@@ -88,6 +88,10 @@
enum evhttp_request_kind kind;
enum evhttp_cmd_type type;
+ size_t headers_number;
+ size_t last_header_size;
+ size_t body_size;
+
char *uri; /* uri after HTTP request was parsed */
char major; /* HTTP Major number */
diff -ur ./libevent-2.0.2-alpha/test/regress_http.c ./libevent-2.0.2-alpha-patched/test/regress_http.c
--- ./libevent-2.0.2-alpha/test/regress_http.c 2009-07-25 06:42:29.000000000 +0300
+++ ./libevent-2.0.2-alpha-patched/test/regress_http.c 2009-09-09 16:07:56.000000000 +0300
@@ -2190,6 +2190,98 @@
evhttp_free(http);
}
+
+static void
+http_data_length_constraints_test_done(struct evhttp_request *req, void *arg)
+{
+ tt_assert(req);
+ tt_int_op(req->response_code, ==, HTTP_BADREQUEST);
+end:
+ event_loopexit(NULL);
+}
+
+static void
+http_data_length_constraints_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+
+ test_ok = 0;
+
+ http = http_setup(&port, NULL);
+
+ evcon = evhttp_connection_new("127.0.0.1", port);
+ tt_assert(evcon);
+
+ /* also bind to local host */
+ evhttp_connection_set_local_address(evcon, "127.0.0.1");
+
+ /*
+ * At this point, we want to schedule an HTTP GET request
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_data_length_constraints_test_done, NULL);
+ tt_assert(req);
+
+ char long_str[8192];
+ memset(long_str, 'a', 8192);
+ long_str[8191] = '\0';
+ /* Add the information that we care about */
+ evhttp_set_max_header_size(http, 8191);
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evhttp_add_header(req->output_headers, "Longheader", long_str);
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+ event_dispatch();
+
+ evhttp_set_max_header_number(http, 5);
+ req = evhttp_request_new(http_data_length_constraints_test_done, NULL);
+ tt_assert(req);
+
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evhttp_add_header(req->output_headers, "Header1", "t");
+ evhttp_add_header(req->output_headers, "Header2", "t");
+ evhttp_add_header(req->output_headers, "Header3", "t");
+ evhttp_add_header(req->output_headers, "Header4", "t");
+ evhttp_add_header(req->output_headers, "Header5", "t");
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+ event_dispatch();
+
+ evhttp_set_max_first_line_size(http, 8191);
+ req = evhttp_request_new(http_data_length_constraints_test_done, NULL);
+ tt_assert(req);
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+
+ /* GET /?arg=verylongvalue HTTP/1.1 */
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+ event_dispatch();
+
+ evhttp_set_max_body_size(http, 8190);
+ req = evhttp_request_new(http_data_length_constraints_test_done, NULL);
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evbuffer_add_printf(req->output_buffer, long_str);
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+ event_dispatch();
+
+ test_ok = 1;
+ end:
+ if (evcon)
+ evhttp_connection_free(evcon);
+ if (http)
+ evhttp_free(http);
+}
+
#define HTTP_LEGACY(name) \
{ #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \
http_##name##_test }
@@ -2224,6 +2316,7 @@
HTTP_LEGACY(stream_in_cancel),
HTTP_LEGACY(connection_retry),
+ HTTP_LEGACY(data_length_constraints),
END_OF_TESTCASES
};