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-util.h"
28 /* After how much time to repeat classic DNS requests */
29 #define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
30 #define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
38 const union in_addr_union
*in_addr
) {
43 assert((type
== DNS_SERVER_LINK
) == !!l
);
46 s
= new0(DnsServer
, 1);
53 s
->address
= *in_addr
;
54 s
->resend_timeout
= DNS_TIMEOUT_MIN_USEC
;
56 if (type
== DNS_SERVER_LINK
) {
57 LIST_FIND_TAIL(servers
, l
->dns_servers
, tail
);
58 LIST_INSERT_AFTER(servers
, l
->dns_servers
, tail
, s
);
60 } else if (type
== DNS_SERVER_SYSTEM
) {
61 LIST_FIND_TAIL(servers
, m
->dns_servers
, tail
);
62 LIST_INSERT_AFTER(servers
, m
->dns_servers
, tail
, s
);
63 } else if (type
== DNS_SERVER_FALLBACK
) {
64 LIST_FIND_TAIL(servers
, m
->fallback_dns_servers
, tail
);
65 LIST_INSERT_AFTER(servers
, m
->fallback_dns_servers
, tail
, s
);
67 assert_not_reached("Unknown server type");
72 /* A new DNS server that isn't fallback is added and the one
73 * we used so far was a fallback one? Then let's try to pick
75 if (type
!= DNS_SERVER_FALLBACK
&&
76 m
->current_dns_server
&&
77 m
->current_dns_server
->type
== DNS_SERVER_FALLBACK
)
78 manager_set_dns_server(m
, NULL
);
86 DnsServer
* dns_server_ref(DnsServer
*s
) {
96 DnsServer
* dns_server_unref(DnsServer
*s
) {
100 assert(s
->n_ref
> 0);
110 void dns_server_unlink(DnsServer
*s
) {
114 /* This removes the specified server from the linked list of
115 * servers, but any server might still stay around if it has
116 * refs, for example from an ongoing transaction. */
123 case DNS_SERVER_LINK
:
125 LIST_REMOVE(servers
, s
->link
->dns_servers
, s
);
128 case DNS_SERVER_SYSTEM
:
129 LIST_REMOVE(servers
, s
->manager
->dns_servers
, s
);
132 case DNS_SERVER_FALLBACK
:
133 LIST_REMOVE(servers
, s
->manager
->fallback_dns_servers
, s
);
139 if (s
->link
&& s
->link
->current_dns_server
== s
)
140 link_set_dns_server(s
->link
, NULL
);
142 if (s
->manager
->current_dns_server
== s
)
143 manager_set_dns_server(s
->manager
, NULL
);
148 void dns_server_packet_received(DnsServer
*s
, usec_t rtt
) {
151 if (rtt
<= s
->max_rtt
)
155 s
->resend_timeout
= MIN(MAX(DNS_TIMEOUT_MIN_USEC
, s
->max_rtt
* 2), DNS_TIMEOUT_MAX_USEC
);
158 void dns_server_packet_lost(DnsServer
*s
, usec_t usec
) {
161 if (s
->resend_timeout
> usec
)
164 s
->resend_timeout
= MIN(s
->resend_timeout
* 2, DNS_TIMEOUT_MAX_USEC
);
167 static void dns_server_hash_func(const void *p
, struct siphash
*state
) {
168 const DnsServer
*s
= p
;
172 siphash24_compress(&s
->family
, sizeof(s
->family
), state
);
173 siphash24_compress(&s
->address
, FAMILY_ADDRESS_SIZE(s
->family
), state
);
176 static int dns_server_compare_func(const void *a
, const void *b
) {
177 const DnsServer
*x
= a
, *y
= b
;
179 if (x
->family
< y
->family
)
181 if (x
->family
> y
->family
)
184 return memcmp(&x
->address
, &y
->address
, FAMILY_ADDRESS_SIZE(x
->family
));
187 const struct hash_ops dns_server_hash_ops
= {
188 .hash
= dns_server_hash_func
,
189 .compare
= dns_server_compare_func
192 DnsServer
*manager_get_first_dns_server(Manager
*m
, DnsServerType t
) {
197 case DNS_SERVER_SYSTEM
:
198 return m
->dns_servers
;
200 case DNS_SERVER_FALLBACK
:
201 return m
->fallback_dns_servers
;
208 void manager_flush_dns_servers(Manager
*m
, DnsServerType type
) {
214 first
= manager_get_first_dns_server(m
, type
);
218 dns_server_unlink(first
);
222 void manager_flush_marked_dns_servers(Manager
*m
, DnsServerType type
) {
223 DnsServer
*first
, *s
, *next
;
227 first
= manager_get_first_dns_server(m
, type
);
229 LIST_FOREACH_SAFE(servers
, s
, next
, first
) {
233 dns_server_unlink(s
);
237 void manager_mark_dns_servers(Manager
*m
, DnsServerType type
) {
238 DnsServer
*first
, *s
;
242 first
= manager_get_first_dns_server(m
, type
);
243 LIST_FOREACH(servers
, s
, first
)
247 DnsServer
* manager_find_dns_server(Manager
*m
, DnsServerType type
, int family
, const union in_addr_union
*in_addr
) {
248 DnsServer
*first
, *s
;
253 first
= manager_get_first_dns_server(m
, type
);
255 LIST_FOREACH(servers
, s
, first
)
256 if (s
->family
== family
&& in_addr_equal(family
, &s
->address
, in_addr
) > 0)
262 DnsServer
*manager_set_dns_server(Manager
*m
, DnsServer
*s
) {
265 if (m
->current_dns_server
== s
)
269 _cleanup_free_
char *ip
= NULL
;
271 in_addr_to_string(s
->family
, &s
->address
, &ip
);
272 log_info("Switching to system DNS server %s.", strna(ip
));
275 dns_server_unref(m
->current_dns_server
);
276 m
->current_dns_server
= dns_server_ref(s
);
278 if (m
->unicast_scope
)
279 dns_cache_flush(&m
->unicast_scope
->cache
);
284 DnsServer
*manager_get_dns_server(Manager
*m
) {
288 /* Try to read updates resolv.conf */
289 manager_read_resolv_conf(m
);
291 /* If no DNS server was chose so far, pick the first one */
292 if (!m
->current_dns_server
)
293 manager_set_dns_server(m
, m
->dns_servers
);
295 if (!m
->current_dns_server
) {
299 /* No DNS servers configured, let's see if there are
300 * any on any links. If not, we use the fallback
303 HASHMAP_FOREACH(l
, m
->links
, i
)
304 if (l
->dns_servers
) {
310 manager_set_dns_server(m
, m
->fallback_dns_servers
);
313 return m
->current_dns_server
;
316 void manager_next_dns_server(Manager
*m
) {
319 /* If there's currently no DNS server set, then the next
320 * manager_get_dns_server() will find one */
321 if (!m
->current_dns_server
)
324 /* Change to the next one, but make sure to follow the linked
325 * list only if the server is still linked. */
326 if (m
->current_dns_server
->linked
&& m
->current_dns_server
->servers_next
) {
327 manager_set_dns_server(m
, m
->current_dns_server
->servers_next
);
331 /* If there was no next one, then start from the beginning of
333 if (m
->current_dns_server
->type
== DNS_SERVER_FALLBACK
)
334 manager_set_dns_server(m
, m
->fallback_dns_servers
);
336 manager_set_dns_server(m
, m
->dns_servers
);