From 703e4f5e39c019da8c002ba10bd450ce378c0e91 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 01:25:24 +0100 Subject: resolved: don't claim DnsQuestion have to have the same names Wen DnsQuestion objects are used for DnsQuery objects all contained keys have to share the same name, but otherwise they generally don't have to, and this can actually happen in real-life because DnsPacket objects for mDNS use DnsQuestion for the question section. Hence, rename: dns_question_is_valid() to dns_question_is_valid_for_query(), since the name uniqueness check it does is only relevant when used for a query. Similar, rename dns_question_name() to dns_question_first_name(), to be more accurate, as this difference matters if we keys don#t have to share the same name. --- src/resolve/resolved-bus.c | 22 +++++++++++----------- src/resolve/resolved-dns-query.c | 4 ++-- src/resolve/resolved-dns-question.c | 8 +++++--- src/resolve/resolved-dns-question.h | 6 +++--- 4 files changed, 21 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index a43aa59cc0..0b6f586d3d 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -38,7 +38,7 @@ static int reply_query_state(DnsQuery *q) { name = ip; } else - name = dns_question_name(q->question); + name = dns_question_first_name(q->question); switch (q->state) { @@ -145,7 +145,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); goto finish; } if (r < 0) @@ -184,7 +184,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); goto finish; } @@ -466,7 +466,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); goto finish; } if (r < 0) @@ -502,7 +502,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_first_name(q->question)); goto finish; } @@ -612,7 +612,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (aux->auxiliary_result != 0) continue; - r = dns_name_equal(dns_question_name(aux->question), rr->srv.name); + r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name); if (r < 0) return r; if (r == 0) @@ -664,7 +664,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (aux->auxiliary_result != 0) continue; - r = dns_name_equal(dns_question_name(aux->question), rr->srv.name); + r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name); if (r < 0) return r; if (r == 0) @@ -773,7 +773,7 @@ static void resolve_service_all_complete(DnsQuery *q) { assert(bad->auxiliary_result != 0); if (bad->auxiliary_result == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(bad->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(bad->question)); goto finish; } @@ -818,7 +818,7 @@ static void resolve_service_all_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); goto finish; } @@ -957,7 +957,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); goto finish; } if (r < 0) @@ -993,7 +993,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { } if (found <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); goto finish; } diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index c1cd650651..0970f3ead7 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -85,7 +85,7 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex assert(m); assert(question); - r = dns_question_is_valid(question); + r = dns_question_is_valid_for_query(question); if (r < 0) return r; @@ -655,7 +655,7 @@ int dns_query_go(DnsQuery *q) { assert(q->question); assert(q->question->n_keys > 0); - name = dns_question_name(q->question); + name = dns_question_first_name(q->question); LIST_FOREACH(scopes, s, q->manager->dns_scopes) { DnsScopeMatch match; diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 9fb3038381..b1aa0175e0 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -125,7 +125,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { return 0; } -int dns_question_is_valid(DnsQuestion *q) { +int dns_question_is_valid_for_query(DnsQuestion *q) { const char *name; unsigned i; int r; @@ -274,8 +274,10 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, return 1; } -const char *dns_question_name(DnsQuestion *q) { - assert(q); +const char *dns_question_first_name(DnsQuestion *q) { + + if (!q) + return NULL; if (q->n_keys < 1) return NULL; diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index 9894ef6ec5..d728b9bed2 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -25,7 +25,7 @@ typedef struct DnsQuestion DnsQuestion; #include "resolved-dns-rr.h" -/* A simple array of resources keys, all sharing the same domain */ +/* A simple array of resources keys */ struct DnsQuestion { unsigned n_ref; @@ -45,12 +45,12 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key); int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr); int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr); -int dns_question_is_valid(DnsQuestion *q); +int dns_question_is_valid_for_query(DnsQuestion *q); int dns_question_contains(DnsQuestion *a, DnsResourceKey *k); int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret); -const char *dns_question_name(DnsQuestion *q); +const char *dns_question_first_name(DnsQuestion *q); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); -- cgit v1.2.3-54-g00ecf From 97f1b2f35478fe8aee5d18dac1a1aa2bb16e6fec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 16:45:12 +0100 Subject: dns-domain: remove prototype for function that doesn't exist --- src/shared/dns-domain.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index a68df330e6..e7e471e8a6 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -78,5 +78,3 @@ bool dns_service_name_is_valid(const char *name); int dns_service_join(const char *name, const char *type, const char *domain, char **ret); int dns_service_split(const char *joined, char **name, char **type, char **domain); - -int dns_name_replace_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret); -- cgit v1.2.3-54-g00ecf From e3528a5c46c815974d4d17ea9964278f7ee9caae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 16:47:20 +0100 Subject: resolved: /etc/resolved.conf missing is not an error Don't propagate any error in this case, it's really not an error. --- src/resolve/resolved-manager.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index a4ca7c89d3..7c6500e477 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -597,9 +597,10 @@ int manager_read_resolv_conf(Manager *m) { 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; + if (errno == ENOENT) + r = 0; + else + r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); goto clear; } @@ -620,9 +621,10 @@ int manager_read_resolv_conf(Manager *m) { f = fopen("/etc/resolv.conf", "re"); if (!f) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); - r = -errno; + if (errno == ENOENT) + r = 0; + else + r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); goto clear; } -- cgit v1.2.3-54-g00ecf From 636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 16:48:13 +0100 Subject: resolved: unify code for parsing dns server information Let's use the same parser when parsing dns server information from /etc/resolv.conf and our native configuration file. Also, move all code that manages lists of dns servers to a single place. resolved-dns-server.c --- src/resolve/resolved-conf.c | 66 ++++++++++++++++++++-------------- src/resolve/resolved-conf.h | 5 ++- src/resolve/resolved-dns-server.c | 42 ++++++++++++++++++++++ src/resolve/resolved-dns-server.h | 4 +++ src/resolve/resolved-gperf.gperf | 6 ++-- src/resolve/resolved-manager.c | 75 ++++++++++----------------------------- src/resolve/resolved-manager.h | 2 -- 7 files changed, 110 insertions(+), 90 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 9207719551..8b49d46a59 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -27,53 +27,65 @@ #include "resolved-conf.h" #include "string-util.h" -int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string) { - DnsServer *first; - int r; +int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) { + union in_addr_union address; + DnsServer *first, *s; + int family, r; assert(m); - assert(string); + assert(word); + + r = in_addr_from_string_auto(word, &family, &address); + if (r < 0) + return r; first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers; + /* Filter out duplicates */ + LIST_FOREACH(servers, s, first) + if (s->family == family && in_addr_equal(family, &s->address, &address)) + break; + + if (s) { + /* + * Drop the marker. This is used to find the servers + * that ceased to exist, see + * manager_mark_dns_servers() and + * manager_flush_marked_dns_servers(). + */ + s->marked = false; + return 0; + } + + return dns_server_new(m, NULL, type, NULL, family, &address); +} + +int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { + int r; + + assert(m); + assert(string); + for(;;) { _cleanup_free_ char *word = NULL; - union in_addr_union addr; - bool found = false; - DnsServer *s; - int family; r = extract_first_word(&string, &word, NULL, 0); if (r < 0) - return log_error_errno(r, "Failed to parse resolved dns server syntax \"%s\": %m", string); + return r; if (r == 0) break; - r = in_addr_from_string_auto(word, &family, &addr); + r = manager_add_dns_server_by_string(m, type, word); if (r < 0) { - log_warning("Ignoring invalid DNS address '%s'", word); + log_warning_errno(r, "Failed to add DNS server address '%s', ignoring.", word); continue; } - - /* Filter out duplicates */ - LIST_FOREACH(servers, s, first) - if (s->family == family && in_addr_equal(family, &s->address, &addr)) { - found = true; - break; - } - - if (found) - continue; - - r = dns_server_new(m, NULL, type, NULL, family, &addr); - if (r < 0) - return r; } return 0; } -int config_parse_dnsv( +int config_parse_dns_servers( const char *unit, const char *filename, unsigned line, @@ -98,7 +110,7 @@ int config_parse_dnsv( manager_flush_dns_servers(m, ltype); else { /* Otherwise, add to the list */ - r = manager_parse_dns_server(m, ltype, rvalue); + r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue); return 0; diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index b3dbea7b6b..170f3e94aa 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -26,7 +26,10 @@ 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_dns_server_by_string(Manager *m, DnsServerType type, const char *word); + const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length); -int config_parse_dnsv(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_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_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-server.c b/src/resolve/resolved-dns-server.c index e803f635ab..3d6a6c92b3 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -161,3 +161,45 @@ const struct hash_ops dns_server_hash_ops = { .hash = dns_server_hash_func, .compare = dns_server_compare_func }; + +void manager_flush_dns_servers(Manager *m, DnsServerType type) { + DnsServer **first, *s; + + assert(m); + + first = type == DNS_SERVER_FALLBACK ? &m->fallback_dns_servers : &m->dns_servers; + + while (*first) { + s = *first; + + LIST_REMOVE(servers, *first, s); + dns_server_unref(s); + } +} + +void manager_flush_marked_dns_servers(Manager *m, DnsServerType type) { + DnsServer **first, *s, *next; + + assert(m); + + first = type == DNS_SERVER_FALLBACK ? &m->fallback_dns_servers : &m->dns_servers; + + LIST_FOREACH_SAFE(servers, s, next, *first) { + if (!s->marked) + continue; + + LIST_REMOVE(servers, *first, s); + dns_server_unref(s); + } +} + +void manager_mark_dns_servers(Manager *m, DnsServerType type) { + DnsServer *first, *s; + + assert(m); + + first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers; + + LIST_FOREACH(servers, s, first) + s->marked = true; +} diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 10111fd6bd..51cae62e42 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -68,6 +68,10 @@ DnsServer* dns_server_unref(DnsServer *s); void dns_server_packet_received(DnsServer *s, usec_t rtt); void dns_server_packet_lost(DnsServer *s, usec_t usec); +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); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); extern const struct hash_ops dns_server_hash_ops; diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 8e78fbf06a..dc4ef74b8c 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -14,6 +14,6 @@ struct ConfigPerfItem; %struct-type %includes %% -Resolve.DNS, config_parse_dnsv, DNS_SERVER_SYSTEM, 0 -Resolve.FallbackDNS, config_parse_dnsv, DNS_SERVER_FALLBACK, 0 -Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) +Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 +Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 +Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 7c6500e477..c35379aa39 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -477,7 +477,7 @@ int manager_new(Manager **ret) { m->llmnr_support = SUPPORT_YES; m->read_resolv_conf = true; - r = manager_parse_dns_server(m, DNS_SERVER_FALLBACK, DNS_SERVERS); + r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS); if (r < 0) return r; @@ -583,7 +583,7 @@ int manager_read_resolv_conf(Manager *m) { _cleanup_fclose_ FILE *f = NULL; struct stat st, own; char line[LINE_MAX]; - DnsServer *s, *nx; + DnsServer *s; usec_t t; int r; @@ -633,14 +633,12 @@ int manager_read_resolv_conf(Manager *m) { goto clear; } - LIST_FOREACH(servers, s, m->dns_servers) - s->marked = true; + manager_mark_dns_servers(m, DNS_SERVER_SYSTEM); FOREACH_LINE(line, f, r = -errno; goto clear) { - union in_addr_union address; - int family; - char *l; + _cleanup_strv_free_ char **d = NULL; const char *a; + char *l; truncate_nl(line); @@ -649,33 +647,16 @@ int manager_read_resolv_conf(Manager *m) { continue; a = first_word(l, "nameserver"); - if (!a) - continue; + if (a) { + r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a); + if (r < 0) + log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); - 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); - } + manager_flush_marked_dns_servers(m, DNS_SERVER_SYSTEM); /* Whenever /etc/resolv.conf changes, start using the first * DNS server of it. This is useful to deal with broken @@ -716,13 +697,14 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { if (*count == MAXNS) fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f); + (*count) ++; fprintf(f, "nameserver %s\n", t); - (*count) ++; } static void write_resolv_conf_search( - const char *domain, FILE *f, + const char *domain, + FILE *f, unsigned *count, unsigned *length) { @@ -740,10 +722,11 @@ static void write_resolv_conf_search( return; } - fprintf(f, " %s", domain); - (*length) += strlen(domain); (*count) ++; + + fputc(' ', f); + fputs(domain, f); } static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { @@ -758,8 +741,8 @@ static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *doma if (ordered_set_isempty(dns)) fputs("# No DNS servers known.\n", f); else { - DnsServer *s; unsigned count = 0; + DnsServer *s; ORDERED_SET_FOREACH(s, dns, i) write_resolv_conf_server(s, f, &count); @@ -802,7 +785,7 @@ int manager_write_resolv_conf(Manager *m) { if (!domains) return -ENOMEM; - /* First add the system-wide servers */ + /* First add the system-wide servers and domains */ LIST_FOREACH(servers, s, m->dns_servers) { r = ordered_set_put(dns, s); if (r == -EEXIST) @@ -1420,28 +1403,6 @@ void manager_verify_all(Manager *m) { dns_zone_verify_all(&s->zone); } -void manager_flush_dns_servers(Manager *m, DnsServerType t) { - DnsServer *s; - - assert(m); - - if (t == DNS_SERVER_SYSTEM) - while (m->dns_servers) { - s = m->dns_servers; - - LIST_REMOVE(servers, m->dns_servers, s); - dns_server_unref(s); - } - - if (t == DNS_SERVER_FALLBACK) - while (m->fallback_dns_servers) { - s = m->fallback_dns_servers; - - LIST_REMOVE(servers, m->fallback_dns_servers, s); - dns_server_unref(s); - } -} - int manager_is_own_hostname(Manager *m, const char *name) { int r; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 074ce6c63d..29b08cf0e9 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -138,8 +138,6 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p); void manager_verify_all(Manager *m); -void manager_flush_dns_servers(Manager *m, DnsServerType t); - DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define EXTRA_CMSG_SPACE 1024 -- cgit v1.2.3-54-g00ecf From f8dc7e343d903f053070d01f3273db819deee951 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 16:55:30 +0100 Subject: resolved: split out all code dealing with /etc/resolv.conf into its own .c file No functional changes. --- Makefile.am | 2 + src/resolve/resolved-manager.c | 275 +-------------------------------- src/resolve/resolved-manager.h | 2 - src/resolve/resolved-resolv-conf.c | 306 +++++++++++++++++++++++++++++++++++++ src/resolve/resolved-resolv-conf.h | 27 ++++ src/resolve/resolved.c | 1 + 6 files changed, 337 insertions(+), 276 deletions(-) create mode 100644 src/resolve/resolved-resolv-conf.c create mode 100644 src/resolve/resolved-resolv-conf.h (limited to 'src') diff --git a/Makefile.am b/Makefile.am index 132e592976..cac5c5e3c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5140,6 +5140,8 @@ systemd_resolved_SOURCES = \ src/resolve/resolved-manager.h \ src/resolve/resolved-conf.c \ src/resolve/resolved-conf.h \ + src/resolve/resolved-resolv-conf.c \ + src/resolve/resolved-resolv-conf.h \ src/resolve/resolved-bus.c \ src/resolve/resolved-bus.h \ src/resolve/resolved-link.h \ diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index c35379aa39..23806aa30b 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -21,7 +21,6 @@ #include #include -#include #include #include "af-list.h" @@ -40,6 +39,7 @@ #include "resolved-conf.h" #include "resolved-llmnr.h" #include "resolved-manager.h" +#include "resolved-resolv-conf.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -579,279 +579,6 @@ Manager *manager_free(Manager *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; - 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) - r = 0; - else - r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); - 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) - r = 0; - else - r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); - goto clear; - } - - if (fstat(fileno(f), &st) < 0) { - r = log_error_errno(errno, "Failed to stat open file: %m"); - goto clear; - } - - manager_mark_dns_servers(m, DNS_SERVER_SYSTEM); - - FOREACH_LINE(line, f, r = -errno; goto clear) { - _cleanup_strv_free_ char **d = NULL; - const char *a; - char *l; - - truncate_nl(line); - - l = strstrip(line); - if (*l == '#' || *l == ';') - continue; - - a = first_word(l, "nameserver"); - if (a) { - r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a); - if (r < 0) - log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); - - continue; - } - } - - manager_flush_marked_dns_servers(m, DNS_SERVER_SYSTEM); - - /* 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); - (*count) ++; - - fprintf(f, "nameserver %s\n", t); -} - -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; - } - - (*length) += strlen(domain); - (*count) ++; - - fputc(' ', f); - fputs(domain, f); -} - -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 { - unsigned count = 0; - DnsServer *s; - - 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 and domains */ - 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 { diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 29b08cf0e9..23ee1f6bc5 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -113,8 +113,6 @@ int manager_new(Manager **ret); Manager* manager_free(Manager *m); int manager_start(Manager *m); -int manager_read_resolv_conf(Manager *m); -int manager_write_resolv_conf(Manager *m); DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr); diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c new file mode 100644 index 0000000000..5a6e3812df --- /dev/null +++ b/src/resolve/resolved-resolv-conf.c @@ -0,0 +1,306 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 + +#include "alloc-util.h" +#include "dns-domain.h" +#include "fd-util.h" +#include "fileio-label.h" +#include "fileio.h" +#include "ordered-set.h" +#include "resolved-conf.h" +#include "resolved-resolv-conf.h" +#include "string-util.h" +#include "strv.h" + +int manager_read_resolv_conf(Manager *m) { + _cleanup_fclose_ FILE *f = NULL; + struct stat st, own; + char line[LINE_MAX]; + DnsServer *s; + 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) + r = 0; + else + r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); + 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) + r = 0; + else + r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); + goto clear; + } + + if (fstat(fileno(f), &st) < 0) { + r = log_error_errno(errno, "Failed to stat open file: %m"); + goto clear; + } + + manager_mark_dns_servers(m, DNS_SERVER_SYSTEM); + + FOREACH_LINE(line, f, r = -errno; goto clear) { + _cleanup_strv_free_ char **d = NULL; + const char *a; + char *l; + + truncate_nl(line); + + l = strstrip(line); + if (*l == '#' || *l == ';') + continue; + + a = first_word(l, "nameserver"); + if (a) { + r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a); + if (r < 0) + log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); + + continue; + } + } + + manager_flush_marked_dns_servers(m, DNS_SERVER_SYSTEM); + + /* 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); + (*count) ++; + + fprintf(f, "nameserver %s\n", t); +} + +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; + } + + (*length) += strlen(domain); + (*count) ++; + + fputc(' ', f); + fputs(domain, f); +} + +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 { + unsigned count = 0; + DnsServer *s; + + 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 and domains */ + 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; +} diff --git a/src/resolve/resolved-resolv-conf.h b/src/resolve/resolved-resolv-conf.h new file mode 100644 index 0000000000..a3355e994b --- /dev/null +++ b/src/resolve/resolved-resolv-conf.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 "resolved-manager.h" + +int manager_read_resolv_conf(Manager *m); +int manager_write_resolv_conf(Manager *m); diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 7ba0546f4a..e07f49ce17 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -26,6 +26,7 @@ #include "mkdir.h" #include "resolved-conf.h" #include "resolved-manager.h" +#include "resolved-resolv-conf.h" #include "selinux-util.h" #include "signal-util.h" #include "user-util.h" -- cgit v1.2.3-54-g00ecf From 84129d46cd6e95e142973da93aede4c7433c9600 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 17:01:09 +0100 Subject: resolved: indent less, by exiting earlier --- src/resolve/resolved-dns-scope.c | 10 +++++----- src/resolve/resolved-dns-server.c | 16 +++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 873d76e40c..5174502af0 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -136,11 +136,11 @@ void dns_scope_next_dns_server(DnsScope *s) { void dns_scope_packet_received(DnsScope *s, usec_t rtt) { assert(s); - if (rtt > s->max_rtt) { - s->max_rtt = rtt; - s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), - MULTICAST_RESEND_TIMEOUT_MAX_USEC); - } + if (rtt <= s->max_rtt) + return; + + s->max_rtt = rtt; + s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), MULTICAST_RESEND_TIMEOUT_MAX_USEC); } void dns_scope_packet_lost(DnsScope *s, usec_t usec) { diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 3d6a6c92b3..8b88a3d690 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -123,18 +123,20 @@ DnsServer* dns_server_unref(DnsServer *s) { void dns_server_packet_received(DnsServer *s, usec_t rtt) { assert(s); - if (rtt > s->max_rtt) { - s->max_rtt = rtt; - s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), - DNS_TIMEOUT_MAX_USEC); - } + if (rtt <= s->max_rtt) + return; + + s->max_rtt = rtt; + s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC); } void dns_server_packet_lost(DnsServer *s, usec_t usec) { assert(s); - if (s->resend_timeout <= usec) - s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC); + if (s->resend_timeout > usec) + return; + + s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC); } static void dns_server_hash_func(const void *p, struct siphash *state) { -- cgit v1.2.3-54-g00ecf From f2f1dbe50fea13abadc9c1e845a29031b90b40f3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 17:03:12 +0100 Subject: resolved: move dns server picking code from resolved-manager.c to resolved-dns-server.c --- src/resolve/resolved-dns-server.c | 94 +++++++++++++++++++++++++++++++++++++++ src/resolve/resolved-dns-server.h | 5 +++ src/resolve/resolved-manager.c | 91 ------------------------------------- src/resolve/resolved-manager.h | 5 --- 4 files changed, 99 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 8b88a3d690..207ec4c603 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -21,7 +21,9 @@ #include "alloc-util.h" #include "resolved-dns-server.h" +#include "resolved-resolv-conf.h" #include "siphash24.h" +#include "string-util.h" /* After how much time to repeat classic DNS requests */ #define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC) @@ -205,3 +207,95 @@ void manager_mark_dns_servers(Manager *m, DnsServerType type) { LIST_FOREACH(servers, s, first) s->marked = true; } + +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 no DNS server was chose so far, pick the first one */ + 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); +} diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 51cae62e42..49b550c1ff 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -68,6 +68,11 @@ DnsServer* dns_server_unref(DnsServer *s); void dns_server_packet_received(DnsServer *s, usec_t rtt); void dns_server_packet_lost(DnsServer *s, usec_t usec); +DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); +DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr); +DnsServer *manager_get_dns_server(Manager *m); +void manager_next_dns_server(Manager *m); + 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); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 23806aa30b..f991dd72fc 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -886,97 +886,6 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add 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; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 23ee1f6bc5..08dd61fb14 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -114,11 +114,6 @@ Manager* manager_free(Manager *m); int manager_start(Manager *m); -DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); -DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr); -DnsServer *manager_get_dns_server(Manager *m); -void manager_next_dns_server(Manager *m); - uint32_t manager_find_mtu(Manager *m); int manager_write(Manager *m, int fd, DnsPacket *p); -- cgit v1.2.3-54-g00ecf 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-conf.c | 9 +-- src/resolve/resolved-dns-server.c | 114 ++++++++++++++++++++++++------------- src/resolve/resolved-dns-server.h | 14 +++-- src/resolve/resolved-link.c | 66 +++++++++++++-------- src/resolve/resolved-link.h | 4 ++ src/resolve/resolved-manager.c | 6 +- src/resolve/resolved-resolv-conf.c | 12 +--- 7 files changed, 137 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 8b49d46a59..fe88006fb7 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -29,8 +29,8 @@ int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) { union in_addr_union address; - DnsServer *first, *s; int family, r; + DnsServer *s; assert(m); assert(word); @@ -39,13 +39,8 @@ int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char if (r < 0) return r; - first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers; - /* Filter out duplicates */ - LIST_FOREACH(servers, s, first) - if (s->family == family && in_addr_equal(family, &s->address, &address)) - break; - + s = manager_find_dns_server(m, type, family, &address); if (s) { /* * Drop the marker. This is used to find the servers diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 207ec4c603..371594c710 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -67,6 +67,7 @@ int dns_server_new( assert_not_reached("Unknown server type"); s->manager = m; + s->linked = true; /* A new DNS server that isn't fallback is added and the one * we used so far was a fallback one? Then let's try to pick @@ -87,39 +88,61 @@ DnsServer* dns_server_ref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref ++; return s; } -static DnsServer* dns_server_free(DnsServer *s) { +DnsServer* dns_server_unref(DnsServer *s) { if (!s) return NULL; - if (s->link && s->link->current_dns_server == s) - link_set_dns_server(s->link, NULL); + assert(s->n_ref > 0); + s->n_ref --; - if (s->manager && s->manager->current_dns_server == s) - manager_set_dns_server(s->manager, NULL); + if (s->n_ref > 0) + return NULL; free(s); - return NULL; } -DnsServer* dns_server_unref(DnsServer *s) { - if (!s) - return NULL; +void dns_server_unlink(DnsServer *s) { + assert(s); + assert(s->manager); - assert(s->n_ref > 0); + /* This removes the specified server from the linked list of + * servers, but any server might still stay around if it has + * refs, for example from an ongoing transaction. */ - if (s->n_ref == 1) - dns_server_free(s); - else - s->n_ref --; + if (!s->linked) + return; - return NULL; + switch (s->type) { + + case DNS_SERVER_LINK: + assert(s->link); + LIST_REMOVE(servers, s->link->dns_servers, s); + break; + + case DNS_SERVER_SYSTEM: + LIST_REMOVE(servers, s->manager->dns_servers, s); + break; + + case DNS_SERVER_FALLBACK: + LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); + break; + } + + s->linked = false; + + if (s->link && s->link->current_dns_server == s) + link_set_dns_server(s->link, NULL); + + if (s->manager->current_dns_server == s) + manager_set_dns_server(s->manager, NULL); + + dns_server_unref(s); } void dns_server_packet_received(DnsServer *s, usec_t rtt) { @@ -166,34 +189,48 @@ const struct hash_ops dns_server_hash_ops = { .compare = dns_server_compare_func }; -void manager_flush_dns_servers(Manager *m, DnsServerType type) { - DnsServer **first, *s; +DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) { + assert(m); + + switch (t) { + + case DNS_SERVER_SYSTEM: + return m->dns_servers; + + case DNS_SERVER_FALLBACK: + return m->fallback_dns_servers; + default: + return NULL; + } +} + +void manager_flush_dns_servers(Manager *m, DnsServerType type) { assert(m); - first = type == DNS_SERVER_FALLBACK ? &m->fallback_dns_servers : &m->dns_servers; + for (;;) { + DnsServer *first; - while (*first) { - s = *first; + first = manager_get_first_dns_server(m, type); + if (!first) + break; - LIST_REMOVE(servers, *first, s); - dns_server_unref(s); + dns_server_unlink(first); } } void manager_flush_marked_dns_servers(Manager *m, DnsServerType type) { - DnsServer **first, *s, *next; + DnsServer *first, *s, *next; assert(m); - first = type == DNS_SERVER_FALLBACK ? &m->fallback_dns_servers : &m->dns_servers; + first = manager_get_first_dns_server(m, type); - LIST_FOREACH_SAFE(servers, s, next, *first) { + LIST_FOREACH_SAFE(servers, s, next, first) { if (!s->marked) continue; - LIST_REMOVE(servers, *first, s); - dns_server_unref(s); + dns_server_unlink(s); } } @@ -202,23 +239,20 @@ void manager_mark_dns_servers(Manager *m, DnsServerType type) { assert(m); - first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers; - + first = manager_get_first_dns_server(m, type); LIST_FOREACH(servers, s, first) s->marked = true; } -DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) { - DnsServer *s; +DnsServer* manager_find_dns_server(Manager *m, DnsServerType type, int family, const union in_addr_union *in_addr) { + DnsServer *first, *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; + first = manager_get_first_dns_server(m, type); - LIST_FOREACH(servers, s, m->fallback_dns_servers) + LIST_FOREACH(servers, s, first) if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) return s; @@ -238,7 +272,8 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { log_info("Switching to system DNS server %s.", strna(ip)); } - m->current_dns_server = s; + dns_server_unref(m->current_dns_server); + m->current_dns_server = dns_server_ref(s); if (m->unicast_scope) dns_cache_flush(&m->unicast_scope->cache); @@ -286,8 +321,9 @@ void manager_next_dns_server(Manager *m) { if (!m->current_dns_server) return; - /* Change to the next one */ - if (m->current_dns_server->servers_next) { + /* Change to the next one, but make sure to follow the linked + * list only if the server is still linked. */ + if (m->current_dns_server->linked && m->current_dns_server->servers_next) { manager_set_dns_server(m, m->current_dns_server->servers_next); return; } diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 49b550c1ff..19916794ab 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -51,6 +51,8 @@ struct DnsServer { bool marked:1; + /* If linked is set, then this server appears in the servers linked list */ + bool linked:1; LIST_FIELDS(DnsServer, servers); }; @@ -65,18 +67,22 @@ int dns_server_new( DnsServer* dns_server_ref(DnsServer *s); DnsServer* dns_server_unref(DnsServer *s); +void dns_server_unlink(DnsServer *s); + void dns_server_packet_received(DnsServer *s, usec_t rtt); void dns_server_packet_lost(DnsServer *s, usec_t usec); -DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); -DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr); -DnsServer *manager_get_dns_server(Manager *m); -void manager_next_dns_server(Manager *m); +DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t); 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_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); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); extern const struct hash_ops dns_server_hash_ops; diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 2892641075..ef3db773fd 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -65,19 +65,14 @@ Link *link_free(Link *l) { if (!l) return NULL; + link_flush_dns_servers(l); + while (l->addresses) link_address_free(l->addresses); if (l->manager) hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); - while (l->dns_servers) { - DnsServer *s = l->dns_servers; - - LIST_REMOVE(servers, l->dns_servers, s); - dns_server_unref(s); - } - dns_scope_free(l->unicast_scope); dns_scope_free(l->llmnr_ipv4_scope); dns_scope_free(l->llmnr_ipv6_scope); @@ -158,7 +153,6 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) { static int link_update_dns_servers(Link *l) { _cleanup_strv_free_ char **nameservers = NULL; char **nameserver; - DnsServer *s, *nx; int r; assert(l); @@ -167,11 +161,11 @@ static int link_update_dns_servers(Link *l) { if (r < 0) goto clear; - LIST_FOREACH(servers, s, l->dns_servers) - s->marked = true; + link_mark_dns_servers(l); STRV_FOREACH(nameserver, nameservers) { union in_addr_union a; + DnsServer *s; int family; r = in_addr_from_string_auto(*nameserver, &family, &a); @@ -188,22 +182,11 @@ static int link_update_dns_servers(Link *l) { } } - LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers) - if (s->marked) { - LIST_REMOVE(servers, l->dns_servers, s); - dns_server_unref(s); - } - + link_flush_marked_dns_servers(l); return 0; clear: - while (l->dns_servers) { - s = l->dns_servers; - - LIST_REMOVE(servers, l->dns_servers, s); - dns_server_unref(s); - } - + link_flush_dns_servers(l); return r; } @@ -303,10 +286,40 @@ 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)) @@ -327,7 +340,8 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) { log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name); } - l->current_dns_server = s; + dns_server_unref(l->current_dns_server); + l->current_dns_server = dns_server_ref(s); if (l->unicast_scope) dns_cache_flush(&l->unicast_scope->cache); @@ -350,7 +364,9 @@ void link_next_dns_server(Link *l) { if (!l->current_dns_server) return; - if (l->current_dns_server->servers_next) { + /* Change to the next one, but make sure to follow the linked + * list only if this server is actually still linked. */ + if (l->current_dns_server->linked && l->current_dns_server->servers_next) { link_set_dns_server(l, l->current_dns_server->servers_next); return; } 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); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index f991dd72fc..5ebf1926be 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -536,15 +536,15 @@ 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); + 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); diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 5a6e3812df..98c1720f0f 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -36,7 +36,6 @@ int manager_read_resolv_conf(Manager *m) { _cleanup_fclose_ FILE *f = NULL; struct stat st, own; char line[LINE_MAX]; - DnsServer *s; usec_t t; int r; @@ -53,7 +52,7 @@ int manager_read_resolv_conf(Manager *m) { if (errno == ENOENT) r = 0; else - r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); + r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m"); goto clear; } @@ -89,7 +88,6 @@ int manager_read_resolv_conf(Manager *m) { manager_mark_dns_servers(m, DNS_SERVER_SYSTEM); FOREACH_LINE(line, f, r = -errno; goto clear) { - _cleanup_strv_free_ char **d = NULL; const char *a; char *l; @@ -124,13 +122,7 @@ int manager_read_resolv_conf(Manager *m) { return 0; clear: - while (m->dns_servers) { - s = m->dns_servers; - - LIST_REMOVE(servers, m->dns_servers, s); - dns_server_unref(s); - } - + manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); return r; } -- cgit v1.2.3-54-g00ecf From 00fa60ae3b2823036cb3e7734f16bce30cb7441d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 18:08:35 +0100 Subject: resolved: make sure FallbackDNS= overrides built-in servers, doesn't extend them Closes #342. --- src/resolve/resolved-conf.c | 25 ++++++++++++++++++++----- src/resolve/resolved-manager.c | 5 +---- src/resolve/resolved-manager.h | 3 ++- src/resolve/resolved.c | 6 ++++-- 4 files changed, 27 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index fe88006fb7..7e3b613816 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -116,6 +116,8 @@ int config_parse_dns_servers( * /etc/resolv.conf */ if (ltype == DNS_SERVER_SYSTEM) m->read_resolv_conf = false; + if (ltype == DNS_SERVER_FALLBACK) + m->need_builtin_fallbacks = false; return 0; } @@ -155,11 +157,24 @@ int config_parse_support( } int manager_parse_config_file(Manager *m) { + int r; + assert(m); - return config_parse_many(PKGSYSCONFDIR "/resolved.conf", - CONF_PATHS_NULSTR("systemd/resolved.conf.d"), - "Resolve\0", - config_item_perf_lookup, resolved_gperf_lookup, - false, m); + r = config_parse_many(PKGSYSCONFDIR "/resolved.conf", + CONF_PATHS_NULSTR("systemd/resolved.conf.d"), + "Resolve\0", + config_item_perf_lookup, resolved_gperf_lookup, + false, m); + if (r < 0) + return r; + + if (m->need_builtin_fallbacks) { + r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS); + if (r < 0) + return r; + } + + return 0; + } diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 5ebf1926be..c3c61a0893 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -476,10 +476,7 @@ int manager_new(Manager **ret) { m->llmnr_support = SUPPORT_YES; m->read_resolv_conf = true; - - r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS); - if (r < 0) - return r; + m->need_builtin_fallbacks = true; r = sd_event_default(&m->event); if (r < 0) diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 08dd61fb14..8a716b3ceb 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -70,7 +70,8 @@ struct Manager { LIST_HEAD(DnsServer, fallback_dns_servers); DnsServer *current_dns_server; - bool read_resolv_conf; + bool need_builtin_fallbacks:1; + bool read_resolv_conf:1; usec_t resolv_conf_mtime; LIST_HEAD(DnsScope, dns_scopes); diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index e07f49ce17..be406b71fe 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -82,8 +82,10 @@ int main(int argc, char *argv[]) { } r = manager_parse_config_file(m); - if (r < 0) - log_warning_errno(r, "Failed to parse configuration file: %m"); + if (r < 0) { + log_error_errno(r, "Failed to parse configuration file: %m"); + goto finish; + } r = manager_start(m); if (r < 0) { -- cgit v1.2.3-54-g00ecf From 444d77fd014d86f3b211abf52981bfb34e7e78be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 18:12:13 +0100 Subject: resolved: drop unused enum type --- src/resolve/resolved-dns-server.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 19916794ab..9fed8f74a2 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -24,7 +24,6 @@ #include "in-addr-util.h" typedef struct DnsServer DnsServer; -typedef enum DnsServerSource DnsServerSource; typedef enum DnsServerType { DNS_SERVER_SYSTEM, -- cgit v1.2.3-54-g00ecf From 0b58db658b5c3f586ac3a837427f1f7fec2abb2e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 20:50:37 +0100 Subject: resolved: make sure order of dns servers is stable Previously, we'd keep adding new dns servers we discover to the end of our linked list of servers. When we encountered a pre-existing server, we'd just leave it where it was. In essence that meant that old servers ended up at the front, and new servers at the end, but not in an order that would reflect the configuration. With this change we ensure that every pre-existing server we want to add again we move to the back of the linked list, so that the order is stable and in sync with the requested configuration. --- src/resolve/resolved-conf.c | 2 +- src/resolve/resolved-dns-server.c | 63 +++++++++++++++++++++++++++++++++++---- src/resolve/resolved-dns-server.h | 7 +++-- src/resolve/resolved-link.c | 2 +- 4 files changed, 63 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 7e3b613816..c11a8badd9 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -48,7 +48,7 @@ int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char * manager_mark_dns_servers() and * manager_flush_marked_dns_servers(). */ - s->marked = false; + dns_server_move_back_and_unmark(s); return 0; } diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 371594c710..81301ab800 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -48,25 +48,34 @@ int dns_server_new( return -ENOMEM; s->n_ref = 1; + s->manager = m; s->type = type; s->family = family; s->address = *in_addr; s->resend_timeout = DNS_TIMEOUT_MIN_USEC; - if (type == DNS_SERVER_LINK) { + switch (type) { + + case DNS_SERVER_LINK: + s->link = l; LIST_FIND_TAIL(servers, l->dns_servers, tail); LIST_INSERT_AFTER(servers, l->dns_servers, tail, s); - s->link = l; - } else if (type == DNS_SERVER_SYSTEM) { + break; + + case DNS_SERVER_SYSTEM: LIST_FIND_TAIL(servers, m->dns_servers, tail); LIST_INSERT_AFTER(servers, m->dns_servers, tail, s); - } else if (type == DNS_SERVER_FALLBACK) { + break; + + case DNS_SERVER_FALLBACK: LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail); LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s); - } else + break; + + default: assert_not_reached("Unknown server type"); + } - s->manager = m; s->linked = true; /* A new DNS server that isn't fallback is added and the one @@ -145,6 +154,48 @@ void dns_server_unlink(DnsServer *s) { dns_server_unref(s); } +void dns_server_move_back_and_unmark(DnsServer *s) { + DnsServer *tail; + + assert(s); + + if (!s->marked) + return; + + s->marked = false; + + if (!s->linked || !s->servers_next) + return; + + /* Move us to the end of the list, so that the order is + * strictly kept, if we are not at the end anyway. */ + + switch (s->type) { + + case DNS_SERVER_LINK: + assert(s->link); + LIST_FIND_TAIL(servers, s, tail); + LIST_REMOVE(servers, s->link->dns_servers, s); + LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s); + break; + + case DNS_SERVER_SYSTEM: + LIST_FIND_TAIL(servers, s, tail); + LIST_REMOVE(servers, s->manager->dns_servers, s); + LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s); + break; + + case DNS_SERVER_FALLBACK: + LIST_FIND_TAIL(servers, s, tail); + LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); + LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s); + break; + + default: + assert_not_reached("Unknown server type"); + } +} + void dns_server_packet_received(DnsServer *s, usec_t rtt) { assert(s); diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 9fed8f74a2..0077456cbc 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -31,6 +31,7 @@ typedef enum DnsServerType { DNS_SERVER_LINK, } DnsServerType; +#include "resolved-manager.h" #include "resolved-link.h" struct DnsServer { @@ -39,7 +40,6 @@ struct DnsServer { unsigned n_ref; DnsServerType type; - Link *link; int family; @@ -57,9 +57,9 @@ struct DnsServer { int dns_server_new( Manager *m, - DnsServer **s, + DnsServer **ret, DnsServerType type, - Link *l, + Link *link, int family, const union in_addr_union *address); @@ -67,6 +67,7 @@ DnsServer* dns_server_ref(DnsServer *s); DnsServer* dns_server_unref(DnsServer *s); void dns_server_unlink(DnsServer *s); +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); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index ef3db773fd..4931fc9d3b 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -174,7 +174,7 @@ static int link_update_dns_servers(Link *l) { s = link_find_dns_server(l, family, &a); if (s) - s->marked = false; + dns_server_move_back_and_unmark(s); else { r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a); if (r < 0) -- 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') 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') 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') 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 From 0264d0726fac78f7d689ded632987ff9edfc03d2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 21:54:22 +0100 Subject: util-lib: add ordered_set_ensure_allocated() ordered_set_ensure_allocated() does for an OrderedSet, what set_ensure_allicated() does for a Set. --- src/basic/ordered-set.h | 11 +++++++++++ src/basic/set.h | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 6c617ab305..da10e90ff2 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -29,6 +29,17 @@ static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) { return (OrderedSet*) ordered_hashmap_new(ops); } +static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) { + if (*s) + return 0; + + *s = ordered_set_new(ops); + if (!*s) + return -ENOMEM; + + return 0; +} + static inline OrderedSet* ordered_set_free(OrderedSet *s) { ordered_hashmap_free((OrderedHashmap*) s); return NULL; diff --git a/src/basic/set.h b/src/basic/set.h index 4554ef2d49..5fd7de08f9 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -27,7 +27,6 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) - static inline Set *set_free(Set *s) { internal_hashmap_free(HASHMAP_BASE(s)); return NULL; -- cgit v1.2.3-54-g00ecf From 9176a57c101d51b4a7fb4141240b5ce03abac57d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 21:55:00 +0100 Subject: resolved: split out calls to compile full list of dns servers and search domains Let's split this out from the resolv.conf parser, so that this becomes generically useful. --- src/resolve/resolved-manager.c | 82 ++++++++++++++++++++++++++++++++++++++ src/resolve/resolved-manager.h | 4 ++ src/resolve/resolved-resolv-conf.c | 75 ++++++---------------------------- 3 files changed, 99 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 31f042e066..64703ab713 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -1055,6 +1055,88 @@ int manager_is_own_hostname(Manager *m, const char *name) { return 0; } +int manager_compile_dns_servers(Manager *m, OrderedSet **dns) { + DnsServer *s; + Iterator i; + Link *l; + int r; + + assert(m); + assert(dns); + + r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops); + if (r < 0) + return r; + + /* 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; + } + + /* 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; + } + } + + /* 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; +} + +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; +} + static const char* const support_table[_SUPPORT_MAX] = { [SUPPORT_NO] = "no", [SUPPORT_YES] = "yes", diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 2bbd5d08e9..d00c444583 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -27,6 +27,7 @@ #include "hashmap.h" #include "list.h" +#include "ordered-set.h" typedef struct Manager Manager; typedef enum Support Support; @@ -148,5 +149,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); int manager_is_own_hostname(Manager *m, const char *name); +int manager_compile_dns_servers(Manager *m, OrderedSet **servers); +int manager_compile_search_domains(Manager *m, OrderedSet **domains); + const char* support_to_string(Support p) _const_; int support_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index f5cce670f0..744969b745 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -220,14 +220,12 @@ static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *doma } int manager_write_resolv_conf(Manager *m) { - static const char path[] = "/run/systemd/resolve/resolv.conf"; + + #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf" + + _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL; _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; int r; assert(m); @@ -236,62 +234,15 @@ int manager_write_resolv_conf(Manager *m) { 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 and domains */ - LIST_FOREACH(servers, s, m->dns_servers) { - r = ordered_set_put(dns, s); - if (r == -EEXIST) - continue; - 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; - } - - /* Then, add the per-link servers and domains */ - 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_FOREACH(domains, d, l->search_domains) { - r = ordered_set_put(domains, d->name); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } + r = manager_compile_dns_servers(m, &dns); + 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 = manager_compile_search_domains(m, &domains); + if (r < 0) + return r; - r = fopen_temporary_label(path, path, &f, &temp_path); + r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path); if (r < 0) return r; @@ -301,7 +252,7 @@ int manager_write_resolv_conf(Manager *m) { if (r < 0) goto fail; - if (rename(temp_path, path) < 0) { + if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) { r = -errno; goto fail; } @@ -309,7 +260,7 @@ int manager_write_resolv_conf(Manager *m) { return 0; fail: - (void) unlink(path); + (void) unlink(PRIVATE_RESOLV_CONF); (void) unlink(temp_path); return r; } -- cgit v1.2.3-54-g00ecf From 7f220d94a938a99c77400fa0ca30485e269bae7c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Nov 2015 22:49:15 +0100 Subject: resolved: expose some properties on the bus For now, let's just expose the LLMNR hostname currently in use; a combined list of all dns servers with their interface indexes; a combined list of all search domains with their interface indexes. --- src/resolve/resolved-bus.c | 115 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) (limited to 'src') diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 0b6f586d3d..537afc0d41 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -1101,8 +1101,123 @@ fail: return r; } +static int append_dns_server(sd_bus_message *reply, DnsServer *s) { + int r; + + assert(reply); + assert(s); + + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "ii", s->link ? s->link->ifindex : 0, s->family); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &s->address, FAMILY_ADDRESS_SIZE(s->family)); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + +static int bus_property_get_dns_servers( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + unsigned c = 0; + DnsServer *s; + Iterator i; + Link *l; + int r; + + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + if (r < 0) + return r; + + LIST_FOREACH(servers, s, m->dns_servers) { + r = append_dns_server(reply, s); + if (r < 0) + return r; + + c++; + } + + HASHMAP_FOREACH(l, m->links, i) { + LIST_FOREACH(servers, s, l->dns_servers) { + r = append_dns_server(reply, s); + if (r < 0) + return r; + c++; + } + } + + if (c == 0) { + LIST_FOREACH(servers, s, m->fallback_dns_servers) { + r = append_dns_server(reply, s); + if (r < 0) + return r; + } + } + + return sd_bus_message_close_container(reply); +} + +static int bus_property_get_search_domains( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + DnsSearchDomain *d; + Iterator i; + Link *l; + int r; + + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'a', "(is)"); + if (r < 0) + return r; + + LIST_FOREACH(domains, d, m->search_domains) { + r = sd_bus_message_append(reply, "(is)", 0, d->name); + if (r < 0) + return r; + } + + HASHMAP_FOREACH(l, m->links, i) { + LIST_FOREACH(domains, d, l->search_domains) { + r = sd_bus_message_append(reply, "is", l->ifindex, d->name); + if (r < 0) + return r; + } + } + + return sd_bus_message_close_container(reply); +} + static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0), + SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0), + SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0), + SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), -- cgit v1.2.3-54-g00ecf From 801ad6a6a9cd8fbd58b9f9c27f20dbb3c87d47dd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 25 Nov 2015 20:47:27 +0100 Subject: resolved: fully support DNS search domains This adds support for searching single-label hostnames in a set of configured search domains. A new object DnsQueryCandidate is added that links queries to scopes. It keeps track of the search domain last used for a query on a specific link. Whenever a host name was unsuccessfuly resolved on a scope all its transactions are flushed out and replaced by a new set, with the next search domain appended. This also adds a new flag SD_RESOLVED_NO_SEARCH to disable search domain behaviour. The "systemd-resolve-host" tool is updated to make this configurable via --search=. Fixes #1697 --- src/resolve-host/resolve-host.c | 13 + src/resolve/resolved-bus.c | 26 +- src/resolve/resolved-def.h | 1 + src/resolve/resolved-dns-answer.c | 2 +- src/resolve/resolved-dns-answer.h | 8 +- src/resolve/resolved-dns-query.c | 537 ++++++++++++++++++++++--------- src/resolve/resolved-dns-query.h | 23 +- src/resolve/resolved-dns-question.c | 8 +- src/resolve/resolved-dns-question.h | 12 +- src/resolve/resolved-dns-rr.c | 82 ++++- src/resolve/resolved-dns-rr.h | 5 +- src/resolve/resolved-dns-scope.c | 76 ++++- src/resolve/resolved-dns-scope.h | 4 + src/resolve/resolved-dns-search-domain.h | 4 + src/resolve/resolved-dns-transaction.c | 17 +- src/resolve/resolved-dns-transaction.h | 7 +- src/resolve/resolved-dns-zone.c | 4 +- 17 files changed, 622 insertions(+), 207 deletions(-) (limited to 'src') diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 2c17ad6ede..f68751a2e5 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -680,6 +680,7 @@ static void help(void) { " --service-address=BOOL Do [not] resolve address for services\n" " --service-txt=BOOL Do [not] resolve TXT records for services\n" " --cname=BOOL Do [not] follow CNAME redirects\n" + " --search=BOOL Do [not] use search domains\n" " --legend=BOOL Do [not] print column headers\n" , program_invocation_short_name, program_invocation_short_name); } @@ -692,6 +693,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_CNAME, ARG_SERVICE_ADDRESS, ARG_SERVICE_TXT, + ARG_SEARCH, }; static const struct option options[] = { @@ -705,6 +707,7 @@ static int parse_argv(int argc, char *argv[]) { { "service", no_argument, NULL, ARG_SERVICE }, { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, + { "search", required_argument, NULL, ARG_SEARCH }, {} }; @@ -834,6 +837,16 @@ static int parse_argv(int argc, char *argv[]) { arg_flags &= ~SD_RESOLVED_NO_TXT; break; + case ARG_SEARCH: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --search argument."); + if (r == 0) + arg_flags |= SD_RESOLVED_NO_SEARCH; + else + arg_flags &= ~SD_RESOLVED_NO_SEARCH; + break; + case '?': return -EINVAL; diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 537afc0d41..8885b83101 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -166,7 +166,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { int ifindex; DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) goto finish; if (r == 0) @@ -254,7 +254,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, if (r == 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); - r = check_ifindex_flags(ifindex, &flags, 0, error); + r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); if (r < 0) return r; @@ -310,7 +310,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (q->answer) { DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) goto finish; if (r == 0) @@ -392,7 +392,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags); + r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; @@ -487,7 +487,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { int ifindex; DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) goto finish; if (r == 0) @@ -566,7 +566,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags); + r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; @@ -620,7 +620,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) DNS_ANSWER_FOREACH(zz, aux->answer) { - r = dns_question_matches_rr(aux->question, zz); + r = dns_question_matches_rr(aux->question, zz, NULL); if (r < 0) return r; if (r == 0) @@ -672,7 +672,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) { - r = dns_question_matches_rr(aux->question, zz); + r = dns_question_matches_rr(aux->question, zz, NULL); if (r < 0) return r; if (r == 0) @@ -798,7 +798,7 @@ static void resolve_service_all_complete(DnsQuery *q) { DnsResourceRecord *rr; DNS_ANSWER_FOREACH(rr, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) goto finish; if (r == 0) @@ -834,7 +834,7 @@ static void resolve_service_all_complete(DnsQuery *q) { DnsResourceRecord *rr; DNS_ANSWER_FOREACH(rr, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) goto finish; if (r == 0) @@ -911,7 +911,7 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin if (r < 0) return r; - r = dns_query_new(q->manager, &aux, question, ifindex, q->flags); + r = dns_query_new(q->manager, &aux, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; @@ -970,7 +970,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { int ifindex; DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) goto finish; if (r == 0) @@ -1078,7 +1078,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags); + r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index 85af1923ff..33f8ddb1f2 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -27,6 +27,7 @@ #define SD_RESOLVED_NO_CNAME ((uint64_t) 8) #define SD_RESOLVED_NO_TXT ((uint64_t) 16) #define SD_RESOLVED_NO_ADDRESS ((uint64_t) 32) +#define SD_RESOLVED_NO_SEARCH ((uint64_t) 64) #define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) #define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 3cf9c68074..4db67f7278 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -141,7 +141,7 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { return 0; for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_key_match_rr(key, a->items[i].rr); + r = dns_resource_key_match_rr(key, a->items[i].rr, NULL); if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index b5b1ad56ba..8814919deb 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -61,7 +61,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref); #define DNS_ANSWER_FOREACH(kk, a) \ for (unsigned _i = ({ \ - (kk) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].rr : NULL); \ + (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \ 0; \ }); \ (a) && ((_i) < (a)->n_rrs); \ @@ -69,9 +69,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref); #define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) \ for (unsigned _i = ({ \ - (kk) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].rr : NULL); \ - (ifindex) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].ifindex : 0); \ + (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \ + (ifindex) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \ 0; \ }); \ (a) && ((_i) < (a)->n_rrs); \ - _i++, (kk) = (_i < (a)->n_rrs ? (a)->items[_i].rr : NULL), (ifindex) = (_i < (a)->n_rrs ? (a)->items[_i].ifindex : 0)) + _i++, (kk) = ((_i < (a)->n_rrs) ? (a)->items[_i].rr : NULL), (ifindex) = ((_i < (a)->n_rrs) ? (a)->items[_i].ifindex : 0)) diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 0970f3ead7..b84f5bf0f3 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -32,19 +32,277 @@ #define QUERIES_MAX 2048 #define AUXILIARY_QUERIES_MAX 64 -static void dns_query_stop(DnsQuery *q) { - DnsTransaction *t; +static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) { + DnsQueryCandidate *c; + assert(ret); assert(q); + assert(s); - q->timeout_event_source = sd_event_source_unref(q->timeout_event_source); + c = new0(DnsQueryCandidate, 1); + if (!c) + return -ENOMEM; + + c->query = q; + c->scope = s; + + LIST_PREPEND(candidates_by_query, q->candidates, c); + LIST_PREPEND(candidates_by_scope, s->query_candidates, c); + + *ret = c; + return 0; +} + +static void dns_query_candidate_stop(DnsQueryCandidate *c) { + DnsTransaction *t; - while ((t = set_steal_first(q->transactions))) { - set_remove(t->queries, q); + assert(c); + + while ((t = set_steal_first(c->transactions))) { + set_remove(t->query_candidates, c); dns_transaction_gc(t); } } +DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) { + + if (!c) + return NULL; + + dns_query_candidate_stop(c); + + set_free(c->transactions); + dns_search_domain_unref(c->search_domain); + + if (c->query) + LIST_REMOVE(candidates_by_query, c->query->candidates, c); + + if (c->scope) + LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c); + + free(c); + + return NULL; +} + +static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) { + _cleanup_(dns_search_domain_unrefp) DnsSearchDomain *previous = NULL; + DnsSearchDomain *next = NULL; + + assert(c); + + if (c->search_domain && c->search_domain->linked) { + next = c->search_domain->domains_next; + + if (!next) { + /* We hit the last entry. Let's see if this + * was the per-link search domain list. If so, + * let's continue with the global one. */ + + if (c->search_domain->type == DNS_SEARCH_DOMAIN_LINK) + next = c->query->manager->search_domains; + + if (!next) /* Still no item? Then we really hit the end of the list. */ + return 0; + } + + } else { + /* If we have, start with the per-link domains */ + next = dns_scope_get_search_domains(c->scope); + + if (!next) /* Fall back to the global search domains */ + next = c->scope->manager->search_domains; + + if (!next) /* OK, there's really nothing. */ + return 0; + } + + dns_search_domain_unref(c->search_domain); + c->search_domain = dns_search_domain_ref(next); + return 1; +} + +static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) { + DnsTransaction *t; + int r; + + assert(c); + assert(key); + + r = set_ensure_allocated(&c->transactions, NULL); + if (r < 0) + return r; + + t = dns_scope_find_transaction(c->scope, key, true); + if (!t) { + r = dns_transaction_new(&t, c->scope, key); + if (r < 0) + return r; + } + + r = set_ensure_allocated(&t->query_candidates, NULL); + if (r < 0) + goto gc; + + r = set_put(t->query_candidates, c); + if (r < 0) + goto gc; + + r = set_put(c->transactions, t); + if (r < 0) { + set_remove(t->query_candidates, c); + goto gc; + } + + return 0; + +gc: + dns_transaction_gc(t); + return r; +} + +static int dns_query_candidate_go(DnsQueryCandidate *c) { + DnsTransaction *t; + Iterator i; + int r; + + assert(c); + + /* Start the transactions that are not started yet */ + SET_FOREACH(t, c->transactions, i) { + if (t->state != DNS_TRANSACTION_NULL) + continue; + + r = dns_transaction_go(t); + if (r < 0) + return r; + } + + return 0; +} + +static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) { + DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; + DnsTransaction *t; + Iterator i; + + assert(c); + + if (c->error_code != 0) + return DNS_TRANSACTION_RESOURCES; + + SET_FOREACH(t, c->transactions, i) { + + switch (t->state) { + + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_NULL: + return t->state; + + case DNS_TRANSACTION_SUCCESS: + state = t->state; + break; + + default: + if (state != DNS_TRANSACTION_SUCCESS) + state = t->state; + + break; + } + } + + return state; +} + +static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) { + DnsResourceKey *key; + int n = 0, r; + + assert(c); + + dns_query_candidate_stop(c); + + /* Create one transaction per question key */ + DNS_QUESTION_FOREACH(key, c->query->question) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL; + + if (c->search_domain) { + r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name); + if (r < 0) + goto fail; + } + + r = dns_query_candidate_add_transaction(c, new_key ?: key); + if (r < 0) + goto fail; + + n++; + } + + return n; + +fail: + dns_query_candidate_stop(c); + return r; +} + +void dns_query_candidate_ready(DnsQueryCandidate *c) { + DnsTransactionState state; + int r; + + assert(c); + + state = dns_query_candidate_state(c); + + if (IN_SET(state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) + return; + + if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) { + + r = dns_query_candidate_next_search_domain(c); + if (r < 0) + goto fail; + + if (r > 0) { + /* OK, there's another search domain to try, let's do so. */ + + r = dns_query_candidate_setup_transactions(c); + if (r < 0) + goto fail; + + if (r > 0) { + /* New transactions where queued. Start them and wait */ + + r = dns_query_candidate_go(c); + if (r < 0) + goto fail; + + return; + } + } + + } + + dns_query_ready(c->query); + return; + +fail: + log_warning_errno(r, "Failed to follow search domains: %m"); + c->error_code = r; + dns_query_ready(c->query); +} + +static void dns_query_stop(DnsQuery *q) { + DnsQueryCandidate *c; + + assert(q); + + q->timeout_event_source = sd_event_source_unref(q->timeout_event_source); + + LIST_FOREACH(candidates_by_query, c, q->candidates) + dns_query_candidate_stop(c); +} + DnsQuery *dns_query_free(DnsQuery *q) { if (!q) return NULL; @@ -58,11 +316,12 @@ DnsQuery *dns_query_free(DnsQuery *q) { LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q); } - dns_query_stop(q); - set_free(q->transactions); + while (q->candidates) + dns_query_candidate_free(q->candidates); dns_question_unref(q->question); dns_answer_unref(q->answer); + dns_search_domain_unref(q->answer_search_domain); sd_bus_message_unref(q->request); sd_bus_track_unref(q->bus_track); @@ -99,6 +358,8 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex q->question = dns_question_ref(question); q->ifindex = ifindex; q->flags = flags; + q->answer_family = AF_UNSPEC; + q->answer_protocol = _DNS_PROTOCOL_INVALID; for (i = 0; i < question->n_keys; i++) { _cleanup_free_ char *p; @@ -170,64 +431,40 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) { return 0; } -static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) { - DnsTransaction *t; +static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { + DnsQueryCandidate *c; int r; assert(q); assert(s); - assert(key); - r = set_ensure_allocated(&q->transactions, NULL); + r = dns_query_candidate_new(&c, q, s); if (r < 0) return r; - t = dns_scope_find_transaction(s, key, true); - if (!t) { - r = dns_transaction_new(&t, s, key); - if (r < 0) - return r; - } - - r = set_ensure_allocated(&t->queries, NULL); - if (r < 0) - goto gc; - - r = set_put(t->queries, q); + /* If this a single-label domain on DNS, we might append a suitable search domain first. */ + r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question)); if (r < 0) - goto gc; + goto fail; + if (r > 0) { + /* OK, we need a search domain now. Let's find one for this scope */ - r = set_put(q->transactions, t); - if (r < 0) { - set_remove(t->queries, q); - goto gc; + r = dns_query_candidate_next_search_domain(c); + if (r <= 0) /* if there's no search domain, then we won't add any transaction. */ + goto fail; } + r = dns_query_candidate_setup_transactions(c); + if (r < 0) + goto fail; + return 0; -gc: - dns_transaction_gc(t); +fail: + dns_query_candidate_free(c); return r; } -static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) { - unsigned i; - int r; - - assert(q); - assert(s); - - /* Create one transaction per question key */ - - for (i = 0; i < q->question->n_keys; i++) { - r = dns_query_add_transaction(q, s, q->question->keys[i]); - if (r < 0) - return r; - } - - return 0; -} - static int SYNTHESIZE_IFINDEX(int ifindex) { /* When the caller asked for resolving on a specific @@ -630,9 +867,9 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { q->answer = answer; answer = NULL; - q->answer_family = SYNTHESIZE_FAMILY(q->flags); - q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags); q->answer_rcode = DNS_RCODE_SUCCESS; + q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags); + q->answer_family = SYNTHESIZE_FAMILY(q->flags); *state = DNS_TRANSACTION_SUCCESS; @@ -642,9 +879,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { int dns_query_go(DnsQuery *q) { DnsScopeMatch found = DNS_SCOPE_NO; DnsScope *s, *first = NULL; - DnsTransaction *t; + DnsQueryCandidate *c; const char *name; - Iterator i; int r; assert(q); @@ -688,7 +924,7 @@ int dns_query_go(DnsQuery *q) { return 1; } - r = dns_query_add_transaction_split(q, first); + r = dns_query_add_candidate(q, first); if (r < 0) goto fail; @@ -702,7 +938,7 @@ int dns_query_go(DnsQuery *q) { if (match != found) continue; - r = dns_query_add_transaction_split(q, s); + r = dns_query_add_candidate(q, s); if (r < 0) goto fail; } @@ -724,14 +960,13 @@ int dns_query_go(DnsQuery *q) { q->state = DNS_TRANSACTION_PENDING; q->block_ready++; - /* Start the transactions that are not started yet */ - SET_FOREACH(t, q->transactions, i) { - if (t->state != DNS_TRANSACTION_NULL) - continue; - - r = dns_transaction_go(t); - if (r < 0) + /* Start the transactions */ + LIST_FOREACH(candidates_by_query, c, q->candidates) { + r = dns_query_candidate_go(c); + if (r < 0) { + q->block_ready--; goto fail; + } } q->block_ready--; @@ -744,124 +979,132 @@ fail: return r; } -void dns_query_ready(DnsQuery *q) { - DnsTransaction *t; +static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - int rcode = 0; - DnsScope *scope = NULL; - bool pending = false; + DnsTransaction *t; Iterator i; assert(q); - assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function, unless the block_ready - * counter was explicitly bumped before doing so. */ - - if (q->block_ready > 0) + if (!c) { + dns_query_synthesize_reply(q, &state); + dns_query_complete(q, state); return; + } - SET_FOREACH(t, q->transactions, i) { + SET_FOREACH(t, c->transactions, i) { - /* If we found a successful answer, ignore all answers from other scopes */ - if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope) - continue; + switch (t->state) { - /* One of the transactions is still going on, let's maybe wait for it */ - if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) { - pending = true; - continue; - } - - /* One of the transactions is successful, let's use - * it, and copy its data out */ - if (t->state == DNS_TRANSACTION_SUCCESS) { - DnsAnswer *a; + case DNS_TRANSACTION_SUCCESS: { + /* We found a successfuly reply, merge it into the answer */ + DnsAnswer *merged, *a; if (t->received) { - rcode = DNS_PACKET_RCODE(t->received); + q->answer_rcode = DNS_PACKET_RCODE(t->received); a = t->received->answer; } else { - rcode = t->cached_rcode; + q->answer_rcode = t->cached_rcode; a = t->cached; } - if (state == DNS_TRANSACTION_SUCCESS) { - DnsAnswer *merged; + merged = dns_answer_merge(q->answer, a); + if (!merged) { + dns_query_complete(q, DNS_TRANSACTION_RESOURCES); + return; + } + + dns_answer_unref(q->answer); + q->answer = merged; + + state = DNS_TRANSACTION_SUCCESS; + break; + } + + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_NULL: + case DNS_TRANSACTION_ABORTED: + /* Ignore transactions that didn't complete */ + continue; + + default: + /* Any kind of failure? Store the data away, + * if there's nothing stored yet. */ - merged = dns_answer_merge(answer, a); - if (!merged) { - dns_query_complete(q, DNS_TRANSACTION_RESOURCES); - return; + if (state != DNS_TRANSACTION_SUCCESS) { + + dns_answer_unref(q->answer); + + if (t->received) { + q->answer = dns_answer_ref(t->received->answer); + q->answer_rcode = DNS_PACKET_RCODE(t->received); + } else { + q->answer = dns_answer_ref(t->cached); + q->answer_rcode = t->cached_rcode; } - dns_answer_unref(answer); - answer = merged; - } else { - dns_answer_unref(answer); - answer = dns_answer_ref(a); + state = t->state; } - scope = t->scope; - state = DNS_TRANSACTION_SUCCESS; - continue; + break; } + } - /* One of the transactions has failed, let's see - * whether we find anything better, but if not, return - * its response data */ - if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) { - DnsAnswer *a; + q->answer_protocol = c->scope->protocol; + q->answer_family = c->scope->family; - if (t->received) { - rcode = DNS_PACKET_RCODE(t->received); - a = t->received->answer; - } else { - rcode = t->cached_rcode; - a = t->cached; - } + dns_search_domain_unref(q->answer_search_domain); + q->answer_search_domain = dns_search_domain_ref(c->search_domain); - dns_answer_unref(answer); - answer = dns_answer_ref(a); + dns_query_synthesize_reply(q, &state); + dns_query_complete(q, state); +} - scope = t->scope; - state = DNS_TRANSACTION_FAILURE; - continue; - } +void dns_query_ready(DnsQuery *q) { - if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS) - state = t->state; - } + DnsQueryCandidate *bad = NULL, *c; + bool pending = false; - if (pending) { + assert(q); + assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - /* If so far we weren't successful, and there's - * something still pending, then wait for it */ - if (state != DNS_TRANSACTION_SUCCESS) + /* Note that this call might invalidate the query. Callers + * should hence not attempt to access the query or transaction + * after calling this function, unless the block_ready + * counter was explicitly bumped before doing so. */ + + if (q->block_ready > 0) + return; + + LIST_FOREACH(candidates_by_query, c, q->candidates) { + DnsTransactionState state; + + state = dns_query_candidate_state(c); + switch (state) { + + case DNS_TRANSACTION_SUCCESS: + /* One of the transactions is successful, + * let's use it, and copy its data out */ + dns_query_accept(q, c); return; - /* If we already were successful, then only wait for - * other transactions on the same scope to finish. */ - SET_FOREACH(t, q->transactions, i) { - if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) - return; - } - } + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_NULL: + /* One of the transactions is still going on, let's maybe wait for it */ + pending = true; + break; - if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) { - q->answer = dns_answer_ref(answer); - q->answer_rcode = rcode; - q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID; - q->answer_family = scope ? scope->family : AF_UNSPEC; + default: + /* Any kind of failure */ + bad = c; + break; + } } - /* Try to synthesize a reply if we couldn't resolve something. */ - dns_query_synthesize_reply(q, &state); + if (pending) + return; - dns_query_complete(q, state); + dns_query_accept(q, bad); } static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) { @@ -900,13 +1143,13 @@ int dns_query_process_cname(DnsQuery *q) { DNS_ANSWER_FOREACH(rr, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) return r; if (r > 0) return 0; /* The answer matches directly, no need to follow cnames */ - r = dns_question_matches_cname(q->question, rr); + r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) return r; if (r > 0 && !cname) @@ -927,7 +1170,7 @@ int dns_query_process_cname(DnsQuery *q) { /* Let's see if the answer can already answer the new * redirected question */ DNS_ANSWER_FOREACH(rr, q->answer) { - r = dns_question_matches_rr(q->question, rr); + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 256dddc00b..fb16747c55 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -26,11 +26,26 @@ #include "set.h" +typedef struct DnsQueryCandidate DnsQueryCandidate; typedef struct DnsQuery DnsQuery; #include "resolved-dns-answer.h" #include "resolved-dns-question.h" #include "resolved-dns-stream.h" +#include "resolved-dns-search-domain.h" + +struct DnsQueryCandidate { + DnsQuery *query; + DnsScope *scope; + + DnsSearchDomain *search_domain; + + int error_code; + Set *transactions; + + LIST_FIELDS(DnsQueryCandidate, candidates_by_query); + LIST_FIELDS(DnsQueryCandidate, candidates_by_scope); +}; struct DnsQuery { Manager *manager; @@ -45,13 +60,13 @@ struct DnsQuery { int auxiliary_result; DnsQuestion *question; - uint64_t flags; int ifindex; DnsTransactionState state; unsigned n_cname_redirects; + LIST_HEAD(DnsQueryCandidate, candidates); sd_event_source *timeout_event_source; /* Discovered data */ @@ -59,6 +74,7 @@ struct DnsQuery { int answer_family; DnsProtocol answer_protocol; int answer_rcode; + DnsSearchDomain *answer_search_domain; /* Bus client information */ sd_bus_message *request; @@ -71,14 +87,15 @@ struct DnsQuery { void (*complete)(DnsQuery* q); unsigned block_ready; - Set *transactions; - sd_bus_track *bus_track; LIST_FIELDS(DnsQuery, queries); LIST_FIELDS(DnsQuery, auxiliary_queries); }; +DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c); +void dns_query_candidate_ready(DnsQueryCandidate *c); + int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags); DnsQuery *dns_query_free(DnsQuery *q); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index b1aa0175e0..3249448d3b 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -89,7 +89,7 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { return 0; } -int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { +int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { unsigned i; int r; @@ -99,7 +99,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { return 0; for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_rr(q->keys[i], rr); + r = dns_resource_key_match_rr(q->keys[i], rr, search_domain); if (r != 0) return r; } @@ -107,7 +107,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { return 0; } -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { +int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { unsigned i; int r; @@ -117,7 +117,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { return 0; for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_cname(q->keys[i], rr); + r = dns_resource_key_match_cname(q->keys[i], rr, search_domain); if (r != 0) return r; } diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index d728b9bed2..e77116c03a 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -43,8 +43,8 @@ int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) int dns_question_add(DnsQuestion *q, DnsResourceKey *key); -int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr); -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr); +int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain); +int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain); int dns_question_is_valid_for_query(DnsQuestion *q); int dns_question_contains(DnsQuestion *a, DnsResourceKey *k); int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); @@ -54,3 +54,11 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, const char *dns_question_first_name(DnsQuestion *q); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); + +#define DNS_QUESTION_FOREACH(key, q) \ + for (unsigned _i = ({ \ + (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \ + 0; \ + }); \ + (q) && ((_i) < (q)->n_keys); \ + _i++, (key) = (_i < (q)->n_keys ? (q)->keys[_i] : NULL)) diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 9b264900fc..4f896b05af 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -86,6 +86,37 @@ DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const D } } +int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) { + DnsResourceKey *new_key; + char *joined; + int r; + + assert(ret); + assert(key); + assert(name); + + r = dns_name_root(name); + if (r < 0) + return r; + if (r > 0) { + *ret = dns_resource_key_ref(key); + return 0; + } + + r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined); + if (r < 0) + return r; + + new_key = dns_resource_key_new_consume(key->class, key->type, joined); + if (!new_key) { + free(joined); + return -ENOMEM; + } + + *ret = new_key; + return 0; +} + DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) { DnsResourceKey *k; @@ -145,20 +176,42 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) { return 1; } -int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) { +int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) { + int r; + assert(key); assert(rr); + /* Checks if an rr matches the specified key. If a search + * domain is specified, it will also be checked if the key + * with the search domain suffixed might match the RR. */ + if (rr->key->class != key->class && key->class != DNS_CLASS_ANY) return 0; if (rr->key->type != key->type && key->type != DNS_TYPE_ANY) return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); + if (r != 0) + return r; + + if (search_domain) { + _cleanup_free_ char *joined = NULL; + + r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined); + if (r < 0) + return r; + + return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), joined); + } + + return 0; } -int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) { +int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) { + int r; + assert(key); assert(rr); @@ -166,11 +219,30 @@ int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRec return 0; if (rr->key->type == DNS_TYPE_CNAME) - return dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key)); else if (rr->key->type == DNS_TYPE_DNAME) - return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key)); else return 0; + + if (r != 0) + return r; + + if (search_domain) { + _cleanup_free_ char *joined = NULL; + + r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined); + if (r < 0) + return r; + + if (rr->key->type == DNS_TYPE_CNAME) + return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(rr->key)); + else if (rr->key->type == DNS_TYPE_DNAME) + return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(rr->key)); + } + + return 0; + } static void dns_resource_key_hash_func(const void *i, struct siphash *state) { diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index c1601fb694..f8066c06a6 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -188,12 +188,13 @@ DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char * DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key); DnsResourceKey* dns_resource_key_new_dname(const DnsResourceKey *key); DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname); +int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name); DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name); DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key); DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key); int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b); -int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr); -int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr); +int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain); +int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain); int dns_resource_key_to_string(const DnsResourceKey *key, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 2e47691c56..daeab9b9ee 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -69,18 +69,12 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int return 0; } -DnsScope* dns_scope_free(DnsScope *s) { +static void dns_scope_abort_transactions(DnsScope *s) { DnsTransaction *t; - DnsResourceRecord *rr; - - if (!s) - return NULL; - - log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); - dns_scope_llmnr_membership(s, false); + assert(s); - while ((t = hashmap_steal_first(s->transactions))) { + while ((t = hashmap_first(s->transactions))) { /* Abort the transaction, but make sure it is not * freed while we still look at it */ @@ -90,6 +84,21 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_transaction_free(t); } +} + +DnsScope* dns_scope_free(DnsScope *s) { + DnsResourceRecord *rr; + + if (!s) + return NULL; + + log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); + + dns_scope_llmnr_membership(s, false); + dns_scope_abort_transactions(s); + + while (s->query_candidates) + dns_query_candidate_free(s->query_candidates); hashmap_free(s->transactions); @@ -344,18 +353,28 @@ 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; + /* Always honour search domains for routing queries. Note that + * we return DNS_SCOPE_YES here, rather than just + * DNS_SCOPE_MAYBE, which means wildcard scopes won't be + * considered anymore. */ 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) { - case DNS_PROTOCOL_DNS: - if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 && - dns_name_single_label(domain) == 0) + case DNS_PROTOCOL_DNS: { + int is_single_label; + + is_single_label = dns_name_single_label(domain); + + if ((is_single_label == 0 || + (is_single_label > 0 && !(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) && + dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && + dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; + } case DNS_PROTOCOL_MDNS: if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || @@ -851,6 +870,10 @@ void dns_scope_dump(DnsScope *s, FILE *f) { } DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) { + assert(s); + + /* Returns the list of *local* search domains -- not the + * global ones. */ if (s->protocol != DNS_PROTOCOL_DNS) return NULL; @@ -860,3 +883,30 @@ DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) { return NULL; } + +bool dns_scope_has_search_domains(DnsScope *s) { + assert(s); + + /* Tests if there are *any* search domains suitable for this + * scope. This means either local or global ones */ + + if (s->protocol != DNS_PROTOCOL_DNS) + return false; + + if (s->manager->search_domains) + return true; + + if (s->link && s->link->search_domains) + return true; + + return false; +} + +int dns_scope_name_needs_search_domain(DnsScope *s, const char *name) { + assert(s); + + if (s->protocol != DNS_PROTOCOL_DNS) + return 0; + + return dns_name_single_label(name); +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index f69d51b203..244bb41314 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -59,6 +59,7 @@ struct DnsScope { usec_t max_rtt; Hashmap *transactions; + LIST_HEAD(DnsQueryCandidate, query_candidates); LIST_FIELDS(DnsScope, scopes); }; @@ -91,3 +92,6 @@ 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); +bool dns_scope_has_search_domains(DnsScope *s); + +int dns_scope_name_needs_search_domain(DnsScope *s, const char *name); diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h index 20b441206c..2e0af31dda 100644 --- a/src/resolve/resolved-dns-search-domain.h +++ b/src/resolve/resolved-dns-search-domain.h @@ -68,4 +68,8 @@ void dns_search_domain_mark_all(DnsSearchDomain *first); int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret); +static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) { + return d ? d->name : NULL; +} + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 37f47c47c0..2fc84aec6f 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -29,7 +29,7 @@ #include "string-table.h" DnsTransaction* dns_transaction_free(DnsTransaction *t) { - DnsQuery *q; + DnsQueryCandidate *c; DnsZoneItem *i; if (!t) @@ -56,9 +56,10 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_resource_key_unref(t->key); - while ((q = set_steal_first(t->queries))) - set_remove(q->transactions, t); - set_free(t->queries); + while ((c = set_steal_first(t->query_candidates))) + set_remove(c->transactions, t); + + set_free(t->query_candidates); while ((i = set_steal_first(t->zone_items))) i->probe_transaction = NULL; @@ -76,7 +77,7 @@ void dns_transaction_gc(DnsTransaction *t) { if (t->block_gc > 0) return; - if (set_isempty(t->queries) && set_isempty(t->zone_items)) + if (set_isempty(t->query_candidates) && set_isempty(t->zone_items)) dns_transaction_free(t); } @@ -181,7 +182,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { } void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { - DnsQuery *q; + DnsQueryCandidate *c; DnsZoneItem *z; Iterator i; @@ -205,8 +206,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { /* Notify all queries that are interested, but make sure the * transaction isn't freed while we are still looking at it */ t->block_gc++; - SET_FOREACH(q, t->queries, i) - dns_query_ready(q); + SET_FOREACH(c, t->query_candidates, i) + dns_query_candidate_ready(c); SET_FOREACH(z, t->zone_items, i) dns_zone_item_ready(z); t->block_gc--; diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index d0970bd695..a2aa73a524 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -71,9 +71,10 @@ struct DnsTransaction { /* TCP connection logic, if we need it */ DnsStream *stream; - /* Queries this transaction is referenced by and that shall be - * notified about this specific transaction completing. */ - Set *queries; + /* Query candidates this transaction is referenced by and that + * shall be notified about this specific transaction + * completing. */ + Set *query_candidates; /* Zone items this transaction is referenced by and that shall * be notified about completion. */ diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index a021ecb93d..493d11dd14 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -311,7 +311,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns found = true; - k = dns_resource_key_match_rr(key, j->rr); + k = dns_resource_key_match_rr(key, j->rr, NULL); if (k < 0) return k; if (k > 0) { @@ -381,7 +381,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns if (j->state != DNS_ZONE_ITEM_PROBING) tentative = false; - k = dns_resource_key_match_rr(key, j->rr); + k = dns_resource_key_match_rr(key, j->rr, NULL); if (k < 0) return k; if (k > 0) { -- cgit v1.2.3-54-g00ecf From dc477e7385e8ab29efb8fadb72ec994077a105c6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 25 Nov 2015 21:07:17 +0100 Subject: dns-domain: simplify dns_name_is_root() and dns_name_is_single_label() Let's change the return value to bool. If we encounter an error while parsing, return "false" instead of the actual parsing error, after all the specified hostname does not qualify for what the function is supposed to test. Dealing with the additional error codes was always cumbersome, and easily misused, like for example in the DHCP code. Let's also rename the functions from dns_name_root() to dns_name_is_root(), to indicate that this function checks something and returns a bool. Similar for dns_name_is_signal_label(). --- src/libsystemd-network/sd-dhcp-client.c | 4 ++-- src/resolve/resolved-dns-rr.c | 5 +---- src/resolve/resolved-dns-scope.c | 19 ++++++++----------- src/resolve/resolved-dns-scope.h | 2 +- src/resolve/resolved-dns-search-domain.c | 5 +---- src/shared/dns-domain.c | 26 +++++++++----------------- src/shared/dns-domain.h | 4 ++-- src/test/test-dns-domain.c | 30 +++++++++++++++--------------- 8 files changed, 39 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 5ec0e661f7..f689c59a1a 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -604,7 +604,7 @@ static int client_send_discover(sd_dhcp_client *client) { their messages MUST NOT also send the Host Name option". Just send one of the two depending on the hostname type. */ - if (dns_name_single_label(client->hostname)) { + if (dns_name_is_single_label(client->hostname)) { /* it is unclear from RFC 2131 if client should send hostname in DHCPDISCOVER but dhclient does and so we do as well */ @@ -719,7 +719,7 @@ static int client_send_request(sd_dhcp_client *client) { } if (client->hostname) { - if (dns_name_single_label(client->hostname)) + if (dns_name_is_single_label(client->hostname)) r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, DHCP_OPTION_HOST_NAME, strlen(client->hostname), client->hostname); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 4f896b05af..4a1abb0cdc 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -95,10 +95,7 @@ int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key assert(key); assert(name); - r = dns_name_root(name); - if (r < 0) - return r; - if (r > 0) { + if (dns_name_is_root(name)) { *ret = dns_resource_key_ref(key); return 0; } diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index daeab9b9ee..20db1fbd81 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -342,7 +342,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0) return DNS_SCOPE_NO; - if (dns_name_root(domain) != 0) + if (dns_name_is_root(domain)) return DNS_SCOPE_NO; /* Never resolve any loopback hostname or IP address via DNS, @@ -362,19 +362,16 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co return DNS_SCOPE_YES; switch (s->protocol) { - case DNS_PROTOCOL_DNS: { - int is_single_label; - is_single_label = dns_name_single_label(domain); + case DNS_PROTOCOL_DNS: - if ((is_single_label == 0 || - (is_single_label > 0 && !(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) && + if ((!dns_name_is_single_label(domain) || + (!(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) && dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; - } case DNS_PROTOCOL_MDNS: if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || @@ -389,7 +386,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co case DNS_PROTOCOL_LLMNR: if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || - (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */ + (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */ !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */ return DNS_SCOPE_MAYBE; @@ -902,11 +899,11 @@ bool dns_scope_has_search_domains(DnsScope *s) { return false; } -int dns_scope_name_needs_search_domain(DnsScope *s, const char *name) { +bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) { assert(s); if (s->protocol != DNS_PROTOCOL_DNS) - return 0; + return false; - return dns_name_single_label(name); + return dns_name_is_single_label(name); } diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 244bb41314..32e6961757 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -94,4 +94,4 @@ void dns_scope_dump(DnsScope *s, FILE *f); DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s); bool dns_scope_has_search_domains(DnsScope *s); -int dns_scope_name_needs_search_domain(DnsScope *s, const char *name); +bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name); diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c index d8a0648aab..f9d966abb1 100644 --- a/src/resolve/resolved-dns-search-domain.c +++ b/src/resolve/resolved-dns-search-domain.c @@ -42,10 +42,7 @@ int dns_search_domain_new( if (r < 0) return r; - r = dns_name_root(normalized); - if (r < 0) - return r; - if (r > 0) + if (dns_name_is_root(normalized)) return -EINVAL; if (l) { diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index e6aad39c74..31b5891435 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -29,6 +29,7 @@ #include "hexdecoct.h" #include "parse-util.h" #include "string-util.h" +#include "strv.h" #include "utf8.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { @@ -752,36 +753,27 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { return 0; } -int dns_name_root(const char *name) { - char label[DNS_LABEL_MAX+1]; - int r; +bool dns_name_is_root(const char *name) { assert(name); - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; + /* There are exactly two ways to encode the root domain name: + * as empty string, or with a single dot. */ - return r == 0 && *name == 0; + return STR_IN_SET(name, "", "."); } -int dns_name_single_label(const char *name) { +bool dns_name_is_single_label(const char *name) { char label[DNS_LABEL_MAX+1]; int r; assert(name); r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; - if (r == 0) - return 0; - - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; + if (r <= 0) + return false; - return r == 0 && *name == 0; + return dns_name_is_root(name); } /* Encode a domain name according to RFC 1035 Section 3.1 */ diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index e7e471e8a6..84be17425b 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -67,8 +67,8 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char int dns_name_reverse(int family, const union in_addr_union *a, char **ret); int dns_name_address(const char *p, int *family, union in_addr_union *a); -int dns_name_root(const char *name); -int dns_name_single_label(const char *name); +bool dns_name_is_root(const char *name); +bool dns_name_is_single_label(const char *name); int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index b76a31a549..cefe8698c8 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -246,21 +246,21 @@ static void test_dns_name_endswith(void) { test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL); } -static void test_dns_name_root(void) { - assert_se(dns_name_root("") == true); - assert_se(dns_name_root(".") == true); - assert_se(dns_name_root("xxx") == false); - assert_se(dns_name_root("xxx.") == false); - assert_se(dns_name_root("..") == -EINVAL); +static void test_dns_name_is_root(void) { + assert_se(dns_name_is_root("")); + assert_se(dns_name_is_root(".")); + assert_se(!dns_name_is_root("xxx")); + assert_se(!dns_name_is_root("xxx.")); + assert_se(!dns_name_is_root("..")); } -static void test_dns_name_single_label(void) { - assert_se(dns_name_single_label("") == false); - assert_se(dns_name_single_label(".") == false); - assert_se(dns_name_single_label("..") == -EINVAL); - assert_se(dns_name_single_label("x") == true); - assert_se(dns_name_single_label("x.") == true); - assert_se(dns_name_single_label("xx.yy") == false); +static void test_dns_name_is_single_label(void) { + assert_se(!dns_name_is_single_label("")); + assert_se(!dns_name_is_single_label(".")); + assert_se(!dns_name_is_single_label("..")); + assert_se(dns_name_is_single_label("x")); + assert_se(dns_name_is_single_label("x.")); + assert_se(!dns_name_is_single_label("xx.yy")); } static void test_dns_name_reverse_one(const char *address, const char *name) { @@ -436,8 +436,8 @@ int main(int argc, char *argv[]) { test_dns_name_equal(); test_dns_name_endswith(); test_dns_name_between(); - test_dns_name_root(); - test_dns_name_single_label(); + test_dns_name_is_root(); + test_dns_name_is_single_label(); test_dns_name_reverse(); test_dns_name_concat(); test_dns_name_is_valid(); -- cgit v1.2.3-54-g00ecf From 7e8131e9c6c150732503899a092206578fdc13de Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 25 Nov 2015 21:15:07 +0100 Subject: dns-domain: change dns_srv_type_is_valid() return value to bool For similar reasons as dns_name_is_root() got changed in the previous commit. --- src/resolve/resolved-bus.c | 9 ++------- src/shared/dns-domain.c | 14 ++++++-------- src/shared/dns-domain.h | 3 +-- src/test/test-dns-domain.c | 44 ++++++++++++++++++++++---------------------- 4 files changed, 31 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 8885b83101..1fd8e78f92 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -1041,13 +1041,8 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s if (isempty(type)) type = NULL; - else { - r = dns_srv_type_verify(type); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type); - } + else if (!dns_srv_type_is_valid(type)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type); r = dns_name_is_valid(domain); if (r < 0) diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 31b5891435..5a9b091ac4 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -838,12 +838,12 @@ static bool srv_type_label_is_valid(const char *label, size_t n) { return true; } -int dns_srv_type_verify(const char *name) { +bool dns_srv_type_is_valid(const char *name) { unsigned c = 0; int r; if (!name) - return 0; + return false; for (;;) { char label[DNS_LABEL_MAX]; @@ -851,18 +851,16 @@ int dns_srv_type_verify(const char *name) { /* This more or less implements RFC 6335, Section 5.1 */ r = dns_label_unescape(&name, label, sizeof(label)); - if (r == -EINVAL) - return 0; if (r < 0) - return r; + return false; if (r == 0) break; if (c >= 2) - return 0; + return false; if (!srv_type_label_is_valid(label, r)) - return 0; + return false; c++; } @@ -901,7 +899,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha assert(domain); assert(ret); - if (!dns_srv_type_verify(type)) + if (!dns_srv_type_is_valid(type)) return -EINVAL; if (!name) diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 84be17425b..2f557d618e 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -72,8 +72,7 @@ bool dns_name_is_single_label(const char *name); int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); -int dns_srv_type_verify(const char *name); - +bool dns_srv_type_is_valid(const char *name); bool dns_service_name_is_valid(const char *name); int dns_service_join(const char *name, const char *type, const char *domain, char **ret); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index cefe8698c8..df34b72ab6 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -327,27 +327,27 @@ static void test_dns_service_name_is_valid(void) { assert_se(!dns_service_name_is_valid("this is an overly long string that is certainly longer than 63 characters")); } -static void test_dns_srv_type_verify(void) { - - assert_se(dns_srv_type_verify("_http._tcp") > 0); - assert_se(dns_srv_type_verify("_foo-bar._tcp") > 0); - assert_se(dns_srv_type_verify("_w._udp") > 0); - assert_se(dns_srv_type_verify("_a800._tcp") > 0); - assert_se(dns_srv_type_verify("_a-800._tcp") > 0); - - assert_se(dns_srv_type_verify(NULL) == 0); - assert_se(dns_srv_type_verify("") == 0); - assert_se(dns_srv_type_verify("x") == 0); - assert_se(dns_srv_type_verify("_foo") == 0); - assert_se(dns_srv_type_verify("_tcp") == 0); - assert_se(dns_srv_type_verify("_") == 0); - assert_se(dns_srv_type_verify("_foo.") == 0); - assert_se(dns_srv_type_verify("_föo._tcp") == 0); - assert_se(dns_srv_type_verify("_f\no._tcp") == 0); - assert_se(dns_srv_type_verify("_800._tcp") == 0); - assert_se(dns_srv_type_verify("_-800._tcp") == 0); - assert_se(dns_srv_type_verify("_-foo._tcp") == 0); - assert_se(dns_srv_type_verify("_piep._foo._udp") == 0); +static void test_dns_srv_type_is_valid(void) { + + assert_se(dns_srv_type_is_valid("_http._tcp")); + assert_se(dns_srv_type_is_valid("_foo-bar._tcp")); + assert_se(dns_srv_type_is_valid("_w._udp")); + assert_se(dns_srv_type_is_valid("_a800._tcp")); + assert_se(dns_srv_type_is_valid("_a-800._tcp")); + + assert_se(!dns_srv_type_is_valid(NULL)); + assert_se(!dns_srv_type_is_valid("")); + assert_se(!dns_srv_type_is_valid("x")); + assert_se(!dns_srv_type_is_valid("_foo")); + assert_se(!dns_srv_type_is_valid("_tcp")); + assert_se(!dns_srv_type_is_valid("_")); + assert_se(!dns_srv_type_is_valid("_foo.")); + assert_se(!dns_srv_type_is_valid("_föo._tcp")); + assert_se(!dns_srv_type_is_valid("_f\no._tcp")); + assert_se(!dns_srv_type_is_valid("_800._tcp")); + assert_se(!dns_srv_type_is_valid("_-800._tcp")); + assert_se(!dns_srv_type_is_valid("_-foo._tcp")); + assert_se(!dns_srv_type_is_valid("_piep._foo._udp")); } static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) { @@ -443,7 +443,7 @@ int main(int argc, char *argv[]) { test_dns_name_is_valid(); test_dns_name_to_wire_format(); test_dns_service_name_is_valid(); - test_dns_srv_type_verify(); + test_dns_srv_type_is_valid(); test_dns_service_join(); test_dns_service_split(); test_dns_name_change_suffix(); -- cgit v1.2.3-54-g00ecf From 422baca0f230913158078fddf884e06c8c64a316 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 25 Nov 2015 21:56:48 +0100 Subject: dns-domain: rework dns_label_escape() to not imply memory allocation The new dns_label_escape() call now operates on a buffer passed in, similar to dns_label_unescape(). This should make decoding a bit faster, and nicer. --- src/libsystemd-network/dhcp6-option.c | 18 ++++--- src/resolve/resolved-dns-packet.c | 18 ++++--- src/resolve/resolved-manager.c | 2 +- src/shared/dns-domain.c | 88 +++++++++++++++++++++++++---------- src/shared/dns-domain.h | 5 +- src/test/test-dns-domain.c | 2 +- 6 files changed, 84 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 62023a9e49..850212aea1 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -360,7 +360,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * /* End of name */ break; else if (c <= 63) { - _cleanup_free_ char *t = NULL; const char *label; /* Literal label */ @@ -369,21 +368,20 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (pos > optlen) return -EMSGSIZE; - r = dns_label_escape(label, c, &t); - if (r < 0) - goto fail; - - if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) { + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { r = -ENOMEM; goto fail; } - if (!first) - ret[n++] = '.'; - else + if (first) first = false; + else + ret[n++] = '.'; + + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + goto fail; - memcpy(ret + n, t, r); n += r; continue; } else { diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 472486777c..aeff3138d7 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -1145,7 +1145,6 @@ int dns_packet_read_name( /* End of name */ break; else if (c <= 63) { - _cleanup_free_ char *t = NULL; const char *label; /* Literal label */ @@ -1153,21 +1152,20 @@ int dns_packet_read_name( if (r < 0) goto fail; - r = dns_label_escape(label, c, &t); - if (r < 0) - goto fail; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) { + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { r = -ENOMEM; goto fail; } - if (!first) - ret[n++] = '.'; - else + if (first) first = false; + else + ret[n++] = '.'; + + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + goto fail; - memcpy(ret + n, t, r); n += r; continue; } else if (allow_compression && (c & 0xc0) == 0xc0) { diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 64703ab713..f1f454c786 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -351,7 +351,7 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { return -EINVAL; } - r = dns_label_escape(label, r, &n); + r = dns_label_escape_new(label, r, &n); if (r < 0) return log_error_errno(r, "Failed to escape host name: %m"); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 5a9b091ac4..4cf6355b71 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -182,30 +182,31 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha return r; } -int dns_label_escape(const char *p, size_t l, char **ret) { - _cleanup_free_ char *s = NULL; +int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { char *q; - int r; - - assert(p); - assert(ret); if (l > DNS_LABEL_MAX) return -EINVAL; + if (sz < 1) + return -ENOSPC; - s = malloc(l * 4 + 1); - if (!s) - return -ENOMEM; + assert(p); + assert(dest); - q = s; + q = dest; while (l > 0) { if (*p == '.' || *p == '\\') { + if (sz < 3) + return -ENOSPC; + /* Dot or backslash */ *(q++) = '\\'; *(q++) = *p; + sz -= 2; + } else if (*p == '_' || *p == '-' || (*p >= '0' && *p <= '9') || @@ -213,15 +214,27 @@ int dns_label_escape(const char *p, size_t l, char **ret) { (*p >= 'A' && *p <= 'Z')) { /* Proper character */ + + if (sz < 2) + return -ENOSPC; + *(q++) = *p; + sz -= 1; + } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) { /* Everything else */ + + if (sz < 5) + return -ENOSPC; + *(q++) = '\\'; *(q++) = '0' + (char) ((uint8_t) *p / 100); *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); *(q++) = '0' + (char) ((uint8_t) *p % 10); + sz -= 4; + } else return -EINVAL; @@ -230,8 +243,28 @@ int dns_label_escape(const char *p, size_t l, char **ret) { } *q = 0; + return (int) (q - dest); +} + +int dns_label_escape_new(const char *p, size_t l, char **ret) { + _cleanup_free_ char *s = NULL; + int r; + + assert(p); + assert(ret); + + if (l > DNS_LABEL_MAX) + return -EINVAL; + + s = new(char, DNS_LABEL_ESCAPED_MAX); + if (!s) + return -ENOMEM; + + r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + *ret = s; - r = q - s; s = NULL; return r; @@ -351,28 +384,32 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { if (k > 0) r = k; - r = dns_label_escape(label, r, &t); - if (r < 0) - return r; - if (_ret) { - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; + r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + if (!first) - ret[n++] = '.'; - else - first = false; + ret[n] = '.'; + } else { + char escaped[DNS_LABEL_ESCAPED_MAX]; - memcpy(ret + n, t, r); + r = dns_label_escape(label, r, escaped, sizeof(escaped)); + if (r < 0) + return r; } + if (!first) + n++; + else + first = false; + n += r; } - if (n > DNS_NAME_MAX) - return -EINVAL; - if (_ret) { if (!GREEDY_REALLOC(ret, allocated, n + 1)) return -ENOMEM; @@ -892,7 +929,8 @@ bool dns_service_name_is_valid(const char *name) { } int dns_service_join(const char *name, const char *type, const char *domain, char **ret) { - _cleanup_free_ char *escaped = NULL, *n = NULL; + char escaped[DNS_LABEL_ESCAPED_MAX]; + _cleanup_free_ char *n = NULL; int r; assert(type); @@ -908,7 +946,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha if (!dns_service_name_is_valid(name)) return -EINVAL; - r = dns_label_escape(name, strlen(name), &escaped); + r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped)); if (r < 0) return r; diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 2f557d618e..99c72574db 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -26,11 +26,12 @@ #include "in-addr-util.h" #define DNS_LABEL_MAX 63 -#define DNS_NAME_MAX 255 +#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) int dns_label_unescape(const char **name, char *dest, size_t sz); int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); -int dns_label_escape(const char *p, size_t l, char **ret); +int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); +int dns_label_escape_new(const char *p, size_t l, char **ret); int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index df34b72ab6..f010e4e19a 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -126,7 +126,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex _cleanup_free_ char *t = NULL; int r; - r = dns_label_escape(what, l, &t); + r = dns_label_escape_new(what, l, &t); assert_se(r == ret); if (r < 0) -- cgit v1.2.3-54-g00ecf