From 0eac462399c8e87bcce252cf058eba9f2678f2bd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 17:59:40 +0100 Subject: resolved: rework dns server lifecycle logic Previously, there was a chance of memory corruption, because when switching to the next DNS server we didn't care whether they linked list of DNS servers was still valid. Clean up lifecycle of the dns server logic: - When a DnsServer object is still in the linked list of DnsServers for a link or the manager, indicate so with a "linked" boolean field, and never follow the linked list if that boolean is not set. - When picking a DnsServer to use for a link ot manager, always explicitly take a reference. This also rearranges some logic, to make the tracking of dns servers by link and globally more alike. --- src/resolve/resolved-link.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/resolve/resolved-link.h') diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index e3ab27c249..76e96e733a 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -75,6 +75,10 @@ bool link_relevant(Link *l, int family); LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr); void link_add_rrs(Link *l, bool force_remove); +void link_flush_dns_servers(Link *l); +void link_flush_marked_dns_servers(Link *l); +void link_mark_dns_servers(Link *l); + DnsServer* link_set_dns_server(Link *l, DnsServer *s); DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr); DnsServer* link_get_dns_server(Link *l); -- cgit v1.2.3-54-g00ecf From a51c10485af349eb15faa4d1a63b9818bcf3e589 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 21:12:51 +0100 Subject: resolved: add a generic DnsSearchDomain concept With this change, we add a new object to resolved, "DnsSearchDomain=" which wraps a search domain. This is then used to introduce a global search domain list, in addition to the existing per-link search domain list which is reword to make use of this new object too. This is preparation for implement proper unicast DNS search domain support. --- Makefile.am | 2 + man/resolved.conf.xml | 14 +- src/resolve/resolved-conf.c | 84 +++++++++++- src/resolve/resolved-conf.h | 7 +- src/resolve/resolved-dns-scope.c | 18 ++- src/resolve/resolved-dns-scope.h | 4 +- src/resolve/resolved-dns-search-domain.c | 223 +++++++++++++++++++++++++++++++ src/resolve/resolved-dns-search-domain.h | 71 ++++++++++ src/resolve/resolved-gperf.gperf | 1 + src/resolve/resolved-link.c | 46 +++++-- src/resolve/resolved-link.h | 6 + src/resolve/resolved-manager.c | 2 + src/resolve/resolved-manager.h | 5 + src/resolve/resolved-resolv-conf.c | 35 +++-- src/resolve/resolved.conf.in | 1 + 15 files changed, 488 insertions(+), 31 deletions(-) create mode 100644 src/resolve/resolved-dns-search-domain.c create mode 100644 src/resolve/resolved-dns-search-domain.h (limited to 'src/resolve/resolved-link.h') diff --git a/Makefile.am b/Makefile.am index cac5c5e3c7..296f2c7e5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5165,6 +5165,8 @@ systemd_resolved_SOURCES = \ src/resolve/resolved-dns-scope.c \ src/resolve/resolved-dns-server.h \ src/resolve/resolved-dns-server.c \ + src/resolve/resolved-dns-search-domain.h \ + src/resolve/resolved-dns-search-domain.c \ src/resolve/resolved-dns-cache.h \ src/resolve/resolved-dns-cache.c \ src/resolve/resolved-dns-zone.h \ diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index c9034a979f..4680b6a4e5 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -1,4 +1,4 @@ - + @@ -77,7 +77,7 @@ sent to one of the listed DNS servers in parallel to any per-interface DNS servers acquired from systemd-networkd.service8. - For compatibility reasons, if this setting is not specified, , + For compatibility reasons, if this setting is not specified, the DNS servers listed in /etc/resolv.conf are used instead, if that file exists and any servers are configured in it. This @@ -98,6 +98,16 @@ instead. + + Domains= + A space-separated list of search domains. For + compatibility reasons, if this setting is not specified, the + search domains listed in /etc/resolv.conf + are used instead, if that file exists and any domains are + configured in it. This setting defaults to the empty + list. + + LLMNR= Takes a boolean argument or diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index c11a8badd9..dbac45170f 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -71,10 +71,49 @@ int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, con break; r = manager_add_dns_server_by_string(m, type, word); - if (r < 0) { + if (r < 0) log_warning_errno(r, "Failed to add DNS server address '%s', ignoring.", word); - continue; - } + } + + return 0; +} + +int manager_add_search_domain_by_string(Manager *m, const char *domain) { + DnsSearchDomain *d; + int r; + + assert(m); + assert(domain); + + r = dns_search_domain_find(m->search_domains, domain, &d); + if (r < 0) + return r; + if (r > 0) { + dns_search_domain_move_back_and_unmark(d); + return 0; + } + + return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain); +} + +int manager_parse_search_domains_and_warn(Manager *m, const char *string) { + int r; + + assert(m); + assert(string); + + for(;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return r; + if (r == 0) + break; + + r = manager_add_search_domain_by_string(m, word); + if (r < 0) + log_warning_errno(r, "Failed to add search domain '%s', ignoring.", word); } return 0; @@ -122,6 +161,45 @@ int config_parse_dns_servers( return 0; } +int config_parse_search_domains( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Manager *m = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(m); + + if (isempty(rvalue)) + /* Empty assignment means clear the list */ + dns_search_domain_unlink_all(m->search_domains); + else { + /* Otherwise, add to the list */ + r = manager_parse_search_domains_and_warn(m, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue); + return 0; + } + } + + /* If we have a manual setting, then we stop reading + * /etc/resolv.conf */ + m->read_resolv_conf = false; + + return 0; +} + int config_parse_support( const char *unit, const char *filename, diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index 170f3e94aa..28d2549d35 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -23,13 +23,16 @@ #include "resolved-manager.h" -int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string); int manager_parse_config_file(Manager *m); -int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string); +int manager_add_search_domain_by_string(Manager *m, const char *domain); +int manager_parse_search_domains_and_warn(Manager *m, const char *string); + int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word); +int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string); const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length); int config_parse_dns_servers(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_search_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_support(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 5174502af0..2e47691c56 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -103,7 +103,6 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_zone_flush(&s->zone); LIST_REMOVE(scopes, s->manager->dns_scopes, s); - strv_free(s->domains); free(s); return NULL; @@ -323,7 +322,7 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add } DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) { - char **i; + DnsSearchDomain *d; assert(s); assert(domain); @@ -345,8 +344,8 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) return DNS_SCOPE_NO; - STRV_FOREACH(i, s->domains) - if (dns_name_endswith(domain, *i) > 0) + LIST_FOREACH(domains, d, dns_scope_get_search_domains(s)) + if (dns_name_endswith(domain, d->name) > 0) return DNS_SCOPE_YES; switch (s->protocol) { @@ -850,3 +849,14 @@ void dns_scope_dump(DnsScope *s, FILE *f) { dns_cache_dump(&s->cache, f); } } + +DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) { + + if (s->protocol != DNS_PROTOCOL_DNS) + return NULL; + + if (s->link) + return s->link->search_domains; + + return NULL; +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index f9b2bfda9d..f69d51b203 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -47,8 +47,6 @@ struct DnsScope { Link *link; - char **domains; - DnsCache cache; DnsZone zone; @@ -91,3 +89,5 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr); void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p); void dns_scope_dump(DnsScope *s, FILE *f); + +DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s); diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c new file mode 100644 index 0000000000..5d927bb2a1 --- /dev/null +++ b/src/resolve/resolved-dns-search-domain.c @@ -0,0 +1,223 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "alloc-util.h" +#include "dns-domain.h" +#include "resolved-dns-search-domain.h" + +int dns_search_domain_new( + Manager *m, + DnsSearchDomain **ret, + DnsSearchDomainType type, + Link *l, + const char *name) { + + _cleanup_free_ char *normalized = NULL; + DnsSearchDomain *d, *tail; + int r; + + assert(m); + assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l); + assert(name); + + r = dns_name_normalize(name, &normalized); + if (r < 0) + return r; + + r = dns_name_root(normalized); + if (r < 0) + return r; + if (r > 0) + return -EINVAL; + + d = new0(DnsSearchDomain, 1); + if (!d) + return -ENOMEM; + + d->n_ref = 1; + d->manager = m; + d->type = type; + d->name = normalized; + normalized = NULL; + + switch (type) { + + case DNS_SEARCH_DOMAIN_LINK: + d->link = l; + LIST_FIND_TAIL(domains, l->search_domains, tail); + LIST_INSERT_AFTER(domains, l->search_domains, tail, d); + break; + + case DNS_SERVER_SYSTEM: + LIST_FIND_TAIL(domains, m->search_domains, tail); + LIST_INSERT_AFTER(domains, m->search_domains, tail, d); + break; + + default: + assert_not_reached("Unknown search domain type"); + } + + d->linked = true; + + if (ret) + *ret = d; + + return 0; +} + +DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) { + if (!d) + return NULL; + + assert(d->n_ref > 0); + d->n_ref++; + + return d; +} + +DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) { + if (!d) + return NULL; + + assert(d->n_ref > 0); + d->n_ref--; + + if (d->n_ref > 0) + return NULL; + + free(d->name); + free(d); + + return NULL; +} + +void dns_search_domain_unlink(DnsSearchDomain *d) { + assert(d); + assert(d->manager); + + if (!d->linked) + return; + + switch (d->type) { + + case DNS_SEARCH_DOMAIN_LINK: + assert(d->link); + LIST_REMOVE(domains, d->link->search_domains, d); + break; + + case DNS_SEARCH_DOMAIN_SYSTEM: + LIST_REMOVE(domains, d->manager->search_domains, d); + break; + } + + d->linked = false; + + dns_search_domain_unref(d); +} + +void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) { + DnsSearchDomain *tail; + + assert(d); + + if (!d->marked) + return; + + d->marked = false; + + if (!d->linked || !d->domains_next) + return; + + switch (d->type) { + + case DNS_SEARCH_DOMAIN_LINK: + assert(d->link); + LIST_FIND_TAIL(domains, d, tail); + LIST_REMOVE(domains, d->link->search_domains, d); + LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d); + break; + + case DNS_SEARCH_DOMAIN_SYSTEM: + LIST_FIND_TAIL(domains, d, tail); + LIST_REMOVE(domains, d->manager->search_domains, d); + LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d); + break; + + default: + assert_not_reached("Unknown search domain type"); + } +} + +void dns_search_domain_unlink_all(DnsSearchDomain *first) { + DnsSearchDomain *next; + + if (!first) + return; + + next = first->domains_next; + dns_search_domain_unlink(first); + + dns_search_domain_unlink_all(next); +} + +void dns_search_domain_unlink_marked(DnsSearchDomain *first) { + DnsSearchDomain *next; + + if (!first) + return; + + next = first->domains_next; + + if (first->marked) + dns_search_domain_unlink(first); + + dns_search_domain_unlink_marked(next); +} + +void dns_search_domain_mark_all(DnsSearchDomain *first) { + if (!first) + return; + + first->marked = true; + dns_search_domain_mark_all(first->domains_next); +} + +int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) { + DnsSearchDomain *d; + int r; + + assert(name); + assert(ret); + + LIST_FOREACH(domains, d, first) { + + r = dns_name_equal(name, d->name); + if (r < 0) + return r; + if (r > 0) { + *ret = d; + return 1; + } + } + + *ret = NULL; + return 0; +} diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h new file mode 100644 index 0000000000..20b441206c --- /dev/null +++ b/src/resolve/resolved-dns-search-domain.h @@ -0,0 +1,71 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "macro.h" + +typedef struct DnsSearchDomain DnsSearchDomain; + +typedef enum DnsSearchDomainType { + DNS_SEARCH_DOMAIN_SYSTEM, + DNS_SEARCH_DOMAIN_LINK, +} DnsSearchDomainType; + +#include "resolved-link.h" +#include "resolved-manager.h" + +struct DnsSearchDomain { + Manager *manager; + + unsigned n_ref; + + DnsSearchDomainType type; + Link *link; + + char *name; + + bool marked:1; + + bool linked:1; + LIST_FIELDS(DnsSearchDomain, domains); +}; + +int dns_search_domain_new( + Manager *m, + DnsSearchDomain **ret, + DnsSearchDomainType type, + Link *link, + const char *name); + +DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d); +DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d); + +void dns_search_domain_unlink(DnsSearchDomain *d); +void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d); + +void dns_search_domain_unlink_all(DnsSearchDomain *first); +void dns_search_domain_unlink_marked(DnsSearchDomain *first); +void dns_search_domain_mark_all(DnsSearchDomain *first); + +int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret); + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref); diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index dc4ef74b8c..50662656d5 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -16,4 +16,5 @@ struct ConfigPerfItem; %% Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 +Resolve.Domains, config_parse_search_domains, 0, 0 Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 4931fc9d3b..c367160d98 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -66,6 +66,7 @@ Link *link_free(Link *l) { return NULL; link_flush_dns_servers(l); + dns_search_domain_unlink_all(l->search_domains); while (l->addresses) link_address_free(l->addresses); @@ -219,29 +220,56 @@ clear: return r; } -static int link_update_domains(Link *l) { +static int link_update_search_domains(Link *l) { + _cleanup_strv_free_ char **domains = NULL; + char **i; int r; - if (!l->unicast_scope) - return 0; - - l->unicast_scope->domains = strv_free(l->unicast_scope->domains); + assert(l); - r = sd_network_link_get_domains(l->ifindex, - &l->unicast_scope->domains); + r = sd_network_link_get_domains(l->ifindex, &domains); if (r < 0) - return r; + goto clear; + + dns_search_domain_mark_all(l->search_domains); + + STRV_FOREACH(i, domains) { + DnsSearchDomain *d; + r = dns_search_domain_find(l->search_domains, *i, &d); + if (r < 0) + goto clear; + + if (r > 0) + dns_search_domain_move_back_and_unmark(d); + else { + r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i); + if (r < 0) + goto clear; + } + } + + dns_search_domain_unlink_marked(l->search_domains); return 0; + +clear: + dns_search_domain_unlink_all(l->search_domains); + return r; } int link_update_monitor(Link *l) { + int r; + assert(l); link_update_dns_servers(l); link_update_llmnr_support(l); link_allocate_scopes(l); - link_update_domains(l); + + r = link_update_search_domains(l); + if (r < 0) + log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name); + link_add_rrs(l, false); return 0; diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index 76e96e733a..d72461de06 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -30,6 +30,8 @@ typedef struct Link Link; typedef struct LinkAddress LinkAddress; #include "resolved-dns-rr.h" +#include "resolved-dns-search-domain.h" +#include "resolved-dns-server.h" #include "resolved-manager.h" struct LinkAddress { @@ -57,6 +59,8 @@ struct Link { LIST_HEAD(DnsServer, dns_servers); DnsServer *current_dns_server; + LIST_HEAD(DnsSearchDomain, search_domains); + Support llmnr_support; DnsScope *unicast_scope; @@ -84,6 +88,8 @@ DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union * DnsServer* link_get_dns_server(Link *l); void link_next_dns_server(Link *l); +void link_flush_search_domains(Link *l); + int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr); LinkAddress *link_address_free(LinkAddress *a); int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index c3c61a0893..0771ac840f 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -536,6 +536,8 @@ Manager *manager_free(Manager *m) { manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); manager_flush_dns_servers(m, DNS_SERVER_FALLBACK); + dns_search_domain_unlink_all(m->search_domains); + while ((l = hashmap_first(m->links))) link_free(l); diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 8a716b3ceb..0683e23f6c 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -40,6 +40,8 @@ enum Support { }; #include "resolved-dns-query.h" +#include "resolved-dns-search-domain.h" +#include "resolved-dns-server.h" #include "resolved-dns-stream.h" #include "resolved-link.h" @@ -70,7 +72,10 @@ struct Manager { LIST_HEAD(DnsServer, fallback_dns_servers); DnsServer *current_dns_server; + LIST_HEAD(DnsSearchDomain, search_domains); + bool need_builtin_fallbacks:1; + bool read_resolv_conf:1; usec_t resolv_conf_mtime; diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 98c1720f0f..2ab0008fce 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -86,13 +86,12 @@ int manager_read_resolv_conf(Manager *m) { } manager_mark_dns_servers(m, DNS_SERVER_SYSTEM); + dns_search_domain_mark_all(m->search_domains); FOREACH_LINE(line, f, r = -errno; goto clear) { const char *a; char *l; - truncate_nl(line); - l = strstrip(line); if (*l == '#' || *l == ';') continue; @@ -105,9 +104,22 @@ int manager_read_resolv_conf(Manager *m) { continue; } + + a = first_word(l, "domain"); + if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */ + a = first_word(l, "search"); + if (a) { + r = manager_parse_search_domains_and_warn(m, a); + if (r < 0) + log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a); + } } + /* Flush out all servers and search domains that are still + * marked. Those are then ones that didn't appear in the new + * /etc/resolv.conf */ manager_flush_marked_dns_servers(m, DNS_SERVER_SYSTEM); + dns_search_domain_unlink_marked(m->search_domains); /* Whenever /etc/resolv.conf changes, start using the first * DNS server of it. This is useful to deal with broken @@ -123,6 +135,7 @@ int manager_read_resolv_conf(Manager *m) { clear: manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); + dns_search_domain_unlink_all(m->search_domains); return r; } @@ -211,6 +224,7 @@ int manager_write_resolv_conf(Manager *m) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL; + DnsSearchDomain *d; DnsServer *s; Iterator i; Link *l; @@ -239,10 +253,16 @@ int manager_write_resolv_conf(Manager *m) { 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; + } + /* 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) @@ -251,11 +271,8 @@ int manager_write_resolv_conf(Manager *m) { return r; } - if (!l->unicast_scope) - continue; - - STRV_FOREACH(domain, l->unicast_scope->domains) { - r = ordered_set_put(domains, *domain); + LIST_FOREACH(domains, d, l->search_domains) { + r = ordered_set_put(domains, d->name); if (r == -EEXIST) continue; if (r < 0) diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 3eb19e42b7..39ecf83217 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -14,4 +14,5 @@ [Resolve] #DNS= #FallbackDNS=@DNS_SERVERS@ +#Domains= #LLMNR=yes -- cgit v1.2.3-54-g00ecf From 4b95f1798f22c1bb75295f448188560cb6ec9ece Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 21:27:29 +0100 Subject: resolved: unify DnsServer handling code between Link and Manager This copies concepts we introduced for the DnsSearchDomain stuff, and reworks the operations on lists of dns servers to be reusable and generic for use both with the Link and the Manager object. --- src/resolve/resolved-conf.c | 4 +- src/resolve/resolved-dns-server.c | 82 +++++++++++++++++--------------------- src/resolve/resolved-dns-server.h | 11 ++--- src/resolve/resolved-link.c | 51 +++--------------------- src/resolve/resolved-link.h | 7 ---- src/resolve/resolved-manager.c | 5 +-- src/resolve/resolved-resolv-conf.c | 6 +-- 7 files changed, 54 insertions(+), 112 deletions(-) (limited to 'src/resolve/resolved-link.h') diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index dbac45170f..3fc7d9ae3d 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -40,7 +40,7 @@ int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char return r; /* Filter out duplicates */ - s = manager_find_dns_server(m, type, family, &address); + s = dns_server_find(manager_get_first_dns_server(m, type), family, &address); if (s) { /* * Drop the marker. This is used to find the servers @@ -141,7 +141,7 @@ int config_parse_dns_servers( if (isempty(rvalue)) /* Empty assignment means clear the list */ - manager_flush_dns_servers(m, ltype); + dns_server_unlink_all(manager_get_first_dns_server(m, ltype)); else { /* Otherwise, add to the list */ r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 81301ab800..93b954139b 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -240,74 +240,64 @@ const struct hash_ops dns_server_hash_ops = { .compare = dns_server_compare_func }; -DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) { - assert(m); - - switch (t) { +void dns_server_unlink_all(DnsServer *first) { + DnsServer *next; - case DNS_SERVER_SYSTEM: - return m->dns_servers; + if (!first) + return; - case DNS_SERVER_FALLBACK: - return m->fallback_dns_servers; + next = first->servers_next; + dns_server_unlink(first); - default: - return NULL; - } + dns_server_unlink_all(next); } -void manager_flush_dns_servers(Manager *m, DnsServerType type) { - assert(m); +void dns_server_unlink_marked(DnsServer *first) { + DnsServer *next; - for (;;) { - DnsServer *first; + if (!first) + return; - first = manager_get_first_dns_server(m, type); - if (!first) - break; + next = first->servers_next; + if (first->marked) dns_server_unlink(first); - } -} - -void manager_flush_marked_dns_servers(Manager *m, DnsServerType type) { - DnsServer *first, *s, *next; - assert(m); - - first = manager_get_first_dns_server(m, type); + dns_server_unlink_marked(next); +} - LIST_FOREACH_SAFE(servers, s, next, first) { - if (!s->marked) - continue; +void dns_server_mark_all(DnsServer *first) { + if (!first) + return; - dns_server_unlink(s); - } + first->marked = true; + dns_server_mark_all(first->servers_next); } -void manager_mark_dns_servers(Manager *m, DnsServerType type) { - DnsServer *first, *s; +DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) { + DnsServer *s; - assert(m); - - first = manager_get_first_dns_server(m, type); LIST_FOREACH(servers, s, first) - s->marked = true; -} + if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) + return s; -DnsServer* manager_find_dns_server(Manager *m, DnsServerType type, int family, const union in_addr_union *in_addr) { - DnsServer *first, *s; + return NULL; +} +DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) { assert(m); - assert(in_addr); - first = manager_get_first_dns_server(m, type); + switch (t) { - LIST_FOREACH(servers, s, first) - if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) - return s; + case DNS_SERVER_SYSTEM: + return m->dns_servers; - return NULL; + case DNS_SERVER_FALLBACK: + return m->fallback_dns_servers; + + default: + return NULL; + } } DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 0077456cbc..3a78d4a3b5 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -72,14 +72,15 @@ void dns_server_move_back_and_unmark(DnsServer *s); void dns_server_packet_received(DnsServer *s, usec_t rtt); void dns_server_packet_lost(DnsServer *s, usec_t usec); -DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t); +DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr); + +void dns_server_unlink_all(DnsServer *first); +void dns_server_unlink_marked(DnsServer *first); +void dns_server_mark_all(DnsServer *first); -void manager_flush_dns_servers(Manager *m, DnsServerType t); -void manager_flush_marked_dns_servers(Manager *m, DnsServerType type); -void manager_mark_dns_servers(Manager *m, DnsServerType type); +DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t); DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); -DnsServer *manager_find_dns_server(Manager *m, DnsServerType t, int family, const union in_addr_union *in_addr); DnsServer *manager_get_dns_server(Manager *m); void manager_next_dns_server(Manager *m); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index c367160d98..ddd9427dab 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -65,7 +65,7 @@ Link *link_free(Link *l) { if (!l) return NULL; - link_flush_dns_servers(l); + dns_server_unlink_marked(l->dns_servers); dns_search_domain_unlink_all(l->search_domains); while (l->addresses) @@ -162,7 +162,7 @@ static int link_update_dns_servers(Link *l) { if (r < 0) goto clear; - link_mark_dns_servers(l); + dns_server_mark_all(l->dns_servers); STRV_FOREACH(nameserver, nameservers) { union in_addr_union a; @@ -173,7 +173,7 @@ static int link_update_dns_servers(Link *l) { if (r < 0) goto clear; - s = link_find_dns_server(l, family, &a); + s = dns_server_find(l->dns_servers, family, &a); if (s) dns_server_move_back_and_unmark(s); else { @@ -183,11 +183,11 @@ static int link_update_dns_servers(Link *l) { } } - link_flush_marked_dns_servers(l); + dns_server_unlink_marked(l->dns_servers); return 0; clear: - link_flush_dns_servers(l); + dns_server_unlink_all(l->dns_servers); return r; } @@ -314,47 +314,6 @@ LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *i return NULL; } -void link_flush_dns_servers(Link *l) { - assert(l); - - while (l->dns_servers) - dns_server_unlink(l->dns_servers); -} - -void link_flush_marked_dns_servers(Link *l) { - DnsServer *s, *next; - - assert(l); - - LIST_FOREACH_SAFE(servers, s, next, l->dns_servers) { - if (!s->marked) - continue; - - dns_server_unlink(s); - } -} - -void link_mark_dns_servers(Link *l) { - DnsServer *s; - - assert(l); - - LIST_FOREACH(servers, s, l->dns_servers) - s->marked = true; -} - -DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) { - DnsServer *s; - - assert(l); - assert(in_addr); - - LIST_FOREACH(servers, s, l->dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, in_addr)) - return s; - return NULL; -} - DnsServer* link_set_dns_server(Link *l, DnsServer *s) { assert(l); diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index d72461de06..a25715d269 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -79,17 +79,10 @@ bool link_relevant(Link *l, int family); LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr); void link_add_rrs(Link *l, bool force_remove); -void link_flush_dns_servers(Link *l); -void link_flush_marked_dns_servers(Link *l); -void link_mark_dns_servers(Link *l); - DnsServer* link_set_dns_server(Link *l, DnsServer *s); -DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr); DnsServer* link_get_dns_server(Link *l); void link_next_dns_server(Link *l); -void link_flush_search_domains(Link *l); - int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr); LinkAddress *link_address_free(LinkAddress *a); int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 0771ac840f..31f042e066 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -533,9 +533,8 @@ Manager *manager_free(Manager *m) { if (!m) return NULL; - manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); - manager_flush_dns_servers(m, DNS_SERVER_FALLBACK); - + 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))) diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 2ab0008fce..f5cce670f0 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -85,7 +85,7 @@ int manager_read_resolv_conf(Manager *m) { goto clear; } - manager_mark_dns_servers(m, DNS_SERVER_SYSTEM); + dns_server_mark_all(m->dns_servers); dns_search_domain_mark_all(m->search_domains); FOREACH_LINE(line, f, r = -errno; goto clear) { @@ -118,7 +118,7 @@ int manager_read_resolv_conf(Manager *m) { /* Flush out all servers and search domains that are still * marked. Those are then ones that didn't appear in the new * /etc/resolv.conf */ - manager_flush_marked_dns_servers(m, DNS_SERVER_SYSTEM); + dns_server_unlink_marked(m->dns_servers); dns_search_domain_unlink_marked(m->search_domains); /* Whenever /etc/resolv.conf changes, start using the first @@ -134,7 +134,7 @@ int manager_read_resolv_conf(Manager *m) { return 0; clear: - manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); + dns_server_unlink_all(m->dns_servers); dns_search_domain_unlink_all(m->search_domains); return r; } -- cgit v1.2.3-54-g00ecf From eed857b71702f8551b46b66b31fa0d08583cf23c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 21:39:14 +0100 Subject: resolved: enforce a maximum limit on both dns servers and search domains --- src/resolve/resolved-dns-search-domain.c | 22 +++++++++++++++++----- src/resolve/resolved-dns-server.c | 30 +++++++++++++++++++++++------- src/resolve/resolved-link.h | 5 +++++ src/resolve/resolved-manager.h | 5 +++++ 4 files changed, 50 insertions(+), 12 deletions(-) (limited to 'src/resolve/resolved-link.h') diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c index 5d927bb2a1..d8a0648aab 100644 --- a/src/resolve/resolved-dns-search-domain.c +++ b/src/resolve/resolved-dns-search-domain.c @@ -31,7 +31,7 @@ int dns_search_domain_new( const char *name) { _cleanup_free_ char *normalized = NULL; - DnsSearchDomain *d, *tail; + DnsSearchDomain *d; int r; assert(m); @@ -48,6 +48,14 @@ int dns_search_domain_new( if (r > 0) return -EINVAL; + if (l) { + if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX) + return -E2BIG; + } else { + if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX) + return -E2BIG; + } + d = new0(DnsSearchDomain, 1); if (!d) return -ENOMEM; @@ -62,13 +70,13 @@ int dns_search_domain_new( case DNS_SEARCH_DOMAIN_LINK: d->link = l; - LIST_FIND_TAIL(domains, l->search_domains, tail); - LIST_INSERT_AFTER(domains, l->search_domains, tail, d); + LIST_APPEND(domains, l->search_domains, d); + l->n_search_domains++; break; case DNS_SERVER_SYSTEM: - LIST_FIND_TAIL(domains, m->search_domains, tail); - LIST_INSERT_AFTER(domains, m->search_domains, tail, d); + LIST_APPEND(domains, m->search_domains, d); + m->n_search_domains++; break; default: @@ -120,11 +128,15 @@ void dns_search_domain_unlink(DnsSearchDomain *d) { case DNS_SEARCH_DOMAIN_LINK: assert(d->link); + assert(d->link->n_search_domains > 0); LIST_REMOVE(domains, d->link->search_domains, d); + d->link->n_search_domains--; break; case DNS_SEARCH_DOMAIN_SYSTEM: + assert(d->manager->n_search_domains > 0); LIST_REMOVE(domains, d->manager->search_domains, d); + d->manager->n_search_domains--; break; } diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 93b954139b..0ebd22fe22 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -37,12 +37,23 @@ int dns_server_new( int family, const union in_addr_union *in_addr) { - DnsServer *s, *tail; + DnsServer *s; assert(m); assert((type == DNS_SERVER_LINK) == !!l); assert(in_addr); + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + if (l) { + if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX) + return -E2BIG; + } else { + if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX) + return -E2BIG; + } + s = new0(DnsServer, 1); if (!s) return -ENOMEM; @@ -58,18 +69,18 @@ int dns_server_new( case DNS_SERVER_LINK: s->link = l; - LIST_FIND_TAIL(servers, l->dns_servers, tail); - LIST_INSERT_AFTER(servers, l->dns_servers, tail, s); + LIST_APPEND(servers, l->dns_servers, s); + l->n_dns_servers++; break; case DNS_SERVER_SYSTEM: - LIST_FIND_TAIL(servers, m->dns_servers, tail); - LIST_INSERT_AFTER(servers, m->dns_servers, tail, s); + LIST_APPEND(servers, m->dns_servers, s); + m->n_dns_servers++; break; case DNS_SERVER_FALLBACK: - LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail); - LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s); + LIST_APPEND(servers, m->fallback_dns_servers, s); + m->n_dns_servers++; break; default: @@ -131,15 +142,20 @@ void dns_server_unlink(DnsServer *s) { case DNS_SERVER_LINK: assert(s->link); + assert(s->link->n_dns_servers > 0); LIST_REMOVE(servers, s->link->dns_servers, s); break; case DNS_SERVER_SYSTEM: + assert(s->manager->n_dns_servers > 0); LIST_REMOVE(servers, s->manager->dns_servers, s); + s->manager->n_dns_servers--; break; case DNS_SERVER_FALLBACK: + assert(s->manager->n_dns_servers > 0); LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); + s->manager->n_dns_servers--; break; } diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index a25715d269..eb00015bd5 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -34,6 +34,9 @@ typedef struct LinkAddress LinkAddress; #include "resolved-dns-server.h" #include "resolved-manager.h" +#define LINK_SEARCH_DOMAINS_MAX 32 +#define LINK_DNS_SERVERS_MAX 32 + struct LinkAddress { Link *link; @@ -58,8 +61,10 @@ struct Link { LIST_HEAD(DnsServer, dns_servers); DnsServer *current_dns_server; + unsigned n_dns_servers; LIST_HEAD(DnsSearchDomain, search_domains); + unsigned n_search_domains; Support llmnr_support; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 0683e23f6c..2bbd5d08e9 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -45,6 +45,9 @@ enum Support { #include "resolved-dns-stream.h" #include "resolved-link.h" +#define MANAGER_SEARCH_DOMAINS_MAX 32 +#define MANAGER_DNS_SERVERS_MAX 32 + struct Manager { sd_event *event; @@ -70,9 +73,11 @@ struct Manager { /* Unicast dns */ LIST_HEAD(DnsServer, dns_servers); LIST_HEAD(DnsServer, fallback_dns_servers); + unsigned n_dns_servers; /* counts both main and fallback */ DnsServer *current_dns_server; LIST_HEAD(DnsSearchDomain, search_domains); + unsigned n_search_domains; bool need_builtin_fallbacks:1; -- cgit v1.2.3-54-g00ecf