dhcp-message: introduce dhcp_message_get_option_domains()
This is for e.g. DHCP option 119 (domain search).
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "dhcp-client-id-internal.h"
|
||||
#include "dhcp-message.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#include "dns-def.h"
|
||||
#include "dns-domain.h"
|
||||
#include "errno-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
@@ -619,6 +620,108 @@ int dhcp_message_get_option_hostname(sd_dhcp_message *message, char **ret) {
|
||||
return dhcp_message_get_option_dns_name(message, SD_DHCP_OPTION_HOST_NAME, ret);
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_domains(sd_dhcp_message *message, uint8_t code, char ***ret) {
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
/* This is mostly for SD_DHCP_OPTION_DOMAIN_SEARCH. */
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = dhcp_message_get_option_alloc(message, code, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const uint8_t *buf = iov.iov_base;
|
||||
size_t len = iov.iov_len;
|
||||
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
size_t n_names = 0;
|
||||
|
||||
_cleanup_free_ char *name = NULL;
|
||||
size_t n = 0;
|
||||
|
||||
for (size_t pos = 0, jump_barrier = 0, next_chunk = 0; pos < len;) {
|
||||
uint8_t c = buf[pos++];
|
||||
|
||||
if (c == 0) {
|
||||
/* End of name */
|
||||
|
||||
if (!string_is_safe(name, /* flags= */ 0))
|
||||
return -EBADMSG;
|
||||
|
||||
_cleanup_free_ char *normalized = NULL;
|
||||
r = normalize_dns_name(name, &normalized);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_consume_with_size(&names, &n_names, TAKE_PTR(normalized));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (next_chunk != 0)
|
||||
pos = next_chunk;
|
||||
|
||||
next_chunk = 0;
|
||||
jump_barrier = pos;
|
||||
|
||||
name = mfree(name);
|
||||
n = 0;
|
||||
|
||||
} else if (c <= 63) {
|
||||
/* Literal label */
|
||||
|
||||
const char *label = (const char*) (buf + pos);
|
||||
pos += c;
|
||||
|
||||
if (pos >= len)
|
||||
return -EBADMSG;
|
||||
|
||||
if (!GREEDY_REALLOC(name, n + 1 + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
if (n != 0)
|
||||
name[n++] = '.';
|
||||
|
||||
r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n += r;
|
||||
|
||||
} else if (FLAGS_SET(c, 0xc0)) {
|
||||
/* Pointer */
|
||||
|
||||
if (pos >= len) /* pointer is 2 bytes, hence we need to read at least one more byte. */
|
||||
return -EBADMSG;
|
||||
|
||||
/* Save the current location so we don't end up re-parsing what's parsed so far. */
|
||||
if (next_chunk == 0)
|
||||
next_chunk = pos + 1;
|
||||
|
||||
pos = ((size_t) (c & ~0xc0) << 8) | ((size_t) buf[pos]);
|
||||
|
||||
/* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
|
||||
if (pos >= jump_barrier)
|
||||
return -EBADMSG;
|
||||
|
||||
jump_barrier = pos;
|
||||
|
||||
} else
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (!isempty(name)) /* trailing garbage?? Should not happen, but for safety. */
|
||||
return -EBADMSG;
|
||||
|
||||
if (strv_isempty(names))
|
||||
return -EBADMSG;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(names);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_sub_tlv(sd_dhcp_message *message, uint8_t code, TLVFlag flags, TLV **ret) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ int dhcp_message_get_option_parameter_request_list(sd_dhcp_message *message, Set
|
||||
int dhcp_message_get_option_fqdn(sd_dhcp_message *message, uint8_t *ret_flags, char **ret_fqdn);
|
||||
int dhcp_message_get_option_dns_name(sd_dhcp_message *message, uint8_t code, char **ret);
|
||||
int dhcp_message_get_option_hostname(sd_dhcp_message *message, char **ret);
|
||||
int dhcp_message_get_option_domains(sd_dhcp_message *message, uint8_t code, char ***ret);
|
||||
int dhcp_message_get_option_sub_tlv(sd_dhcp_message *message, uint8_t code, TLVFlag flags, TLV **ret);
|
||||
int dhcp_message_get_option_length_prefixed_data(sd_dhcp_message *message, uint8_t code, size_t length_size, struct iovec_wrapper *ret);
|
||||
|
||||
|
||||
@@ -330,4 +330,122 @@ TEST(dhcp_message) {
|
||||
ASSERT_TRUE(iovw_equal(&iovw, &iovw2));
|
||||
}
|
||||
|
||||
static void test_domains_one(size_t len, const uint8_t *data, char * const *expected) {
|
||||
_cleanup_strv_free_ char **strv = NULL;
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
|
||||
ASSERT_OK(dhcp_message_new(&m));
|
||||
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_DOMAIN_SEARCH, len, data));
|
||||
ASSERT_OK(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_DOMAIN_SEARCH, &strv));
|
||||
ASSERT_TRUE(strv_equal(strv, expected));
|
||||
|
||||
dhcp_message_remove_option(m, SD_DHCP_OPTION_DOMAIN_SEARCH);
|
||||
strv = strv_free(strv);
|
||||
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_DOMAIN_SEARCH, len / 2, data));
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_DOMAIN_SEARCH, len - len / 2, data + len / 2));
|
||||
ASSERT_OK(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_DOMAIN_SEARCH, &strv));
|
||||
ASSERT_TRUE(strv_equal(strv, expected));
|
||||
}
|
||||
|
||||
static void test_domains_fail(size_t len, const uint8_t *data) {
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
|
||||
ASSERT_OK(dhcp_message_new(&m));
|
||||
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_DOMAIN_SEARCH, len, data));
|
||||
ASSERT_ERROR(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_DOMAIN_SEARCH, /* ret= */ NULL), EBADMSG);
|
||||
|
||||
dhcp_message_remove_option(m, SD_DHCP_OPTION_DOMAIN_SEARCH);
|
||||
|
||||
_cleanup_free_ uint8_t *sip = new(uint8_t, len + 1);
|
||||
sip[0] = 0;
|
||||
memcpy(sip + 1, data, len);
|
||||
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_SIP_SERVER, len + 1, sip));
|
||||
ASSERT_ERROR(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_SIP_SERVER, /* ret= */ NULL), EBADMSG);
|
||||
}
|
||||
|
||||
TEST(domains) {
|
||||
/* simple */
|
||||
static uint8_t simple[] = {
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
4, 'h', 'o', 'g', 'e',
|
||||
3, 'f', 'o', 'o',
|
||||
0,
|
||||
};
|
||||
test_domains_one(ELEMENTSOF(simple), simple,
|
||||
STRV_MAKE("example.com", "hoge.foo"));
|
||||
|
||||
/* compressed */
|
||||
static uint8_t compressed[] = {
|
||||
4, 'h', 'o', 'g', 'e',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
3, 'f', 'o', 'o',
|
||||
0xc0, 5,
|
||||
3, 'b', 'a', 'r',
|
||||
0xc0, 18,
|
||||
3, 'b', 'a', 'z',
|
||||
0xc0, 0,
|
||||
};
|
||||
test_domains_one(ELEMENTSOF(compressed), compressed,
|
||||
STRV_MAKE("hoge.example.com", "foo.example.com", "bar.foo.example.com", "baz.hoge.example.com"));
|
||||
|
||||
/* invalid pointer */
|
||||
static uint8_t invalid[] = {
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
3, 'f', 'o', 'o',
|
||||
0xc0, 0xff,
|
||||
};
|
||||
test_domains_fail(ELEMENTSOF(invalid), invalid);
|
||||
|
||||
/* forward pointer */
|
||||
static uint8_t forward[] = {
|
||||
4, 'h', 'o', 'g', 'e',
|
||||
0xc0, 11,
|
||||
3, 'f', 'o', 'o',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
};
|
||||
test_domains_fail(ELEMENTSOF(forward), forward);
|
||||
|
||||
/* infinite loop */
|
||||
static uint8_t loop1[] = {
|
||||
0xc0, 0x00,
|
||||
};
|
||||
test_domains_fail(ELEMENTSOF(loop1), loop1);
|
||||
|
||||
static uint8_t loop2[] = {
|
||||
0xc0, 0x02,
|
||||
0xc0, 0x00,
|
||||
};
|
||||
test_domains_fail(ELEMENTSOF(loop2), loop2);
|
||||
|
||||
static uint8_t loop3[] = {
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
0xc0, 0x00,
|
||||
};
|
||||
test_domains_fail(ELEMENTSOF(loop3), loop3);
|
||||
|
||||
/* unterminated */
|
||||
static uint8_t unterminated[] = {
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
};
|
||||
test_domains_fail(ELEMENTSOF(unterminated), unterminated);
|
||||
|
||||
/* truncated label */
|
||||
static uint8_t truncated[] = {
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o',
|
||||
};
|
||||
test_domains_fail(ELEMENTSOF(truncated), truncated);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
Reference in New Issue
Block a user