along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <resolv.h>
-#include <sys/ioctl.h>
-#include <poll.h>
#include <netinet/in.h>
+#include <poll.h>
+#include <sys/ioctl.h>
-#include "netlink-util.h"
-#include "network-internal.h"
-#include "socket-util.h"
#include "af-list.h"
-#include "utf8.h"
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "fd-util.h"
#include "fileio-label.h"
+#include "hostname-util.h"
+#include "io-util.h"
+#include "netlink-util.h"
+#include "network-internal.h"
#include "ordered-set.h"
+#include "parse-util.h"
#include "random-util.h"
-#include "hostname-util.h"
-
-#include "dns-domain.h"
-#include "resolved-conf.h"
#include "resolved-bus.h"
-#include "resolved-manager.h"
+#include "resolved-conf.h"
#include "resolved-llmnr.h"
+#include "resolved-manager.h"
+#include "resolved-resolv-conf.h"
+#include "resolved-mdns.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "utf8.h"
#define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC)
break;
case RTM_DELADDR:
- if (a)
- link_address_free(a);
+ link_address_free(a);
break;
}
}
static int manager_rtnl_listen(Manager *m) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *i;
int r;
return 0;
}
-static int determine_hostname(char **ret) {
+static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
_cleanup_free_ char *h = NULL, *n = NULL;
- int r;
+ char label[DNS_LABEL_MAX];
+ const char *p;
+ int r, k;
- assert(ret);
+ assert(llmnr_hostname);
+ assert(mdns_hostname);
+
+ /* Extract and normalize the first label of the locally
+ * configured hostname, and check it's not "localhost". */
h = gethostname_malloc();
if (!h)
return log_oom();
- if (!utf8_is_valid(h)) {
+ p = h;
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r < 0)
+ return log_error_errno(r, "Failed to unescape host name: %m");
+ if (r == 0) {
+ log_error("Couldn't find a single label in hosntame.");
+ return -EINVAL;
+ }
+
+ k = dns_label_undo_idna(label, r, label, sizeof(label));
+ if (k < 0)
+ return log_error_errno(k, "Failed to undo IDNA: %m");
+ if (k > 0)
+ r = k;
+
+ if (!utf8_is_valid(label)) {
log_error("System hostname is not UTF-8 clean.");
return -EINVAL;
}
- r = dns_name_normalize(h, &n);
- if (r < 0) {
- log_error("System hostname '%s' cannot be normalized.", h);
- return r;
+ r = dns_label_escape_new(label, r, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to escape host name: %m");
+
+ if (is_localhost(n)) {
+ log_debug("System hostname is 'localhost', ignoring.");
+ return -EINVAL;
}
- *ret = n;
+ r = dns_name_concat(n, "local", mdns_hostname);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine mDNS hostname: %m");
+
+ *llmnr_hostname = n;
n = NULL;
return 0;
}
static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- _cleanup_free_ char *h = NULL;
+ _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL;
Manager *m = userdata;
int r;
assert(m);
- r = determine_hostname(&h);
+ r = determine_hostname(&llmnr_hostname, &mdns_hostname);
if (r < 0)
return 0; /* ignore invalid hostnames */
- if (streq(h, m->hostname))
+ if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname))
return 0;
- log_info("System hostname changed to '%s'.", h);
- free(m->hostname);
- m->hostname = h;
- h = NULL;
+ log_info("System hostname changed to '%s'.", llmnr_hostname);
+
+ free(m->llmnr_hostname);
+ free(m->mdns_hostname);
+
+ m->llmnr_hostname = llmnr_hostname;
+ m->mdns_hostname = mdns_hostname;
+
+ llmnr_hostname = mdns_hostname = NULL;
manager_refresh_rrs(m);
return log_error_errno(r, "Failed to add hostname event source: %m");
}
- r = determine_hostname(&m->hostname);
+ r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname);
if (r < 0) {
log_info("Defaulting to hostname 'linux'.");
- m->hostname = strdup("linux");
- if (!m->hostname)
+ m->llmnr_hostname = strdup("linux");
+ if (!m->llmnr_hostname)
+ return log_oom();
+
+ m->mdns_hostname = strdup("linux.local");
+ if (!m->mdns_hostname)
return log_oom();
} else
- log_info("Using system hostname '%s'.", m->hostname);
+ log_info("Using system hostname '%s'.", m->llmnr_hostname);
return 0;
}
+static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ _cleanup_free_ char *buffer = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ Manager *m = userdata;
+ size_t size = 0;
+ DnsScope *scope;
+
+ assert(s);
+ assert(si);
+ assert(m);
+
+ f = open_memstream(&buffer, &size);
+ if (!f)
+ return log_oom();
+
+ LIST_FOREACH(scopes, scope, m->dns_scopes)
+ dns_scope_dump(scope, f);
+
+ if (fflush_and_check(f) < 0)
+ return log_oom();
+
+ log_dump(LOG_INFO, buffer);
+ return 0;
+}
+
int manager_new(Manager **ret) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
+ m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
m->hostname_fd = -1;
- m->llmnr_support = SUPPORT_YES;
+ m->llmnr_support = RESOLVE_SUPPORT_YES;
+ m->mdns_support = RESOLVE_SUPPORT_NO;
+ m->dnssec_mode = DNSSEC_NO;
m->read_resolv_conf = true;
+ m->need_builtin_fallbacks = true;
+
+ r = dns_trust_anchor_load(&m->trust_anchor);
+ if (r < 0)
+ return r;
- r = manager_parse_dns_server(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
+ r = manager_parse_config_file(m);
if (r < 0)
return r;
if (r < 0)
return r;
+ (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m);
+
*ret = m;
m = NULL;
if (r < 0)
return r;
+ r = manager_mdns_start(m);
+ if (r < 0)
+ return r;
+
return 0;
}
if (!m)
return NULL;
+ dns_server_unlink_all(m->dns_servers);
+ dns_server_unlink_all(m->fallback_dns_servers);
+ dns_search_domain_unlink_all(m->search_domains);
+
while ((l = hashmap_first(m->links)))
link_free(l);
while (m->dns_queries)
dns_query_free(m->dns_queries);
- manager_flush_dns_servers(m, DNS_SERVER_SYSTEM);
- manager_flush_dns_servers(m, DNS_SERVER_FALLBACK);
-
dns_scope_free(m->unicast_scope);
hashmap_free(m->links);
sd_event_source_unref(m->network_event_source);
sd_network_monitor_unref(m->network_monitor);
+ sd_netlink_unref(m->rtnl);
+ sd_event_source_unref(m->rtnl_event_source);
+
manager_llmnr_stop(m);
+ manager_mdns_stop(m);
sd_bus_slot_unref(m->prepare_for_sleep_slot);
sd_event_source_unref(m->bus_retry_event_source);
sd_bus_unref(m->bus);
+ sd_event_source_unref(m->sigusr1_event_source);
+
sd_event_unref(m->event);
- dns_resource_key_unref(m->host_ipv4_key);
- dns_resource_key_unref(m->host_ipv6_key);
+ dns_resource_key_unref(m->llmnr_host_ipv4_key);
+ dns_resource_key_unref(m->llmnr_host_ipv6_key);
- safe_close(m->hostname_fd);
sd_event_source_unref(m->hostname_event_source);
- free(m->hostname);
+ safe_close(m->hostname_fd);
+ free(m->llmnr_hostname);
+ free(m->mdns_hostname);
+
+ dns_trust_anchor_flush(&m->trust_anchor);
free(m);
return NULL;
}
-int manager_read_resolv_conf(Manager *m) {
- _cleanup_fclose_ FILE *f = NULL;
- struct stat st, own;
- char line[LINE_MAX];
- DnsServer *s, *nx;
- usec_t t;
- int r;
-
- assert(m);
-
- /* Reads the system /etc/resolv.conf, if it exists and is not
- * symlinked to our own resolv.conf instance */
-
- if (!m->read_resolv_conf)
- return 0;
-
- r = stat("/etc/resolv.conf", &st);
- if (r < 0) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
- r = -errno;
- goto clear;
- }
-
- /* Have we already seen the file? */
- t = timespec_load(&st.st_mtim);
- if (t == m->resolv_conf_mtime)
- return 0;
-
- m->resolv_conf_mtime = t;
-
- /* Is it symlinked to our own file? */
- if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
- st.st_dev == own.st_dev &&
- st.st_ino == own.st_ino) {
- r = 0;
- goto clear;
- }
-
- f = fopen("/etc/resolv.conf", "re");
- if (!f) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
- r = -errno;
- goto clear;
- }
-
- if (fstat(fileno(f), &st) < 0) {
- log_error_errno(errno, "Failed to stat open file: %m");
- r = -errno;
- goto clear;
- }
-
- LIST_FOREACH(servers, s, m->dns_servers)
- s->marked = true;
-
- FOREACH_LINE(line, f, r = -errno; goto clear) {
- union in_addr_union address;
- int family;
- char *l;
- const char *a;
-
- truncate_nl(line);
-
- l = strstrip(line);
- if (*l == '#' || *l == ';')
- continue;
-
- a = first_word(l, "nameserver");
- if (!a)
- continue;
-
- r = in_addr_from_string_auto(a, &family, &address);
- if (r < 0) {
- log_warning("Failed to parse name server %s.", a);
- continue;
- }
-
- LIST_FOREACH(servers, s, m->dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, &address) > 0)
- break;
-
- if (s)
- s->marked = false;
- else {
- r = dns_server_new(m, NULL, DNS_SERVER_SYSTEM, NULL, family, &address);
- if (r < 0)
- goto clear;
- }
- }
-
- LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers)
- if (s->marked) {
- LIST_REMOVE(servers, m->dns_servers, s);
- dns_server_unref(s);
- }
-
- /* Whenever /etc/resolv.conf changes, start using the first
- * DNS server of it. This is useful to deal with broken
- * network managing implementations (like NetworkManager),
- * that when connecting to a VPN place both the VPN DNS
- * servers and the local ones in /etc/resolv.conf. Without
- * resetting the DNS server to use back to the first entry we
- * will continue to use the local one thus being unable to
- * resolve VPN domains. */
- manager_set_dns_server(m, m->dns_servers);
-
- return 0;
-
-clear:
- while (m->dns_servers) {
- s = m->dns_servers;
-
- LIST_REMOVE(servers, m->dns_servers, s);
- dns_server_unref(s);
- }
-
- return r;
-}
-
-static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(s);
- assert(f);
- assert(count);
-
- r = in_addr_to_string(s->family, &s->address, &t);
- if (r < 0) {
- log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
- return;
- }
-
- if (*count == MAXNS)
- fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
-
- fprintf(f, "nameserver %s\n", t);
- (*count) ++;
-}
-
-static void write_resolv_conf_search(
- const char *domain, FILE *f,
- unsigned *count,
- unsigned *length) {
-
- assert(domain);
- assert(f);
- assert(length);
-
- if (*count >= MAXDNSRCH ||
- *length + strlen(domain) > 256) {
- if (*count == MAXDNSRCH)
- fputs(" # Too many search domains configured, remaining ones ignored.", f);
- if (*length <= 256)
- fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
-
- return;
- }
-
- fprintf(f, " %s", domain);
-
- (*length) += strlen(domain);
- (*count) ++;
-}
-
-static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
- Iterator i;
-
- fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
- "# Third party programs must not access this file directly, but\n"
- "# only through the symlink at /etc/resolv.conf. To manage\n"
- "# resolv.conf(5) in a different way, replace the symlink by a\n"
- "# static file or a different symlink.\n\n", f);
-
- if (ordered_set_isempty(dns))
- fputs("# No DNS servers known.\n", f);
- else {
- DnsServer *s;
- unsigned count = 0;
-
- ORDERED_SET_FOREACH(s, dns, i)
- write_resolv_conf_server(s, f, &count);
- }
-
- if (!ordered_set_isempty(domains)) {
- unsigned length = 0, count = 0;
- char *domain;
-
- fputs("search", f);
- ORDERED_SET_FOREACH(domain, domains, i)
- write_resolv_conf_search(domain, f, &count, &length);
- fputs("\n", f);
- }
-
- return fflush_and_check(f);
-}
-
-int manager_write_resolv_conf(Manager *m) {
- static const char path[] = "/run/systemd/resolve/resolv.conf";
- _cleanup_free_ char *temp_path = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
- DnsServer *s;
- Iterator i;
- Link *l;
- int r;
-
- assert(m);
-
- /* Read the system /etc/resolv.conf first */
- manager_read_resolv_conf(m);
-
- /* Add the full list to a set, to filter out duplicates */
- dns = ordered_set_new(&dns_server_hash_ops);
- if (!dns)
- return -ENOMEM;
-
- domains = ordered_set_new(&dns_name_hash_ops);
- if (!domains)
- return -ENOMEM;
-
- /* First add the system-wide servers */
- LIST_FOREACH(servers, s, m->dns_servers) {
- r = ordered_set_put(dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
-
- /* Then, add the per-link servers and domains */
- HASHMAP_FOREACH(l, m->links, i) {
- char **domain;
-
- LIST_FOREACH(servers, s, l->dns_servers) {
- r = ordered_set_put(dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
-
- if (!l->unicast_scope)
- continue;
-
- STRV_FOREACH(domain, l->unicast_scope->domains) {
- r = ordered_set_put(domains, *domain);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- /* If we found nothing, add the fallback servers */
- if (ordered_set_isempty(dns)) {
- LIST_FOREACH(servers, s, m->fallback_dns_servers) {
- r = ordered_set_put(dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- r = fopen_temporary_label(path, path, &f, &temp_path);
- if (r < 0)
- return r;
-
- fchmod(fileno(f), 0644);
-
- r = write_resolv_conf_contents(f, dns, domains);
- if (r < 0)
- goto fail;
-
- if (rename(temp_path, path) < 0) {
- r = -errno;
- goto fail;
- }
-
- return 0;
-
-fail:
- (void) unlink(path);
- (void) unlink(temp_path);
- return r;
-}
-
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
union {
int manager_write(Manager *m, int fd, DnsPacket *p) {
int r;
- log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
+ log_debug("Sending %s packet with id %" PRIu16 ".", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
r = write_loop(fd, DNS_PACKET_DATA(p), p->size);
if (r < 0)
assert(port > 0);
assert(p);
- log_debug("Sending %s packet with id %u on interface %i/%s", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
+ log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
if (family == AF_INET)
return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p);
return -EAFNOSUPPORT;
}
-DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) {
- DnsServer *s;
-
- assert(m);
- assert(in_addr);
-
- LIST_FOREACH(servers, s, m->dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
- return s;
-
- LIST_FOREACH(servers, s, m->fallback_dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
- return s;
-
- return NULL;
-}
-
-DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
- assert(m);
-
- if (m->current_dns_server == s)
- return s;
-
- if (s) {
- _cleanup_free_ char *ip = NULL;
-
- in_addr_to_string(s->family, &s->address, &ip);
- log_info("Switching to system DNS server %s.", strna(ip));
- }
-
- m->current_dns_server = s;
-
- if (m->unicast_scope)
- dns_cache_flush(&m->unicast_scope->cache);
-
- return s;
-}
-
-DnsServer *manager_get_dns_server(Manager *m) {
- Link *l;
- assert(m);
-
- /* Try to read updates resolv.conf */
- manager_read_resolv_conf(m);
-
- if (!m->current_dns_server)
- manager_set_dns_server(m, m->dns_servers);
-
- if (!m->current_dns_server) {
- bool found = false;
- Iterator i;
-
- /* No DNS servers configured, let's see if there are
- * any on any links. If not, we use the fallback
- * servers */
-
- HASHMAP_FOREACH(l, m->links, i)
- if (l->dns_servers) {
- found = true;
- break;
- }
-
- if (!found)
- manager_set_dns_server(m, m->fallback_dns_servers);
- }
-
- return m->current_dns_server;
-}
-
-void manager_next_dns_server(Manager *m) {
- assert(m);
-
- /* If there's currently no DNS server set, then the next
- * manager_get_dns_server() will find one */
- if (!m->current_dns_server)
- return;
-
- /* Change to the next one */
- if (m->current_dns_server->servers_next) {
- manager_set_dns_server(m, m->current_dns_server->servers_next);
- return;
- }
-
- /* If there was no next one, then start from the beginning of
- * the list */
- if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
- manager_set_dns_server(m, m->fallback_dns_servers);
- else
- manager_set_dns_server(m, m->dns_servers);
-}
-
uint32_t manager_find_mtu(Manager *m) {
uint32_t mtu = 0;
Link *l;
assert(m);
- m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key);
- m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key);
+ m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key);
+ m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key);
HASHMAP_FOREACH(l, m->links, i) {
link_add_rrs(l, true);
int manager_next_hostname(Manager *m) {
const char *p;
uint64_t u, a;
- char *h;
+ char *h, *k;
+ int r;
assert(m);
- p = strchr(m->hostname, 0);
+ p = strchr(m->llmnr_hostname, 0);
assert(p);
- while (p > m->hostname) {
+ while (p > m->llmnr_hostname) {
if (!strchr("0123456789", p[-1]))
break;
random_bytes(&a, sizeof(a));
u += 1 + a % 10;
- if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0)
+ if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0)
return -ENOMEM;
- log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->hostname, h);
+ r = dns_name_concat(h, "local", &k);
+ if (r < 0) {
+ free(h);
+ return r;
+ }
+
+ log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h);
- free(m->hostname);
- m->hostname = h;
+ free(m->llmnr_hostname);
+ m->llmnr_hostname = h;
+
+ free(m->mdns_hostname);
+ m->mdns_hostname = k;
manager_refresh_rrs(m);
if (!l)
return NULL;
- if (p->protocol == DNS_PROTOCOL_LLMNR) {
+ switch (p->protocol) {
+ case DNS_PROTOCOL_LLMNR:
if (p->family == AF_INET)
return l->llmnr_ipv4_scope;
else if (p->family == AF_INET6)
return l->llmnr_ipv6_scope;
+
+ break;
+
+ case DNS_PROTOCOL_MDNS:
+ if (p->family == AF_INET)
+ return l->mdns_ipv4_scope;
+ else if (p->family == AF_INET6)
+ return l->mdns_ipv6_scope;
+
+ break;
+
+ default:
+ break;
}
return NULL;
dns_zone_verify_all(&s->zone);
}
-void manager_flush_dns_servers(Manager *m, DnsServerType t) {
+int manager_is_own_hostname(Manager *m, const char *name) {
+ int r;
+
+ assert(m);
+ assert(name);
+
+ if (m->llmnr_hostname) {
+ r = dns_name_equal(name, m->llmnr_hostname);
+ if (r != 0)
+ return r;
+ }
+
+ if (m->mdns_hostname)
+ return dns_name_equal(name, m->mdns_hostname);
+
+ return 0;
+}
+
+int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
DnsServer *s;
+ Iterator i;
+ Link *l;
+ int r;
assert(m);
+ assert(dns);
- if (t == DNS_SERVER_SYSTEM)
- while (m->dns_servers) {
- s = m->dns_servers;
+ r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops);
+ if (r < 0)
+ return r;
- LIST_REMOVE(servers, m->dns_servers, s);
- dns_server_unref(s);
- }
+ /* First add the system-wide servers and domains */
+ LIST_FOREACH(servers, s, m->dns_servers) {
+ r = ordered_set_put(*dns, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
- if (t == DNS_SERVER_FALLBACK)
- while (m->fallback_dns_servers) {
- s = m->fallback_dns_servers;
+ /* Then, add the per-link servers */
+ HASHMAP_FOREACH(l, m->links, i) {
+ LIST_FOREACH(servers, s, l->dns_servers) {
+ r = ordered_set_put(*dns, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
- LIST_REMOVE(servers, m->fallback_dns_servers, s);
- dns_server_unref(s);
+ /* If we found nothing, add the fallback servers */
+ if (ordered_set_isempty(*dns)) {
+ LIST_FOREACH(servers, s, m->fallback_dns_servers) {
+ r = ordered_set_put(*dns, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
}
+ }
+
+ return 0;
}
-static const char* const support_table[_SUPPORT_MAX] = {
- [SUPPORT_NO] = "no",
- [SUPPORT_YES] = "yes",
- [SUPPORT_RESOLVE] = "resolve",
-};
-DEFINE_STRING_TABLE_LOOKUP(support, Support);
+int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
+ DnsSearchDomain *d;
+ Iterator i;
+ Link *l;
+ int r;
+
+ assert(m);
+ assert(domains);
+
+ r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops);
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(domains, d, m->search_domains) {
+ r = ordered_set_put(*domains, d->name);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+
+ HASHMAP_FOREACH(l, m->links, i) {
+
+ LIST_FOREACH(domains, d, l->search_domains) {
+ r = ordered_set_put(*domains, d->name);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}