From: Stephan Bosch Date: Tue, 10 Sep 2019 21:20:26 +0000 (+0200) Subject: lib-smtp: smtp-address - Add smtp_address_parse_any(). X-Git-Tag: 2.3.9~130 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6ba323b96ed730d23266cd950fa2ff2acf25460a;p=thirdparty%2Fdovecot%2Fcore.git lib-smtp: smtp-address - Add smtp_address_parse_any(). This allows parsing any RFC5321 address as a string, even severely broken ones. --- diff --git a/src/lib-smtp/smtp-address.c b/src/lib-smtp/smtp-address.c index 7442127311..1c64c6ad1f 100644 --- a/src/lib-smtp/smtp-address.c +++ b/src/lib-smtp/smtp-address.c @@ -497,6 +497,77 @@ void smtp_address_detail_parse_temp(const char *delimiters, address, username_r, delim_r, detail_r); } +int smtp_address_parse_any(const char *in, const char **address_r, + const char **endp_r) +{ + const unsigned char *p, *pend, *poffset; + bool path = FALSE; + bool quoted = FALSE; + + if (endp_r != NULL) + *endp_r = in; + + poffset = p = (const unsigned char *)in; + pend = p + strlen(in); + if (*p == '<') { + path = TRUE; + p++; + poffset = p; + } + if (*p == '"') { + quoted = TRUE; + p++; + } + + while (p < pend) { + if (quoted && *p == '\\') { + p++; + if (p == pend || *p < 0x20) + return -1; + p++; + if (p == pend) + break; + } + switch (*p) { + case '"': + quoted = FALSE; + break; + case ' ': + if (!quoted) { + if (path) + return -1; + if (address_r != NULL) + *address_r = t_strdup_until(poffset, p); + if (endp_r != NULL) + *endp_r = (const char *)p; + return 0; + } + break; + case '>': + if (!quoted) { + if (address_r != NULL) + *address_r = t_strdup_until(poffset, p); + if (endp_r != NULL) + *endp_r = (const char *)(p + 1); + return 0; + } + break; + default: + if (*p < 0x20) + return -1; + break; + } + p++; + } + if (quoted || path) + return -1; + if (address_r != NULL) + *address_r = t_strdup_until(poffset, p); + if (endp_r != NULL) + *endp_r = (const char *)p; + return 0; +} + /* * SMTP address construction */ diff --git a/src/lib-smtp/smtp-address.h b/src/lib-smtp/smtp-address.h index f539fc4433..e1adabd3fa 100644 --- a/src/lib-smtp/smtp-address.h +++ b/src/lib-smtp/smtp-address.h @@ -79,6 +79,17 @@ void smtp_address_detail_parse_temp(const char *delimiters, const char **username_r, char *delim_r, const char **detail_r); +/* Parse any (possibly broken) address on the input to the best of our ability + until end of input or unquoted ` '. Things that are truly evil (unending + quoted string, control characters and a path without a closing '>') will + still fail and return -1. If the parse was successful, it will return 0. + The parsed address string is returned in address_r. Any outer < and > are + omitted in the parsed address. The endp_r parameter is used to return a + pointer to the end of the path string, so that the caller can continue + parsing from there.*/ +int smtp_address_parse_any(const char *in, const char **address_r, + const char **endp_r); + /* * SMTP address construction */ diff --git a/src/lib-smtp/test-smtp-address.c b/src/lib-smtp/test-smtp-address.c index 0623f5f77b..b23dd89167 100644 --- a/src/lib-smtp/test-smtp-address.c +++ b/src/lib-smtp/test-smtp-address.c @@ -2,6 +2,7 @@ #include "test-lib.h" #include "str.h" +#include "str-sanitize.h" #include "test-common.h" #include "smtp-address.h" @@ -977,6 +978,192 @@ static void test_smtp_address_detail_parse(void) } T_END; } +/* + * Skip address tests + */ + +struct any_address_parse_test { + const char *input; + const char *address; + size_t pos; + int ret; +}; + +static const struct any_address_parse_test +any_address_parse_tests[] = { + { + .input = "", + .address = "", + .pos = 0, + .ret = 0, + }, + { + .input = " ", + .address = "", + .pos = 0, + .ret = 0, + }, + { + .input = "frop@example.com", + .address = "frop@example.com", + .pos = 16, + .ret = 0, + }, + { + .input = "frop@example.com ", + .address = "frop@example.com", + .pos = 16, + .ret = 0, + }, + { + .input = "", + .address = "frop@example.com", + .pos = 18, + .ret = 0, + }, + { + .input = " ", + .address = "frop@example.com", + .pos = 18, + .ret = 0, + }, + { + .input = "op\"@example.com>", + .address = "\"fr>op\"@example.com", + .pos = 21, + .ret = 0, + }, + { + .input = "<\"fr>op\"@example.com> ", + .address = "\"fr>op\"@example.com", + .pos = 21, + .ret = 0, + }, + { + .input = "<\"fr>op\"@example.com", + .pos = 0, + .ret = -1, + }, + { + .input = "<\"fr>op\"@example.com ", + .pos = 0, + .ret = -1, + }, + { + .input = "<\"frop\">", + .address = "\"frop\"", + .pos = 8, + .ret = 0, + }, + { + .input = "<\"frop\"> ", + .address = "\"frop\"", + .pos = 8, + .ret = 0, + }, + { + .input = "<\"frop\"", + .pos = 0, + .ret = -1, + }, + { + .input = "<\"frop\" ", + .pos = 0, + .ret = -1, + }, + { + .input = "\"frop\\\" ", + .pos = 0, + .ret = -1, + }, + { + .input = "\"frop\\\"", + .pos = 0, + .ret = -1, + }, +}; + +unsigned int any_address_parse_tests_count = + N_ELEMENTS(any_address_parse_tests); + +static void test_smtp_parse_any_address(void) +{ + unsigned int i; + + for (i = 0; i < any_address_parse_tests_count; i++) T_BEGIN { + const struct any_address_parse_test *test; + const char *address = NULL, *pos = NULL; + int ret; + + test = &any_address_parse_tests[i]; + ret = smtp_address_parse_any(test->input, &address, &pos); + + test_begin(t_strdup_printf("smtp parse any [%d]", i)); + test_out_quiet(t_strdup_printf("parse(\"%s\")", + str_sanitize(test->input, 256)), + (ret == test->ret) && + ((size_t)(pos - test->input) == test->pos) && + (null_strcmp(test->address, address ) == 0)); + test_end(); + } T_END; +} + /* * Tests */ @@ -991,6 +1178,7 @@ int main(void) test_smtp_path_parse_invalid, test_smtp_username_parse_invalid, test_smtp_address_detail_parse, + test_smtp_parse_any_address, NULL }; return test_run(test_functions);