dhcp-message: introduce several more functions to parse/append DHCP options (#42063)
This commit is contained in:
@@ -12,12 +12,6 @@
|
||||
#include "sd-forward.h"
|
||||
#include "list.h"
|
||||
|
||||
struct sd_dhcp_route {
|
||||
struct in_addr dst_addr;
|
||||
struct in_addr gw_addr;
|
||||
unsigned char dst_prefixlen;
|
||||
};
|
||||
|
||||
struct sd_dhcp_raw_option {
|
||||
LIST_FIELDS(struct sd_dhcp_raw_option, options);
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
#include "dhcp-client-id-internal.h"
|
||||
#include "dhcp-message.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#include "dhcp-route.h"
|
||||
#include "dns-def.h"
|
||||
#include "dns-domain.h"
|
||||
#include "dns-resolver-internal.h"
|
||||
#include "errno-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "iovec-wrapper.h"
|
||||
#include "ip-util.h"
|
||||
@@ -17,6 +21,7 @@
|
||||
#include "set.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
static sd_dhcp_message* dhcp_message_free(sd_dhcp_message *message) {
|
||||
if (!message)
|
||||
@@ -190,6 +195,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);
|
||||
}
|
||||
|
||||
@@ -208,6 +231,138 @@ int dhcp_message_append_option_string(sd_dhcp_message *message, uint8_t code, co
|
||||
return dhcp_message_append_option(message, code, strlen(data), data);
|
||||
}
|
||||
|
||||
static int dhcp_message_append_option_static_routes(sd_dhcp_message *message, size_t n_routes, const sd_dhcp_route *routes) {
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(routes || n_routes == 0);
|
||||
|
||||
if (n_routes == 0)
|
||||
return 0;
|
||||
|
||||
if (size_multiply_overflow(2 * sizeof(struct in_addr), n_routes))
|
||||
return -ENOBUFS;
|
||||
|
||||
_cleanup_free_ struct in_addr *buf = new(struct in_addr, 2 * n_routes);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
size_t count = 0;
|
||||
FOREACH_ARRAY(route, routes, n_routes) {
|
||||
uint8_t prefixlen;
|
||||
r = in4_addr_default_prefixlen(&route->dst_addr, &prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (prefixlen != route->dst_prefixlen)
|
||||
return -EINVAL;
|
||||
|
||||
struct in_addr dst = route->dst_addr;
|
||||
(void) in4_addr_mask(&dst, prefixlen);
|
||||
|
||||
buf[count++] = dst;
|
||||
buf[count++] = route->gw_addr;
|
||||
}
|
||||
|
||||
assert(count == 2 * n_routes);
|
||||
|
||||
return dhcp_message_append_option_addresses(message, SD_DHCP_OPTION_STATIC_ROUTE, 2 * n_routes, buf);
|
||||
}
|
||||
|
||||
static int dhcp_message_append_option_classless_static_routes(sd_dhcp_message *message, uint8_t code, size_t n_routes, const sd_dhcp_route *routes) {
|
||||
assert(message);
|
||||
assert(routes || n_routes == 0);
|
||||
assert(IN_SET(code,
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,
|
||||
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE));
|
||||
|
||||
if (n_routes == 0)
|
||||
return 0;
|
||||
|
||||
if (size_multiply_overflow(1 + 2 * sizeof(struct in_addr), n_routes))
|
||||
return -ENOBUFS;
|
||||
|
||||
_cleanup_free_ uint8_t *buf = new(uint8_t, (1 + 2 * sizeof(struct in_addr)) * n_routes);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
uint8_t *p = buf;
|
||||
FOREACH_ARRAY(route, routes, n_routes) {
|
||||
if (route->dst_prefixlen > sizeof(struct in_addr) * 8)
|
||||
return -EINVAL;
|
||||
|
||||
*p++ = route->dst_prefixlen;
|
||||
struct in_addr dst = route->dst_addr;
|
||||
(void) in4_addr_mask(&dst, route->dst_prefixlen);
|
||||
p = mempcpy(p, &dst, DIV_ROUND_UP(route->dst_prefixlen, 8));
|
||||
p = mempcpy(p, &route->gw_addr, sizeof(struct in_addr));
|
||||
}
|
||||
|
||||
return dhcp_message_append_option(message, code, p - buf, buf);
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_routes(sd_dhcp_message *message, uint8_t code, size_t n_routes, const sd_dhcp_route *routes) {
|
||||
switch (code) {
|
||||
case SD_DHCP_OPTION_STATIC_ROUTE:
|
||||
return dhcp_message_append_option_static_routes(message, n_routes, routes);
|
||||
case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
|
||||
case SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE:
|
||||
return dhcp_message_append_option_classless_static_routes(message, code, n_routes, routes);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t ipv4masklen,
|
||||
uint8_t prefixlen,
|
||||
const struct in6_addr *prefix,
|
||||
size_t n_br_addresses,
|
||||
const struct in_addr *br_addresses) {
|
||||
|
||||
assert(message);
|
||||
assert(prefix);
|
||||
assert(n_br_addresses == 0 || br_addresses);
|
||||
|
||||
/* See RFC 5969 Section 7.1.1 and dhcp_message_get_option_6rd() below. */
|
||||
|
||||
if (dhcp_message_has_option(message, SD_DHCP_OPTION_6RD))
|
||||
return -EEXIST;
|
||||
|
||||
if (ipv4masklen > 32)
|
||||
return -EINVAL;
|
||||
|
||||
if (32 - ipv4masklen + prefixlen > 128)
|
||||
return -EINVAL;
|
||||
|
||||
if (n_br_addresses == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (size_multiply_overflow(sizeof(struct in_addr), n_br_addresses))
|
||||
return -ENOBUFS;
|
||||
|
||||
size_t buflen = size_add(2 + sizeof(struct in6_addr), sizeof(struct in_addr) * n_br_addresses);
|
||||
if (buflen == SIZE_MAX)
|
||||
return -ENOBUFS;
|
||||
|
||||
_cleanup_free_ uint8_t *buf = new(uint8_t, buflen);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
uint8_t *p = buf;
|
||||
*p++ = ipv4masklen;
|
||||
*p++ = prefixlen;
|
||||
|
||||
struct in6_addr masked = *prefix;
|
||||
(void) in6_addr_mask(&masked, prefixlen);
|
||||
p = mempcpy(p, &masked, sizeof(struct in6_addr));
|
||||
|
||||
memcpy(p, br_addresses, n_br_addresses * sizeof(struct in_addr));
|
||||
|
||||
return dhcp_message_append_option(message, SD_DHCP_OPTION_6RD, buflen, buf);
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_client_id(sd_dhcp_message *message, const sd_dhcp_client_id *id) {
|
||||
assert(message);
|
||||
assert(id);
|
||||
@@ -306,6 +461,46 @@ int dhcp_message_append_option_hostname(sd_dhcp_message *message, uint8_t flags,
|
||||
return dhcp_message_append_option_fqdn(message, flags, is_client, hostname);
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_sub_tlv(sd_dhcp_message *message, uint8_t code, const TLV *tlv) {
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
if (tlv_isempty(tlv))
|
||||
return 0;
|
||||
|
||||
if (dhcp_message_has_option(message, code))
|
||||
return -EEXIST;
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = tlv_build(tlv, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dhcp_message_append_option(message, code, iov.iov_len, iov.iov_base);
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_length_prefixed_data(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t code,
|
||||
size_t length_size,
|
||||
const struct iovec_wrapper *iovw) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = iovw_merge(iovw, length_size, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!iovec_is_set(&iov))
|
||||
return 0;
|
||||
|
||||
return dhcp_message_append_option(message, code, iov.iov_len, iov.iov_base);
|
||||
}
|
||||
|
||||
int dhcp_message_get_option(sd_dhcp_message *message, uint8_t code, size_t length, void *ret) {
|
||||
int r;
|
||||
|
||||
@@ -382,11 +577,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;
|
||||
|
||||
@@ -394,8 +600,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;
|
||||
@@ -433,6 +648,210 @@ int dhcp_message_get_option_string(sd_dhcp_message *message, uint8_t code, char
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp_message_get_option_static_routes(sd_dhcp_message *message, size_t *ret_n_routes, sd_dhcp_route **ret_routes) {
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
size_t n;
|
||||
_cleanup_free_ struct in_addr *addrs = NULL;
|
||||
r = dhcp_message_get_option_addresses(message, SD_DHCP_OPTION_STATIC_ROUTE, &n, &addrs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (n % 2 != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
_cleanup_free_ sd_dhcp_route *routes = NULL;
|
||||
size_t n_routes = 0;
|
||||
|
||||
for (size_t i = 0; i < n; i += 2) {
|
||||
struct in_addr dst = addrs[i];
|
||||
|
||||
uint8_t prefixlen;
|
||||
if (in4_addr_default_prefixlen(&dst, &prefixlen) < 0)
|
||||
continue;
|
||||
|
||||
(void) in4_addr_mask(&dst, prefixlen);
|
||||
|
||||
/* RFC 2132 section 5.8:
|
||||
* The default route (0.0.0.0) is an illegal destination for a static route.*/
|
||||
if (in4_addr_is_null(&dst))
|
||||
continue;
|
||||
|
||||
if (!ret_routes) {
|
||||
n_routes++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(routes, n_routes + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
routes[n_routes++] = (struct sd_dhcp_route) {
|
||||
.dst_addr = dst,
|
||||
.gw_addr = addrs[i + 1],
|
||||
.dst_prefixlen = prefixlen,
|
||||
};
|
||||
}
|
||||
|
||||
if (n_routes == 0)
|
||||
return -ENODATA;
|
||||
|
||||
if (ret_routes)
|
||||
*ret_routes = TAKE_PTR(routes);
|
||||
if (ret_n_routes)
|
||||
*ret_n_routes = n_routes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp_message_get_option_classless_static_routes(sd_dhcp_message *message, uint8_t code, size_t *ret_n_routes, sd_dhcp_route **ret_routes) {
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(IN_SET(code,
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,
|
||||
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE));
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = dhcp_message_get_option_alloc(message, code, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ sd_dhcp_route *routes = NULL;
|
||||
size_t n_routes = 0;
|
||||
|
||||
for (struct iovec i = iov; iovec_is_set(&i);) {
|
||||
uint8_t prefixlen = *(uint8_t*) i.iov_base;
|
||||
iovec_inc(&i, 1);
|
||||
|
||||
if (prefixlen > 32)
|
||||
return -EBADMSG;
|
||||
|
||||
size_t n = DIV_ROUND_UP(prefixlen, 8);
|
||||
if (n > i.iov_len)
|
||||
return -EBADMSG;
|
||||
|
||||
struct in_addr dst = {};
|
||||
memcpy_safe(&dst, i.iov_base, n);
|
||||
(void) in4_addr_mask(&dst, prefixlen);
|
||||
iovec_inc(&i, n);
|
||||
|
||||
if (i.iov_len < sizeof(struct in_addr))
|
||||
return -EBADMSG;
|
||||
|
||||
struct in_addr gw;
|
||||
memcpy(&gw, i.iov_base, sizeof(struct in_addr));
|
||||
iovec_inc(&i, sizeof(struct in_addr));
|
||||
|
||||
if (!ret_routes) {
|
||||
n_routes++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(routes, n_routes + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
routes[n_routes++] = (struct sd_dhcp_route) {
|
||||
.dst_addr = dst,
|
||||
.gw_addr = gw,
|
||||
.dst_prefixlen = prefixlen,
|
||||
};
|
||||
}
|
||||
|
||||
if (n_routes == 0)
|
||||
return -ENODATA;
|
||||
|
||||
if (ret_routes)
|
||||
*ret_routes = TAKE_PTR(routes);
|
||||
if (ret_n_routes)
|
||||
*ret_n_routes = n_routes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_routes(sd_dhcp_message *message, uint8_t code, size_t *ret_n_routes, sd_dhcp_route **ret_routes) {
|
||||
switch (code) {
|
||||
case SD_DHCP_OPTION_STATIC_ROUTE:
|
||||
return dhcp_message_get_option_static_routes(message, ret_n_routes, ret_routes);
|
||||
case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
|
||||
case SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE:
|
||||
return dhcp_message_get_option_classless_static_routes(message, code, ret_n_routes, ret_routes);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t *ret_ipv4masklen,
|
||||
uint8_t *ret_prefixlen,
|
||||
struct in6_addr *ret_prefix,
|
||||
size_t *ret_n_br_addresses,
|
||||
struct in_addr **ret_br_addresses) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(ret_n_br_addresses || !ret_br_addresses);
|
||||
|
||||
/* See RFC 5969 Section 7.1.1 */
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = dhcp_message_get_option_alloc(message, SD_DHCP_OPTION_6RD, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
|
||||
if (iov.iov_len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
|
||||
(iov.iov_len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
size_t n_br_addresses = (iov.iov_len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
|
||||
assert(n_br_addresses > 0); /* We have already checked that in the above. */
|
||||
|
||||
const uint8_t *p = iov.iov_base;
|
||||
|
||||
/* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
|
||||
* within a given 6rd domain. This may be any value between 0 and 32. Any value
|
||||
* greater than 32 is invalid. */
|
||||
uint8_t ipv4masklen = *p++;
|
||||
if (ipv4masklen > 32)
|
||||
return -EBADMSG;
|
||||
|
||||
/* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
|
||||
* purpose of bounds checking by DHCP option processing, the sum of
|
||||
* (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
|
||||
uint8_t prefixlen = *p++;
|
||||
if (32 - ipv4masklen + prefixlen > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
/* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
|
||||
* The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
|
||||
* MUST be initialized to zero by the sender and ignored by the receiver. */
|
||||
struct in6_addr prefix;
|
||||
memcpy(&prefix, p, sizeof(struct in6_addr));
|
||||
(void) in6_addr_mask(&prefix, prefixlen);
|
||||
p += sizeof(struct in6_addr);
|
||||
|
||||
/* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relays for a given 6rd domain. */
|
||||
if (ret_br_addresses) {
|
||||
struct in_addr *br_addresses = newdup(struct in_addr, p, n_br_addresses);
|
||||
if (!br_addresses)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_br_addresses = br_addresses;
|
||||
}
|
||||
|
||||
if (ret_ipv4masklen)
|
||||
*ret_ipv4masklen = ipv4masklen;
|
||||
if (ret_prefixlen)
|
||||
*ret_prefixlen = prefixlen;
|
||||
if (ret_prefix)
|
||||
*ret_prefix = prefix;
|
||||
if (ret_n_br_addresses)
|
||||
*ret_n_br_addresses = n_br_addresses;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_client_id(sd_dhcp_message *message, sd_dhcp_client_id *ret) {
|
||||
int r;
|
||||
|
||||
@@ -579,6 +998,331 @@ 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 and SD_DHCP_OPTION_SIP_SERVER. */
|
||||
|
||||
_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;
|
||||
|
||||
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;
|
||||
|
||||
_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;
|
||||
|
||||
assert(message);
|
||||
assert(!FLAGS_SET(flags, TLV_TEMPORARY));
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = dhcp_message_get_option_alloc(message, code, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(tlv_unrefp) TLV *tlv = tlv_new(flags);
|
||||
if (!tlv)
|
||||
return -ENOMEM;
|
||||
|
||||
r = tlv_parse(tlv, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (tlv_isempty(tlv))
|
||||
return -ENODATA;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(tlv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_length_prefixed_data(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t code,
|
||||
size_t length_size,
|
||||
struct iovec_wrapper *ret) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = dhcp_message_get_option_alloc(message, code, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
|
||||
r = iovec_split(&iov, length_size, &iovw);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (iovw_isempty(&iovw))
|
||||
return -ENODATA;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_STRUCT(iovw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_dnr_one(const struct iovec *iov, sd_dns_resolver *ret) {
|
||||
int r;
|
||||
|
||||
assert(iovec_is_set(iov));
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(sd_dns_resolver_done) sd_dns_resolver resolver = {};
|
||||
struct iovec i = *iov;
|
||||
|
||||
/* service priority */
|
||||
if (i.iov_len < sizeof(be16_t))
|
||||
return -EBADMSG;
|
||||
|
||||
resolver.priority = unaligned_read_be16(i.iov_base);
|
||||
iovec_inc(&i, sizeof(be16_t));
|
||||
|
||||
/* RFC 9460 section 2.4.1:
|
||||
* When SvcPriority is 0, the SVCB record is in AliasMode.
|
||||
*
|
||||
* We do not support the alias mode. But the entry itself is not invalid. */
|
||||
if (resolver.priority == 0) {
|
||||
*ret = (sd_dns_resolver) {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* authentication domain name */
|
||||
if (!iovec_is_set(&i))
|
||||
return -EBADMSG;
|
||||
|
||||
size_t name_len = *(uint8_t*) i.iov_base;
|
||||
iovec_inc(&i, 1);
|
||||
if (i.iov_len < name_len)
|
||||
return -EBADMSG;
|
||||
|
||||
const uint8_t *name_buf = i.iov_base;
|
||||
iovec_inc(&i, name_len);
|
||||
|
||||
r = dns_name_from_wire_format(&name_buf, &name_len, &resolver.auth_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0 || name_len != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
r = dns_name_is_valid_ldh(resolver.auth_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
if (dns_name_is_root(resolver.auth_name))
|
||||
return -EBADMSG;
|
||||
|
||||
/* RFC9463 section 3.1.6: In ADN-only mode, server omits everything after the ADN.
|
||||
*
|
||||
* We don't support these, but they are not invalid. */
|
||||
if (!iovec_is_set(&i)) {
|
||||
*ret = (sd_dns_resolver) {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* IPv4 addresses */
|
||||
size_t n = *(uint8_t*) i.iov_base;
|
||||
iovec_inc(&i, 1);
|
||||
|
||||
if (n % sizeof(struct in_addr) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
n /= sizeof(struct in_addr);
|
||||
|
||||
/* RFC9463 section 3.1.8: option MUST include at least one valid IP addr */
|
||||
if (n == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
resolver.family = AF_INET;
|
||||
resolver.n_addrs = n;
|
||||
resolver.addrs = new(union in_addr_union, n);
|
||||
if (!resolver.addrs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
if (i.iov_len < sizeof(struct in_addr))
|
||||
return -EBADMSG;
|
||||
|
||||
struct in_addr a;
|
||||
memcpy(&a, i.iov_base, sizeof(struct in_addr));
|
||||
iovec_inc(&i, sizeof(struct in_addr));
|
||||
|
||||
/* RFC9463 section 5.2: client MUST discard multicast and host loopback addresses */
|
||||
if (in4_addr_is_multicast(&a) || in4_addr_is_localhost(&a))
|
||||
return -EBADMSG;
|
||||
|
||||
resolver.addrs[j] = (union in_addr_union) { .in = a };
|
||||
}
|
||||
|
||||
/* service params */
|
||||
r = dnr_parse_svc_params(i.iov_base, i.iov_len, &resolver);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* We can't use this record, but it is not invalid. */
|
||||
*ret = (sd_dns_resolver) {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ret = TAKE_STRUCT(resolver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_dnr(sd_dhcp_message *message, size_t *ret_n_resolvers, sd_dns_resolver **ret_resolvers) {
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(ret_n_resolvers || !ret_resolvers);
|
||||
|
||||
/* See RFC 9463 section 5.1 */
|
||||
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
|
||||
r = dhcp_message_get_option_length_prefixed_data(message, SD_DHCP_OPTION_V4_DNR, /* length_size= */ 2, &iovw);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sd_dns_resolver *resolvers = NULL;
|
||||
size_t n_resolvers = 0;
|
||||
CLEANUP_ARRAY(resolvers, n_resolvers, dns_resolver_free_array);
|
||||
FOREACH_ARRAY(i, iovw.iovec, iovw.count) {
|
||||
_cleanup_(sd_dns_resolver_done) sd_dns_resolver dnr = {};
|
||||
r = parse_dnr_one(i, &dnr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
if (!ret_resolvers) {
|
||||
n_resolvers++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(resolvers, n_resolvers + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
resolvers[n_resolvers++] = TAKE_STRUCT(dnr);
|
||||
}
|
||||
|
||||
if (n_resolvers == 0) /* no supported resolver */
|
||||
return -ENODATA;
|
||||
|
||||
if (ret_resolvers) {
|
||||
/* Sort the resolvers with their priorities. */
|
||||
typesafe_qsort(resolvers, n_resolvers, dns_resolver_prio_compare);
|
||||
*ret_resolvers = TAKE_PTR(resolvers);
|
||||
}
|
||||
if (ret_n_resolvers)
|
||||
*ret_n_resolvers = n_resolvers;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp_message_verify_header(
|
||||
const struct iovec *iov,
|
||||
uint8_t op,
|
||||
|
||||
@@ -39,9 +39,19 @@ int dhcp_message_append_option_sec(sd_dhcp_message *message, uint8_t code, usec_
|
||||
int dhcp_message_append_option_address(sd_dhcp_message *message, uint8_t code, const struct in_addr *addr);
|
||||
int dhcp_message_append_option_addresses(sd_dhcp_message *message, uint8_t code, size_t n_addr, const struct in_addr *addr);
|
||||
int dhcp_message_append_option_string(sd_dhcp_message *message, uint8_t code, const char *data);
|
||||
int dhcp_message_append_option_routes(sd_dhcp_message *message, uint8_t code, size_t n_routes, const sd_dhcp_route *routes);
|
||||
int dhcp_message_append_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t ipv4masklen,
|
||||
uint8_t prefixlen,
|
||||
const struct in6_addr *prefix,
|
||||
size_t n_br_addresses,
|
||||
const struct in_addr *br_addresses);
|
||||
int dhcp_message_append_option_client_id(sd_dhcp_message *message, const sd_dhcp_client_id *id);
|
||||
int dhcp_message_append_option_parameter_request_list(sd_dhcp_message *message, Set *prl);
|
||||
int dhcp_message_append_option_hostname(sd_dhcp_message *message, uint8_t flags, bool is_client, const char *hostname);
|
||||
int dhcp_message_append_option_sub_tlv(sd_dhcp_message *message, uint8_t code, const TLV *tlv);
|
||||
int dhcp_message_append_option_length_prefixed_data(sd_dhcp_message *message, uint8_t code, size_t length_size, const struct iovec_wrapper *iovw);
|
||||
|
||||
int dhcp_message_get_option(sd_dhcp_message *message, uint8_t code, size_t length, void *ret);
|
||||
int dhcp_message_get_option_alloc(sd_dhcp_message *message, uint8_t code, struct iovec *ret);
|
||||
@@ -53,11 +63,23 @@ int dhcp_message_get_option_sec(sd_dhcp_message *message, uint8_t code, bool max
|
||||
int dhcp_message_get_option_address(sd_dhcp_message *message, uint8_t code, struct in_addr *ret);
|
||||
int dhcp_message_get_option_addresses(sd_dhcp_message *message, uint8_t code, size_t *ret_n_addr, struct in_addr **ret_addr);
|
||||
int dhcp_message_get_option_string(sd_dhcp_message *message, uint8_t code, char **ret);
|
||||
int dhcp_message_get_option_routes(sd_dhcp_message *message, uint8_t code, size_t *ret_n_routes, sd_dhcp_route **ret_routes);
|
||||
int dhcp_message_get_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t *ret_ipv4masklen,
|
||||
uint8_t *ret_prefixlen,
|
||||
struct in6_addr *ret_prefix,
|
||||
size_t *ret_n_br_addresses,
|
||||
struct in_addr **ret_br_addresses);
|
||||
int dhcp_message_get_option_client_id(sd_dhcp_message *message, sd_dhcp_client_id *ret);
|
||||
int dhcp_message_get_option_parameter_request_list(sd_dhcp_message *message, Set **ret);
|
||||
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);
|
||||
int dhcp_message_get_option_dnr(sd_dhcp_message *message, size_t *ret_n_resolvers, sd_dns_resolver **ret_resolvers);
|
||||
|
||||
int dhcp_message_parse(
|
||||
const struct iovec *iov,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-dhcp-lease.h"
|
||||
|
||||
#include "dhcp-route.h" /* IWYU pragma: keep */
|
||||
|
||||
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *ret) {
|
||||
assert_return(route, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
*ret = route->dst_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *ret) {
|
||||
assert_return(route, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
*ret = route->dst_prefixlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *ret) {
|
||||
assert_return(route, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
*ret = route->gw_addr;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-forward.h"
|
||||
|
||||
#include "in-addr-util.h" /* IWYU pragma: keep */
|
||||
|
||||
struct sd_dhcp_route {
|
||||
struct in_addr dst_addr;
|
||||
struct in_addr gw_addr;
|
||||
uint8_t dst_prefixlen;
|
||||
};
|
||||
@@ -8,6 +8,7 @@ libsystemd_network_sources = files(
|
||||
'dhcp-option.c',
|
||||
'dhcp-packet.c',
|
||||
'dhcp-protocol.c',
|
||||
'dhcp-route.c',
|
||||
'dhcp6-network.c',
|
||||
'dhcp6-option.c',
|
||||
'dhcp6-protocol.c',
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sd-dhcp-lease.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "dhcp-route.h"
|
||||
#include "dns-resolver-internal.h"
|
||||
#include "extract-word.h"
|
||||
#include "hexdecoct.h"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "dhcp-option.h"
|
||||
#include "dhcp-route.h"
|
||||
#include "dns-def.h"
|
||||
#include "dns-domain.h"
|
||||
#include "dns-resolver-internal.h"
|
||||
@@ -1802,27 +1803,3 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **ret) {
|
||||
*ret = lease->timezone;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
|
||||
assert_return(route, -EINVAL);
|
||||
assert_return(destination, -EINVAL);
|
||||
|
||||
*destination = route->dst_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
|
||||
assert_return(route, -EINVAL);
|
||||
assert_return(length, -EINVAL);
|
||||
|
||||
*length = route->dst_prefixlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
|
||||
assert_return(route, -EINVAL);
|
||||
assert_return(gateway, -EINVAL);
|
||||
|
||||
*gateway = route->gw_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "dhcp-client-id-internal.h"
|
||||
#include "dhcp-message.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#include "dhcp-route.h"
|
||||
#include "dns-packet.h"
|
||||
#include "dns-resolver-internal.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "iovec-wrapper.h"
|
||||
@@ -68,7 +71,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 +85,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) {
|
||||
@@ -96,6 +108,47 @@ static void verify_multiple_strings(sd_dhcp_message *m, char * const *expected)
|
||||
ASSERT_STREQ(s, joined);
|
||||
}
|
||||
|
||||
static void verify_routes(sd_dhcp_message *m, size_t n_expected, const sd_dhcp_route *expected) {
|
||||
uint8_t code;
|
||||
FOREACH_ARGUMENT(code,
|
||||
SD_DHCP_OPTION_STATIC_ROUTE,
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,
|
||||
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE) {
|
||||
|
||||
_cleanup_free_ sd_dhcp_route *routes = NULL;
|
||||
size_t n;
|
||||
ASSERT_OK(dhcp_message_get_option_routes(m, code, &n, &routes));
|
||||
ASSERT_EQ(n, n_expected);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
ASSERT_EQ(routes[i].dst_addr.s_addr, expected[i].dst_addr.s_addr);
|
||||
ASSERT_EQ(routes[i].gw_addr.s_addr, expected[i].gw_addr.s_addr);
|
||||
ASSERT_EQ(routes[i].dst_prefixlen, expected[i].dst_prefixlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verify_6rd(
|
||||
sd_dhcp_message *m,
|
||||
uint8_t expected_ipv4masklen,
|
||||
uint8_t expected_prefixlen,
|
||||
const struct in6_addr *expected_prefix,
|
||||
size_t expected_n_br_addresses,
|
||||
const struct in_addr *expected_br_addresses) {
|
||||
|
||||
uint8_t ipv4masklen, prefixlen;
|
||||
struct in6_addr prefix;
|
||||
size_t n_br_addresses;
|
||||
_cleanup_free_ struct in_addr *br_addresses = NULL;
|
||||
|
||||
ASSERT_OK(dhcp_message_get_option_6rd(m, NULL, NULL, NULL, NULL, NULL));
|
||||
ASSERT_OK(dhcp_message_get_option_6rd(m, &ipv4masklen, &prefixlen, &prefix, &n_br_addresses, &br_addresses));
|
||||
ASSERT_EQ(ipv4masklen, expected_ipv4masklen);
|
||||
ASSERT_EQ(prefixlen, expected_prefixlen);
|
||||
ASSERT_TRUE(in6_addr_equal(&prefix, expected_prefix));
|
||||
ASSERT_EQ(n_br_addresses, expected_n_br_addresses);
|
||||
ASSERT_EQ(memcmp(br_addresses, expected_br_addresses, sizeof(struct in_addr) * n_br_addresses), 0);
|
||||
}
|
||||
|
||||
static void verify_client_id(sd_dhcp_message *m, const sd_dhcp_client_id *expected) {
|
||||
sd_dhcp_client_id id = {};
|
||||
ASSERT_OK(dhcp_message_get_option_client_id(m, &id));
|
||||
@@ -114,6 +167,26 @@ static void verify_hostname(sd_dhcp_message *m, const char *expected) {
|
||||
ASSERT_STREQ(s, expected);
|
||||
}
|
||||
|
||||
static void verify_sub_tlv(sd_dhcp_message *m, TLV *expected) {
|
||||
_cleanup_(tlv_unrefp) TLV *tlv = NULL;
|
||||
ASSERT_OK(dhcp_message_get_option_sub_tlv(
|
||||
m,
|
||||
SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION,
|
||||
TLV_DHCP4_SUBOPTION,
|
||||
&tlv));
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {}, iov_expected = {};
|
||||
ASSERT_OK(tlv_build(tlv, &iov));
|
||||
ASSERT_OK(tlv_build(expected, &iov_expected));
|
||||
ASSERT_TRUE(iovec_equal(&iov, &iov_expected));
|
||||
}
|
||||
|
||||
static void verify_length_prefixed_data(sd_dhcp_message *m, const struct iovec_wrapper *expected) {
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
|
||||
ASSERT_OK(dhcp_message_get_option_length_prefixed_data(m, SD_DHCP_OPTION_USER_CLASS, 1, &iovw));
|
||||
ASSERT_TRUE(iovw_equal(&iovw, expected));
|
||||
}
|
||||
|
||||
TEST(dhcp_message) {
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
|
||||
|
||||
@@ -140,6 +213,43 @@ 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) },
|
||||
};
|
||||
|
||||
struct sd_dhcp_route routes[3] = {
|
||||
{ /* class A: 10.0.0.0/8 -> 192.0.2.33 */
|
||||
.dst_addr = { .s_addr = htobe32(0x0A000000) },
|
||||
.gw_addr = { .s_addr = htobe32(0xC0000221) },
|
||||
.dst_prefixlen = 8,
|
||||
},
|
||||
{ /* class B: 172.16.0.0/16 -> 192.0.2.34 */
|
||||
.dst_addr = { .s_addr = htobe32(0xAC100000) },
|
||||
.gw_addr = { .s_addr = htobe32(0xC0000222) },
|
||||
.dst_prefixlen = 16,
|
||||
},
|
||||
{ /* class C: 192.168.0.0/24 -> 192.0.2.35 */
|
||||
.dst_addr = { .s_addr = htobe32(0xC0A80000) },
|
||||
.gw_addr = { .s_addr = htobe32(0xC0000223) },
|
||||
.dst_prefixlen = 24,
|
||||
},
|
||||
};
|
||||
|
||||
uint8_t sixrd_ipv4masklen = 24;
|
||||
uint8_t sixrd_prefixlen = 64;
|
||||
struct in6_addr sixrd_prefix = {
|
||||
.s6_addr = { 0x20, 0x01, 0x0d, 0xb8, },
|
||||
};
|
||||
struct in_addr sixrd_br_addresses[3] = {
|
||||
{ .s_addr = htobe32(0xC0000231) },
|
||||
{ .s_addr = htobe32(0xC0000232) },
|
||||
{ .s_addr = htobe32(0xC0000233) },
|
||||
};
|
||||
|
||||
sd_dhcp_client_id id = {
|
||||
.raw = { 1, 3, 3, 3, 3, 3, 3, },
|
||||
.size = 7,
|
||||
@@ -153,6 +263,23 @@ TEST(dhcp_message) {
|
||||
const char *vendor_class = "hogehoge";
|
||||
char **root_path = STRV_MAKE("/path/to/root", "/hogehoge/foofoo");
|
||||
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper user_class = {}, user_class_1 = {}, user_class_2 = {};
|
||||
FOREACH_STRING(s, "hoge", "foo", "bar") {
|
||||
ASSERT_OK(iovw_extend(&user_class, s, strlen(s)));
|
||||
ASSERT_OK(iovw_extend(&user_class_1, s, strlen(s)));
|
||||
}
|
||||
FOREACH_STRING(s, "aaa", "bbb", "ccc") {
|
||||
ASSERT_OK(iovw_extend(&user_class, s, strlen(s)));
|
||||
ASSERT_OK(iovw_extend(&user_class_2, s, strlen(s)));
|
||||
}
|
||||
|
||||
_cleanup_(tlv_done) TLV vendor = TLV_INIT(TLV_DHCP4_SUBOPTION);
|
||||
for (unsigned i = 0; i < 3; i++) {
|
||||
uint8_t buf[255];
|
||||
memset(buf, 42 + i, sizeof(buf));
|
||||
ASSERT_OK(tlv_append(&vendor, i + 1, 255, buf));
|
||||
}
|
||||
|
||||
ASSERT_OK(dhcp_message_init_header(
|
||||
m,
|
||||
BOOTREQUEST,
|
||||
@@ -205,7 +332,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);
|
||||
@@ -220,6 +350,22 @@ TEST(dhcp_message) {
|
||||
ASSERT_OK(dhcp_message_append_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, vendor_class));
|
||||
verify_string(m, vendor_class);
|
||||
|
||||
/* routes */
|
||||
ASSERT_OK(dhcp_message_append_option_routes(m, SD_DHCP_OPTION_STATIC_ROUTE, ELEMENTSOF(routes), routes));
|
||||
ASSERT_OK(dhcp_message_append_option_routes(m, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, ELEMENTSOF(routes), routes));
|
||||
ASSERT_OK(dhcp_message_append_option_routes(m, SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, ELEMENTSOF(routes), routes));
|
||||
verify_routes(m, ELEMENTSOF(routes), routes);
|
||||
|
||||
/* 6rd */
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, 33, sixrd_prefixlen, &sixrd_prefix, 1, sixrd_br_addresses), EINVAL);
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, 127, &sixrd_prefix, 1, sixrd_br_addresses), EINVAL);
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, 0, sixrd_br_addresses), EINVAL);
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, SIZE_MAX, sixrd_br_addresses), ENOBUFS);
|
||||
ASSERT_OK(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, 1, sixrd_br_addresses));
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, 1, sixrd_br_addresses), EEXIST);
|
||||
ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_6RD, ELEMENTSOF(sixrd_br_addresses) - 1, sixrd_br_addresses + 1));
|
||||
verify_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, ELEMENTSOF(sixrd_br_addresses), sixrd_br_addresses);
|
||||
|
||||
/* client ID */
|
||||
ASSERT_OK(dhcp_message_append_option_client_id(m, &id));
|
||||
verify_client_id(m, &id);
|
||||
@@ -243,6 +389,16 @@ TEST(dhcp_message) {
|
||||
ASSERT_OK(dhcp_message_append_option_hostname(m, /* flags= */ 0, /* is_client= */ false, hostname));
|
||||
verify_hostname(m, hostname);
|
||||
|
||||
/* vendor specific */
|
||||
ASSERT_OK(dhcp_message_append_option_sub_tlv(m, SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, &vendor));
|
||||
ASSERT_ERROR(dhcp_message_append_option_sub_tlv(m, SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, &vendor), EEXIST);
|
||||
verify_sub_tlv(m, &vendor);
|
||||
|
||||
/* user class */
|
||||
ASSERT_OK(dhcp_message_append_option_length_prefixed_data(m, SD_DHCP_OPTION_USER_CLASS, 1, &user_class_1));
|
||||
ASSERT_OK(dhcp_message_append_option_length_prefixed_data(m, SD_DHCP_OPTION_USER_CLASS, 1, &user_class_2));
|
||||
verify_length_prefixed_data(m, &user_class);
|
||||
|
||||
/* build and parse */
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
|
||||
ASSERT_OK(dhcp_message_build(m, &iovw));
|
||||
@@ -269,11 +425,15 @@ 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_routes(m2, ELEMENTSOF(routes), routes);
|
||||
verify_6rd(m2, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, ELEMENTSOF(sixrd_br_addresses), sixrd_br_addresses);
|
||||
verify_client_id(m2, &id);
|
||||
verify_prl(m2, prl);
|
||||
verify_hostname(m2, hostname);
|
||||
verify_sub_tlv(m2, &vendor);
|
||||
verify_length_prefixed_data(m2, &user_class);
|
||||
|
||||
/* build again, and verify the packet */
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw2 = {};
|
||||
@@ -281,4 +441,332 @@ 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));
|
||||
|
||||
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) {
|
||||
_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);
|
||||
}
|
||||
|
||||
TEST(dnr) {
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
|
||||
ASSERT_OK(dhcp_message_new(&m));
|
||||
|
||||
sd_dns_resolver *resolvers = NULL;
|
||||
size_t n_resolvers = 0;
|
||||
CLEANUP_ARRAY(resolvers, n_resolvers, dns_resolver_free_array);
|
||||
|
||||
static uint8_t data[] = {
|
||||
/* Instance 1 */
|
||||
/* length */
|
||||
0, 78,
|
||||
/* priority */
|
||||
0, 1,
|
||||
/* authentication domain name */
|
||||
22,
|
||||
8, 'r', 'e', 's', 'o', 'l', 'v', 'e', 'r',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
/* addresses */
|
||||
8,
|
||||
192, 0, 2, 1,
|
||||
192, 0, 2, 2,
|
||||
/* service parameters */
|
||||
/* ALPN */
|
||||
0, DNS_SVC_PARAM_KEY_ALPN,
|
||||
0, 14,
|
||||
2, 'h', '2',
|
||||
2, 'h', '3',
|
||||
3, 'd', 'o', 't',
|
||||
3, 'd', 'o', 'q',
|
||||
/* port */
|
||||
0, DNS_SVC_PARAM_KEY_PORT,
|
||||
0, 2,
|
||||
0, 42,
|
||||
/* DoH path*/
|
||||
0, DNS_SVC_PARAM_KEY_DOHPATH,
|
||||
0, 16,
|
||||
'/', 'd', 'n', 's', '-', 'q', 'u', 'e', 'r', 'y', '{', '?', 'd', 'n', 's', '}',
|
||||
|
||||
/* Instance 2 */
|
||||
/* length */
|
||||
0, 44,
|
||||
/* priority */
|
||||
0, 2,
|
||||
/* authentication domain name */
|
||||
22,
|
||||
8, 'h', 'o', 'g', 'e', 'h', 'o', 'g', 'e',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
/* addresses */
|
||||
4,
|
||||
192, 0, 2, 3,
|
||||
/* service parameters */
|
||||
/* ALPN */
|
||||
0, DNS_SVC_PARAM_KEY_ALPN,
|
||||
0, 4,
|
||||
3, 'd', 'o', 't',
|
||||
/* port */
|
||||
0, DNS_SVC_PARAM_KEY_PORT,
|
||||
0, 2,
|
||||
0, 33,
|
||||
|
||||
/* Instance 3 (no address, ignored) */
|
||||
/* length */
|
||||
0, 20,
|
||||
/* priority */
|
||||
0, 3,
|
||||
/* authentication domain name */
|
||||
17,
|
||||
3, 'f', 'o', 'o',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
|
||||
/* Instance 4 (unknown alpn, ignored) */
|
||||
/* length */
|
||||
0, 37,
|
||||
/* priority */
|
||||
0, 4,
|
||||
/* authentication domain name */
|
||||
20,
|
||||
6, 'b', 'a', 'r', 'b', 'a', 'z',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
/* addresses */
|
||||
4,
|
||||
192, 0, 2, 4,
|
||||
/* service parameters */
|
||||
/* ALPN */
|
||||
0, DNS_SVC_PARAM_KEY_ALPN,
|
||||
0, 5,
|
||||
4, 'h', 'o', 'g', 'e',
|
||||
};
|
||||
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_V4_DNR, ELEMENTSOF(data), data));
|
||||
ASSERT_OK(dhcp_message_get_option_dnr(m, &n_resolvers, &resolvers));
|
||||
ASSERT_EQ(n_resolvers, 2u);
|
||||
|
||||
ASSERT_EQ(resolvers[0].priority, 1u);
|
||||
ASSERT_STREQ(resolvers[0].auth_name, "resolver.example.com");
|
||||
ASSERT_EQ(resolvers[0].family, AF_INET);
|
||||
ASSERT_EQ(resolvers[0].n_addrs, 2u);
|
||||
ASSERT_STREQ(IN_ADDR_TO_STRING(resolvers[0].family, &resolvers[0].addrs[0]), "192.0.2.1");
|
||||
ASSERT_STREQ(IN_ADDR_TO_STRING(resolvers[0].family, &resolvers[0].addrs[1]), "192.0.2.2");
|
||||
ASSERT_EQ(resolvers[0].transports, SD_DNS_ALPN_HTTP_2_TLS | SD_DNS_ALPN_HTTP_3 | SD_DNS_ALPN_DOT | SD_DNS_ALPN_DOQ);
|
||||
ASSERT_EQ(resolvers[0].port, 42u);
|
||||
ASSERT_STREQ(resolvers[0].dohpath, "/dns-query{?dns}");
|
||||
|
||||
ASSERT_EQ(resolvers[1].priority, 2u);
|
||||
ASSERT_STREQ(resolvers[1].auth_name, "hogehoge.example.com");
|
||||
ASSERT_EQ(resolvers[1].family, AF_INET);
|
||||
ASSERT_EQ(resolvers[1].n_addrs, 1u);
|
||||
ASSERT_STREQ(IN_ADDR_TO_STRING(resolvers[1].family, &resolvers[1].addrs[0]), "192.0.2.3");
|
||||
ASSERT_EQ(resolvers[1].transports, SD_DNS_ALPN_DOT);
|
||||
ASSERT_EQ(resolvers[1].port, 33u);
|
||||
ASSERT_NULL(resolvers[1].dohpath);
|
||||
|
||||
/* missing DoH path */
|
||||
static uint8_t invalid[] = {
|
||||
/* length */
|
||||
0, 35,
|
||||
/* priority */
|
||||
0, 5,
|
||||
/* authentication domain name */
|
||||
20,
|
||||
6, 'b', 'a', 'r', 'b', 'a', 'z',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
/* addresses */
|
||||
4,
|
||||
192, 0, 2, 5,
|
||||
/* service parameters */
|
||||
/* ALPN */
|
||||
0, DNS_SVC_PARAM_KEY_ALPN,
|
||||
0, 3,
|
||||
2, 'h', '2',
|
||||
};
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_V4_DNR, ELEMENTSOF(invalid), invalid));
|
||||
ASSERT_ERROR(dhcp_message_get_option_dnr(m, NULL, NULL), EBADMSG);
|
||||
|
||||
dhcp_message_remove_option(m, SD_DHCP_OPTION_V4_DNR);
|
||||
|
||||
/* missing ALPN */
|
||||
static uint8_t invalid2[] = {
|
||||
/* length */
|
||||
0, 28,
|
||||
/* priority */
|
||||
0, 6,
|
||||
/* authentication domain name */
|
||||
20,
|
||||
6, 'b', 'a', 'r', 'b', 'a', 'z',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o', 'm',
|
||||
0,
|
||||
/* addresses */
|
||||
4,
|
||||
192, 0, 2, 6,
|
||||
};
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_V4_DNR, ELEMENTSOF(invalid2), invalid2));
|
||||
ASSERT_ERROR(dhcp_message_get_option_dnr(m, NULL, NULL), EBADMSG);
|
||||
|
||||
dhcp_message_remove_option(m, SD_DHCP_OPTION_V4_DNR);
|
||||
|
||||
/* truncated domain name */
|
||||
static uint8_t invalid3[] = {
|
||||
/* length */
|
||||
0, 34,
|
||||
/* priority */
|
||||
0, 7,
|
||||
/* authentication domain name */
|
||||
18,
|
||||
6, 'b', 'a', 'r', 'b', 'a', 'z',
|
||||
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||
3, 'c', 'o',
|
||||
/* addresses */
|
||||
4,
|
||||
192, 0, 2, 7,
|
||||
/* service parameters */
|
||||
/* ALPN */
|
||||
0, DNS_SVC_PARAM_KEY_ALPN,
|
||||
0, 4,
|
||||
3, 'd', 'o', 't',
|
||||
};
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_V4_DNR, ELEMENTSOF(invalid3), invalid3));
|
||||
ASSERT_ERROR(dhcp_message_get_option_dnr(m, NULL, NULL), EMSGSIZE);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "dhcp-route.h"
|
||||
#include "hashmap.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "network-internal.h"
|
||||
|
||||
@@ -86,9 +86,9 @@ int sd_dhcp_lease_get_6rd(
|
||||
size_t *ret_n_br_addresses);
|
||||
int sd_dhcp_lease_has_6rd(sd_dhcp_lease *lease);
|
||||
|
||||
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
|
||||
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
|
||||
int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
|
||||
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *ret);
|
||||
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *ret);
|
||||
int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *ret);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user