mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 09:21:26 +03:00
network: Implement DHCP Option 119 (Domain Search List) (#5932)
This adds a modified version of dhcp6_option_parse_domainname() that is able to parse compressed domain names, borrowing the idea from dns_packet_read_name(). It also adds pieces in networkd-link and networkd-manager to properly save/load the added option field. Resolves #2710.
This commit is contained in:
parent
6e4177315f
commit
b85bc551c3
@ -3690,6 +3690,14 @@ test_dhcp_option_LDADD = \
|
||||
libsystemd-network.la \
|
||||
libsystemd-shared.la
|
||||
|
||||
test_sd_dhcp_lease_SOURCES = \
|
||||
src/libsystemd-network/dhcp-lease-internal.h \
|
||||
src/libsystemd-network/test-sd-dhcp-lease.c
|
||||
|
||||
test_sd_dhcp_lease_LDADD = \
|
||||
libsystemd-network.la \
|
||||
libsystemd-shared.la
|
||||
|
||||
test_dhcp_client_SOURCES = \
|
||||
src/systemd/sd-dhcp-client.h \
|
||||
src/libsystemd-network/dhcp-protocol.h \
|
||||
@ -3768,6 +3776,7 @@ tests += \
|
||||
test-dhcp-option \
|
||||
test-dhcp-client \
|
||||
test-dhcp-server \
|
||||
test-sd-dhcp-lease \
|
||||
test-ipv4ll \
|
||||
test-ndisc-rs \
|
||||
test-dhcp6-client \
|
||||
|
@ -75,6 +75,7 @@ struct sd_dhcp_lease {
|
||||
uint16_t mtu; /* 0 if unset */
|
||||
|
||||
char *domainname;
|
||||
char **search_domains;
|
||||
char *hostname;
|
||||
char *root_path;
|
||||
|
||||
@ -92,6 +93,7 @@ struct sd_dhcp_lease {
|
||||
int dhcp_lease_new(sd_dhcp_lease **ret);
|
||||
|
||||
int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata);
|
||||
int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains);
|
||||
int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len);
|
||||
|
||||
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
|
||||
|
@ -231,6 +231,21 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
|
||||
return (int) lease->static_route_size;
|
||||
}
|
||||
|
||||
int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
|
||||
unsigned r;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(domains, -EINVAL);
|
||||
|
||||
r = strv_length(lease->search_domains);
|
||||
if (r > 0) {
|
||||
*domains = lease->search_domains;
|
||||
return (int) r;
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(data, -EINVAL);
|
||||
@ -282,6 +297,7 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
|
||||
free(lease->static_route);
|
||||
free(lease->client_id);
|
||||
free(lease->vendor_specific);
|
||||
strv_free(lease->search_domains);
|
||||
return mfree(lease);
|
||||
}
|
||||
|
||||
@ -605,6 +621,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST:
|
||||
r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m");
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_HOST_NAME:
|
||||
r = lease_parse_domain(option, len, &lease->hostname);
|
||||
if (r < 0) {
|
||||
@ -696,6 +718,96 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parses compressed domain names. */
|
||||
int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
size_t pos = 0, cnt = 0;
|
||||
int r;
|
||||
|
||||
assert(domains);
|
||||
assert_return(option && len > 0, -ENODATA);
|
||||
|
||||
while (pos < len) {
|
||||
_cleanup_free_ char *name = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
size_t jump_barrier = pos, next_chunk = 0;
|
||||
bool first = true;
|
||||
|
||||
for (;;) {
|
||||
uint8_t c;
|
||||
c = option[pos++];
|
||||
|
||||
if (c == 0) {
|
||||
/* End of name */
|
||||
break;
|
||||
} else if (c <= 63) {
|
||||
const char *label;
|
||||
|
||||
/* Literal label */
|
||||
label = (const char*) (option + pos);
|
||||
pos += c;
|
||||
if (pos >= len)
|
||||
return -EBADMSG;
|
||||
|
||||
if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
name[n++] = '.';
|
||||
|
||||
r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n += r;
|
||||
} else if ((c & 0xc0) == 0xc0) {
|
||||
/* Pointer */
|
||||
|
||||
uint8_t d;
|
||||
uint16_t ptr;
|
||||
|
||||
if (pos >= len)
|
||||
return -EBADMSG;
|
||||
|
||||
d = option[pos++];
|
||||
ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
|
||||
|
||||
/* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
|
||||
if (ptr >= jump_barrier)
|
||||
return -EBADMSG;
|
||||
jump_barrier = ptr;
|
||||
|
||||
/* Save current location so we don't end up re-parsing what's parsed so far. */
|
||||
if (next_chunk == 0)
|
||||
next_chunk = pos;
|
||||
|
||||
pos = ptr;
|
||||
} else
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(name, allocated, n + 1))
|
||||
return -ENOMEM;
|
||||
name[n] = 0;
|
||||
|
||||
r = strv_extend(&names, name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
cnt++;
|
||||
|
||||
if (next_chunk != 0)
|
||||
pos = next_chunk;
|
||||
}
|
||||
|
||||
*domains = names;
|
||||
names = NULL;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
|
||||
struct sd_dhcp_raw_option *cur, *option;
|
||||
|
||||
@ -751,6 +863,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
|
||||
const char *string;
|
||||
uint16_t mtu;
|
||||
_cleanup_free_ sd_dhcp_route **routes = NULL;
|
||||
char **search_domains = NULL;
|
||||
uint32_t t1, t2, lifetime;
|
||||
int r;
|
||||
|
||||
@ -824,6 +937,13 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
|
||||
if (r >= 0)
|
||||
fprintf(f, "DOMAINNAME=%s\n", string);
|
||||
|
||||
r = sd_dhcp_lease_get_search_domains(lease, &search_domains);
|
||||
if (r > 0) {
|
||||
fputs("DOMAIN_SEARCH_LIST=", f);
|
||||
fputstrv(f, search_domains, NULL, NULL);
|
||||
fputs("\n", f);
|
||||
}
|
||||
|
||||
r = sd_dhcp_lease_get_hostname(lease, &string);
|
||||
if (r >= 0)
|
||||
fprintf(f, "HOSTNAME=%s\n", string);
|
||||
@ -905,6 +1025,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
||||
*ntp = NULL,
|
||||
*mtu = NULL,
|
||||
*routes = NULL,
|
||||
*domains = NULL,
|
||||
*client_id_hex = NULL,
|
||||
*vendor_specific_hex = NULL,
|
||||
*lifetime = NULL,
|
||||
@ -933,6 +1054,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
||||
"MTU", &mtu,
|
||||
"DOMAINNAME", &lease->domainname,
|
||||
"HOSTNAME", &lease->hostname,
|
||||
"DOMAIN_SEARCH_LIST", &domains,
|
||||
"ROOT_PATH", &lease->root_path,
|
||||
"ROUTES", &routes,
|
||||
"CLIENTID", &client_id_hex,
|
||||
@ -1038,6 +1160,18 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
||||
log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
|
||||
}
|
||||
|
||||
if (domains) {
|
||||
_cleanup_strv_free_ char **a = NULL;
|
||||
a = strv_split(domains, " ");
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!strv_isempty(a)) {
|
||||
lease->search_domains = a;
|
||||
a = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (routes) {
|
||||
r = deserialize_dhcp_routes(
|
||||
&lease->static_route,
|
||||
|
90
src/libsystemd-network/test-sd-dhcp-lease.c
Normal file
90
src/libsystemd-network/test-sd-dhcp-lease.c
Normal file
@ -0,0 +1,90 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
/* According to RFC1035 section 4.1.4, a domain name in a message can be either:
|
||||
* - a sequence of labels ending in a zero octet
|
||||
* - a pointer
|
||||
* - a sequence of labels ending with a pointer
|
||||
*/
|
||||
static void test_dhcp_lease_parse_search_domains_basic(void) {
|
||||
int r;
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
static const uint8_t optionbuf[] = {
|
||||
0x03, 'F', 'O', 'O', 0x03, 'B', 'A', 'R', 0x00,
|
||||
0x04, 'A', 'B', 'C', 'D', 0x03, 'E', 'F', 'G', 0x00,
|
||||
};
|
||||
|
||||
r = dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains);
|
||||
assert_se(r == 2);
|
||||
assert_se(streq(domains[0], "FOO.BAR"));
|
||||
assert_se(streq(domains[1], "ABCD.EFG"));
|
||||
}
|
||||
|
||||
static void test_dhcp_lease_parse_search_domains_ptr(void) {
|
||||
int r;
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
static const uint8_t optionbuf[] = {
|
||||
0x03, 'F', 'O', 'O', 0x00, 0xC0, 0x00,
|
||||
};
|
||||
|
||||
r = dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains);
|
||||
assert_se(r == 2);
|
||||
assert_se(streq(domains[0], "FOO"));
|
||||
assert_se(streq(domains[1], "FOO"));
|
||||
}
|
||||
|
||||
static void test_dhcp_lease_parse_search_domains_labels_and_ptr(void) {
|
||||
int r;
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
static const uint8_t optionbuf[] = {
|
||||
0x03, 'F', 'O', 'O', 0x03, 'B', 'A', 'R', 0x00,
|
||||
0x03, 'A', 'B', 'C', 0xC0, 0x04,
|
||||
};
|
||||
|
||||
r = dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains);
|
||||
assert_se(r == 2);
|
||||
assert_se(streq(domains[0], "FOO.BAR"));
|
||||
assert_se(streq(domains[1], "ABC.BAR"));
|
||||
}
|
||||
|
||||
/* Tests for exceptions. */
|
||||
|
||||
static void test_dhcp_lease_parse_search_domains_no_data(void) {
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
static const uint8_t optionbuf[3] = {0, 0, 0};
|
||||
|
||||
assert_se(dhcp_lease_parse_search_domains(NULL, 0, &domains) == -ENODATA);
|
||||
assert_se(dhcp_lease_parse_search_domains(optionbuf, 0, &domains) == -ENODATA);
|
||||
}
|
||||
|
||||
static void test_dhcp_lease_parse_search_domains_loops(void) {
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
static const uint8_t optionbuf[] = {
|
||||
0x03, 'F', 'O', 'O', 0x00, 0x03, 'B', 'A', 'R', 0xC0, 0x06,
|
||||
};
|
||||
|
||||
assert_se(dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains) == -EBADMSG);
|
||||
}
|
||||
|
||||
static void test_dhcp_lease_parse_search_domains_wrong_len(void) {
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
static const uint8_t optionbuf[] = {
|
||||
0x03, 'F', 'O', 'O', 0x03, 'B', 'A', 'R', 0x00,
|
||||
0x04, 'A', 'B', 'C', 'D', 0x03, 'E', 'F', 'G', 0x00,
|
||||
};
|
||||
|
||||
assert_se(dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf) - 5, &domains) == -EBADMSG);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_dhcp_lease_parse_search_domains_basic();
|
||||
test_dhcp_lease_parse_search_domains_ptr();
|
||||
test_dhcp_lease_parse_search_domains_labels_and_ptr();
|
||||
test_dhcp_lease_parse_search_domains_no_data();
|
||||
test_dhcp_lease_parse_search_domains_loops();
|
||||
test_dhcp_lease_parse_search_domains_wrong_len();
|
||||
}
|
@ -3266,6 +3266,7 @@ int link_save(Link *link) {
|
||||
sd_dhcp6_lease *dhcp6_lease = NULL;
|
||||
const char *dhcp_domainname = NULL;
|
||||
char **dhcp6_domains = NULL;
|
||||
char **dhcp_domains = NULL;
|
||||
unsigned j;
|
||||
|
||||
if (link->dhcp6_client) {
|
||||
@ -3375,13 +3376,16 @@ int link_save(Link *link) {
|
||||
fputc('\n', f);
|
||||
|
||||
if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
|
||||
if (link->dhcp_lease)
|
||||
if (link->dhcp_lease) {
|
||||
(void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
|
||||
(void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains);
|
||||
}
|
||||
if (dhcp6_lease)
|
||||
(void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
|
||||
}
|
||||
|
||||
fputs("DOMAINS=", f);
|
||||
space = false;
|
||||
fputstrv(f, link->network->search_domains, NULL, &space);
|
||||
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
|
||||
@ -3389,6 +3393,8 @@ int link_save(Link *link) {
|
||||
|
||||
if (dhcp_domainname)
|
||||
fputs_with_space(f, dhcp_domainname, NULL, &space);
|
||||
if (dhcp_domains)
|
||||
fputstrv(f, dhcp_domains, NULL, &space);
|
||||
if (dhcp6_domains)
|
||||
fputstrv(f, dhcp6_domains, NULL, &space);
|
||||
|
||||
@ -3399,13 +3405,16 @@ int link_save(Link *link) {
|
||||
fputc('\n', f);
|
||||
|
||||
fputs("ROUTE_DOMAINS=", f);
|
||||
fputstrv(f, link->network->route_domains, NULL, NULL);
|
||||
space = false;
|
||||
fputstrv(f, link->network->route_domains, NULL, &space);
|
||||
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
|
||||
NDiscDNSSL *dd;
|
||||
|
||||
if (dhcp_domainname)
|
||||
fputs_with_space(f, dhcp_domainname, NULL, &space);
|
||||
if (dhcp_domains)
|
||||
fputstrv(f, dhcp_domains, NULL, &space);
|
||||
if (dhcp6_domains)
|
||||
fputstrv(f, dhcp6_domains, NULL, &space);
|
||||
|
||||
|
@ -961,15 +961,20 @@ static int manager_save(Manager *m) {
|
||||
|
||||
if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
|
||||
const char *domainname;
|
||||
char **domains = NULL;
|
||||
|
||||
OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains;
|
||||
r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
|
||||
if (r >= 0) {
|
||||
r = ordered_set_put_strdup(target_domains, domainname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (r != -ENODATA)
|
||||
return r;
|
||||
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES)
|
||||
r = ordered_set_put_strdup(search_domains, domainname);
|
||||
else
|
||||
r = ordered_set_put_strdup(route_domains, domainname);
|
||||
|
||||
r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains);
|
||||
if (r >= 0) {
|
||||
r = ordered_set_put_strdupv(target_domains, domains);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (r != -ENODATA)
|
||||
|
@ -76,6 +76,7 @@ enum {
|
||||
SD_DHCP_OPTION_FQDN = 81,
|
||||
SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
|
||||
SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
|
||||
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
|
||||
SD_DHCP_OPTION_PRIVATE_BASE = 224,
|
||||
SD_DHCP_OPTION_PRIVATE_LAST = 254,
|
||||
|
@ -49,6 +49,7 @@ int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
|
||||
int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
|
||||
int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
|
||||
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
|
||||
int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
|
||||
int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
|
||||
int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
|
||||
int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
|
||||
|
@ -800,6 +800,12 @@ tests += [
|
||||
libsystemd_network],
|
||||
[]],
|
||||
|
||||
[['src/libsystemd-network/test-sd-dhcp-lease.c',
|
||||
'src/libsystemd-network/dhcp-lease-internal.h'],
|
||||
[libshared,
|
||||
libsystemd_network],
|
||||
[]],
|
||||
|
||||
[['src/libsystemd-network/test-dhcp-client.c',
|
||||
'src/libsystemd-network/dhcp-protocol.h',
|
||||
'src/libsystemd-network/dhcp-internal.h',
|
||||
|
Loading…
Reference in New Issue
Block a user