resolved-dns-server.c revision 6bb2c08597c999c429e889cd2403b2fef5f3e1a0
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier/***
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier This file is part of systemd.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier Copyright 2014 Lennart Poettering
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier systemd is free software; you can redistribute it and/or modify it
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier under the terms of the GNU Lesser General Public License as published by
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier the Free Software Foundation; either version 2.1 of the License, or
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier (at your option) any later version.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier systemd is distributed in the hope that it will be useful, but
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier WITHOUT ANY WARRANTY; without even the implied warranty of
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier Lesser General Public License for more details.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier You should have received a copy of the GNU Lesser General Public License
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier***/
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#include "alloc-util.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include "resolved-dns-server.h"
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#include "resolved-resolv-conf.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "siphash24.h"
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#include "string-table.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "string-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering/* After how much time to repeat classic DNS requests */
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier/* The amount of time to wait before retrying with a full feature set */
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#define DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC (5 * USEC_PER_MINUTE)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier/* The number of times we will attempt a certain feature set before degrading */
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#define DNS_SERVER_FEATURE_RETRY_ATTEMPTS 3
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalierint dns_server_new(
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier Manager *m,
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier DnsServer **ret,
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier DnsServerType type,
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier Link *l,
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier int family,
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier const union in_addr_union *in_addr) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
4c1fc3e404d648c70bd2f50ac50aeac6ece8872eDaniel Mack DnsServer *s;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
f2068bcce01db31cdc9422f44185f3b49c04d2ceLennart Poettering assert(m);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert((type == DNS_SERVER_LINK) == !!l);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(in_addr);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering if (!IN_SET(family, AF_INET, AF_INET6))
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return -EAFNOSUPPORT;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (l) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return -E2BIG;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering } else {
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering return -E2BIG;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering }
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s = new0(DnsServer, 1);
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering if (!s)
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering return -ENOMEM;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->n_ref = 1;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->manager = m;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
4c1fc3e404d648c70bd2f50ac50aeac6ece8872eDaniel Mack s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
7430ec6ac08f2c0416d9f806964c46b30f3862b2Lennart Poettering s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
7430ec6ac08f2c0416d9f806964c46b30f3862b2Lennart Poettering s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->type = type;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->family = family;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->address = *in_addr;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering switch (type) {
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering case DNS_SERVER_LINK:
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering s->link = l;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier LIST_APPEND(servers, l->dns_servers, s);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l->n_dns_servers++;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier break;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier case DNS_SERVER_SYSTEM:
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier LIST_APPEND(servers, m->dns_servers, s);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier m->n_dns_servers++;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier break;
c6878637502b1717a110a9a7e8bba32a8583fcdfLennart Poettering
c6878637502b1717a110a9a7e8bba32a8583fcdfLennart Poettering case DNS_SERVER_FALLBACK:
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier LIST_APPEND(servers, m->fallback_dns_servers, s);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier m->n_dns_servers++;
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering break;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier default:
4c1fc3e404d648c70bd2f50ac50aeac6ece8872eDaniel Mack assert_not_reached("Unknown server type");
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier }
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier s->linked = true;
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering /* A new DNS server that isn't fallback is added and the one
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier * we used so far was a fallback one? Then let's try to pick
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier * the new one */
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (type != DNS_SERVER_FALLBACK &&
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier m->current_dns_server &&
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier m->current_dns_server->type == DNS_SERVER_FALLBACK)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier manager_set_dns_server(m, NULL);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (ret)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier *ret = s;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering return 0;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier}
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny ChevalierDnsServer* dns_server_ref(DnsServer *s) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (!s)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return NULL;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(s->n_ref > 0);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier s->n_ref ++;
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering return s;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier}
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny ChevalierDnsServer* dns_server_unref(DnsServer *s) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (!s)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return NULL;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(s->n_ref > 0);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier s->n_ref --;
c6878637502b1717a110a9a7e8bba32a8583fcdfLennart Poettering
c6878637502b1717a110a9a7e8bba32a8583fcdfLennart Poettering if (s->n_ref > 0)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return NULL;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek free(s);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek return NULL;
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek}
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmekvoid dns_server_unlink(DnsServer *s) {
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek assert(s);
4f36d4004c407c16508001a20450c5f14f7d4d31Dimitri John Ledkov assert(s->manager);
4f36d4004c407c16508001a20450c5f14f7d4d31Dimitri John Ledkov
4f36d4004c407c16508001a20450c5f14f7d4d31Dimitri John Ledkov /* This removes the specified server from the linked list of
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek * servers, but any server might still stay around if it has
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek * refs, for example from an ongoing transaction. */
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek if (!s->linked)
59f448cf15f94bc5ebfd5b254de6f2441d02fbecLennart Poettering return;
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek switch (s->type) {
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek case DNS_SERVER_LINK:
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek assert(s->link);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek assert(s->link->n_dns_servers > 0);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->link->dns_servers, s);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek break;
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek case DNS_SERVER_SYSTEM:
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek assert(s->manager->n_dns_servers > 0);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->manager->dns_servers, s);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek s->manager->n_dns_servers--;
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek break;
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek case DNS_SERVER_FALLBACK:
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek assert(s->manager->n_dns_servers > 0);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek s->manager->n_dns_servers--;
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek break;
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek }
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier s->linked = false;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering if (s->link && s->link->current_dns_server == s)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier link_set_dns_server(s->link, NULL);
ad5ecc113821fbfa33f6fd43cdaee9c538cdff78Zbigniew Jędrzejewski-Szmek
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (s->manager->current_dns_server == s)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier manager_set_dns_server(s->manager, NULL);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
dns_server_unref(s);
}
void dns_server_move_back_and_unmark(DnsServer *s) {
DnsServer *tail;
assert(s);
if (!s->marked)
return;
s->marked = false;
if (!s->linked || !s->servers_next)
return;
/* Move us to the end of the list, so that the order is
* strictly kept, if we are not at the end anyway. */
switch (s->type) {
case DNS_SERVER_LINK:
assert(s->link);
LIST_FIND_TAIL(servers, s, tail);
LIST_REMOVE(servers, s->link->dns_servers, s);
LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
break;
case DNS_SERVER_SYSTEM:
LIST_FIND_TAIL(servers, s, tail);
LIST_REMOVE(servers, s->manager->dns_servers, s);
LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
break;
case DNS_SERVER_FALLBACK:
LIST_FIND_TAIL(servers, s, tail);
LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
break;
default:
assert_not_reached("Unknown server type");
}
}
static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
if (s->verified_feature_level > level)
return;
if (s->verified_feature_level != level) {
log_debug("Verified feature level %s.", dns_server_feature_level_to_string(level));
s->verified_feature_level = level;
}
assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
}
void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
assert(s);
if (protocol == IPPROTO_UDP) {
if (s->possible_feature_level == level)
s->n_failed_udp = 0;
if (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
/* Even if we successfully receive a reply to a request announcing support for large packets,
that does not mean we can necessarily receive large packets. */
dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_LARGE - 1);
else
/* A successful UDP reply, verifies UDP, ENDS0 and DO levels */
dns_server_verified(s, level);
} else if (protocol == IPPROTO_TCP) {
if (s->possible_feature_level == level)
s->n_failed_tcp = 0;
/* Successful TCP connections are only useful to verify the TCP feature level. */
dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_TCP);
}
/* Remember the size of the largest UDP packet we received from a server,
we know that we can always announce support for packets with at least
this size. */
if (protocol == IPPROTO_UDP && s->received_udp_packet_max < size)
s->received_udp_packet_max = size;
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);
}
}
void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec) {
assert(s);
assert(s->manager);
if (s->possible_feature_level == level) {
if (protocol == IPPROTO_UDP)
s->n_failed_udp ++;
else if (protocol == IPPROTO_TCP)
s->n_failed_tcp ++;
}
if (s->resend_timeout > usec)
return;
s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
}
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
assert(s->manager);
/* 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);
assert(s->manager);
/* Invoked whenever we get a packet with TC bit set. */
if (s->possible_feature_level != level)
return;
s->packet_truncated = true;
}
void dns_server_packet_rrsig_missing(DnsServer *s) {
_cleanup_free_ char *ip = NULL;
assert(s);
assert(s->manager);
in_addr_to_string(s->family, &s->address, &ip);
log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", strna(ip));
s->rrsig_missing = true;
}
static bool dns_server_grace_period_expired(DnsServer *s) {
usec_t ts;
assert(s);
assert(s->manager);
if (s->verified_usec == 0)
return false;
assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
if (s->verified_usec + s->features_grace_period_usec > ts)
return false;
s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC);
return true;
}
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)) {
_cleanup_free_ char *ip = NULL;
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
s->n_failed_udp = 0;
s->n_failed_tcp = 0;
s->packet_failed = false;
s->packet_truncated = false;
s->verified_usec = 0;
s->rrsig_missing = false;
in_addr_to_string(s->family, &s->address, &ip);
log_info("Grace period over, resuming full feature set (%s) for DNS server %s",
dns_server_feature_level_to_string(s->possible_feature_level), strna(ip));
} else if (s->possible_feature_level <= s->verified_feature_level)
s->possible_feature_level = s->verified_feature_level;
else {
DnsServerFeatureLevel p = s->possible_feature_level;
if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP)
/* We are at the TCP (lowest) level, and we tried a couple of TCP connections, and it didn't
* work. Upgrade back to UDP again. */
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
else if ((s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) ||
(s->packet_failed &&
s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) ||
(s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
s->packet_truncated &&
s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP))
/* Downgrade the feature one level, maybe things will work better then. We do this under any of
* three conditions:
*
* 1. 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.
*
* 2. 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.
*
* 3. 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 data we hope to make the packet smaller, so that it still works via UDP given that
* TCP appears not to be a fallback. Note that if we are already at the lowest UDP level, we
* don't go further down, since that's TCP, and TCP failed too often after all.
*/
s->possible_feature_level--;
if (p != s->possible_feature_level) {
_cleanup_free_ char *ip = NULL;
/* We changed the feature level, reset the counting */
s->n_failed_udp = 0;
s->n_failed_tcp = 0;
s->packet_failed = false;
s->packet_truncated = false;
s->verified_usec = 0;
in_addr_to_string(s->family, &s->address, &ip);
log_warning("Using degraded feature set (%s) for DNS server %s",
dns_server_feature_level_to_string(s->possible_feature_level), strna(ip));
}
}
return s->possible_feature_level;
}
int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
size_t packet_size;
bool edns_do;
int r;
assert(server);
assert(packet);
assert(packet->protocol == DNS_PROTOCOL_DNS);
/* Fix the OPT field in the packet to match our current feature level. */
r = dns_packet_truncate_opt(packet);
if (r < 0)
return r;
if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
return 0;
edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
if (level >= DNS_SERVER_FEATURE_LEVEL_LARGE)
packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
else
packet_size = server->received_udp_packet_max;
return dns_packet_append_opt(packet, packet_size, edns_do, NULL);
}
static void dns_server_hash_func(const void *p, struct siphash *state) {
const DnsServer *s = p;
assert(s);
siphash24_compress(&s->family, sizeof(s->family), state);
siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
}
static int dns_server_compare_func(const void *a, const void *b) {
const DnsServer *x = a, *y = b;
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));
}
const struct hash_ops dns_server_hash_ops = {
.hash = dns_server_hash_func,
.compare = dns_server_compare_func
};
void dns_server_unlink_all(DnsServer *first) {
DnsServer *next;
if (!first)
return;
next = first->servers_next;
dns_server_unlink(first);
dns_server_unlink_all(next);
}
void dns_server_unlink_marked(DnsServer *first) {
DnsServer *next;
if (!first)
return;
next = first->servers_next;
if (first->marked)
dns_server_unlink(first);
dns_server_unlink_marked(next);
}
void dns_server_mark_all(DnsServer *first) {
if (!first)
return;
first->marked = true;
dns_server_mark_all(first->servers_next);
}
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
DnsServer *s;
LIST_FOREACH(servers, s, first)
if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
return s;
return NULL;
}
DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
assert(m);
switch (t) {
case DNS_SERVER_SYSTEM:
return m->dns_servers;
case DNS_SERVER_FALLBACK:
return m->fallback_dns_servers;
default:
return NULL;
}
}
DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
assert(m);
if (m->current_dns_server == s)
return s;
if (s) {
_cleanup_free_ char *ip = NULL;
in_addr_to_string(s->family, &s->address, &ip);
log_info("Switching to system DNS server %s.", strna(ip));
}
dns_server_unref(m->current_dns_server);
m->current_dns_server = dns_server_ref(s);
if (m->unicast_scope)
dns_cache_flush(&m->unicast_scope->cache);
return s;
}
DnsServer *manager_get_dns_server(Manager *m) {
Link *l;
assert(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 (!m->current_dns_server)
manager_set_dns_server(m, m->dns_servers);
if (!m->current_dns_server) {
bool found = false;
Iterator i;
/* No DNS servers configured, let's see if there are
* any on any links. If not, we use the fallback
* servers */
HASHMAP_FOREACH(l, m->links, i)
if (l->dns_servers) {
found = true;
break;
}
if (!found)
manager_set_dns_server(m, m->fallback_dns_servers);
}
return m->current_dns_server;
}
void manager_next_dns_server(Manager *m) {
assert(m);
/* If there's currently no DNS server set, then the next
* manager_get_dns_server() will find one */
if (!m->current_dns_server)
return;
/* Change to the next one, but make sure to follow the linked
* list only if the server is still linked. */
if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
manager_set_dns_server(m, m->current_dns_server->servers_next);
return;
}
/* If there was no next one, then start from the beginning of
* the list */
if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
manager_set_dns_server(m, m->fallback_dns_servers);
else
manager_set_dns_server(m, m->dns_servers);
}
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",
[DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
[DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
[DNS_SERVER_FEATURE_LEVEL_LARGE] = "UDP+EDNS0+DO+LARGE",
};
DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);