dovecot-2.2: lib-http: Added option to the header parser to make...

dovecot at dovecot.org dovecot at dovecot.org
Fri Jan 10 22:00:54 EET 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/4650bfc057fb
changeset: 17094:4650bfc057fb
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Fri Jan 10 15:00:28 2014 -0500
description:
lib-http: Added option to the header parser to make it lenient towards illegal characters in header field content.
The offending characters are then skipped without error. This is required
for the http client as a workaround for bugs in certain HTTP servers. This
behavior is explicitly not enabled for the http-request-parser as used by
our own HTTP server implementation.

diffstat:

 src/lib-http/http-header-parser.c        |  26 ++++++++++++++---
 src/lib-http/http-header-parser.h        |   2 +-
 src/lib-http/http-message-parser.c       |   7 ++--
 src/lib-http/http-message-parser.h       |   4 ++-
 src/lib-http/http-request-parser.c       |   2 +-
 src/lib-http/http-response-parser.c      |   2 +-
 src/lib-http/http-transfer-chunked.c     |   3 +-
 src/lib-http/test-http-header-parser.c   |  44 +++++++++++++++++++++++++----
 src/lib-http/test-http-response-parser.c |  46 ++++++++++++++++++++++++-------
 9 files changed, 105 insertions(+), 31 deletions(-)

diffs (truncated from 370 to 300 lines):

diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/http-header-parser.c
--- a/src/lib-http/http-header-parser.c	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/http-header-parser.c	Fri Jan 10 15:00:28 2014 -0500
@@ -37,16 +37,19 @@
 	buffer_t *value_buf;
 
 	enum http_header_parse_state state;
+
+	unsigned int lenient:1;
 };
 
 struct http_header_parser *
 http_header_parser_init(struct istream *input,
-	const struct http_header_limits *limits)
+	const struct http_header_limits *limits, bool lenient)
 {
 	struct http_header_parser *parser;
 
 	parser = i_new(struct http_header_parser, 1);
 	parser->input = input;
+	parser->lenient = lenient;
 	parser->name = str_new(default_pool, 128);
 	parser->value_buf = buffer_create_dynamic(default_pool, 4096);
 
@@ -116,14 +119,27 @@
 
 static int http_header_parse_content(struct http_header_parser *parser)
 {
-	const unsigned char *first = parser->cur;
+	const unsigned char *first;
 
 	/* field-content  = *( HTAB / SP / VCHAR / obs-text )
 	 */
-	while (parser->cur < parser->end && http_char_is_text(*parser->cur))
-		parser->cur++;
+	do {
+		first = parser->cur;
+		while (parser->cur < parser->end && http_char_is_text(*parser->cur)) {
+			parser->cur++;
+		}
+		buffer_append(parser->value_buf, first, parser->cur-first);
 
-	buffer_append(parser->value_buf, first, parser->cur-first);
+		if (!parser->lenient)
+			break;
+
+		/* We'll be lenient here to accommodate for some bad servers. We just
+		   drop offending characters */
+		while (parser->cur < parser->end && !http_char_is_text(*parser->cur) &&
+			(*parser->cur != '\r' && *parser->cur != '\n'))
+			parser->cur++;
+	} while (parser->cur < parser->end &&
+		(*parser->cur != '\r' && *parser->cur != '\n'));
 
 	if (parser->cur == parser->end)
 		return 0;
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/http-header-parser.h
--- a/src/lib-http/http-header-parser.h	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/http-header-parser.h	Fri Jan 10 15:00:28 2014 -0500
@@ -6,7 +6,7 @@
 
 struct http_header_parser *
 http_header_parser_init(struct istream *input,
-	const struct http_header_limits *limits);
+	const struct http_header_limits *limits, bool lenient);
 void http_header_parser_deinit(struct http_header_parser **_parser);
 
 void http_header_parser_reset(struct http_header_parser *parser);
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/http-message-parser.c
--- a/src/lib-http/http-message-parser.c	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/http-message-parser.c	Fri Jan 10 15:00:28 2014 -0500
@@ -14,13 +14,14 @@
 
 void http_message_parser_init(struct http_message_parser *parser,
 	struct istream *input, const struct http_header_limits *hdr_limits,
-	uoff_t max_payload_size)
+	uoff_t max_payload_size, bool lenient)
 {
 	memset(parser, 0, sizeof(*parser));
 	parser->input = input;
 	if (hdr_limits != NULL)
 		parser->header_limits = *hdr_limits;
 	parser->max_payload_size = max_payload_size;
+	parser->lenient = lenient;
 }
 
 void http_message_parser_deinit(struct http_message_parser *parser)
