]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dns: introduce dns_name_from_wire_format
authorRonan Pigott <ronan@rjp.ie>
Tue, 23 Jan 2024 19:29:32 +0000 (12:29 -0700)
committerRonan Pigott <ronan@rjp.ie>
Sat, 14 Sep 2024 05:57:50 +0000 (22:57 -0700)
This is implemented in various places, but it is better to share this
code.

src/shared/dns-domain.c
src/shared/dns-domain.h
src/test/test-dns-domain.c

index ba24a77dad993feecbe4d4aa8bfc867b765bc417..48c59b064f3c23f4e5ec2f2ec42d0aadca141982 100644 (file)
@@ -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);
 
index 8ad00d6e4bc5a757ecf6bbe76b2d519f1d826f89..edfb2f00ca367b4cfc8a7aabb9633eeeb60a574d 100644 (file)
@@ -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);
index d775601dae84afb7c93529d0d148f5ebe37dcbc4..de66199f543a7634826c5c1544d16f3b70c90bc4 100644 (file)
@@ -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;