diff options
-rw-r--r-- | src/shared/dns-domain.c | 69 | ||||
-rw-r--r-- | src/shared/dns-domain.h | 1 | ||||
-rw-r--r-- | src/test/test-dns-domain.c | 89 |
3 files changed, 159 insertions, 0 deletions
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index ba24a77dad..48c59b064f 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -898,6 +898,75 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo return out - buffer; } +/* Decode a domain name according to RFC 1035 Section 3.1, without compression */ +int dns_name_from_wire_format(const uint8_t **data, size_t *len, char **ret) { + _cleanup_free_ char *domain = NULL; + const uint8_t *optval; + size_t optlen, n = 0; + int r; + + assert(data); + assert(len); + assert(*data || *len == 0); + assert(ret); + + optval = *data; + optlen = *len; + + for (;;) { + const char *label; + uint8_t c; + + /* Unterminated name */ + if (optlen == 0) + return -EBADMSG; + + /* RFC 1035 § 3.1 total length of encoded name is limited to 255 octets */ + if (*len - optlen > 255) + return -EMSGSIZE; + + c = *optval; + optval++; + optlen--; + + if (c == 0) + /* End label */ + break; + if (c > DNS_LABEL_MAX) + return -EBADMSG; + if (c > optlen) + return -EMSGSIZE; + + /* Literal label */ + label = (const char*) optval; + optval += c; + optlen -= c; + + if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + if (n != 0) + domain[n++] = '.'; + + r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; + } + + if (!GREEDY_REALLOC(domain, n + 1)) + return -ENOMEM; + + domain[n] = '\0'; + + *ret = TAKE_PTR(domain); + *data = optval; + *len = optlen; + + return n; +} + static bool srv_type_label_is_valid(const char *label, size_t n) { assert(label); diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 8ad00d6e4b..edfb2f00ca 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -79,6 +79,7 @@ bool dns_name_is_root(const char *name); bool dns_name_is_single_label(const char *name); int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical); +int dns_name_from_wire_format(const uint8_t **data, size_t *len, char **ret); bool dns_srv_type_is_valid(const char *name); bool dnssd_srv_type_is_valid(const char *name); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index d775601dae..de66199f54 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -119,6 +119,95 @@ TEST(dns_name_to_wire_format) { test_dns_name_to_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", out4, sizeof(out4), sizeof(out4)); } +static void test_dns_name_from_wire_format_one(const char *expect, const uint8_t *what, size_t len, int ret) { + _cleanup_free_ char *name = NULL; + int r; + + log_info("%s, %s, %zu, →%d", what, strnull(expect), len, ret); + + r = dns_name_from_wire_format(&what, &len, &name); + assert_se(r == ret); + + if (r >= 0) { + assert(expect); /* for gcc */ + assert_se(memcmp(name, expect, r) == 0); + } +} + +TEST(dns_name_from_wire_format) { + static const uint8_t in0[] = { 0 }; + static const uint8_t in1[] = { 3, 'f', 'o', 'o', 0 }; + static const uint8_t in2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + static const uint8_t in2_1[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 0, 'b', 'a', 'r', 0 }; + static const uint8_t in3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + static const uint8_t in4[] = { 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 3, 'a', '1', '2', 0 }; /* 255 octets */ + static const uint8_t in5[] = { 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 3, 'a', '1', '2', 0 }; /* 265 octets */ + + test_dns_name_from_wire_format_one("", in0, sizeof(in0), strlen("")); + + test_dns_name_from_wire_format_one("foo", in1, sizeof(in1), strlen("foo")); + test_dns_name_from_wire_format_one(NULL, in1, sizeof(in1) - 1, -EBADMSG); + + test_dns_name_from_wire_format_one("hallo.foo.bar", in2, sizeof(in2), strlen("hallo.foo.bar")); + test_dns_name_from_wire_format_one("hallo.foo", in2_1, sizeof(in2_1), strlen("hallo.foo")); + + test_dns_name_from_wire_format_one("\\032foo.bar", in3, sizeof(in3), strlen("\\032foo.bar")); + + test_dns_name_from_wire_format_one(NULL, in5, sizeof(in5), -EMSGSIZE); + test_dns_name_from_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", in4, sizeof(in4), 253); +} + 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; |