X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fsystemd.git;a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-server.c;h=d470a645245e44f62590a9f118435df88def3dd2;hp=e1d20258632f984ede8ebb3a884c4088fa39dc0d;hb=53e1b683907c2f12330f00feb9630150196f064d;hpb=1cdc94482302cca88e0a7282bbc161c1d77c381c diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index e1d20258632..d470a645245 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -1,5 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -19,17 +18,18 @@ along with systemd; If not, see . ***/ -#include +#include "sd-messages.h" #include "alloc-util.h" #include "resolved-dns-server.h" +#include "resolved-dns-stub.h" #include "resolved-resolv-conf.h" #include "siphash24.h" #include "string-table.h" #include "string-util.h" /* After how much time to repeat classic DNS requests */ -#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC) +#define DNS_TIMEOUT_MIN_USEC (750 * USEC_PER_MSEC) #define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC) /* The amount of time to wait before retrying with a full feature set */ @@ -45,7 +45,8 @@ int dns_server_new( DnsServerType type, Link *l, int family, - const union in_addr_union *in_addr) { + const union in_addr_union *in_addr, + int ifindex) { DnsServer *s; @@ -70,14 +71,12 @@ int dns_server_new( s->n_ref = 1; s->manager = m; - s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; - s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST; - s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC; - s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX; s->type = type; s->family = family; s->address = *in_addr; - s->resend_timeout = DNS_TIMEOUT_MIN_USEC; + s->ifindex = ifindex; + + dns_server_reset_features(s); switch (type) { @@ -122,7 +121,7 @@ DnsServer* dns_server_ref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref ++; + s->n_ref++; return s; } @@ -132,14 +131,13 @@ DnsServer* dns_server_unref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref --; + s->n_ref--; if (s->n_ref > 0) return NULL; free(s->server_string); - free(s); - return NULL; + return mfree(s); } void dns_server_unlink(DnsServer *s) { @@ -159,6 +157,7 @@ void dns_server_unlink(DnsServer *s) { assert(s->link); assert(s->link->n_dns_servers > 0); LIST_REMOVE(servers, s->link->dns_servers, s); + s->link->n_dns_servers--; break; case DNS_SERVER_SYSTEM: @@ -243,6 +242,26 @@ static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) { assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0); } +static void dns_server_reset_counters(DnsServer *s) { + assert(s); + + s->n_failed_udp = 0; + s->n_failed_tcp = 0; + s->packet_truncated = false; + s->verified_usec = 0; + + /* Note that we do not reset s->packet_bad_opt and s->packet_rrsig_missing here. We reset them only when the + * grace period ends, but not when lowering the possible feature level, as a lower level feature level should + * not make RRSIGs appear or OPT appear, but rather make them disappear. If the reappear anyway, then that's + * indication for a differently broken OPT/RRSIG implementation, and we really don't want to support that + * either. + * + * This is particularly important to deal with certain Belkin routers which break OPT for certain lookups (A), + * but pass traffic through for others (AAAA). If we detect the broken behaviour on one lookup we should not + * reenable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be + * incomplete. */ +} + void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) { assert(s); @@ -283,7 +302,10 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve if (s->max_rtt < rtt) { s->max_rtt = rtt; s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC); - } + } else if (s->resend_timeout > rtt) + /* If we received the packet faster than the resend_timeout, bias + * the resend_timeout back to the rtt. */ + s->resend_timeout = CLAMP((2 * s->resend_timeout + rtt) / 3, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC); } void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec) { @@ -292,9 +314,9 @@ void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel le if (s->possible_feature_level == level) { if (protocol == IPPROTO_UDP) - s->n_failed_udp ++; + s->n_failed_udp++; else if (protocol == IPPROTO_TCP) - s->n_failed_tcp ++; + s->n_failed_tcp++; } if (s->resend_timeout > usec) @@ -303,17 +325,6 @@ void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel le s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC); } -void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) { - assert(s); - - /* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. */ - - if (s->possible_feature_level != level) - return; - - s->packet_failed = true; -} - void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) { assert(s); @@ -351,6 +362,24 @@ void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level) { s->packet_bad_opt = true; } +void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level) { + assert(s); + + /* Invoked whenever we got a FORMERR, SERVFAIL or NOTIMP rcode from a server and downgrading the feature level + * for the transaction made it go away. In this case we immediately downgrade to the feature level that made + * things work. */ + + if (s->verified_feature_level > level) + s->verified_feature_level = level; + + if (s->possible_feature_level > level) { + s->possible_feature_level = level; + dns_server_reset_counters(s); + } + + log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s)); +} + static bool dns_server_grace_period_expired(DnsServer *s) { usec_t ts; @@ -370,34 +399,25 @@ static bool dns_server_grace_period_expired(DnsServer *s) { return true; } -static void dns_server_reset_counters(DnsServer *s) { +DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { + DnsServerFeatureLevel best; + assert(s); - s->n_failed_udp = 0; - s->n_failed_tcp = 0; - s->packet_failed = false; - s->packet_truncated = false; - s->verified_usec = 0; + /* Determine the best feature level we care about. If DNSSEC mode is off there's no point in using anything + * better than EDNS0, hence don't even try. */ + best = dns_server_get_dnssec_mode(s) == DNSSEC_NO ? + DNS_SERVER_FEATURE_LEVEL_EDNS0 : + DNS_SERVER_FEATURE_LEVEL_BEST; - /* Note that we do not reset s->packet_bad_opt and s->packet_rrsig_missing here. We reset them only when the - * grace period ends, but not when lowering the possible feature level, as a lower level feature level should - * not make RRSIGs appear or OPT appear, but rather make them disappear. If the reappear anyway, then that's - * indication for a differently broken OPT/RRSIG implementation, and we really don't want to support that - * either. - * - * This is particularly important to deal with certain Belkin routers which break OPT for certain lookups (A), - * but pass traffic through for others (AAAA). If we detect the broken behaviour on one lookup we should not - * reenable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be - * incomplete. */ -} + /* Clamp the feature level the highest level we care about. The DNSSEC mode might have changed since the last + * time, hence let's downgrade if we are still at a higher level. */ + if (s->possible_feature_level > best) + s->possible_feature_level = best; -DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { - assert(s); - - if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST && - dns_server_grace_period_expired(s)) { + if (s->possible_feature_level < best && dns_server_grace_period_expired(s)) { - s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST; + s->possible_feature_level = best; dns_server_reset_counters(s); @@ -408,6 +428,8 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { dns_server_feature_level_to_string(s->possible_feature_level), dns_server_string(s)); + dns_server_flush_cache(s); + } else if (s->possible_feature_level <= s->verified_feature_level) s->possible_feature_level = s->verified_feature_level; else { @@ -444,28 +466,22 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0; } else if (s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && - s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) { + s->possible_feature_level >= (dns_server_get_dnssec_mode(s) == DNSSEC_YES ? DNS_SERVER_FEATURE_LEVEL_LARGE : DNS_SERVER_FEATURE_LEVEL_UDP)) { /* We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If the * packets are lost, maybe the server cannot parse them, hence downgrading sounds like a good - * idea. We might downgrade all the way down to TCP this way. */ + * idea. We might downgrade all the way down to TCP this way. + * + * If strict DNSSEC mode is used we won't downgrade below DO level however, as packet loss + * might have many reasons, a broken DNSSEC implementation being only one reason. And if the + * user is strict on DNSSEC, then let's assume that DNSSEC is not the fault here. */ log_debug("Lost too many UDP packets, downgrading feature level..."); s->possible_feature_level--; - } else if (s->packet_failed && - s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) { - - /* We got a failure packet, and are at a feature level above UDP. Note that in this case we - * downgrade no further than UDP, under the assumption that a failure packet indicates an - * incompatible packet contents, but not a problem with the transport. */ - - log_debug("Got server failure, downgrading feature level..."); - s->possible_feature_level--; - } else if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && s->packet_truncated && - s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) { + s->possible_feature_level > (dns_server_get_dnssec_mode(s) == DNSSEC_YES ? DNS_SERVER_FEATURE_LEVEL_LARGE : DNS_SERVER_FEATURE_LEVEL_UDP)) { /* We got too many TCP connection failures in a row, we had at least one truncated packet, and * are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or EDNS0 @@ -516,14 +532,27 @@ int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeature else packet_size = server->received_udp_packet_max; - return dns_packet_append_opt(packet, packet_size, edns_do, NULL); + return dns_packet_append_opt(packet, packet_size, edns_do, 0, NULL); +} + +int dns_server_ifindex(const DnsServer *s) { + assert(s); + + /* The link ifindex always takes precedence */ + if (s->link) + return s->link->ifindex; + + if (s->ifindex > 0) + return s->ifindex; + + return 0; } const char *dns_server_string(DnsServer *server) { assert(server); if (!server->server_string) - (void) in_addr_to_string(server->family, &server->address, &server->server_string); + (void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &server->server_string); return strna(server->server_string); } @@ -556,7 +585,7 @@ void dns_server_warn_downgrade(DnsServer *server) { return; log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE), + "MESSAGE_ID=" SD_MESSAGE_DNSSEC_DOWNGRADE_STR, LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)), "DNS_SERVER=%s", dns_server_string(server), "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level), @@ -565,6 +594,26 @@ void dns_server_warn_downgrade(DnsServer *server) { server->warned_downgrade = true; } +bool dns_server_limited_domains(DnsServer *server) { + DnsSearchDomain *domain; + bool domain_restricted = false; + + /* Check if the server has route-only domains without ~., i. e. whether + * it should only be used for particular domains */ + if (!server->link) + return false; + + LIST_FOREACH(domains, domain, server->link->search_domains) + if (domain->route_only) { + domain_restricted = true; + /* ~. means "any domain", thus it is a global server */ + if (dns_name_is_root(DNS_SEARCH_DOMAIN_NAME(domain))) + return false; + } + + return domain_restricted; +} + static void dns_server_hash_func(const void *p, struct siphash *state) { const DnsServer *s = p; @@ -572,17 +621,28 @@ static void dns_server_hash_func(const void *p, struct siphash *state) { siphash24_compress(&s->family, sizeof(s->family), state); siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state); + siphash24_compress(&s->ifindex, sizeof(s->ifindex), state); } static int dns_server_compare_func(const void *a, const void *b) { const DnsServer *x = a, *y = b; + int r; if (x->family < y->family) return -1; if (x->family > y->family) return 1; - return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); + r = memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); + if (r != 0) + return r; + + if (x->ifindex < y->ifindex) + return -1; + if (x->ifindex > y->ifindex) + return 1; + + return 0; } const struct hash_ops dns_server_hash_ops = { @@ -624,11 +684,11 @@ void dns_server_mark_all(DnsServer *first) { dns_server_mark_all(first->servers_next); } -DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) { +DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex) { DnsServer *s; LIST_FOREACH(servers, s, first) - if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) + if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && s->ifindex == ifindex) return s; return NULL; @@ -657,7 +717,9 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { return s; if (s) - log_info("Switching to system DNS server %s.", dns_server_string(s)); + log_debug("Switching to %s DNS server %s.", + dns_server_type_to_string(s->type), + dns_server_string(s)); dns_server_unref(m->current_dns_server); m->current_dns_server = dns_server_ref(s); @@ -675,7 +737,7 @@ DnsServer *manager_get_dns_server(Manager *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 no DNS server was chosen so far, pick the first one */ if (!m->current_dns_server) manager_set_dns_server(m, m->dns_servers); @@ -723,6 +785,133 @@ void manager_next_dns_server(Manager *m) { manager_set_dns_server(m, m->dns_servers); } +bool dns_server_address_valid(int family, const union in_addr_union *sa) { + + /* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */ + + if (in_addr_is_null(family, sa)) + return false; + + if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB)) + return false; + + return true; +} + +DnssecMode dns_server_get_dnssec_mode(DnsServer *s) { + assert(s); + + if (s->link) + return link_get_dnssec_mode(s->link); + + return manager_get_dnssec_mode(s->manager); +} + +void dns_server_flush_cache(DnsServer *s) { + DnsServer *current; + DnsScope *scope; + + assert(s); + + /* Flush the cache of the scope this server belongs to */ + + current = s->link ? s->link->current_dns_server : s->manager->current_dns_server; + if (current != s) + return; + + scope = s->link ? s->link->unicast_scope : s->manager->unicast_scope; + if (!scope) + return; + + dns_cache_flush(&scope->cache); +} + +void dns_server_reset_features(DnsServer *s) { + assert(s); + + s->max_rtt = 0; + s->resend_timeout = DNS_TIMEOUT_MIN_USEC; + + s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; + s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST; + + s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX; + + s->packet_bad_opt = false; + s->packet_rrsig_missing = false; + + s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC; + + s->warned_downgrade = false; + + dns_server_reset_counters(s); +} + +void dns_server_reset_features_all(DnsServer *s) { + DnsServer *i; + + LIST_FOREACH(servers, i, s) + dns_server_reset_features(i); +} + +void dns_server_dump(DnsServer *s, FILE *f) { + assert(s); + + if (!f) + f = stdout; + + fputs("[Server ", f); + fputs(dns_server_string(s), f); + fputs(" type=", f); + fputs(dns_server_type_to_string(s->type), f); + + if (s->type == DNS_SERVER_LINK) { + assert(s->link); + + fputs(" interface=", f); + fputs(s->link->name, f); + } + + fputs("]\n", f); + + fputs("\tVerified feature level: ", f); + fputs(strna(dns_server_feature_level_to_string(s->verified_feature_level)), f); + fputc('\n', f); + + fputs("\tPossible feature level: ", f); + fputs(strna(dns_server_feature_level_to_string(s->possible_feature_level)), f); + fputc('\n', f); + + fputs("\tDNSSEC Mode: ", f); + fputs(strna(dnssec_mode_to_string(dns_server_get_dnssec_mode(s))), f); + fputc('\n', f); + + fputs("\tCan do DNSSEC: ", f); + fputs(yes_no(dns_server_dnssec_supported(s)), f); + fputc('\n', f); + + fprintf(f, + "\tMaximum UDP packet size received: %zu\n" + "\tFailed UDP attempts: %u\n" + "\tFailed TCP attempts: %u\n" + "\tSeen truncated packet: %s\n" + "\tSeen OPT RR getting lost: %s\n" + "\tSeen RRSIG RR missing: %s\n", + s->received_udp_packet_max, + s->n_failed_udp, + s->n_failed_tcp, + yes_no(s->packet_truncated), + yes_no(s->packet_bad_opt), + yes_no(s->packet_rrsig_missing)); +} + +static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = { + [DNS_SERVER_SYSTEM] = "system", + [DNS_SERVER_FALLBACK] = "fallback", + [DNS_SERVER_LINK] = "link", +}; +DEFINE_STRING_TABLE_LOOKUP(dns_server_type, DnsServerType); + static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = { [DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP", [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",