dns: introduce dns_name_from_wire_format
This is implemented in various places, but it is better to share this code.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user