]>
Commit | Line | Data |
---|---|---|
74b2466e LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2014 Lennart Poettering | |
7 | ||
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. | |
12 | ||
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. | |
17 | ||
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/>. | |
20 | ***/ | |
21 | ||
b5efdb8a | 22 | #include "alloc-util.h" |
74b2466e | 23 | #include "resolved-dns-server.h" |
f2f1dbe5 | 24 | #include "resolved-resolv-conf.h" |
b5efdb8a | 25 | #include "siphash24.h" |
be808ea0 | 26 | #include "string-table.h" |
f2f1dbe5 | 27 | #include "string-util.h" |
74b2466e | 28 | |
9df3ba6c TG |
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) | |
32 | ||
be808ea0 TG |
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) | |
36 | ||
37 | /* The number of times we will attempt a certain feature set before degrading */ | |
38 | #define DNS_SERVER_FEATURE_RETRY_ATTEMPTS 3 | |
39 | ||
74b2466e LP |
40 | int dns_server_new( |
41 | Manager *m, | |
42 | DnsServer **ret, | |
4e945a6f | 43 | DnsServerType type, |
74b2466e | 44 | Link *l, |
0dd25fb9 | 45 | int family, |
3c0cf502 | 46 | const union in_addr_union *in_addr) { |
74b2466e | 47 | |
eed857b7 | 48 | DnsServer *s; |
74b2466e LP |
49 | |
50 | assert(m); | |
4e945a6f | 51 | assert((type == DNS_SERVER_LINK) == !!l); |
74b2466e | 52 | assert(in_addr); |
74b2466e | 53 | |
eed857b7 LP |
54 | if (!IN_SET(family, AF_INET, AF_INET6)) |
55 | return -EAFNOSUPPORT; | |
56 | ||
57 | if (l) { | |
58 | if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX) | |
59 | return -E2BIG; | |
60 | } else { | |
61 | if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX) | |
62 | return -E2BIG; | |
63 | } | |
64 | ||
74b2466e LP |
65 | s = new0(DnsServer, 1); |
66 | if (!s) | |
67 | return -ENOMEM; | |
68 | ||
91b14d6f | 69 | s->n_ref = 1; |
0b58db65 | 70 | s->manager = m; |
be808ea0 TG |
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; | |
d74fb368 | 74 | s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX; |
4e945a6f | 75 | s->type = type; |
74b2466e LP |
76 | s->family = family; |
77 | s->address = *in_addr; | |
9df3ba6c | 78 | s->resend_timeout = DNS_TIMEOUT_MIN_USEC; |
74b2466e | 79 | |
0b58db65 LP |
80 | switch (type) { |
81 | ||
82 | case DNS_SERVER_LINK: | |
83 | s->link = l; | |
eed857b7 LP |
84 | LIST_APPEND(servers, l->dns_servers, s); |
85 | l->n_dns_servers++; | |
0b58db65 LP |
86 | break; |
87 | ||
88 | case DNS_SERVER_SYSTEM: | |
eed857b7 LP |
89 | LIST_APPEND(servers, m->dns_servers, s); |
90 | m->n_dns_servers++; | |
0b58db65 LP |
91 | break; |
92 | ||
93 | case DNS_SERVER_FALLBACK: | |
eed857b7 LP |
94 | LIST_APPEND(servers, m->fallback_dns_servers, s); |
95 | m->n_dns_servers++; | |
0b58db65 LP |
96 | break; |
97 | ||
98 | default: | |
4e945a6f | 99 | assert_not_reached("Unknown server type"); |
0b58db65 | 100 | } |
74b2466e | 101 | |
0eac4623 | 102 | s->linked = true; |
74b2466e | 103 | |
4e945a6f LP |
104 | /* A new DNS server that isn't fallback is added and the one |
105 | * we used so far was a fallback one? Then let's try to pick | |
106 | * the new one */ | |
107 | if (type != DNS_SERVER_FALLBACK && | |
3e684349 LP |
108 | m->current_dns_server && |
109 | m->current_dns_server->type == DNS_SERVER_FALLBACK) | |
110 | manager_set_dns_server(m, NULL); | |
4e945a6f | 111 | |
74b2466e LP |
112 | if (ret) |
113 | *ret = s; | |
114 | ||
115 | return 0; | |
116 | } | |
117 | ||
91b14d6f | 118 | DnsServer* dns_server_ref(DnsServer *s) { |
74b2466e LP |
119 | if (!s) |
120 | return NULL; | |
121 | ||
91b14d6f | 122 | assert(s->n_ref > 0); |
91b14d6f | 123 | s->n_ref ++; |
cab5b059 | 124 | |
91b14d6f TG |
125 | return s; |
126 | } | |
127 | ||
0eac4623 | 128 | DnsServer* dns_server_unref(DnsServer *s) { |
91b14d6f TG |
129 | if (!s) |
130 | return NULL; | |
3e684349 | 131 | |
0eac4623 LP |
132 | assert(s->n_ref > 0); |
133 | s->n_ref --; | |
91b14d6f | 134 | |
0eac4623 LP |
135 | if (s->n_ref > 0) |
136 | return NULL; | |
74b2466e | 137 | |
74b2466e | 138 | free(s); |
74b2466e LP |
139 | return NULL; |
140 | } | |
87f5a193 | 141 | |
0eac4623 LP |
142 | void dns_server_unlink(DnsServer *s) { |
143 | assert(s); | |
144 | assert(s->manager); | |
91b14d6f | 145 | |
0eac4623 LP |
146 | /* This removes the specified server from the linked list of |
147 | * servers, but any server might still stay around if it has | |
148 | * refs, for example from an ongoing transaction. */ | |
91b14d6f | 149 | |
0eac4623 LP |
150 | if (!s->linked) |
151 | return; | |
91b14d6f | 152 | |
0eac4623 LP |
153 | switch (s->type) { |
154 | ||
155 | case DNS_SERVER_LINK: | |
156 | assert(s->link); | |
eed857b7 | 157 | assert(s->link->n_dns_servers > 0); |
0eac4623 LP |
158 | LIST_REMOVE(servers, s->link->dns_servers, s); |
159 | break; | |
160 | ||
161 | case DNS_SERVER_SYSTEM: | |
eed857b7 | 162 | assert(s->manager->n_dns_servers > 0); |
0eac4623 | 163 | LIST_REMOVE(servers, s->manager->dns_servers, s); |
eed857b7 | 164 | s->manager->n_dns_servers--; |
0eac4623 LP |
165 | break; |
166 | ||
167 | case DNS_SERVER_FALLBACK: | |
eed857b7 | 168 | assert(s->manager->n_dns_servers > 0); |
0eac4623 | 169 | LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); |
eed857b7 | 170 | s->manager->n_dns_servers--; |
0eac4623 LP |
171 | break; |
172 | } | |
173 | ||
174 | s->linked = false; | |
175 | ||
176 | if (s->link && s->link->current_dns_server == s) | |
177 | link_set_dns_server(s->link, NULL); | |
178 | ||
179 | if (s->manager->current_dns_server == s) | |
180 | manager_set_dns_server(s->manager, NULL); | |
181 | ||
182 | dns_server_unref(s); | |
91b14d6f TG |
183 | } |
184 | ||
0b58db65 LP |
185 | void dns_server_move_back_and_unmark(DnsServer *s) { |
186 | DnsServer *tail; | |
187 | ||
188 | assert(s); | |
189 | ||
190 | if (!s->marked) | |
191 | return; | |
192 | ||
193 | s->marked = false; | |
194 | ||
195 | if (!s->linked || !s->servers_next) | |
196 | return; | |
197 | ||
198 | /* Move us to the end of the list, so that the order is | |
199 | * strictly kept, if we are not at the end anyway. */ | |
200 | ||
201 | switch (s->type) { | |
202 | ||
203 | case DNS_SERVER_LINK: | |
204 | assert(s->link); | |
205 | LIST_FIND_TAIL(servers, s, tail); | |
206 | LIST_REMOVE(servers, s->link->dns_servers, s); | |
207 | LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s); | |
208 | break; | |
209 | ||
210 | case DNS_SERVER_SYSTEM: | |
211 | LIST_FIND_TAIL(servers, s, tail); | |
212 | LIST_REMOVE(servers, s->manager->dns_servers, s); | |
213 | LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s); | |
214 | break; | |
215 | ||
216 | case DNS_SERVER_FALLBACK: | |
217 | LIST_FIND_TAIL(servers, s, tail); | |
218 | LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); | |
219 | LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s); | |
220 | break; | |
221 | ||
222 | default: | |
223 | assert_not_reached("Unknown server type"); | |
224 | } | |
225 | } | |
226 | ||
d74fb368 | 227 | void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size) { |
9df3ba6c TG |
228 | assert(s); |
229 | ||
d74fb368 TG |
230 | if (features == DNS_SERVER_FEATURE_LEVEL_LARGE) { |
231 | /* even if we successfully receive a reply to a request announcing | |
232 | support for large packets, that does not mean we can necessarily | |
233 | receive large packets. */ | |
234 | if (s->verified_features < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) { | |
235 | s->verified_features = DNS_SERVER_FEATURE_LEVEL_LARGE - 1; | |
236 | assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0); | |
237 | } | |
238 | } else if (s->verified_features < features) { | |
be808ea0 TG |
239 | s->verified_features = features; |
240 | assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0); | |
241 | } | |
242 | ||
243 | if (s->possible_features == features) | |
244 | s->n_failed_attempts = 0; | |
84129d46 | 245 | |
d74fb368 TG |
246 | /* Remember the size of the largest UDP packet we received from a server, |
247 | we know that we can always announce support for packets with at least | |
248 | this size. */ | |
249 | if (s->received_udp_packet_max < size) | |
250 | s->received_udp_packet_max = size; | |
251 | ||
be808ea0 TG |
252 | if (s->max_rtt < rtt) { |
253 | s->max_rtt = rtt; | |
254 | s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC); | |
255 | } | |
9df3ba6c TG |
256 | } |
257 | ||
be808ea0 | 258 | void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) { |
9df3ba6c | 259 | assert(s); |
be808ea0 TG |
260 | assert(s->manager); |
261 | ||
262 | if (s->possible_features == features) | |
263 | s->n_failed_attempts ++; | |
9df3ba6c | 264 | |
84129d46 LP |
265 | if (s->resend_timeout > usec) |
266 | return; | |
267 | ||
268 | s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC); | |
9df3ba6c TG |
269 | } |
270 | ||
4e0b8b17 TG |
271 | void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features) { |
272 | assert(s); | |
273 | assert(s->manager); | |
274 | ||
275 | if (s->possible_features != features) | |
276 | return; | |
277 | ||
278 | s->n_failed_attempts = (unsigned) -1; | |
279 | } | |
280 | ||
be808ea0 TG |
281 | static bool dns_server_grace_period_expired(DnsServer *s) { |
282 | usec_t ts; | |
283 | ||
284 | assert(s); | |
285 | assert(s->manager); | |
286 | ||
287 | if (s->verified_usec == 0) | |
288 | return false; | |
289 | ||
290 | assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); | |
291 | ||
292 | if (s->verified_usec + s->features_grace_period_usec > ts) | |
293 | return false; | |
294 | ||
295 | s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC); | |
296 | ||
297 | return true; | |
298 | } | |
299 | ||
300 | DnsServerFeatureLevel dns_server_possible_features(DnsServer *s) { | |
301 | assert(s); | |
302 | ||
303 | if (s->possible_features != DNS_SERVER_FEATURE_LEVEL_BEST && | |
304 | dns_server_grace_period_expired(s)) { | |
305 | _cleanup_free_ char *ip = NULL; | |
306 | ||
307 | s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST; | |
308 | s->n_failed_attempts = 0; | |
309 | s->verified_usec = 0; | |
310 | ||
311 | in_addr_to_string(s->family, &s->address, &ip); | |
312 | log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip)); | |
313 | } else if (s->possible_features <= s->verified_features) | |
314 | s->possible_features = s->verified_features; | |
315 | else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && | |
316 | s->possible_features > DNS_SERVER_FEATURE_LEVEL_WORST) { | |
317 | _cleanup_free_ char *ip = NULL; | |
318 | ||
319 | s->possible_features --; | |
320 | s->n_failed_attempts = 0; | |
321 | s->verified_usec = 0; | |
322 | ||
323 | in_addr_to_string(s->family, &s->address, &ip); | |
324 | log_warning("Using degraded feature set (%s) for DNS server %s", | |
325 | dns_server_feature_level_to_string(s->possible_features), strna(ip)); | |
326 | } | |
327 | ||
328 | return s->possible_features; | |
329 | } | |
330 | ||
b826ab58 | 331 | static void dns_server_hash_func(const void *p, struct siphash *state) { |
87f5a193 | 332 | const DnsServer *s = p; |
87f5a193 | 333 | |
b826ab58 | 334 | assert(s); |
87f5a193 | 335 | |
b826ab58 TG |
336 | siphash24_compress(&s->family, sizeof(s->family), state); |
337 | siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state); | |
87f5a193 LP |
338 | } |
339 | ||
d5099efc | 340 | static int dns_server_compare_func(const void *a, const void *b) { |
87f5a193 LP |
341 | const DnsServer *x = a, *y = b; |
342 | ||
343 | if (x->family < y->family) | |
344 | return -1; | |
345 | if (x->family > y->family) | |
346 | return 1; | |
347 | ||
348 | return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); | |
349 | } | |
d5099efc MS |
350 | |
351 | const struct hash_ops dns_server_hash_ops = { | |
352 | .hash = dns_server_hash_func, | |
353 | .compare = dns_server_compare_func | |
354 | }; | |
636e813d | 355 | |
4b95f179 LP |
356 | void dns_server_unlink_all(DnsServer *first) { |
357 | DnsServer *next; | |
0eac4623 | 358 | |
4b95f179 LP |
359 | if (!first) |
360 | return; | |
0eac4623 | 361 | |
4b95f179 LP |
362 | next = first->servers_next; |
363 | dns_server_unlink(first); | |
636e813d | 364 | |
4b95f179 | 365 | dns_server_unlink_all(next); |
0eac4623 LP |
366 | } |
367 | ||
4b95f179 LP |
368 | void dns_server_unlink_marked(DnsServer *first) { |
369 | DnsServer *next; | |
636e813d | 370 | |
4b95f179 LP |
371 | if (!first) |
372 | return; | |
636e813d | 373 | |
4b95f179 | 374 | next = first->servers_next; |
636e813d | 375 | |
4b95f179 | 376 | if (first->marked) |
0eac4623 | 377 | dns_server_unlink(first); |
636e813d | 378 | |
4b95f179 LP |
379 | dns_server_unlink_marked(next); |
380 | } | |
636e813d | 381 | |
4b95f179 LP |
382 | void dns_server_mark_all(DnsServer *first) { |
383 | if (!first) | |
384 | return; | |
636e813d | 385 | |
4b95f179 LP |
386 | first->marked = true; |
387 | dns_server_mark_all(first->servers_next); | |
636e813d LP |
388 | } |
389 | ||
4b95f179 LP |
390 | DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) { |
391 | DnsServer *s; | |
636e813d | 392 | |
636e813d | 393 | LIST_FOREACH(servers, s, first) |
4b95f179 LP |
394 | if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) |
395 | return s; | |
f2f1dbe5 | 396 | |
4b95f179 LP |
397 | return NULL; |
398 | } | |
f2f1dbe5 | 399 | |
4b95f179 | 400 | DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) { |
f2f1dbe5 | 401 | assert(m); |
f2f1dbe5 | 402 | |
4b95f179 | 403 | switch (t) { |
f2f1dbe5 | 404 | |
4b95f179 LP |
405 | case DNS_SERVER_SYSTEM: |
406 | return m->dns_servers; | |
f2f1dbe5 | 407 | |
4b95f179 LP |
408 | case DNS_SERVER_FALLBACK: |
409 | return m->fallback_dns_servers; | |
410 | ||
411 | default: | |
412 | return NULL; | |
413 | } | |
f2f1dbe5 LP |
414 | } |
415 | ||
416 | DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { | |
417 | assert(m); | |
418 | ||
419 | if (m->current_dns_server == s) | |
420 | return s; | |
421 | ||
422 | if (s) { | |
423 | _cleanup_free_ char *ip = NULL; | |
424 | ||
425 | in_addr_to_string(s->family, &s->address, &ip); | |
426 | log_info("Switching to system DNS server %s.", strna(ip)); | |
427 | } | |
428 | ||
0eac4623 LP |
429 | dns_server_unref(m->current_dns_server); |
430 | m->current_dns_server = dns_server_ref(s); | |
f2f1dbe5 LP |
431 | |
432 | if (m->unicast_scope) | |
433 | dns_cache_flush(&m->unicast_scope->cache); | |
434 | ||
435 | return s; | |
436 | } | |
437 | ||
438 | DnsServer *manager_get_dns_server(Manager *m) { | |
439 | Link *l; | |
440 | assert(m); | |
441 | ||
442 | /* Try to read updates resolv.conf */ | |
443 | manager_read_resolv_conf(m); | |
444 | ||
445 | /* If no DNS server was chose so far, pick the first one */ | |
446 | if (!m->current_dns_server) | |
447 | manager_set_dns_server(m, m->dns_servers); | |
448 | ||
449 | if (!m->current_dns_server) { | |
450 | bool found = false; | |
451 | Iterator i; | |
452 | ||
453 | /* No DNS servers configured, let's see if there are | |
454 | * any on any links. If not, we use the fallback | |
455 | * servers */ | |
456 | ||
457 | HASHMAP_FOREACH(l, m->links, i) | |
458 | if (l->dns_servers) { | |
459 | found = true; | |
460 | break; | |
461 | } | |
462 | ||
463 | if (!found) | |
464 | manager_set_dns_server(m, m->fallback_dns_servers); | |
465 | } | |
466 | ||
467 | return m->current_dns_server; | |
468 | } | |
469 | ||
470 | void manager_next_dns_server(Manager *m) { | |
471 | assert(m); | |
472 | ||
473 | /* If there's currently no DNS server set, then the next | |
474 | * manager_get_dns_server() will find one */ | |
475 | if (!m->current_dns_server) | |
476 | return; | |
477 | ||
0eac4623 LP |
478 | /* Change to the next one, but make sure to follow the linked |
479 | * list only if the server is still linked. */ | |
480 | if (m->current_dns_server->linked && m->current_dns_server->servers_next) { | |
f2f1dbe5 LP |
481 | manager_set_dns_server(m, m->current_dns_server->servers_next); |
482 | return; | |
483 | } | |
484 | ||
485 | /* If there was no next one, then start from the beginning of | |
486 | * the list */ | |
487 | if (m->current_dns_server->type == DNS_SERVER_FALLBACK) | |
488 | manager_set_dns_server(m, m->fallback_dns_servers); | |
489 | else | |
490 | manager_set_dns_server(m, m->dns_servers); | |
491 | } | |
be808ea0 TG |
492 | |
493 | static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = { | |
494 | [DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP", | |
495 | [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP", | |
9c5e12a4 | 496 | [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0", |
7586f4d1 | 497 | [DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO", |
d74fb368 | 498 | [DNS_SERVER_FEATURE_LEVEL_LARGE] = "UDP+EDNS0+DO+LARGE", |
be808ea0 TG |
499 | }; |
500 | DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel); |