dhcp-message: introduce dhcp_message_{append,get}_option_string()
These are for DHCP options that takes a string e.g. DHCP options 17 (root path), 60 (vendor class identifier), and so on.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "iovec-wrapper.h"
|
||||
#include "ip-util.h"
|
||||
#include "network-common.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static sd_dhcp_message* dhcp_message_free(sd_dhcp_message *message) {
|
||||
if (!message)
|
||||
@@ -186,6 +187,21 @@ int dhcp_message_append_option_addresses(sd_dhcp_message *message, uint8_t code,
|
||||
return dhcp_message_append_option(message, code, sizeof(struct in_addr) * n_addr, addr);
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_string(sd_dhcp_message *message, uint8_t code, const char *data) {
|
||||
assert(message);
|
||||
|
||||
if (isempty(data))
|
||||
return 0;
|
||||
|
||||
if (!string_is_safe(data, STRING_ALLOW_BACKSLASHES | STRING_ALLOW_QUOTES | STRING_ALLOW_GLOBS))
|
||||
return -EINVAL;
|
||||
|
||||
if (dhcp_message_has_option(message, code))
|
||||
return -EEXIST;
|
||||
|
||||
return dhcp_message_append_option(message, code, strlen(data), data);
|
||||
}
|
||||
|
||||
int dhcp_message_get_option(sd_dhcp_message *message, uint8_t code, size_t length, void *ret) {
|
||||
int r;
|
||||
|
||||
@@ -281,6 +297,38 @@ int dhcp_message_get_option_addresses(sd_dhcp_message *message, uint8_t code, si
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_string(sd_dhcp_message *message, uint8_t code, char **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;
|
||||
|
||||
if (!iovec_is_set(&iov))
|
||||
return -ENODATA;
|
||||
|
||||
/* Allow NUL at the end for buggy DHCP servers, but refuse intermediate NUL. */
|
||||
if (memchr(iov.iov_base, 0, iov.iov_len - 1))
|
||||
return -EBADMSG;
|
||||
|
||||
/* Note, dhcp_message_get_option_alloc() -> tlv_get_alloc() allocates an extra byte to make
|
||||
* iov.iov_base can be handled as a NUL-terminated string. Hence, we can directly pass it to
|
||||
* isempty() and string_is_safe(). */
|
||||
|
||||
if (isempty(iov.iov_base))
|
||||
return -ENODATA;
|
||||
|
||||
if (!string_is_safe(iov.iov_base, STRING_ALLOW_BACKSLASHES | STRING_ALLOW_QUOTES | STRING_ALLOW_GLOBS))
|
||||
return -EBADMSG;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(iov.iov_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp_message_verify_header(
|
||||
const struct iovec *iov,
|
||||
uint8_t op,
|
||||
|
||||
@@ -37,6 +37,7 @@ int dhcp_message_append_option_be32(sd_dhcp_message *message, uint8_t code, be32
|
||||
int dhcp_message_append_option_sec(sd_dhcp_message *message, uint8_t code, usec_t 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_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);
|
||||
@@ -47,6 +48,7 @@ int dhcp_message_get_option_be32(sd_dhcp_message *message, uint8_t code, be32_t
|
||||
int dhcp_message_get_option_sec(sd_dhcp_message *message, uint8_t code, bool max_as_infinity, usec_t *ret);
|
||||
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_parse(
|
||||
const struct iovec *iov,
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
#include <net/if_arp.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-message.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "iovec-wrapper.h"
|
||||
#include "random-util.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
|
||||
static void verify_header(sd_dhcp_message *m, uint32_t xid, const struct hw_addr_data *hw_addr) {
|
||||
@@ -79,6 +81,19 @@ static void verify_addresses(
|
||||
ASSERT_EQ(memcmp(addrs, ntp, sizeof(struct in_addr) * n), 0);
|
||||
}
|
||||
|
||||
static void verify_string(sd_dhcp_message *m, const char *expected) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
ASSERT_OK(dhcp_message_get_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, &s));
|
||||
ASSERT_STREQ(s, expected);
|
||||
}
|
||||
|
||||
static void verify_multiple_strings(sd_dhcp_message *m, char * const *expected) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
ASSERT_OK(dhcp_message_get_option_string(m, SD_DHCP_OPTION_ROOT_PATH, &s));
|
||||
_cleanup_free_ char *joined = ASSERT_NOT_NULL(strv_join(expected, /* separator= */ ""));
|
||||
ASSERT_STREQ(s, joined);
|
||||
}
|
||||
|
||||
TEST(dhcp_message) {
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
|
||||
|
||||
@@ -105,6 +120,9 @@ TEST(dhcp_message) {
|
||||
{ .s_addr = htobe32(0xC0000204) },
|
||||
};
|
||||
|
||||
const char *vendor_class = "hogehoge";
|
||||
char **root_path = STRV_MAKE("/path/to/root", "/hogehoge/foofoo");
|
||||
|
||||
ASSERT_OK(dhcp_message_init_header(
|
||||
m,
|
||||
BOOTREQUEST,
|
||||
@@ -118,6 +136,11 @@ TEST(dhcp_message) {
|
||||
ASSERT_ERROR(dhcp_message_append_option(m, SD_DHCP_OPTION_PAD, 0, NULL), EINVAL);
|
||||
ASSERT_ERROR(dhcp_message_append_option(m, SD_DHCP_OPTION_END, 0, NULL), EINVAL);
|
||||
|
||||
/* multiple strings */
|
||||
STRV_FOREACH(s, root_path)
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_ROOT_PATH, strlen(*s), *s));
|
||||
verify_multiple_strings(m, root_path);
|
||||
|
||||
/* flag */
|
||||
ASSERT_ERROR(dhcp_message_get_option_flag(m, SD_DHCP_OPTION_RAPID_COMMIT), ENODATA);
|
||||
ASSERT_OK(dhcp_message_append_option_flag(m, SD_DHCP_OPTION_RAPID_COMMIT));
|
||||
@@ -154,6 +177,19 @@ TEST(dhcp_message) {
|
||||
ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_NTP_SERVER, ELEMENTSOF(ntp) - 1, ntp + 1));
|
||||
verify_addresses(m, ELEMENTSOF(ntp), ntp);
|
||||
|
||||
/* string */
|
||||
ASSERT_ERROR(dhcp_message_get_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, NULL), ENODATA);
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, 0, NULL));
|
||||
ASSERT_ERROR(dhcp_message_get_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, NULL), ENODATA);
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, 1, "\0"));
|
||||
ASSERT_ERROR(dhcp_message_get_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, NULL), ENODATA);
|
||||
ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, 9, "hoge\0hoge"));
|
||||
ASSERT_ERROR(dhcp_message_get_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, NULL), EBADMSG);
|
||||
ASSERT_ERROR(dhcp_message_append_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, vendor_class), EEXIST);
|
||||
dhcp_message_remove_option(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER);
|
||||
ASSERT_OK(dhcp_message_append_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, vendor_class));
|
||||
verify_string(m, vendor_class);
|
||||
|
||||
/* build and parse */
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
|
||||
ASSERT_OK(dhcp_message_build(m, &iovw));
|
||||
@@ -174,12 +210,14 @@ TEST(dhcp_message) {
|
||||
|
||||
/* verify parsed message */
|
||||
verify_header(m2, xid, &hw_addr);
|
||||
verify_multiple_strings(m2, root_path);
|
||||
verify_flag(m2);
|
||||
verify_u8(m2, DHCP_DISCOVER);
|
||||
verify_u16(m2, 512);
|
||||
verify_sec(m2, lease_time);
|
||||
verify_address(m2, &addr);
|
||||
verify_addresses(m2, ELEMENTSOF(ntp), ntp);
|
||||
verify_string(m2, vendor_class);
|
||||
|
||||
/* build again, and verify the packet */
|
||||
_cleanup_(iovw_done_free) struct iovec_wrapper iovw2 = {};
|
||||
|
||||
Reference in New Issue
Block a user