resolved-dns-server.c revision 4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4
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"
24882e06c135584f16f31ba8a00fecde8b7f6fadLennart Poettering/* After how much time to repeat classic DNS requests */
24882e06c135584f16f31ba8a00fecde8b7f6fadLennart Poettering#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
24882e06c135584f16f31ba8a00fecde8b7f6fadLennart Poettering#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek/* The amount of time to wait before retrying with a full feature set */
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering#define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering#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 */
c6878637502b1717a110a9a7e8bba32a8583fcdfLennart Poettering#define DNS_SERVER_FEATURE_RETRY_ATTEMPTS 3
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)
2c5859afecee81e345fc9526b1083bf79990ffb8Daniel Mack s->verified_features = _DNS_SERVER_FEATURE_LEVEL_INVALID;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek 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, l->dns_servers, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek LIST_APPEND(servers, m->dns_servers, s);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew 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
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmek * we used so far was a fallback one? Then let's try to pick
670b110c3b59dfa335ac43065b2038400d1d04a9Zbigniew Jędrzejewski-Szmek * the new one */
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew 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-Szmekvoid dns_server_unlink(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);
348ced909724a1331b85d57aede80a102a00e428Zbigniew 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);
da2e288bbc4d8cebaa1d38a80f6eec8cde3e9cceLennart Poettering 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);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt 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)
43cf8388ea4ffed1801468d4b650d6e48eefce9eMichal Schmidt return false;
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 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));
ea69bd41c5923f4f278a09bb7d8cb1abcfa122e1Lennart Poettering } else if (s->possible_features <= s->verified_features)
ea69bd41c5923f4f278a09bb7d8cb1abcfa122e1Lennart Poettering s->possible_features = s->verified_features;
ea69bd41c5923f4f278a09bb7d8cb1abcfa122e1Lennart Poettering else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
ea69bd41c5923f4f278a09bb7d8cb1abcfa122e1Lennart Poettering 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));
ea69bd41c5923f4f278a09bb7d8cb1abcfa122e1Lennart Poetteringstatic void dns_server_hash_func(const void *p, struct siphash *state) {
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek siphash24_compress(&s->family, sizeof(s->family), state);
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew 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 = {
fc55baee9964a118afbddbf82b8e667a0ad80b99Zbigniew Jędrzejewski-Szmek .compare = dns_server_compare_func
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmekvoid dns_server_unlink_all(DnsServer *first) {
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidtvoid dns_server_unlink_marked(DnsServer *first) {
26687bf8a907009dedcff79346860ed41511405eOleksii Shevchukvoid dns_server_mark_all(DnsServer *first) {
ea69bd41c5923f4f278a09bb7d8cb1abcfa122e1Lennart Poettering dns_server_mark_all(first->servers_next);
ea69bd41c5923f4f278a09bb7d8cb1abcfa122e1Lennart PoetteringDnsServer *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)
dbd2a83fbf051fc51bdca3aa7536c78479488c5bLennart PoetteringDnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
da927ba997d68401563b927f92e6e40e021a8e5cMichal SchmidtDnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek in_addr_to_string(s->family, &s->address, &ip);
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek 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
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poettering manager_set_dns_server(m, m->fallback_dns_servers);
0c24bb2346b6b6232d67aacd5236b56ea4989de4Lennart Poetteringvoid 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",