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 if (!IN_SET(family
, AF_INET
, AF_INET6
))
50 if (l
->n_dns_servers
>= LINK_DNS_SERVERS_MAX
)
53 if (m
->n_dns_servers
>= MANAGER_DNS_SERVERS_MAX
)
57 s
= new0(DnsServer
, 1);
65 s
->address
= *in_addr
;
66 s
->resend_timeout
= DNS_TIMEOUT_MIN_USEC
;
72 LIST_APPEND(servers
, l
->dns_servers
, s
);
76 case DNS_SERVER_SYSTEM
:
77 LIST_APPEND(servers
, m
->dns_servers
, s
);
81 case DNS_SERVER_FALLBACK
:
82 LIST_APPEND(servers
, m
->fallback_dns_servers
, s
);
87 assert_not_reached("Unknown server type");
92 /* A new DNS server that isn't fallback is added and the one
93 * we used so far was a fallback one? Then let's try to pick
95 if (type
!= DNS_SERVER_FALLBACK
&&
96 m
->current_dns_server
&&
97 m
->current_dns_server
->type
== DNS_SERVER_FALLBACK
)
98 manager_set_dns_server(m
, NULL
);
106 DnsServer
* dns_server_ref(DnsServer
*s
) {
110 assert(s
->n_ref
> 0);
116 DnsServer
* dns_server_unref(DnsServer
*s
) {
120 assert(s
->n_ref
> 0);
130 void dns_server_unlink(DnsServer
*s
) {
134 /* This removes the specified server from the linked list of
135 * servers, but any server might still stay around if it has
136 * refs, for example from an ongoing transaction. */
143 case DNS_SERVER_LINK
:
145 assert(s
->link
->n_dns_servers
> 0);
146 LIST_REMOVE(servers
, s
->link
->dns_servers
, s
);
149 case DNS_SERVER_SYSTEM
:
150 assert(s
->manager
->n_dns_servers
> 0);
151 LIST_REMOVE(servers
, s
->manager
->dns_servers
, s
);
152 s
->manager
->n_dns_servers
--;
155 case DNS_SERVER_FALLBACK
:
156 assert(s
->manager
->n_dns_servers
> 0);
157 LIST_REMOVE(servers
, s
->manager
->fallback_dns_servers
, s
);
158 s
->manager
->n_dns_servers
--;
164 if (s
->link
&& s
->link
->current_dns_server
== s
)
165 link_set_dns_server(s
->link
, NULL
);
167 if (s
->manager
->current_dns_server
== s
)
168 manager_set_dns_server(s
->manager
, NULL
);
173 void dns_server_move_back_and_unmark(DnsServer
*s
) {
183 if (!s
->linked
|| !s
->servers_next
)
186 /* Move us to the end of the list, so that the order is
187 * strictly kept, if we are not at the end anyway. */
191 case DNS_SERVER_LINK
:
193 LIST_FIND_TAIL(servers
, s
, tail
);
194 LIST_REMOVE(servers
, s
->link
->dns_servers
, s
);
195 LIST_INSERT_AFTER(servers
, s
->link
->dns_servers
, tail
, s
);
198 case DNS_SERVER_SYSTEM
:
199 LIST_FIND_TAIL(servers
, s
, tail
);
200 LIST_REMOVE(servers
, s
->manager
->dns_servers
, s
);
201 LIST_INSERT_AFTER(servers
, s
->manager
->dns_servers
, tail
, s
);
204 case DNS_SERVER_FALLBACK
:
205 LIST_FIND_TAIL(servers
, s
, tail
);
206 LIST_REMOVE(servers
, s
->manager
->fallback_dns_servers
, s
);
207 LIST_INSERT_AFTER(servers
, s
->manager
->fallback_dns_servers
, tail
, s
);
211 assert_not_reached("Unknown server type");
215 void dns_server_packet_received(DnsServer
*s
, usec_t rtt
) {
218 if (rtt
<= s
->max_rtt
)
222 s
->resend_timeout
= MIN(MAX(DNS_TIMEOUT_MIN_USEC
, s
->max_rtt
* 2), DNS_TIMEOUT_MAX_USEC
);
225 void dns_server_packet_lost(DnsServer
*s
, usec_t usec
) {
228 if (s
->resend_timeout
> usec
)
231 s
->resend_timeout
= MIN(s
->resend_timeout
* 2, DNS_TIMEOUT_MAX_USEC
);
234 static void dns_server_hash_func(const void *p
, struct siphash
*state
) {
235 const DnsServer
*s
= p
;
239 siphash24_compress(&s
->family
, sizeof(s
->family
), state
);
240 siphash24_compress(&s
->address
, FAMILY_ADDRESS_SIZE(s
->family
), state
);
243 static int dns_server_compare_func(const void *a
, const void *b
) {
244 const DnsServer
*x
= a
, *y
= b
;
246 if (x
->family
< y
->family
)
248 if (x
->family
> y
->family
)
251 return memcmp(&x
->address
, &y
->address
, FAMILY_ADDRESS_SIZE(x
->family
));
254 const struct hash_ops dns_server_hash_ops
= {
255 .hash
= dns_server_hash_func
,
256 .compare
= dns_server_compare_func
259 void dns_server_unlink_all(DnsServer
*first
) {
265 next
= first
->servers_next
;
266 dns_server_unlink(first
);
268 dns_server_unlink_all(next
);
271 void dns_server_unlink_marked(DnsServer
*first
) {
277 next
= first
->servers_next
;
280 dns_server_unlink(first
);
282 dns_server_unlink_marked(next
);
285 void dns_server_mark_all(DnsServer
*first
) {
289 first
->marked
= true;
290 dns_server_mark_all(first
->servers_next
);
293 DnsServer
*dns_server_find(DnsServer
*first
, int family
, const union in_addr_union
*in_addr
) {
296 LIST_FOREACH(servers
, s
, first
)
297 if (s
->family
== family
&& in_addr_equal(family
, &s
->address
, in_addr
) > 0)
303 DnsServer
*manager_get_first_dns_server(Manager
*m
, DnsServerType t
) {
308 case DNS_SERVER_SYSTEM
:
309 return m
->dns_servers
;
311 case DNS_SERVER_FALLBACK
:
312 return m
->fallback_dns_servers
;
319 DnsServer
*manager_set_dns_server(Manager
*m
, DnsServer
*s
) {
322 if (m
->current_dns_server
== s
)
326 _cleanup_free_
char *ip
= NULL
;
328 in_addr_to_string(s
->family
, &s
->address
, &ip
);
329 log_info("Switching to system DNS server %s.", strna(ip
));
332 dns_server_unref(m
->current_dns_server
);
333 m
->current_dns_server
= dns_server_ref(s
);
335 if (m
->unicast_scope
)
336 dns_cache_flush(&m
->unicast_scope
->cache
);
341 DnsServer
*manager_get_dns_server(Manager
*m
) {
345 /* Try to read updates resolv.conf */
346 manager_read_resolv_conf(m
);
348 /* If no DNS server was chose so far, pick the first one */
349 if (!m
->current_dns_server
)
350 manager_set_dns_server(m
, m
->dns_servers
);
352 if (!m
->current_dns_server
) {
356 /* No DNS servers configured, let's see if there are
357 * any on any links. If not, we use the fallback
360 HASHMAP_FOREACH(l
, m
->links
, i
)
361 if (l
->dns_servers
) {
367 manager_set_dns_server(m
, m
->fallback_dns_servers
);
370 return m
->current_dns_server
;
373 void manager_next_dns_server(Manager
*m
) {
376 /* If there's currently no DNS server set, then the next
377 * manager_get_dns_server() will find one */
378 if (!m
->current_dns_server
)
381 /* Change to the next one, but make sure to follow the linked
382 * list only if the server is still linked. */
383 if (m
->current_dns_server
->linked
&& m
->current_dns_server
->servers_next
) {
384 manager_set_dns_server(m
, m
->current_dns_server
->servers_next
);
388 /* If there was no next one, then start from the beginning of
390 if (m
->current_dns_server
->type
== DNS_SERVER_FALLBACK
)
391 manager_set_dns_server(m
, m
->fallback_dns_servers
);
393 manager_set_dns_server(m
, m
->dns_servers
);