diff options
-rw-r--r-- | src/shared/dns-domain.c | 62 | ||||
-rw-r--r-- | src/shared/dns-domain.h | 1 | ||||
-rw-r--r-- | src/test/test-dns-domain.c | 41 |
3 files changed, 104 insertions, 0 deletions
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 20a44ce4e1..8a472fbcb4 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -114,6 +114,68 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { return r; } +/* @label_terminal: terminal character of a label, updated to point to the terminal character of + * the previous label (always skipping one dot) or to NULL if there are no more + * labels. */ +int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) { + const char *terminal; + int r; + + assert(name); + assert(label_terminal); + assert(dest); + + /* no more labels */ + if (!*label_terminal) { + if (sz >= 1) + *dest = 0; + + return 0; + } + + assert(**label_terminal == '.' || **label_terminal == 0); + + /* skip current terminal character */ + terminal = *label_terminal - 1; + + /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */ + for (;;) { + if (terminal < name) { + /* reached the first label, so indicate that there are no more */ + terminal = NULL; + break; + } + + /* find the start of the last label */ + if (*terminal == '.') { + const char *y; + unsigned slashes = 0; + + for (y = terminal - 1; y >= name && *y == '\\'; y--) + slashes ++; + + if (slashes % 2 == 0) { + /* the '.' was not escaped */ + name = terminal + 1; + break; + } else { + terminal = y; + continue; + } + } + + terminal --; + } + + r = dns_label_unescape(&name, dest, sz); + if (r < 0) + return r; + + *label_terminal = terminal; + + return r; +} + int dns_label_escape(const char *p, size_t l, char **ret) { _cleanup_free_ char *s = NULL; char *q; diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 00caf5d700..5728ce34bb 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -29,6 +29,7 @@ #define DNS_NAME_MAX 255 int dns_label_unescape(const char **name, char *dest, size_t sz); +int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); int dns_label_escape(const char *p, size_t l, char **ret); int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 527cdd3b54..9a5a2a1c23 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -50,6 +50,46 @@ static void test_dns_label_unescape(void) { test_dns_label_unescape_one("foobar.", "foobar", 20, 6); } +static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) { + char buffer[buffer_sz]; + const char *label; + int r; + + label = what + strlen(what); + + r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz); + assert_se(r == ret1); + if (r >= 0) + assert_se(streq(buffer, expect1)); + + r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz); + assert_se(r == ret2); + if (r >= 0) + assert_se(streq(buffer, expect2)); +} + +static void test_dns_label_unescape_suffix(void) { + test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0); + test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOSPC, -ENOSPC); + test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0); + test_dns_label_unescape_suffix_one("hallo\\.foobar", "hallo.foobar", "", 20, 12, 0); + test_dns_label_unescape_suffix_one("hallo.foobar", "foobar", "hallo", 10, 6, 5); + test_dns_label_unescape_suffix_one("hallo.foobar\n", "foobar", "foobar", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo ", "", 20, 7, 0); + test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0); + test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0); + test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL); + test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6); + test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0); + test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3); + test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL); + test_dns_label_unescape_suffix_one("foo...bar", "bar", "", 20, 3, -EINVAL); + test_dns_label_unescape_suffix_one("foo\\.bar", "foo.bar", "", 20, 7, 0); + test_dns_label_unescape_suffix_one("foo\\\\.bar", "bar", "foo\\", 20, 3, 4); + test_dns_label_unescape_suffix_one("foo\\\\\\.bar", "foo\\.bar", "", 20, 8, 0); +} + static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) { _cleanup_free_ char *t = NULL; int r; @@ -180,6 +220,7 @@ static void test_dns_name_reverse(void) { int main(int argc, char *argv[]) { test_dns_label_unescape(); + test_dns_label_unescape_suffix(); test_dns_label_escape(); test_dns_name_normalize(); test_dns_name_equal(); |