From: Stephan Bosch Date: Fri, 10 Jan 2014 20:00:39 +0000 (-0500) Subject: http: Improved HTTP header parser state machine and error messages. X-Git-Tag: 2.2.11~46 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b4d90cf5b73018ef7a64d60748dde8ec6f539d60;p=thirdparty%2Fdovecot%2Fcore.git http: Improved HTTP header parser state machine and error messages. --- diff --git a/src/lib-http/http-header-parser.c b/src/lib-http/http-header-parser.c index 88da94926f..875b92f14d 100644 --- a/src/lib-http/http-header-parser.c +++ b/src/lib-http/http-header-parser.c @@ -19,7 +19,6 @@ enum http_header_parse_state { HTTP_HEADER_PARSE_STATE_CR, HTTP_HEADER_PARSE_STATE_LF, HTTP_HEADER_PARSE_STATE_NEW_LINE, - HTTP_HEADER_PARSE_STATE_LAST_LINE, HTTP_HEADER_PARSE_STATE_EOH }; @@ -172,16 +171,24 @@ static int http_header_parse(struct http_header_parser *parser) case HTTP_HEADER_PARSE_STATE_INIT: buffer_set_used_size(parser->value_buf, 0); str_truncate(parser->name, 0); + if (*parser->cur == '\r') { + /* last CRLF */ + parser->cur++; + parser->state = HTTP_HEADER_PARSE_STATE_EOH; + if (parser->cur == parser->end) + return 0; + break; + } else if (*parser->cur == '\n') { + /* last LF */ + parser->state = HTTP_HEADER_PARSE_STATE_EOH; + break; + } + /* next line */ parser->state = HTTP_HEADER_PARSE_STATE_NAME; /* fall through */ case HTTP_HEADER_PARSE_STATE_NAME: - if (http_char_is_token(*parser->cur)) { - if ((ret=http_header_parse_name(parser)) <= 0) - return ret; - } else if (*parser->cur != ':' && str_len(parser->name) == 0) { - parser->state = HTTP_HEADER_PARSE_STATE_LAST_LINE; - break; - } + if ((ret=http_header_parse_name(parser)) <= 0) + return ret; parser->state = HTTP_HEADER_PARSE_STATE_COLON; /* fall through */ case HTTP_HEADER_PARSE_STATE_COLON: @@ -216,6 +223,12 @@ static int http_header_parse(struct http_header_parser *parser) case HTTP_HEADER_PARSE_STATE_CR: if (*parser->cur == '\r') { parser->cur++; + } else if (*parser->cur != '\n') { + parser->error = t_strdup_printf + ("Invalid character %s in content of header field '%s'", + _chr_sanitize(*parser->cur), + str_sanitize(str_c(parser->name),64)); + return -1; } parser->state = HTTP_HEADER_PARSE_STATE_LF; if (parser->cur == parser->end) @@ -224,7 +237,7 @@ static int http_header_parse(struct http_header_parser *parser) case HTTP_HEADER_PARSE_STATE_LF: if (*parser->cur != '\n') { parser->error = t_strdup_printf - ("Expected line end after header field '%s', but found %s", + ("Expected LF after CR at end of header field '%s', but found %s", str_sanitize(str_c(parser->name),64), _chr_sanitize(*parser->cur)); return -1; @@ -240,31 +253,14 @@ static int http_header_parse(struct http_header_parser *parser) buffer_append_c(parser->value_buf, ' '); parser->state = HTTP_HEADER_PARSE_STATE_OWS; break; - } + } + /* next header line */ parser->state = HTTP_HEADER_PARSE_STATE_INIT; return 1; - case HTTP_HEADER_PARSE_STATE_LAST_LINE: - if (*parser->cur == '\r') { - /* last CRLF */ - parser->cur++; - parser->state = HTTP_HEADER_PARSE_STATE_EOH; - if (parser->cur == parser->end) - return 0; - break; - } else if (*parser->cur == '\n') { - /* header fully parsed */ - parser->cur++; - parser->state = HTTP_HEADER_PARSE_STATE_EOH; - return 1; - } - parser->error = t_strdup_printf - ("Expected CRLF or header field name, but found %s", - _chr_sanitize(*parser->cur)); - return -1; case HTTP_HEADER_PARSE_STATE_EOH: if (*parser->cur != '\n') { parser->error = t_strdup_printf - ("Expected LF after CR at end of header, but found %s", + ("Encountered stray CR at beginning of header line, followed by %s", _chr_sanitize(*parser->cur)); return -1; } @@ -350,7 +346,11 @@ int http_header_parse_next_field(struct http_header_parser *parser, } i_assert(ret != -2); - if (ret < 0) - *error_r = "Stream error"; + if (ret < 0) { + if (i_stream_is_eof(parser->input)) + *error_r = "Premature end of input"; + else + *error_r = "Stream error"; + } return ret; }