From: Ronan Pigott Date: Tue, 23 Jan 2024 19:29:32 +0000 (-0700) Subject: dns: introduce dns_name_from_wire_format X-Git-Tag: v257-rc1~171^2~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=427166c3b0775e8ea929b6bf68fdd82aac168d3f;p=thirdparty%2Fsystemd.git dns: introduce dns_name_from_wire_format This is implemented in various places, but it is better to share this code. --- diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index ba24a77dad9..48c59b064f3 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 8ad00d6e4bc..edfb2f00ca3 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 d775601dae8..de66199f543 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;