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.
This commit is contained in:
Lennart Poettering 2015-11-24 21:12:51 +01:00
parent 0b58db658b
commit a51c10485a
15 changed files with 488 additions and 31 deletions

View File

@ -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 \

View File

@ -1,4 +1,4 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@ -77,7 +77,7 @@
sent to one of the listed DNS servers in parallel to any
per-interface DNS servers acquired from
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
For compatibility reasons, if this setting is not specified, ,
For compatibility reasons, if this setting is not specified,
the DNS servers listed in
<filename>/etc/resolv.conf</filename> are used instead, if
that file exists and any servers are configured in it. This
@ -98,6 +98,16 @@
instead.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Domains=</varname></term>
<listitem><para>A space-separated list of search domains. For
compatibility reasons, if this setting is not specified, the
search domains listed in <filename>/etc/resolv.conf</filename>
are used instead, if that file exists and any domains are
configured in it. This setting defaults to the empty
list.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>LLMNR=</varname></term>
<listitem><para>Takes a boolean argument or

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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 <http://www.gnu.org/licenses/>.
***/
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
***/
#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);

View File

@ -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)

View File

@ -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;
assert(l);
l->unicast_scope->domains = strv_free(l->unicast_scope->domains);
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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -14,4 +14,5 @@
[Resolve]
#DNS=
#FallbackDNS=@DNS_SERVERS@
#Domains=
#LLMNR=yes