@@ -39,8 +40,8 @@
 	i_assert(parser->payload == NULL);
 
 	if (parser->header_parser == NULL) {
-		parser->header_parser =
-			http_header_parser_init(parser->input, &parser->header_limits);
+		parser->header_parser = http_header_parser_init
+				(parser->input, &parser->header_limits,	parser->lenient);
 	} else {
 		http_header_parser_reset(parser->header_parser);
 	}
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/http-message-parser.h
--- a/src/lib-http/http-message-parser.h	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/http-message-parser.h	Fri Jan 10 15:00:28 2014 -0500
@@ -51,11 +51,13 @@
 
 	pool_t msg_pool;
 	struct http_message msg;
+
+	unsigned int lenient:1;
 };
 
 void http_message_parser_init(struct http_message_parser *parser,
 	struct istream *input, const struct http_header_limits *hdr_limits,
-	uoff_t max_payload_size) ATTR_NULL(3);
+	uoff_t max_payload_size, bool lenient) ATTR_NULL(3);
 void http_message_parser_deinit(struct http_message_parser *parser);
 void http_message_parser_restart(struct http_message_parser *parser,
 	pool_t pool);
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/http-request-parser.c
--- a/src/lib-http/http-request-parser.c	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/http-request-parser.c	Fri Jan 10 15:00:28 2014 -0500
@@ -66,7 +66,7 @@
 		max_payload_size = HTTP_REQUEST_DEFAULT_MAX_PAYLOAD_SIZE;
 
 	http_message_parser_init
-		(&parser->parser, input, &hdr_limits, max_payload_size);
+		(&parser->parser, input, &hdr_limits, max_payload_size, FALSE);
 	return parser;
 }
 
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/http-response-parser.c
--- a/src/lib-http/http-response-parser.c	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/http-response-parser.c	Fri Jan 10 15:00:28 2014 -0500
@@ -37,7 +37,7 @@
 
 	/* FIXME: implement status line limit */
 	parser = i_new(struct http_response_parser, 1);
