resolved: rework how we determine which scope to send a query to

Fixes: #10830 #9825 #9472
This commit is contained in:
Lennart Poettering 2018-12-03 16:25:00 +01:00
parent 89307df394
commit a97a3b256c
3 changed files with 47 additions and 20 deletions

View file

@ -682,22 +682,15 @@ int dns_query_go(DnsQuery *q) {
continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
if (match < 0)
return match;
if (match == DNS_SCOPE_NO)
if (match < 0) {
log_debug("Couldn't check if '%s' matches against scope, ignoring.", name);
continue;
}
found = match;
if (match == DNS_SCOPE_YES) {
if (match > found) { /* Does this match better? If so, remember how well it matched, and the first one
* that matches this well */
found = match;
first = s;
break;
} else {
assert(match == DNS_SCOPE_MAYBE);
if (!first)
first = s;
}
}
@ -725,10 +718,12 @@ int dns_query_go(DnsQuery *q) {
continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
if (match < 0)
goto fail;
if (match < 0) {
log_debug("Couldn't check if '%s' matches agains scope, ignoring.", name);
continue;
}
if (match != found)
if (match < found)
continue;
r = dns_query_add_candidate(q, s);

View file

@ -459,9 +459,25 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add
return dns_scope_socket(s, SOCK_STREAM, family, address, server, port, ret_socket_address);
}
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
DnsScopeMatch dns_scope_good_domain(
DnsScope *s,
int ifindex,
uint64_t flags,
const char *domain) {
DnsSearchDomain *d;
/* This returns the following return values:
*
* DNS_SCOPE_NO This scope is not suitable for lookups of this domain, at all
* DNS_SCOPE_MAYBE This scope is suitable, but only if nothing else wants it
* DNS_SCOPE_YES_BASE+n This scope is suitable, and 'n' suffix labels match
*
* (The idea is that the caller will only use the scopes with the longest 'n' returned. If no scopes return
* DNS_SCOPE_YES_BASE+n, then it should use those which returned DNS_SCOPE_MAYBE. It should never use those
* which returned DNS_SCOPE_NO.)
*/
assert(s);
assert(domain);
@ -497,6 +513,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
case DNS_PROTOCOL_DNS: {
DnsServer *dns_server;
int n_best = -1;
/* Never route things to scopes that lack DNS servers */
dns_server = dns_scope_get_dns_server(s);
@ -507,8 +524,22 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
* we return DNS_SCOPE_YES here, rather than just DNS_SCOPE_MAYBE, which means other 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;
if (dns_name_endswith(domain, d->name) > 0) {
int c;
c = dns_name_count_labels(d->name);
if (c < 0)
continue;
if (c > n_best)
n_best = c;
}
/* Let's return the number of labels in the best matching result */
if (n_best >= 0) {
assert(n_best <= DNS_SCOPE_YES_END - DNS_SCOPE_YES_BASE);
return DNS_SCOPE_YES_BASE + n_best;
}
/* If the DNS server has route-only domains, don't send other requests to it. This would be a privacy
* violation, will most probably fail anyway, and adds unnecessary load. */

View file

@ -18,7 +18,8 @@ typedef struct DnsScope DnsScope;
typedef enum DnsScopeMatch {
DNS_SCOPE_NO,
DNS_SCOPE_MAYBE,
DNS_SCOPE_YES,
DNS_SCOPE_YES_BASE, /* Add the number of matching labels to this */
DNS_SCOPE_YES_END = DNS_SCOPE_YES_BASE + DNS_N_LABELS_MAX,
_DNS_SCOPE_MATCH_MAX,
_DNS_SCOPE_MATCH_INVALID = -1
} DnsScopeMatch;