diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 66e9e0046b..24bdc33cec 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -1536,7 +1536,6 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r } unsigned internal_hashmap_size(HashmapBase *h) { - if (!h) return 0; @@ -1544,7 +1543,6 @@ unsigned internal_hashmap_size(HashmapBase *h) { } unsigned internal_hashmap_buckets(HashmapBase *h) { - if (!h) return 0; @@ -1904,8 +1902,7 @@ IteratedCache *iterated_cache_free(IteratedCache *cache) { if (cache) { free(cache->keys.ptr); free(cache->values.ptr); - free(cache); } - return NULL; + return mfree(cache); } diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c index ed9ba776a4..7fdb47e064 100644 --- a/src/basic/ordered-set.c +++ b/src/basic/ordered-set.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include "fileio.h" #include "ordered-set.h" #include "strv.h" @@ -45,3 +46,37 @@ int ordered_set_put_strdupv(OrderedSet *s, char **l) { return n; } + +int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l) { + int n = 0, r; + Iterator i; + char *p; + + /* Like ordered_set_put_strv, but for an OrderedSet of strings */ + + ORDERED_SET_FOREACH(p, l, i) { + r = ordered_set_put_strdup(s, p); + if (r < 0) + return r; + + n += r; + } + + return n; +} + +void ordered_set_print(FILE *f, const char *field, OrderedSet *s) { + bool space = false; + Iterator i; + char *p; + + if (ordered_set_isempty(s)) + return; + + fputs(field, f); + + ORDERED_SET_FOREACH(p, s, i) + fputs_with_space(f, p, NULL, &space); + + fputc('\n', f); +} diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 7cbb71819b..61107eb148 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + #include "hashmap.h" typedef struct OrderedSet OrderedSet; @@ -48,9 +50,15 @@ static inline void* ordered_set_steal_first(OrderedSet *s) { return ordered_hashmap_steal_first((OrderedHashmap*) s); } +static inline char **ordered_set_get_strv(OrderedSet *s) { + return internal_hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); +} + int ordered_set_consume(OrderedSet *s, void *p); int ordered_set_put_strdup(OrderedSet *s, const char *p); int ordered_set_put_strdupv(OrderedSet *s, char **l); +int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l); +void ordered_set_print(FILE *f, const char *field, OrderedSet *s); #define ORDERED_SET_FOREACH(e, s, i) \ for ((i) = ITERATOR_FIRST; ordered_set_iterate((s), &(i), (void**)&(e)); ) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 8bca042889..e6a1cfd5d2 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -4057,9 +4057,7 @@ int link_save(Link *link) { (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); } - fputs("DOMAINS=", f); - space = false; - fputstrv(f, link->network->search_domains, NULL, &space); + ordered_set_print(f, "DOMAINS=", link->network->search_domains); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { NDiscDNSSL *dd; @@ -4077,9 +4075,7 @@ int link_save(Link *link) { fputc('\n', f); - fputs("ROUTE_DOMAINS=", f); - space = false; - fputstrv(f, link->network->route_domains, NULL, &space); + ordered_set_print(f, "ROUTE_DOMAINS=", link->network->route_domains); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { NDiscDNSSL *dd; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index edb5801b69..8eb6e86c33 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1060,22 +1060,6 @@ static int ordered_set_put_in4_addrv(OrderedSet *s, return c; } -static void print_string_set(FILE *f, const char *field, OrderedSet *s) { - bool space = false; - Iterator i; - char *p; - - if (ordered_set_isempty(s)) - return; - - fputs(field, f); - - ORDERED_SET_FOREACH(p, s, i) - fputs_with_space(f, p, NULL, &space); - - fputc('\n', f); -} - static int manager_save(Manager *m) { _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL; Link *link; @@ -1125,11 +1109,11 @@ static int manager_save(Manager *m) { if (r < 0) return r; - r = ordered_set_put_strdupv(search_domains, link->network->search_domains); + r = ordered_set_put_string_set(search_domains, link->network->search_domains); if (r < 0) return r; - r = ordered_set_put_strdupv(route_domains, link->network->route_domains); + r = ordered_set_put_string_set(route_domains, link->network->route_domains); if (r < 0) return r; @@ -1198,10 +1182,10 @@ static int manager_save(Manager *m) { "# This is private data. Do not parse.\n" "OPER_STATE=%s\n", operstate_str); - print_string_set(f, "DNS=", dns); - print_string_set(f, "NTP=", ntp); - print_string_set(f, "DOMAINS=", search_domains); - print_string_set(f, "ROUTE_DOMAINS=", route_domains); + ordered_set_print(f, "DNS=", dns); + ordered_set_print(f, "NTP=", ntp); + ordered_set_print(f, "DOMAINS=", search_domains); + ordered_set_print(f, "ROUTE_DOMAINS=", route_domains); r = routing_policy_serialize_rules(m->rules, f); if (r < 0) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 4f3fb5d2d1..a83df49038 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -107,28 +107,34 @@ static int network_verify(Network *network) { if (network->bond) { /* Bonding slave does not support addressing. */ if (network->ipv6_accept_ra > 0) { - log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.", network->filename); + log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.", + network->filename); network->ipv6_accept_ra = 0; } if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) { - log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.", network->filename); + log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.", + network->filename); network->link_local = ADDRESS_FAMILY_NO; } if (network->dhcp != ADDRESS_FAMILY_NO) { - log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.", network->filename); + log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.", + network->filename); network->dhcp = ADDRESS_FAMILY_NO; } if (network->dhcp_server) { - log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.", network->filename); + log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.", + network->filename); network->dhcp_server = false; } if (network->n_static_addresses > 0) { - log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.", network->filename); + log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.", + network->filename); while ((address = network->static_addresses)) address_free(address); } if (network->n_static_routes > 0) { - log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.", network->filename); + log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.", + network->filename); while ((route = network->static_routes)) route_free(route); } @@ -379,11 +385,11 @@ void network_free(Network *network) { strv_free(network->ntp); free(network->dns); - strv_free(network->search_domains); - strv_free(network->route_domains); + ordered_set_free_free(network->search_domains); + ordered_set_free_free(network->route_domains); strv_free(network->bind_carrier); - strv_free(network->router_search_domains); + ordered_set_free_free(network->router_search_domains); free(network->router_dns); netdev_unref(network->bridge); @@ -547,8 +553,8 @@ int network_apply(Network *network, Link *link) { if (network->n_dns > 0 || !strv_isempty(network->ntp) || - !strv_isempty(network->search_domains) || - !strv_isempty(network->route_domains)) + !ordered_set_isempty(network->search_domains) || + !ordered_set_isempty(network->route_domains)) link_dirty(link); return 0; @@ -599,18 +605,21 @@ int config_parse_netdev(const char *unit, kind = netdev_kind_from_string(kind_string); if (kind == _NETDEV_KIND_INVALID) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid NetDev kind: %s", lvalue); return 0; } r = netdev_get(network->manager, rvalue, &netdev); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "%s could not be found, ignoring assignment: %s", lvalue, rvalue); return 0; } if (netdev->kind != kind) { - log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue); return 0; } @@ -677,8 +686,8 @@ int config_parse_domains( assert(rvalue); if (isempty(rvalue)) { - n->search_domains = strv_free(n->search_domains); - n->route_domains = strv_free(n->route_domains); + n->search_domains = ordered_set_free_free(n->search_domains); + n->route_domains = ordered_set_free_free(n->route_domains); return 0; } @@ -690,7 +699,8 @@ int config_parse_domains( r = extract_first_word(&p, &w, NULL, 0); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract search or route domain, ignoring: %s", rvalue); break; } if (r == 0) @@ -700,36 +710,39 @@ int config_parse_domains( domain = is_route ? w + 1 : w; if (dns_name_is_root(domain) || streq(domain, "*")) { - /* If the root domain appears as is, or the special token "*" is found, we'll consider this as - * routing domain, unconditionally. */ + /* If the root domain appears as is, or the special token "*" is found, we'll + * consider this as routing domain, unconditionally. */ is_route = true; - domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */ + domain = "."; /* make sure we don't allow empty strings, thus write the root + * domain as "." */ } else { r = dns_name_normalize(domain, 0, &normalized); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain); + log_syntax(unit, LOG_ERR, filename, line, r, + "'%s' is not a valid domain name, ignoring.", domain); continue; } domain = normalized; if (is_localhost(domain)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain); + log_syntax(unit, LOG_ERR, filename, line, 0, + "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s", + domain); continue; } } - if (is_route) - r = strv_extend(&n->route_domains, domain); - else - r = strv_extend(&n->search_domains, domain); + OrderedSet **set = is_route ? &n->route_domains : &n->search_domains; + r = ordered_set_ensure_allocated(set, &string_hash_ops); + if (r < 0) + return r; + + r = ordered_set_put_strdup(*set, domain); if (r < 0) return log_oom(); } - strv_uniq(n->route_domains); - strv_uniq(n->search_domains); - return 0; } @@ -754,7 +767,8 @@ int config_parse_tunnel(const char *unit, r = netdev_get(network->manager, rvalue, &netdev); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Tunnel is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -779,7 +793,8 @@ int config_parse_tunnel(const char *unit, r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Cannot add VLAN '%s' to network, ignoring: %m", rvalue); return 0; } @@ -854,7 +869,8 @@ int config_parse_dhcp( else if (streq(rvalue, "both")) s = ADDRESS_FAMILY_YES; else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse DHCP option, ignoring: %s", rvalue); return 0; } @@ -874,7 +890,8 @@ static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier); -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier, "Failed to parse client identifier type"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier, + "Failed to parse client identifier type"); int config_parse_ipv6token( const char* unit, @@ -899,17 +916,20 @@ int config_parse_ipv6token( r = in_addr_from_string(AF_INET6, rvalue, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse IPv6 token, ignoring: %s", rvalue); return 0; } if (in_addr_is_null(AF_INET6, &buffer)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token cannot be the ANY address, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "IPv6 token cannot be the ANY address, ignoring: %s", rvalue); return 0; } if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue); return 0; } @@ -963,7 +983,8 @@ int config_parse_ipv6_privacy_extensions( if (streq(rvalue, "kernel")) s = _IPV6_PRIVACY_EXTENSIONS_INVALID; else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); return 0; } } @@ -999,17 +1020,20 @@ int config_parse_hostname( return r; if (!hostname_is_valid(hn, false)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Hostname is not valid, ignoring assignment: %s", rvalue); return 0; } r = dns_name_is_valid(hn); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue); return 0; } if (r == 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue); return 0; } @@ -1041,7 +1065,8 @@ int config_parse_timezone( return r; if (!timezone_is_valid(tz, LOG_ERR)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Timezone is not valid, ignoring assignment: %s", rvalue); return 0; } @@ -1076,14 +1101,16 @@ int config_parse_dhcp_server_dns( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) break; if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse DNS server address, ignoring: %s", w); continue; } @@ -1126,7 +1153,8 @@ int config_parse_radv_dns( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) @@ -1143,8 +1171,8 @@ int config_parse_radv_dns( n->router_dns = m; } else - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); - + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse DNS server address, ignoring: %s", w); } return 0; @@ -1177,7 +1205,8 @@ int config_parse_radv_search_domains( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) @@ -1185,18 +1214,20 @@ int config_parse_radv_search_domains( r = dns_name_apply_idna(w, &idna); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to apply IDNA to domain name '%s', ignoring: %m", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to apply IDNA to domain name '%s', ignoring: %m", w); continue; - } - if (r > 0) { - r = strv_push(&n->router_search_domains, idna); - if (r >= 0) - idna = NULL; - } else { - r = strv_push(&n->router_search_domains, w); - if (r >= 0) - w = NULL; - } + } else if (r == 0) + /* transfer ownership to simplify subsequent operations */ + idna = TAKE_PTR(w); + + r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops); + if (r < 0) + return r; + + r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna)); + if (r < 0) + return r; } return 0; @@ -1230,14 +1261,16 @@ int config_parse_dhcp_server_ntp( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) return 0; if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse NTP server address, ignoring: %s", w); continue; } @@ -1279,7 +1312,8 @@ int config_parse_dns( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); break; } if (r == 0) @@ -1287,7 +1321,8 @@ int config_parse_dns( r = in_addr_from_string_auto(w, &family, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse dns server address, ignoring: %s", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse dns server address, ignoring: %s", w); continue; } @@ -1336,7 +1371,8 @@ int config_parse_dnssec_negative_trust_anchors( r = extract_first_word(&p, &w, NULL, 0); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract negative trust anchor domain, ignoring: %s", rvalue); break; } if (r == 0) @@ -1344,7 +1380,8 @@ int config_parse_dnssec_negative_trust_anchors( r = dns_name_is_valid(w); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "%s is not a valid domain name, ignoring.", w); continue; } @@ -1393,7 +1430,8 @@ int config_parse_ntp( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract NTP server name, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract NTP server name, ignoring: %s", rvalue); break; } if (r == 0) @@ -1401,7 +1439,8 @@ int config_parse_ntp( r = dns_name_is_valid_or_address(w); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name or IP address, ignoring.", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "%s is not a valid domain name or IP address, ignoring.", w); continue; } @@ -1446,14 +1485,16 @@ int config_parse_dhcp_user_class( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split user classes option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to split user classes option, ignoring: %s", rvalue); break; } if (r == 0) break; if (strlen(w) > 255) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s length is not in the range 1-255, ignoring.", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "%s length is not in the range 1-255, ignoring.", w); continue; } @@ -1506,7 +1547,8 @@ int config_parse_section_route_table( return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, + "Failed to parse DHCP use domains setting"); static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { [DHCP_USE_DOMAINS_NO] = "no", diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index e61c391c76..5681e8d212 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -20,6 +20,7 @@ #include "networkd-route.h" #include "networkd-routing-policy-rule.h" #include "networkd-util.h" +#include "ordered-set.h" #include "resolve-util.h" #define DHCP_ROUTE_METRIC 1024 @@ -173,7 +174,7 @@ struct Network { usec_t router_dns_lifetime_usec; struct in6_addr *router_dns; unsigned n_router_dns; - char **router_search_domains; + OrderedSet *router_search_domains; bool dhcp6_force_pd_other_information; /* Start DHCPv6 PD also when 'O' RA flag is set, see RFC 7084, WPD-4 */ @@ -267,7 +268,8 @@ struct Network { /* All kinds of DNS configuration */ struct in_addr_data *dns; unsigned n_dns; - char **search_domains, **route_domains; + OrderedSet *search_domains, *route_domains; + int dns_default_route; ResolveSupport llmnr; ResolveSupport mdns; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 92cead052a..ecf984e226 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -391,8 +391,9 @@ static int radv_set_dns(Link *link, Link *uplink) { } static int radv_set_domains(Link *link, Link *uplink) { - char **search_domains; + OrderedSet *search_domains; usec_t lifetime_usec; + _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */ if (!link->network->router_emit_domains) return 0; @@ -423,9 +424,13 @@ static int radv_set_domains(Link *link, Link *uplink) { return 0; set_domains: + s = ordered_set_get_strv(search_domains); + if (!s) + return log_oom(); + return sd_radv_set_dnssl(link->radv, DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), - search_domains); + s); } diff --git a/src/test/meson.build b/src/test/meson.build index 7537bdae34..86d8a30afa 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -440,6 +440,10 @@ tests += [ [], []], + [['src/test/test-ordered-set.c'], + [], + []], + [['src/test/test-set-disable-mempool.c'], [], [threads]], diff --git a/src/test/test-ordered-set.c b/src/test/test-ordered-set.c new file mode 100644 index 0000000000..0d29fcfad2 --- /dev/null +++ b/src/test/test-ordered-set.c @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "ordered-set.h" +#include "string-util.h" +#include "strv.h" + +static void test_set_steal_first(void) { + _cleanup_ordered_set_free_ OrderedSet *m = NULL; + int seen[3] = {}; + char *val; + + m = ordered_set_new(&string_hash_ops); + assert_se(m); + + assert_se(ordered_set_put(m, (void*) "1") == 1); + assert_se(ordered_set_put(m, (void*) "22") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 1); + + ordered_set_print(stdout, "SET=", m); + + while ((val = ordered_set_steal_first(m))) + seen[strlen(val) - 1]++; + + assert_se(seen[0] == 1 && seen[1] == 1 && seen[2] == 1); + + assert_se(ordered_set_isempty(m)); + + ordered_set_print(stdout, "SET=", m); +} + +typedef struct Item { + int seen; +} Item; +static void item_seen(Item *item) { + item->seen++; +} + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, void, trivial_hash_func, trivial_compare_func, Item, item_seen); + +static void test_set_free_with_hash_ops(void) { + OrderedSet *m; + struct Item items[4] = {}; + unsigned i; + + assert_se(m = ordered_set_new(&item_hash_ops)); + for (i = 0; i < ELEMENTSOF(items) - 1; i++) + assert_se(ordered_set_put(m, items + i) == 1); + + m = ordered_set_free(m); + assert_se(items[0].seen == 1); + assert_se(items[1].seen == 1); + assert_se(items[2].seen == 1); + assert_se(items[3].seen == 0); +} + +static void test_set_put(void) { + _cleanup_ordered_set_free_ OrderedSet *m = NULL; + _cleanup_free_ char **t = NULL; + + m = ordered_set_new(&string_hash_ops); + assert_se(m); + + assert_se(ordered_set_put(m, (void*) "1") == 1); + assert_se(ordered_set_put(m, (void*) "22") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 0); + assert_se(ordered_set_remove(m, (void*) "333")); + assert_se(ordered_set_put(m, (void*) "333") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 0); + assert_se(ordered_set_put(m, (void*) "22") == 0); + + assert_se(t = ordered_set_get_strv(m)); + assert_se(streq(t[0], "1")); + assert_se(streq(t[1], "22")); + assert_se(streq(t[2], "333")); + assert_se(!t[3]); + + ordered_set_print(stdout, "FOO=", m); +} + +static void test_set_put_string_set(void) { + _cleanup_ordered_set_free_free_ OrderedSet *m = NULL; + _cleanup_ordered_set_free_ OrderedSet *q = NULL; + _cleanup_free_ char **final = NULL; /* "just free" because the strings are in the set */ + void *t; + + m = ordered_set_new(&string_hash_ops); + assert_se(m); + + q = ordered_set_new(&string_hash_ops); + assert_se(q); + + assert_se(t = strdup("1")); + assert_se(ordered_set_put(m, t) == 1); + assert_se(t = strdup("22")); + assert_se(ordered_set_put(m, t) == 1); + assert_se(t = strdup("333")); + assert_se(ordered_set_put(m, t) == 1); + + assert_se(ordered_set_put(q, (void*) "11") == 1); + assert_se(ordered_set_put(q, (void*) "22") == 1); + assert_se(ordered_set_put(q, (void*) "33") == 1); + + assert_se(ordered_set_put_string_set(m, q) == 2); + + assert_se(final = ordered_set_get_strv(m)); + assert_se(strv_equal(final, STRV_MAKE("1", "22", "333", "11", "33"))); + + ordered_set_print(stdout, "BAR=", m); +} + +int main(int argc, const char *argv[]) { + test_set_steal_first(); + test_set_free_with_hash_ops(); + test_set_put(); + test_set_put_string_set(); + + return 0; +} diff --git a/src/test/test-set.c b/src/test/test-set.c index 340edeb65f..b4e7a52fd9 100644 --- a/src/test/test-set.c +++ b/src/test/test-set.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "set.h" +#include "strv.h" static void test_set_steal_first(void) { _cleanup_set_free_ Set *m = NULL; @@ -77,6 +78,12 @@ static void test_set_put(void) { assert_se(set_put(m, (void*) "333") == 1); assert_se(set_put(m, (void*) "333") == 0); assert_se(set_put(m, (void*) "22") == 0); + + _cleanup_free_ char **t = set_get_strv(m); + assert_se(strv_contains(t, "1")); + assert_se(strv_contains(t, "22")); + assert_se(strv_contains(t, "333")); + assert_se(strv_length(t) == 3); } int main(int argc, const char *argv[]) { diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-13059 b/test/fuzz/fuzz-network-parser/oss-fuzz-13059 new file mode 100644 index 0000000000..d4267ffc0e Binary files /dev/null and b/test/fuzz/fuzz-network-parser/oss-fuzz-13059 differ