resolved-dns-server.c revision 7586f4d172dd9c3ccc3126fc47dca9e49adec132
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek This file is part of systemd.
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek Copyright 2014 Lennart Poettering
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek (at your option) any later version.
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek#include "resolved-resolv-conf.h"
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek/* After how much time to repeat classic DNS requests */
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering/* The amount of time to wait before retrying with a full feature set */
a5c32cff1f56afe6f0c6c70d91a88a7a8238b2d7Harald Hoyer#define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek#define DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC (5 * USEC_PER_MINUTE)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek/* The number of times we will attempt a certain feature set before degrading */
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek#define DNS_SERVER_FEATURE_RETRY_ATTEMPTS 3
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek const union in_addr_union *in_addr) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek assert((type == DNS_SERVER_LINK) == !!l);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (!IN_SET(family, AF_INET, AF_INET6))
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->verified_features = _DNS_SERVER_FEATURE_LEVEL_INVALID;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
2c5859afecee81e345fc9526b1083bf79990ffb8Daniel Mack s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_APPEND(servers, m->dns_servers, s);
db91ea32aa223d1b087d99811226a9c59a1bb281Zbigniew Jędrzejewski-Szmek LIST_APPEND(servers, m->fallback_dns_servers, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek assert_not_reached("Unknown server type");
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek /* A new DNS server that isn't fallback is added and the one
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek * we used so far was a fallback one? Then let's try to pick
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek * the new one */
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmek if (type != DNS_SERVER_FALLBACK &&
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek m->current_dns_server->type == DNS_SERVER_FALLBACK)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek manager_set_dns_server(m, NULL);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-SzmekDnsServer* dns_server_ref(DnsServer *s) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-SzmekDnsServer* dns_server_unref(DnsServer *s) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek /* This removes the specified server from the linked list of
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek * servers, but any server might still stay around if it has
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek * refs, for example from an ongoing transaction. */
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek assert(s->link->n_dns_servers > 0);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->link->dns_servers, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek assert(s->manager->n_dns_servers > 0);
348ced909724a1331b85d57aede80a102a00e428Zbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->manager->dns_servers, s);
348ced909724a1331b85d57aede80a102a00e428Zbigniew Jędrzejewski-Szmek assert(s->manager->n_dns_servers > 0);
348ced909724a1331b85d57aede80a102a00e428Zbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmek if (s->link && s->link->current_dns_server == s)
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmek link_set_dns_server(s->link, NULL);
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmek if (s->manager->current_dns_server == s)
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmek manager_set_dns_server(s->manager, NULL);
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmekvoid dns_server_move_back_and_unmark(DnsServer *s) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (!s->linked || !s->servers_next)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek /* Move us to the end of the list, so that the order is
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek * strictly kept, if we are not at the end anyway. */
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_FIND_TAIL(servers, s, tail);
4608af4333d0f7f5f8e3bc632801b04ef07d246dLennart Poettering LIST_REMOVE(servers, s->link->dns_servers, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_FIND_TAIL(servers, s, tail);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->manager->dns_servers, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_FIND_TAIL(servers, s, tail);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek assert_not_reached("Unknown server type");
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekvoid dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt) {
23ad4dd8844c582929115a11ed2830a1371568d6Jan Alexander Steffens (heftig) if (s->verified_features < features) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->verified_features = features;
23ad4dd8844c582929115a11ed2830a1371568d6Jan Alexander Steffens (heftig) assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (s->possible_features == features)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekvoid dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (s->possible_features == features)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekvoid dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (s->possible_features != features)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->n_failed_attempts = (unsigned) -1;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekstatic bool dns_server_grace_period_expired(DnsServer *s) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (s->verified_usec + s->features_grace_period_usec > ts)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-SzmekDnsServerFeatureLevel dns_server_possible_features(DnsServer *s) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (s->possible_features != DNS_SERVER_FEATURE_LEVEL_BEST &&
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek dns_server_grace_period_expired(s)) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek in_addr_to_string(s->family, &s->address, &ip);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip));
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek } else if (s->possible_features <= s->verified_features)
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek s->possible_features = s->verified_features;
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek s->possible_features > DNS_SERVER_FEATURE_LEVEL_WORST) {
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek in_addr_to_string(s->family, &s->address, &ip);
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek log_warning("Using degraded feature set (%s) for DNS server %s",
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek dns_server_feature_level_to_string(s->possible_features), strna(ip));
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmekstatic void dns_server_hash_func(const void *p, struct siphash *state) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek siphash24_compress(&s->family, sizeof(s->family), state);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekstatic int dns_server_compare_func(const void *a, const void *b) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek const DnsServer *x = a, *y = b;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmekconst struct hash_ops dns_server_hash_ops = {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek .compare = dns_server_compare_func
26687bf8a907009dedcff79346860ed41511405eOleksii Shevchukvoid dns_server_unlink_all(DnsServer *first) {
26687bf8a907009dedcff79346860ed41511405eOleksii Shevchukvoid dns_server_unlink_marked(DnsServer *first) {
63c8666b824e8762ffb73647e1caee165dfbc868Zbigniew Jędrzejewski-Szmekvoid dns_server_mark_all(DnsServer *first) {
63c8666b824e8762ffb73647e1caee165dfbc868Zbigniew Jędrzejewski-Szmek dns_server_mark_all(first->servers_next);
63c8666b824e8762ffb73647e1caee165dfbc868Zbigniew Jędrzejewski-SzmekDnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
63c8666b824e8762ffb73647e1caee165dfbc868Zbigniew Jędrzejewski-Szmek if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-SzmekDnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-SzmekDnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering in_addr_to_string(s->family, &s->address, &ip);
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering log_info("Switching to system DNS server %s.", strna(ip));
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering m->current_dns_server = dns_server_ref(s);
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering dns_cache_flush(&m->unicast_scope->cache);
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart PoetteringDnsServer *manager_get_dns_server(Manager *m) {
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering /* Try to read updates resolv.conf */
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering /* If no DNS server was chose so far, pick the first one */
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering manager_set_dns_server(m, m->dns_servers);
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering /* No DNS servers configured, let's see if there are
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering * any on any links. If not, we use the fallback
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek manager_set_dns_server(m, m->fallback_dns_servers);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekvoid manager_next_dns_server(Manager *m) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek /* If there's currently no DNS server set, then the next
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek * manager_get_dns_server() will find one */
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek /* Change to the next one, but make sure to follow the linked
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek * list only if the server is still linked. */
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek manager_set_dns_server(m, m->current_dns_server->servers_next);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek /* If there was no next one, then start from the beginning of
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek manager_set_dns_server(m, m->fallback_dns_servers);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek manager_set_dns_server(m, m->dns_servers);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekstatic const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek [DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP",
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek [DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",