resolved-dns-server.c revision 519ef04651b07a547f010d6462603669d7fde4e5
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen This file is part of systemd.
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen Copyright 2014 Lennart Poettering
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen systemd is free software; you can redistribute it and/or modify it
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen under the terms of the GNU Lesser General Public License as published by
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen (at your option) any later version.
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen systemd is distributed in the hope that it will be useful, but
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen Lesser General Public License for more details.
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen You should have received a copy of the GNU Lesser General Public License
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen/* After how much time to repeat classic DNS requests */
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen/* The amount of time to wait before retrying with a full feature set */
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen#define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen#define DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC (5 * USEC_PER_MINUTE)
b95cc756de7a27f7546e42dd9acf6da0669da402Tom Gundersen/* The number of times we will attempt a certain feature set before degrading */
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen s->verified_features = _DNS_SERVER_FEATURE_LEVEL_INVALID;
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_APPEND(servers, m->fallback_dns_servers, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen /* A new DNS server that isn't fallback is added and the one
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen * we used so far was a fallback one? Then let's try to pick
b95cc756de7a27f7546e42dd9acf6da0669da402Tom Gundersen * the new one */
b95cc756de7a27f7546e42dd9acf6da0669da402Tom Gundersen m->current_dns_server->type == DNS_SERVER_FALLBACK)
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen /* This removes the specified server from the linked list of
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen * servers, but any server might still stay around if it has
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen * refs, for example from an ongoing transaction. */
b95cc756de7a27f7546e42dd9acf6da0669da402Tom Gundersen switch (s->type) {
b95cc756de7a27f7546e42dd9acf6da0669da402Tom Gundersen LIST_REMOVE(servers, s->link->dns_servers, s);
b95cc756de7a27f7546e42dd9acf6da0669da402Tom Gundersen LIST_REMOVE(servers, s->manager->dns_servers, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen if (s->link && s->link->current_dns_server == s)
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersenvoid dns_server_move_back_and_unmark(DnsServer *s) {
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen /* Move us to the end of the list, so that the order is
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen * strictly kept, if we are not at the end anyway. */
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen switch (s->type) {
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_REMOVE(servers, s->link->dns_servers, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_REMOVE(servers, s->manager->dns_servers, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersenvoid dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size) {
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen if (features == DNS_SERVER_FEATURE_LEVEL_LARGE) {
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen /* Even if we successfully receive a reply to a
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen request announcing support for large packets, that
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen does not mean we can necessarily receive large
9c5a882b7fc256ddc0b227677fa06546f0e944a8Tom Gundersen if (s->verified_features < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen s->verified_features = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen /* Remember the size of the largest UDP packet we received from a server,
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen we know that we can always announce support for packets with at least
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen this size. */
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenvoid dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenvoid dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenvoid dns_server_packet_rrsig_missing(DnsServer *s) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen in_addr_to_string(s->family, &s->address, &ip);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", strna(ip));
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenstatic bool dns_server_grace_period_expired(DnsServer *s) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen return false;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen if (s->verified_usec + s->features_grace_period_usec > ts)
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen return false;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC);
89489ef7d451d61e176764deb608a84e29b1fd38Tom GundersenDnsServerFeatureLevel dns_server_possible_features(DnsServer *s) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen if (s->possible_features != DNS_SERVER_FEATURE_LEVEL_BEST &&
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen in_addr_to_string(s->family, &s->address, &ip);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip));
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen } else if (s->possible_features <= s->verified_features)
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen s->possible_features > DNS_SERVER_FEATURE_LEVEL_WORST) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen in_addr_to_string(s->family, &s->address, &ip);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen log_warning("Using degraded feature set (%s) for DNS server %s",
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen dns_server_feature_level_to_string(s->possible_features), strna(ip));
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenint dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen /* Fix the OPT field in the packet to match our current feature level. */
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen packet_size = server->received_udp_packet_max;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen return dns_packet_append_opt(packet, packet_size, edns_do, NULL);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenstatic void dns_server_hash_func(const void *p, struct siphash *state) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen siphash24_compress(&s->family, sizeof(s->family), state);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenstatic int dns_server_compare_func(const void *a, const void *b) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen const DnsServer *x = a, *y = b;
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersenvoid dns_server_unlink_marked(DnsServer *first) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom GundersenDnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
89489ef7d451d61e176764deb608a84e29b1fd38Tom GundersenDnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom GundersenDnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen in_addr_to_string(s->family, &s->address, &ip);
89489ef7d451d61e176764deb608a84e29b1fd38Tom Gundersen log_info("Switching to system DNS server %s.", strna(ip));
Link *l;
assert(m);
/* Try to read updates resolv.conf */
if (!m->current_dns_server)
if (!m->current_dns_server) {
bool found = false;
Iterator i;
if (l->dns_servers) {
found = true;
if (!found)
return m->current_dns_server;
assert(m);
if (!m->current_dns_server)