]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-smtp: smtp-address - Add smtp_address_parse_any().
authorStephan Bosch <stephan.bosch@open-xchange.com>
Tue, 10 Sep 2019 21:20:26 +0000 (23:20 +0200)
committerStephan Bosch <stephan.bosch@open-xchange.com>
Fri, 4 Oct 2019 11:59:35 +0000 (13:59 +0200)
This allows parsing any RFC5321 address as a string, even severely broken ones.

src/lib-smtp/smtp-address.c
src/lib-smtp/smtp-address.h
src/lib-smtp/test-smtp-address.c

index 74421273117ffda8091bc0e5c0576d92513c028f..1c64c6ad1f89b29e1d51f60739566d8ac16f3f17 100644 (file)
@@ -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
  */
index f539fc443313e7e7671b7554d4f18592fe5f42db..e1adabd3fa15e2f0acfc7672255c2d5e0ac7702e 100644 (file)
@@ -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
  */
index 0623f5f77b8a8b8ea843015df38391827e5db562..b23dd891671469f2c9148e007afa4f14a4fd7dc6 100644 (file)
@@ -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 = "<frop@example.com>",
+               .address = "frop@example.com",
+               .pos = 18,
+               .ret = 0,
+       },
+       {
+               .input = "<frop@example.com> ",
+               .address = "frop@example.com",
+               .pos = 18,
+               .ret = 0,
+       },
+       {
+               .input = "<frop@example.com",
+               .pos = 0,
+               .ret = -1,
+       },
+       {
+               .input = "<frop@example.com ",
+               .pos = 0,
+               .ret = -1,
+       },
+       {
+               .input = "fr\"op@example.com",
+               .address = "fr\"op@example.com",
+               .pos = 17,
+               .ret = 0,
+       },
+       {
+               .input = "fr\"op@example.com ",
+               .address = "fr\"op@example.com",
+               .pos = 17,
+               .ret = 0,
+       },
+       {
+               .input = "fr<op@example.com",
+               .address = "fr<op@example.com",
+               .pos = 17,
+               .ret = 0,
+       },
+       {
+               .input = "fr<op@example.com ",
+               .address = "fr<op@example.com",
+               .pos = 17,
+               .ret = 0,
+       },
+       {
+               .input = "\"frop\"@example.com",
+               .address = "\"frop\"@example.com",
+               .pos = 18,
+               .ret = 0,
+       },
+       {
+               .input = "\"frop\"@example.com ",
+               .address = "\"frop\"@example.com",
+               .pos = 18,
+               .ret = 0,
+       },
+       {
+               .input = "\"frop\\\"@example.com",
+               .pos = 0,
+               .ret = -1,
+       },
+       {
+               .input = "\"frop\\\"@example.com ",
+               .pos = 0,
+               .ret = -1,
+       },
+       {
+               .input = "<\"fr>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);