]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
http: Improved HTTP header parser state machine and error messages.
authorStephan Bosch <stephan@rename-it.nl>
Fri, 10 Jan 2014 20:00:39 +0000 (15:00 -0500)
committerStephan Bosch <stephan@rename-it.nl>
Fri, 10 Jan 2014 20:00:39 +0000 (15:00 -0500)
src/lib-http/http-header-parser.c

index 88da94926fbfcdf8cf5eedec270eb564684ca51a..875b92f14deae6bc4ce28eb1a43226252e7816f7 100644 (file)
@@ -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;
 }