-	http_message_parser_init(&parser->parser, input, hdr_limits, 0);
+	http_message_parser_init(&parser->parser, input, hdr_limits, 0, TRUE);
 	return parser;
 }
 
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/http-transfer-chunked.c
--- a/src/lib-http/http-transfer-chunked.c	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/http-transfer-chunked.c	Fri Jan 10 15:00:28 2014 -0500
@@ -434,9 +434,10 @@
 	int ret;
 
 	if (tcstream->header_parser == NULL) {
+		/* NOTE: trailer is currently ignored */
 		/* FIXME: limit trailer size */
 		tcstream->header_parser =
-			http_header_parser_init(tcstream->istream.parent, 0);
+			http_header_parser_init(tcstream->istream.parent, 0, TRUE);
 	}
 
 	while ((ret=http_header_parse_next_field(tcstream->header_parser,
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/test-http-header-parser.c
--- a/src/lib-http/test-http-header-parser.c	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/test-http-header-parser.c	Fri Jan 10 15:00:28 2014 -0500
@@ -1,6 +1,7 @@
 /* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
 
 #include "test-lib.h"
+#include "str-sanitize.h"
 #include "istream.h"
 #include "test-common.h"
 #include "http-response.h"
@@ -83,6 +84,16 @@
 	{ NULL, NULL }
 };
 
+static struct http_header_parse_result valid_header_parse_result6[] = {
+	{ "X-Frop", "This text\x80 contains obs-text\x81 characters" },
+	{ NULL, NULL }
+};
+
+static struct http_header_parse_result valid_header_parse_result7[] = {
+	{ "X-Frop", "This text contains invalid characters" },
+	{ NULL, NULL }
+};
+
 static const struct http_header_parse_test valid_header_parse_tests[] = {
 	{ .header = 
 			"Date: Sat, 06 Oct 2012 16:01:44 GMT\r\n"
@@ -150,6 +161,16 @@
 		.header =
 			"\r\n",
 		.fields = valid_header_parse_result5
+	},{
+		.header =
+			"X-Frop: This text\x80 contains obs-text\x81 characters\r\n"
+			"\r\n",
+		.fields = valid_header_parse_result6
+	},{
+		.header =
+			"X-Frop: This text\x01 contains invalid\x7f characters\r\n"
+			"\r\n",
+		.fields = valid_header_parse_result7
 	}
 };
 
@@ -173,7 +194,7 @@
 		header_len = strlen(header);
 		limits = &valid_header_parse_tests[i].limits;
 		input = test_istream_create_data(header, header_len);
-		parser = http_header_parser_init(input, limits);
+		parser = http_header_parser_init(input, limits, TRUE);
 
 		test_begin(t_strdup_printf("http header valid [%d]", i));
 
@@ -196,15 +217,16 @@
 			field_value = t_strndup(field_data, field_size);
 
 			if (result->name == NULL) {
-				test_out_reason("valid", FALSE,
-					t_strdup_printf("%s: %s", field_name, field_value));
+				test_out_reason("valid", FALSE,	t_strdup_printf
+					("%s: %s", field_name, str_sanitize(field_value, 100)));
 				break;
 			}
 
 			test_out_reason("valid",
 				strcmp(result->name, field_name) == 0 &&
-				strcmp(result->value, field_value) == 0,
-				t_strdup_printf("%s: %s", field_name, field_value));
+					strcmp(result->value, field_value) == 0,
+				t_strdup_printf("%s: %s", field_name,
+					str_sanitize(field_value, 100)));
 			j++;
 		}
 
@@ -238,10 +260,15 @@
 		.header = 
 			"Host: p5-lrqzb4yavu4l7nagydw-428649-i2-v6exp3-ds.metric.example.com\n"
 			"User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0)\n"
-			"Accept:\t\timage/png,image/*;q=0.8,*/\177;q=0.5\n"
+			"Accept:\t\timage/png,image/*;q=0.8,*/\1;q=0.5\n"
 			"\n"
 	},{
 		.header = 
+			"Date: Sat, 06 Oct 2012 17:18:22 GMT\r\n"
+			"Server: Apache/2.2.3\177 (CentOS)\r\n"
+			"\r\n"
+	},{
+		.header = 
 			"Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n"
 			"Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14 with\r\n"
 			"Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6\r\n"
@@ -249,6 +276,9 @@
 			"\r\n"
 	},{
 		.header = 
+			"Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n"
+	},{
+		.header = 
 			"Age: 58        \r\n"
 			"Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n"
 			"Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n"
@@ -318,7 +348,7 @@
 		header = invalid_header_parse_tests[i].header;
 		limits = &invalid_header_parse_tests[i].limits;
 		input = i_stream_create_from_data(header, strlen(header));
-		parser = http_header_parser_init(input, limits);
+		parser = http_header_parser_init(input, limits, FALSE);
 
 		test_begin(t_strdup_printf("http header invalid [%d]", i));
 
diff -r 9360aeba0099 -r 4650bfc057fb src/lib-http/test-http-response-parser.c
--- a/src/lib-http/test-http-response-parser.c	Fri Jan 10 21:59:15 2014 +0200
+++ b/src/lib-http/test-http-response-parser.c	Fri Jan 10 15:00:28 2014 -0500
@@ -20,7 +20,7 @@
 	const char *payload;
 };
 
-/* Valid header tests */
+/* Valid response tests */
 
 static const struct http_response_parse_test
 valid_response_parse_tests[] = {
@@ -39,7 +39,7 @@
 			"This is a piece of stupid text.\r\n",
 		.status = 200,
 		.payload = "This is a piece of stupid text.\r\n"
-	},{ 
+	},{
 		.response =
 			"HTTP/1.1 200 OK\r\n"
 			"Date: Sun, 07 Oct 2012 13:02:27 GMT\r\n"
@@ -134,7 +134,7 @@
 		}
 
 		test_out("parse success", ret == 0);
-		
+
 		if (ret == 0) {
 			/* verify last response only */
 			test_out(t_strdup_printf("response->status = %d",test->status),
@@ -156,6 +156,10 @@
 	buffer_free(&payload_buffer);
 }
 


More information about the dovecot-cvs mailing list