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);
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);
+
+ if (!parser->lenient)
+ break;
- buffer_append(parser->value_buf, first, parser->cur-first);
+ /* 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;
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);
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)
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);
}
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);
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;
}
/* 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;
}
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,
/* 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"
{ 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"
.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
}
};
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));
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++;
}
.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"
"Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6\r\n"
"mod_ssl/2.2.16 OpenSSL/0.9.8o mod_perl/2.0.4 Perl/v5.10.1\r\n"
"\r\n"
+ },{
+ .header =
+ "Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n"
},{
.header =
"Age: 58 \r\n"
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));
const char *payload;
};
-/* Valid header tests */
+/* Valid response tests */
static const struct http_response_parse_test
valid_response_parse_tests[] = {
"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"
}
test_out("parse success", ret == 0);
-
+
if (ret == 0) {
/* verify last response only */
test_out(t_strdup_printf("response->status = %d",test->status),
buffer_free(&payload_buffer);
}
+/*
+ * Invalid response tests
+ */
+
static const char *invalid_response_parse_tests[] = {
"XMPP/1.0 302 Found\r\n"
"Location: http://www.example.nl/\r\n"
"Cache-Control: private\n\r"
};
-static unsigned char invalid_response_with_nuls[] =
- "HTTP/1.1 200 OK\r\n"
- "Server: text\0server\r\n"
- "\r\n";
-
unsigned int invalid_response_parse_test_count =
N_ELEMENTS(invalid_response_parse_tests);
test_end();
http_response_parser_deinit(&parser);
} T_END;
+}
+
+/*
+ * Bad response tests
+ */
+
+static unsigned char bad_response_with_nuls[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Server: text\0server\r\n"
+ "\r\n";
+
+static void test_http_response_parse_bad(void)
+{
+ struct http_response_parser *parser;
+ struct http_response response;
+ const char *header, *error;
+ struct istream *input;
+ int ret;
/* parse failure guarantees http_response_header.size equals
strlen(http_response_header.value) */
test_begin("http response with NULs");
- input = i_stream_create_from_data(invalid_response_with_nuls,
- sizeof(invalid_response_with_nuls)-1);
+ input = i_stream_create_from_data(bad_response_with_nuls,
+ sizeof(bad_response_with_nuls)-1);
parser = http_response_parser_init(input, 0);
while ((ret=http_response_parse_next(parser, FALSE, &response, &error)) > 0);
- test_assert(ret < 0);
+ test_out("parse success", ret == 0);
+ header = http_response_header_get(&response, "server");
+ test_out("header present", header != NULL);
+ if (header != NULL) {
+ test_out(t_strdup_printf("header Server: %s", header),
+ strcmp(header, "textserver") == 0);
+ }
test_end();
}
static void (*test_functions[])(void) = {
test_http_response_parse_valid,
test_http_response_parse_invalid,
+ test_http_response_parse_bad,
NULL
};
return test_run(test_functions);