dhcp-message: add SIP server option support

The DHCP option 120 (SIP server) option takes a list of addresses or
domain names, and the first byte in the data classifies which type is
stored. Let's extend _addresses() and _domains() to make them support
the SIP server option.
This commit is contained in:
Yu Watanabe
2026-05-13 09:41:24 +09:00
parent c588c31144
commit e178adc6e1
2 changed files with 95 additions and 8 deletions
+54 -5
View File
@@ -191,6 +191,24 @@ int dhcp_message_append_option_addresses(sd_dhcp_message *message, uint8_t code,
if (size_multiply_overflow(sizeof(struct in_addr), n_addr))
return -ENOBUFS;
if (code == SD_DHCP_OPTION_SIP_SERVER) {
if (dhcp_message_has_option(message, SD_DHCP_OPTION_SIP_SERVER))
return -EEXIST;
size_t len = size_add(1, sizeof(struct in_addr) * n_addr);
if (len == SIZE_MAX)
return -ENOBUFS;
_cleanup_free_ uint8_t *buf = new(uint8_t, len);
if (!buf)
return -ENOMEM;
buf[0] = 1; /* 'enc' field, 0: domains, 1: addresses */
memcpy(buf + 1, addr, sizeof(struct in_addr) * n_addr);
return dhcp_message_append_option(message, code, len, buf);
}
return dhcp_message_append_option(message, code, sizeof(struct in_addr) * n_addr, addr);
}
@@ -423,11 +441,22 @@ int dhcp_message_get_option_addresses(sd_dhcp_message *message, uint8_t code, si
assert(message);
assert(ret_n_addr || !ret_addr);
_cleanup_(iovec_done) struct iovec iov = {};
r = dhcp_message_get_option_alloc(message, code, &iov);
_cleanup_(iovec_done) struct iovec iov_free = {};
r = dhcp_message_get_option_alloc(message, code, &iov_free);
if (r < 0)
return r;
struct iovec iov = iov_free;
if (code == SD_DHCP_OPTION_SIP_SERVER) {
if (!iovec_is_set(&iov))
return -EBADMSG;
if (*(uint8_t*) iov.iov_base != 1) /* 'enc' field, 0: domains, 1: addresses */
return -ENODATA;
iovec_inc(&iov, 1);
}
if (iov.iov_len % sizeof(struct in_addr) != 0)
return -EBADMSG;
@@ -435,8 +464,17 @@ int dhcp_message_get_option_addresses(sd_dhcp_message *message, uint8_t code, si
if (n == 0)
return -ENODATA;
if (ret_addr)
*ret_addr = (struct in_addr*) TAKE_PTR(iov.iov_base);
if (ret_addr) {
if (code == SD_DHCP_OPTION_SIP_SERVER) {
struct in_addr *addr = newdup(struct in_addr, iov.iov_base, n);
if (!addr)
return -ENOMEM;
*ret_addr = addr;
} else {
*ret_addr = iov.iov_base;
TAKE_STRUCT(iov_free);
}
}
if (ret_n_addr)
*ret_n_addr = n;
return 0;
@@ -625,7 +663,7 @@ int dhcp_message_get_option_domains(sd_dhcp_message *message, uint8_t code, char
assert(message);
/* This is mostly for SD_DHCP_OPTION_DOMAIN_SEARCH. */
/* This is mostly for SD_DHCP_OPTION_DOMAIN_SEARCH and SD_DHCP_OPTION_SIP_SERVER. */
_cleanup_(iovec_done) struct iovec iov = {};
r = dhcp_message_get_option_alloc(message, code, &iov);
@@ -635,6 +673,17 @@ int dhcp_message_get_option_domains(sd_dhcp_message *message, uint8_t code, char
const uint8_t *buf = iov.iov_base;
size_t len = iov.iov_len;
if (code == SD_DHCP_OPTION_SIP_SERVER) {
if (len == 0)
return -EBADMSG;
if (buf[0] != 0) /* 'enc' field, 0: domains, 1: addresses */
return -ENODATA;
len--;
buf++;
}
_cleanup_strv_free_ char **names = NULL;
size_t n_names = 0;
+41 -3
View File
@@ -68,7 +68,8 @@ static void verify_address(sd_dhcp_message *m, const struct in_addr *expected) {
static void verify_addresses(
sd_dhcp_message *m,
size_t n_ntp, const struct in_addr *ntp) {
size_t n_ntp, const struct in_addr *ntp,
size_t n_sip, const struct in_addr *sip) {
struct in_addr a;
ASSERT_OK(dhcp_message_get_option_be32(m, SD_DHCP_OPTION_NTP_SERVER, &a.s_addr));
@@ -81,6 +82,14 @@ static void verify_addresses(
ASSERT_OK(dhcp_message_get_option_addresses(m, SD_DHCP_OPTION_NTP_SERVER, &n, &addrs));
ASSERT_EQ(n, n_ntp);
ASSERT_EQ(memcmp(addrs, ntp, sizeof(struct in_addr) * n), 0);
ASSERT_ERROR(dhcp_message_get_option_be32(m, SD_DHCP_OPTION_SIP_SERVER, NULL), ENODATA);
ASSERT_ERROR(dhcp_message_get_option_address(m, SD_DHCP_OPTION_SIP_SERVER, NULL), ENODATA);
addrs = mfree(addrs);
ASSERT_OK(dhcp_message_get_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, &n, &addrs));
ASSERT_EQ(n, n_sip);
ASSERT_EQ(memcmp(addrs, sip, sizeof(struct in_addr) * n), 0);
}
static void verify_string(sd_dhcp_message *m, const char *expected) {
@@ -160,6 +169,14 @@ TEST(dhcp_message) {
{ .s_addr = htobe32(0xC0000204) },
};
/* 192.0.2.17 - 20 */
struct in_addr sip[4] = {
{ .s_addr = htobe32(0xC0000211) },
{ .s_addr = htobe32(0xC0000212) },
{ .s_addr = htobe32(0xC0000213) },
{ .s_addr = htobe32(0xC0000214) },
};
sd_dhcp_client_id id = {
.raw = { 1, 3, 3, 3, 3, 3, 3, },
.size = 7,
@@ -242,7 +259,10 @@ TEST(dhcp_message) {
/* multiple addresses */
ASSERT_OK(dhcp_message_append_option_address(m, SD_DHCP_OPTION_NTP_SERVER, &ntp[0]));
ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_NTP_SERVER, ELEMENTSOF(ntp) - 1, ntp + 1));
verify_addresses(m, ELEMENTSOF(ntp), ntp);
ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, ELEMENTSOF(sip), sip));
ASSERT_ERROR(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, ELEMENTSOF(sip), sip), EEXIST);
ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, 0, NULL));
verify_addresses(m, ELEMENTSOF(ntp), ntp, ELEMENTSOF(sip), sip);
/* string */
ASSERT_ERROR(dhcp_message_get_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, NULL), ENODATA);
@@ -316,7 +336,7 @@ TEST(dhcp_message) {
verify_u16(m2, 512);
verify_sec(m2, lease_time);
verify_address(m2, &addr);
verify_addresses(m2, ELEMENTSOF(ntp), ntp);
verify_addresses(m2, ELEMENTSOF(ntp), ntp, ELEMENTSOF(sip), sip);
verify_string(m2, vendor_class);
verify_client_id(m2, &id);
verify_prl(m2, prl);
@@ -346,6 +366,24 @@ static void test_domains_one(size_t len, const uint8_t *data, char * const *expe
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));
strv = strv_free(strv);
_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_OK(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_SIP_SERVER, &strv));
ASSERT_TRUE(strv_equal(strv, expected));
dhcp_message_remove_option(m, SD_DHCP_OPTION_SIP_SERVER);
strv = strv_free(strv);
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_SIP_SERVER, (len + 1) / 2, sip));
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_SIP_SERVER, len + 1 - (len + 1) / 2, sip + (len + 1) / 2));
ASSERT_OK(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_SIP_SERVER, &strv));
ASSERT_TRUE(strv_equal(strv, expected));
}
static void test_domains_fail(size_t len, const uint8_t *data) {