1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "resolved-dns-server.h"
24 #include "resolved-resolv-conf.h"
25 #include "siphash24.h"
26 #include "string-table.h"
27 #include "string-util.h"
29 /* After how much time to repeat classic DNS requests */
30 #define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
31 #define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
33 /* The amount of time to wait before retrying with a full feature set */
34 #define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
35 #define DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC (5 * USEC_PER_MINUTE)
37 /* The number of times we will attempt a certain feature set before degrading */
38 #define DNS_SERVER_FEATURE_RETRY_ATTEMPTS 3
46 const union in_addr_union
*in_addr
) {
51 assert((type
== DNS_SERVER_LINK
) == !!l
);
54 if (!IN_SET(family
, AF_INET
, AF_INET6
))
58 if (l
->n_dns_servers
>= LINK_DNS_SERVERS_MAX
)
61 if (m
->n_dns_servers
>= MANAGER_DNS_SERVERS_MAX
)
65 s
= new0(DnsServer
, 1);
71 s
->verified_features
= _DNS_SERVER_FEATURE_LEVEL_INVALID
;
72 s
->possible_features
= DNS_SERVER_FEATURE_LEVEL_BEST
;
73 s
->features_grace_period_usec
= DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC
;
76 s
->address
= *in_addr
;
77 s
->resend_timeout
= DNS_TIMEOUT_MIN_USEC
;
83 LIST_APPEND(servers
, l
->dns_servers
, s
);
87 case DNS_SERVER_SYSTEM
:
88 LIST_APPEND(servers
, m
->dns_servers
, s
);
92 case DNS_SERVER_FALLBACK
:
93 LIST_APPEND(servers
, m
->fallback_dns_servers
, s
);
98 assert_not_reached("Unknown server type");
103 /* A new DNS server that isn't fallback is added and the one
104 * we used so far was a fallback one? Then let's try to pick
106 if (type
!= DNS_SERVER_FALLBACK
&&
107 m
->current_dns_server
&&
108 m
->current_dns_server
->type
== DNS_SERVER_FALLBACK
)
109 manager_set_dns_server(m
, NULL
);
117 DnsServer
* dns_server_ref(DnsServer
*s
) {
121 assert(s
->n_ref
> 0);
127 DnsServer
* dns_server_unref(DnsServer
*s
) {
131 assert(s
->n_ref
> 0);
141 void dns_server_unlink(DnsServer
*s
) {
145 /* This removes the specified server from the linked list of
146 * servers, but any server might still stay around if it has
147 * refs, for example from an ongoing transaction. */
154 case DNS_SERVER_LINK
:
156 assert(s
->link
->n_dns_servers
> 0);
157 LIST_REMOVE(servers
, s
->link
->dns_servers
, s
);
160 case DNS_SERVER_SYSTEM
:
161 assert(s
->manager
->n_dns_servers
> 0);
162 LIST_REMOVE(servers
, s
->manager
->dns_servers
, s
);
163 s
->manager
->n_dns_servers
--;
166 case DNS_SERVER_FALLBACK
:
167 assert(s
->manager
->n_dns_servers
> 0);
168 LIST_REMOVE(servers
, s
->manager
->fallback_dns_servers
, s
);
169 s
->manager
->n_dns_servers
--;
175 if (s
->link
&& s
->link
->current_dns_server
== s
)
176 link_set_dns_server(s
->link
, NULL
);
178 if (s
->manager
->current_dns_server
== s
)
179 manager_set_dns_server(s
->manager
, NULL
);
184 void dns_server_move_back_and_unmark(DnsServer
*s
) {
194 if (!s
->linked
|| !s
->servers_next
)
197 /* Move us to the end of the list, so that the order is
198 * strictly kept, if we are not at the end anyway. */
202 case DNS_SERVER_LINK
:
204 LIST_FIND_TAIL(servers
, s
, tail
);
205 LIST_REMOVE(servers
, s
->link
->dns_servers
, s
);
206 LIST_INSERT_AFTER(servers
, s
->link
->dns_servers
, tail
, s
);
209 case DNS_SERVER_SYSTEM
:
210 LIST_FIND_TAIL(servers
, s
, tail
);
211 LIST_REMOVE(servers
, s
->manager
->dns_servers
, s
);
212 LIST_INSERT_AFTER(servers
, s
->manager
->dns_servers
, tail
, s
);
215 case DNS_SERVER_FALLBACK
:
216 LIST_FIND_TAIL(servers
, s
, tail
);
217 LIST_REMOVE(servers
, s
->manager
->fallback_dns_servers
, s
);
218 LIST_INSERT_AFTER(servers
, s
->manager
->fallback_dns_servers
, tail
, s
);
222 assert_not_reached("Unknown server type");
226 void dns_server_packet_received(DnsServer
*s
, DnsServerFeatureLevel features
, usec_t rtt
) {
229 if (s
->verified_features
< features
) {
230 s
->verified_features
= features
;
231 assert_se(sd_event_now(s
->manager
->event
, clock_boottime_or_monotonic(), &s
->verified_usec
) >= 0);
234 if (s
->possible_features
== features
)
235 s
->n_failed_attempts
= 0;
237 if (s
->max_rtt
< rtt
) {
239 s
->resend_timeout
= MIN(MAX(DNS_TIMEOUT_MIN_USEC
, s
->max_rtt
* 2), DNS_TIMEOUT_MAX_USEC
);
243 void dns_server_packet_lost(DnsServer
*s
, DnsServerFeatureLevel features
, usec_t usec
) {
247 if (s
->possible_features
== features
)
248 s
->n_failed_attempts
++;
250 if (s
->resend_timeout
> usec
)
253 s
->resend_timeout
= MIN(s
->resend_timeout
* 2, DNS_TIMEOUT_MAX_USEC
);
256 static bool dns_server_grace_period_expired(DnsServer
*s
) {
262 if (s
->verified_usec
== 0)
265 assert_se(sd_event_now(s
->manager
->event
, clock_boottime_or_monotonic(), &ts
) >= 0);
267 if (s
->verified_usec
+ s
->features_grace_period_usec
> ts
)
270 s
->features_grace_period_usec
= MIN(s
->features_grace_period_usec
* 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC
);
275 DnsServerFeatureLevel
dns_server_possible_features(DnsServer
*s
) {
278 if (s
->possible_features
!= DNS_SERVER_FEATURE_LEVEL_BEST
&&
279 dns_server_grace_period_expired(s
)) {
280 _cleanup_free_
char *ip
= NULL
;
282 s
->possible_features
= DNS_SERVER_FEATURE_LEVEL_BEST
;
283 s
->n_failed_attempts
= 0;
284 s
->verified_usec
= 0;
286 in_addr_to_string(s
->family
, &s
->address
, &ip
);
287 log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip
));
288 } else if (s
->possible_features
<= s
->verified_features
)
289 s
->possible_features
= s
->verified_features
;
290 else if (s
->n_failed_attempts
>= DNS_SERVER_FEATURE_RETRY_ATTEMPTS
&&
291 s
->possible_features
> DNS_SERVER_FEATURE_LEVEL_WORST
) {
292 _cleanup_free_
char *ip
= NULL
;
294 s
->possible_features
--;
295 s
->n_failed_attempts
= 0;
296 s
->verified_usec
= 0;
298 in_addr_to_string(s
->family
, &s
->address
, &ip
);
299 log_warning("Using degraded feature set (%s) for DNS server %s",
300 dns_server_feature_level_to_string(s
->possible_features
), strna(ip
));
303 return s
->possible_features
;
306 static void dns_server_hash_func(const void *p
, struct siphash
*state
) {
307 const DnsServer
*s
= p
;
311 siphash24_compress(&s
->family
, sizeof(s
->family
), state
);
312 siphash24_compress(&s
->address
, FAMILY_ADDRESS_SIZE(s
->family
), state
);
315 static int dns_server_compare_func(const void *a
, const void *b
) {
316 const DnsServer
*x
= a
, *y
= b
;
318 if (x
->family
< y
->family
)
320 if (x
->family
> y
->family
)
323 return memcmp(&x
->address
, &y
->address
, FAMILY_ADDRESS_SIZE(x
->family
));
326 const struct hash_ops dns_server_hash_ops
= {
327 .hash
= dns_server_hash_func
,
328 .compare
= dns_server_compare_func
331 void dns_server_unlink_all(DnsServer
*first
) {
337 next
= first
->servers_next
;
338 dns_server_unlink(first
);
340 dns_server_unlink_all(next
);
343 void dns_server_unlink_marked(DnsServer
*first
) {
349 next
= first
->servers_next
;
352 dns_server_unlink(first
);
354 dns_server_unlink_marked(next
);
357 void dns_server_mark_all(DnsServer
*first
) {
361 first
->marked
= true;
362 dns_server_mark_all(first
->servers_next
);
365 DnsServer
*dns_server_find(DnsServer
*first
, int family
, const union in_addr_union
*in_addr
) {
368 LIST_FOREACH(servers
, s
, first
)
369 if (s
->family
== family
&& in_addr_equal(family
, &s
->address
, in_addr
) > 0)
375 DnsServer
*manager_get_first_dns_server(Manager
*m
, DnsServerType t
) {
380 case DNS_SERVER_SYSTEM
:
381 return m
->dns_servers
;
383 case DNS_SERVER_FALLBACK
:
384 return m
->fallback_dns_servers
;
391 DnsServer
*manager_set_dns_server(Manager
*m
, DnsServer
*s
) {
394 if (m
->current_dns_server
== s
)
398 _cleanup_free_
char *ip
= NULL
;
400 in_addr_to_string(s
->family
, &s
->address
, &ip
);
401 log_info("Switching to system DNS server %s.", strna(ip
));
404 dns_server_unref(m
->current_dns_server
);
405 m
->current_dns_server
= dns_server_ref(s
);
407 if (m
->unicast_scope
)
408 dns_cache_flush(&m
->unicast_scope
->cache
);
413 DnsServer
*manager_get_dns_server(Manager
*m
) {
417 /* Try to read updates resolv.conf */
418 manager_read_resolv_conf(m
);
420 /* If no DNS server was chose so far, pick the first one */
421 if (!m
->current_dns_server
)
422 manager_set_dns_server(m
, m
->dns_servers
);
424 if (!m
->current_dns_server
) {
428 /* No DNS servers configured, let's see if there are
429 * any on any links. If not, we use the fallback
432 HASHMAP_FOREACH(l
, m
->links
, i
)
433 if (l
->dns_servers
) {
439 manager_set_dns_server(m
, m
->fallback_dns_servers
);
442 return m
->current_dns_server
;
445 void manager_next_dns_server(Manager
*m
) {
448 /* If there's currently no DNS server set, then the next
449 * manager_get_dns_server() will find one */
450 if (!m
->current_dns_server
)
453 /* Change to the next one, but make sure to follow the linked
454 * list only if the server is still linked. */
455 if (m
->current_dns_server
->linked
&& m
->current_dns_server
->servers_next
) {
456 manager_set_dns_server(m
, m
->current_dns_server
->servers_next
);
460 /* If there was no next one, then start from the beginning of
462 if (m
->current_dns_server
->type
== DNS_SERVER_FALLBACK
)
463 manager_set_dns_server(m
, m
->fallback_dns_servers
);
465 manager_set_dns_server(m
, m
->dns_servers
);
468 static const char* const dns_server_feature_level_table
[_DNS_SERVER_FEATURE_LEVEL_MAX
] = {
469 [DNS_SERVER_FEATURE_LEVEL_TCP
] = "TCP",
470 [DNS_SERVER_FEATURE_LEVEL_UDP
] = "UDP",
472 DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level
, DnsServerFeatureLevel
);