summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/shared/dns-domain.c69
-rw-r--r--src/shared/dns-domain.h1
-rw-r--r--src/test/test-dns-domain.c89
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;