From 8d2ad317fce454e0aca3bd34ebf5fcf967393e3c Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Sat, 21 Apr 2018 16:27:21 +0300 Subject: [PATCH] lib-mail: rfc822-parser: Add nul_replacement_char --- src/lib-mail/rfc822-parser.c | 29 +++++++++++++++++++++++++++-- src/lib-mail/rfc822-parser.h | 6 ++++++ src/lib-mail/test-rfc822-parser.c | 27 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/lib-mail/rfc822-parser.c b/src/lib-mail/rfc822-parser.c index 789d64be74..b8508ffbcb 100644 --- a/src/lib-mail/rfc822-parser.c +++ b/src/lib-mail/rfc822-parser.c @@ -75,6 +75,16 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx) start = ++ctx->data; for (; ctx->data < ctx->end; ctx->data++) { switch (*ctx->data) { + case '\0': + if (ctx->last_comment != NULL && + ctx->nul_replacement_char != '\0') { + str_append_data(ctx->last_comment, start, + ctx->data - start); + str_append_c(ctx->last_comment, + ctx->nul_replacement_char); + start = ctx->data + 1; + } + break; case '(': level++; break; @@ -103,8 +113,9 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx) if (ctx->data >= ctx->end) return -1; - if (*ctx->data == '\r' || *ctx->data == '\n') { - /* quoted-pair doesn't allow CR/LF. + if (*ctx->data == '\r' || *ctx->data == '\n' || + *ctx->data == '\0') { + /* quoted-pair doesn't allow CR/LF/NUL. They are part of the obs-qp though, so don't return them as error. */ ctx->data--; @@ -235,6 +246,13 @@ int rfc822_parse_quoted_string(struct rfc822_parser_context *ctx, string_t *str) for (start = ctx->data; ctx->data < ctx->end; ctx->data++) { switch (*ctx->data) { + case '\0': + if (ctx->nul_replacement_char != '\0') { + str_append_data(str, start, ctx->data - start); + str_append_c(str, ctx->nul_replacement_char); + start = ctx->data + 1; + } + break; case '"': str_append_data(str, start, ctx->data - start); ctx->data++; @@ -345,6 +363,13 @@ rfc822_parse_domain_literal(struct rfc822_parser_context *ctx, string_t *str) for (start = ctx->data++; ctx->data < ctx->end; ctx->data++) { switch (*ctx->data) { + case '\0': + if (ctx->nul_replacement_char != '\0') { + str_append_data(str, start, ctx->data - start); + str_append_c(str, ctx->nul_replacement_char); + start = ctx->data + 1; + } + break; case '[': /* not allowed */ return -1; diff --git a/src/lib-mail/rfc822-parser.h b/src/lib-mail/rfc822-parser.h index fe6afd2628..f36ec103f0 100644 --- a/src/lib-mail/rfc822-parser.h +++ b/src/lib-mail/rfc822-parser.h @@ -1,9 +1,15 @@ #ifndef RFC822_PARSER_H #define RFC822_PARSER_H +/* This can be used as a common NUL replacement character */ +#define RFC822_NUL_REPLACEMENT_CHAR 0x80 + struct rfc822_parser_context { const unsigned char *data, *end; string_t *last_comment; + + /* Replace NULs with this character */ + char nul_replacement_char; }; #define IS_ATEXT(c) \ diff --git a/src/lib-mail/test-rfc822-parser.c b/src/lib-mail/test-rfc822-parser.c index 569232afcf..7f0e1b1c5f 100644 --- a/src/lib-mail/test-rfc822-parser.c +++ b/src/lib-mail/test-rfc822-parser.c @@ -50,6 +50,32 @@ static void test_rfc822_parse_comment(void) test_end(); } +static void test_rfc822_parse_comment_nuls(void) +{ + const unsigned char input[] = "(\000a\000\000b\\\000c(\000d)\000)"; + const char output[] = "!a!!b\\!c(!d)!"; + struct rfc822_parser_context parser; + string_t *str = t_str_new(64); + + test_begin("rfc822 parse comment with NULs"); + + rfc822_parser_init(&parser, input, sizeof(input)-1, str); + test_assert(rfc822_skip_comment(&parser) == 0); + /* should be same as input, except the outer () removed */ + test_assert(str_len(str) == sizeof(input)-1-2 && + memcmp(input+1, str_data(str), str_len(str)) == 0); + rfc822_parser_deinit(&parser); + + str_truncate(str, 0); + rfc822_parser_init(&parser, input, sizeof(input)-1, str); + parser.nul_replacement_char = '!'; + test_assert(rfc822_skip_comment(&parser) == 0); + test_assert(strcmp(str_c(str), output) == 0); + rfc822_parser_deinit(&parser); + + test_end(); +} + static void test_rfc822_parse_quoted_string(void) { static const struct { @@ -154,6 +180,7 @@ int main(void) { static void (*const test_functions[])(void) = { test_rfc822_parse_comment, + test_rfc822_parse_comment_nuls, test_rfc822_parse_quoted_string, test_rfc822_parse_domain_literal, test_rfc822_parse_content_param,