bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "lib.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "buffer.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "istream.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "str.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "str-sanitize.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-parser.h"
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch#include "http-header.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-header-parser.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschenum http_header_parse_state {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_INIT = 0,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_NAME,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_COLON,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_OWS,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_CONTENT,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_CR,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_LF,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_NEW_LINE,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_HEADER_PARSE_STATE_EOH
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch};
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_header_parser {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct istream *input;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch struct http_header_limits limits;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch enum http_header_parse_flags flags;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch uoff_t size, field_size;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch unsigned int field_count;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const unsigned char *begin, *cur, *end;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *error;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch string_t *name;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_t *value_buf;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch enum http_header_parse_state state;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch};
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Boschstruct http_header_parser *
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Boschhttp_header_parser_init(struct istream *input,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch const struct http_header_limits *limits,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch enum http_header_parse_flags flags)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_header_parser *parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser = i_new(struct http_header_parser, 1);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->input = input;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (limits != NULL)
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->limits = *limits;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (parser->limits.max_size == 0)
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->limits.max_size = (uoff_t)-1;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (parser->limits.max_field_size == 0)
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->limits.max_field_size = (uoff_t)-1;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (parser->limits.max_fields == 0)
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->limits.max_fields = (unsigned int)-1;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch parser->flags = flags;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch parser->name = str_new(default_pool, 128);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch parser->value_buf = buffer_create_dynamic(default_pool, 4096);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_header_parser_deinit(struct http_header_parser **_parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_header_parser *parser = *_parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainen *_parser = NULL;
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch //i_stream_skip(ctx->input, ctx->skip);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_free(&parser->value_buf);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_free(&parser->name);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_free(parser);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_header_parser_reset(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_INIT;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->size = 0;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->field_size = 0;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->field_count = 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse_name(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const unsigned char *first = parser->cur;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* field-name = token
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch token = 1*tchar
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while (parser->cur < parser->end && http_char_is_token(*parser->cur))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->cur++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_append_n(parser->name, first, parser->cur-first);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->cur == parser->end)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (str_len(parser->name) == 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->error = "Empty header field name";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse_ows(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* OWS = *( SP / HTAB )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ; "optional" whitespace
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while (parser->cur < parser->end &&
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (*parser->cur == ' ' || *parser->cur == '\t'))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->cur++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return (parser->cur == parser->end ? 0 : 1);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse_content(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch const unsigned char *first;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch field-vchar = VCHAR / obs-text
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch do {
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch first = parser->cur;
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch while (parser->cur < parser->end && http_char_is_text(*parser->cur)) {
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch parser->cur++;
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch }
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch buffer_append(parser->value_buf, first, parser->cur-first);
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if ((parser->flags & HTTP_HEADER_PARSE_FLAG_STRICT) != 0)
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch break;
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch /* We'll be lenient here to accommodate for some bad servers. We just
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch drop offending characters */
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch while (parser->cur < parser->end && !http_char_is_text(*parser->cur) &&
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch (*parser->cur != '\r' && *parser->cur != '\n'))
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch parser->cur++;
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch } while (parser->cur < parser->end &&
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch (*parser->cur != '\r' && *parser->cur != '\n'));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->cur == parser->end)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic inline const char *_chr_sanitize(unsigned char c)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (c >= 0x20 && c < 0x7F)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return t_strdup_printf("'%c'", c);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return t_strdup_printf("0x%02x", c);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.2: Header Fields
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch 'header' = *( header-field CRLF ) CRLF
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch ; Actually part of HTTP-message syntax
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen header-field = field-name ":" OWS field-value OWS
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch field-name = token
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch field-value = *( field-content / obs-fold )
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch field-vchar = VCHAR / obs-text
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch obs-fold = CRLF 1*( SP / HTAB )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ; obsolete line folding
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch ; see Section 3.2.4
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch for (;;) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch switch (parser->state) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_INIT:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_set_used_size(parser->value_buf, 0);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_truncate(parser->name, 0);
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch if (*parser->cur == '\r') {
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* last CRLF */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch parser->cur++;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_EOH;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch if (parser->cur == parser->end)
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch return 0;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch break;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch } else if (*parser->cur == '\n') {
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* last LF */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_EOH;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch break;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch }
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* next line */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_NAME;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_NAME:
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch if ((ret=http_header_parse_name(parser)) <= 0)
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch return ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_COLON;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_COLON:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (*parser->cur != ':') {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->error = t_strdup_printf
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ("Expected ':' after header field name '%s', but found %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_sanitize(str_c(parser->name),64),
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch _chr_sanitize(*parser->cur));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->cur++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (str_len(parser->name) == 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->error = "Empty header field name";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (++parser->field_count > parser->limits.max_fields) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->error = "Excessive number of header fields";
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch return -1;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_OWS;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_OWS:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_header_parse_ows(parser)) <= 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_CONTENT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_CONTENT:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_header_parse_content(parser)) <= 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_CR;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_CR:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (*parser->cur == '\r') {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->cur++;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch } else if (*parser->cur != '\n') {
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch parser->error = t_strdup_printf
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch ("Invalid character %s in content of header field '%s'",
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch _chr_sanitize(*parser->cur),
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch str_sanitize(str_c(parser->name),64));
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_LF;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->cur == parser->end)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_LF:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (*parser->cur != '\n') {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->error = t_strdup_printf
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch ("Expected LF after CR at end of header field '%s', but found %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_sanitize(str_c(parser->name),64),
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch _chr_sanitize(*parser->cur));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->cur++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_NEW_LINE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->cur == parser->end)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_NEW_LINE:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (*parser->cur == ' ' || *parser->cur == '\t') {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* obs-fold */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_append_c(parser->value_buf, ' ');
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_OWS;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch break;
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch }
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* next header line */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_INIT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_HEADER_PARSE_STATE_EOH:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (*parser->cur != '\n') {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->error = t_strdup_printf
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch ("Encountered stray CR at beginning of header line, followed by %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch _chr_sanitize(*parser->cur));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* header fully parsed */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->cur++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch default:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_unreached();
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_unreached();
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschint http_header_parse_next_field(struct http_header_parser *parser,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char **name_r, const unsigned char **data_r, size_t *size_r,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch const uoff_t max_size = parser->limits.max_size;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch const uoff_t max_field_size = parser->limits.max_field_size;
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen const unsigned char *data;
fe145bd9fbeb609a691e23998117d62f40a3d00cTimo Sirainen size_t size;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch *error_r = NULL;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
9184983183ae28fb543695c54c85bc5396c07e42Phil Carmody while ((ret=i_stream_read_more(parser->input, &parser->begin, &size)) > 0) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* check header size limits */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (parser->size >= max_size) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch *error_r = "Excessive header size";
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch return -1;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch }
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (parser->field_size > max_field_size) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch *error_r = "Excessive header field size";
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch return -1;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch }
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* don't parse beyond header size limits */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (size > (max_size - parser->size))
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch size = max_size - parser->size;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (size > (max_field_size - parser->field_size)) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch size = max_field_size - parser->field_size;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch size = (size == 0 ? 1 : size); /* need to parse one more byte */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch }
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->cur = parser->begin;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->end = parser->cur + size;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_header_parse(parser)) < 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *error_r = parser->error;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_skip(parser->input, parser->cur - parser->begin);
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->size += parser->cur - parser->begin;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->field_size += parser->cur - parser->begin;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (ret == 1) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->field_size = 0;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->state != HTTP_HEADER_PARSE_STATE_EOH) {
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen data = buffer_get_data(parser->value_buf, &size);
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen /* trim trailing OWS */
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen while (size > 0 &&
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen (data[size-1] == ' ' || data[size-1] == '\t'))
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen size--;
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *name_r = str_c(parser->name);
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen *data_r = data;
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen *size_r = size;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_INIT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch } else {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *name_r = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *data_r = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(ret != -2);
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch if (ret < 0) {
53393ea8bd72644c808f77cf0b79189376769d5dTimo Sirainen i_assert(parser->input->eof);
53393ea8bd72644c808f77cf0b79189376769d5dTimo Sirainen if (parser->input->stream_errno == 0)
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch *error_r = "Premature end of input";
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch else
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen *error_r = t_strdup_printf("Stream error: %s",
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen i_stream_get_error(parser->input));
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}