]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/resolve/resolved-dns-server.c
resolved: add missing case to switch statement
[thirdparty/systemd.git] / src / resolve / resolved-dns-server.c
... / ...
CommitLineData
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
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"
28
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
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
40int dns_server_new(
41 Manager *m,
42 DnsServer **ret,
43 DnsServerType type,
44 Link *l,
45 int family,
46 const union in_addr_union *in_addr) {
47
48 DnsServer *s;
49
50 assert(m);
51 assert((type == DNS_SERVER_LINK) == !!l);
52 assert(in_addr);
53
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
65 s = new0(DnsServer, 1);
66 if (!s)
67 return -ENOMEM;
68
69 s->n_ref = 1;
70 s->manager = m;
71 s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
72 s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
73 s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
74 s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
75 s->type = type;
76 s->family = family;
77 s->address = *in_addr;
78 s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
79
80 switch (type) {
81
82 case DNS_SERVER_LINK:
83 s->link = l;
84 LIST_APPEND(servers, l->dns_servers, s);
85 l->n_dns_servers++;
86 break;
87
88 case DNS_SERVER_SYSTEM:
89 LIST_APPEND(servers, m->dns_servers, s);
90 m->n_dns_servers++;
91 break;
92
93 case DNS_SERVER_FALLBACK:
94 LIST_APPEND(servers, m->fallback_dns_servers, s);
95 m->n_dns_servers++;
96 break;
97
98 default:
99 assert_not_reached("Unknown server type");
100 }
101
102 s->linked = true;
103
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 &&
108 m->current_dns_server &&
109 m->current_dns_server->type == DNS_SERVER_FALLBACK)
110 manager_set_dns_server(m, NULL);
111
112 if (ret)
113 *ret = s;
114
115 return 0;
116}
117
118DnsServer* dns_server_ref(DnsServer *s) {
119 if (!s)
120 return NULL;
121
122 assert(s->n_ref > 0);
123 s->n_ref ++;
124
125 return s;
126}
127
128DnsServer* dns_server_unref(DnsServer *s) {
129 if (!s)
130 return NULL;
131
132 assert(s->n_ref > 0);
133 s->n_ref --;
134
135 if (s->n_ref > 0)
136 return NULL;
137
138 free(s);
139 return NULL;
140}
141
142void dns_server_unlink(DnsServer *s) {
143 assert(s);
144 assert(s->manager);
145
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. */
149
150 if (!s->linked)
151 return;
152
153 switch (s->type) {
154
155 case DNS_SERVER_LINK:
156 assert(s->link);
157 assert(s->link->n_dns_servers > 0);
158 LIST_REMOVE(servers, s->link->dns_servers, s);
159 break;
160
161 case DNS_SERVER_SYSTEM:
162 assert(s->manager->n_dns_servers > 0);
163 LIST_REMOVE(servers, s->manager->dns_servers, s);
164 s->manager->n_dns_servers--;
165 break;
166
167 case DNS_SERVER_FALLBACK:
168 assert(s->manager->n_dns_servers > 0);
169 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
170 s->manager->n_dns_servers--;
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);
183}
184
185void 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
227void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
228 assert(s);
229
230 if (level == DNS_SERVER_FEATURE_LEVEL_LARGE) {
231 /* Even if we successfully receive a reply to a
232 request announcing support for large packets, that
233 does not mean we can necessarily receive large
234 packets. */
235
236 if (s->verified_feature_level < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
237 s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
238 assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
239 }
240 } else if (s->verified_feature_level < level) {
241 s->verified_feature_level = level;
242 assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
243 }
244
245 if (s->possible_feature_level == level)
246 s->n_failed_attempts = 0;
247
248 /* Remember the size of the largest UDP packet we received from a server,
249 we know that we can always announce support for packets with at least
250 this size. */
251 if (s->received_udp_packet_max < size)
252 s->received_udp_packet_max = size;
253
254 if (s->max_rtt < rtt) {
255 s->max_rtt = rtt;
256 s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC);
257 }
258}
259
260void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel level, usec_t usec) {
261 assert(s);
262 assert(s->manager);
263
264 if (s->possible_feature_level == level)
265 s->n_failed_attempts ++;
266
267 if (s->resend_timeout > usec)
268 return;
269
270 s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
271}
272
273void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
274 assert(s);
275 assert(s->manager);
276
277 if (s->possible_feature_level != level)
278 return;
279
280 /* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. This is an immediate trigger for
281 * us to go one feature level down. Except when we are already at TCP or UDP level, in which case there's no
282 * point in changing, under the assumption that packet failures are caused by packet contents, not by used
283 * transport. */
284
285 if (s->possible_feature_level <= DNS_SERVER_FEATURE_LEVEL_UDP)
286 return;
287
288 s->n_failed_attempts = (unsigned) -1;
289}
290
291void dns_server_packet_rrsig_missing(DnsServer *s) {
292 _cleanup_free_ char *ip = NULL;
293 assert(s);
294 assert(s->manager);
295
296 in_addr_to_string(s->family, &s->address, &ip);
297 log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", strna(ip));
298
299 s->rrsig_missing = true;
300}
301
302static bool dns_server_grace_period_expired(DnsServer *s) {
303 usec_t ts;
304
305 assert(s);
306 assert(s->manager);
307
308 if (s->verified_usec == 0)
309 return false;
310
311 assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
312
313 if (s->verified_usec + s->features_grace_period_usec > ts)
314 return false;
315
316 s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC);
317
318 return true;
319}
320
321DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
322 assert(s);
323
324 if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
325 dns_server_grace_period_expired(s)) {
326 _cleanup_free_ char *ip = NULL;
327
328 s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
329 s->n_failed_attempts = 0;
330 s->verified_usec = 0;
331 s->rrsig_missing = false;
332
333 in_addr_to_string(s->family, &s->address, &ip);
334 log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip));
335 } else if (s->possible_feature_level <= s->verified_feature_level)
336 s->possible_feature_level = s->verified_feature_level;
337 else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS) {
338 _cleanup_free_ char *ip = NULL;
339
340 /* Switch one feature level down. Except when we are at TCP already, in which case we try UDP
341 * again. Thus, if a DNS server is not responding we'll keep toggling between UDP and TCP until it
342 * responds on one of them. Note that we generally prefer UDP over TCP (which is why it is at a higher
343 * feature level), but many DNS servers support lack TCP support. */
344
345 if (s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP)
346 s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
347 else {
348 assert(s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_WORST);
349 s->possible_feature_level --;
350 }
351
352 s->n_failed_attempts = 0;
353 s->verified_usec = 0;
354
355 in_addr_to_string(s->family, &s->address, &ip);
356 log_warning("Using degraded feature set (%s) for DNS server %s",
357 dns_server_feature_level_to_string(s->possible_feature_level), strna(ip));
358 }
359
360 return s->possible_feature_level;
361}
362
363int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
364 size_t packet_size;
365 bool edns_do;
366 int r;
367
368 assert(server);
369 assert(packet);
370 assert(packet->protocol == DNS_PROTOCOL_DNS);
371
372 /* Fix the OPT field in the packet to match our current feature level. */
373
374 r = dns_packet_truncate_opt(packet);
375 if (r < 0)
376 return r;
377
378 if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
379 return 0;
380
381 edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
382
383 if (level >= DNS_SERVER_FEATURE_LEVEL_LARGE)
384 packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
385 else
386 packet_size = server->received_udp_packet_max;
387
388 return dns_packet_append_opt(packet, packet_size, edns_do, NULL);
389}
390
391static void dns_server_hash_func(const void *p, struct siphash *state) {
392 const DnsServer *s = p;
393
394 assert(s);
395
396 siphash24_compress(&s->family, sizeof(s->family), state);
397 siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
398}
399
400static int dns_server_compare_func(const void *a, const void *b) {
401 const DnsServer *x = a, *y = b;
402
403 if (x->family < y->family)
404 return -1;
405 if (x->family > y->family)
406 return 1;
407
408 return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
409}
410
411const struct hash_ops dns_server_hash_ops = {
412 .hash = dns_server_hash_func,
413 .compare = dns_server_compare_func
414};
415
416void dns_server_unlink_all(DnsServer *first) {
417 DnsServer *next;
418
419 if (!first)
420 return;
421
422 next = first->servers_next;
423 dns_server_unlink(first);
424
425 dns_server_unlink_all(next);
426}
427
428void dns_server_unlink_marked(DnsServer *first) {
429 DnsServer *next;
430
431 if (!first)
432 return;
433
434 next = first->servers_next;
435
436 if (first->marked)
437 dns_server_unlink(first);
438
439 dns_server_unlink_marked(next);
440}
441
442void dns_server_mark_all(DnsServer *first) {
443 if (!first)
444 return;
445
446 first->marked = true;
447 dns_server_mark_all(first->servers_next);
448}
449
450DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
451 DnsServer *s;
452
453 LIST_FOREACH(servers, s, first)
454 if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
455 return s;
456
457 return NULL;
458}
459
460DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
461 assert(m);
462
463 switch (t) {
464
465 case DNS_SERVER_SYSTEM:
466 return m->dns_servers;
467
468 case DNS_SERVER_FALLBACK:
469 return m->fallback_dns_servers;
470
471 default:
472 return NULL;
473 }
474}
475
476DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
477 assert(m);
478
479 if (m->current_dns_server == s)
480 return s;
481
482 if (s) {
483 _cleanup_free_ char *ip = NULL;
484
485 in_addr_to_string(s->family, &s->address, &ip);
486 log_info("Switching to system DNS server %s.", strna(ip));
487 }
488
489 dns_server_unref(m->current_dns_server);
490 m->current_dns_server = dns_server_ref(s);
491
492 if (m->unicast_scope)
493 dns_cache_flush(&m->unicast_scope->cache);
494
495 return s;
496}
497
498DnsServer *manager_get_dns_server(Manager *m) {
499 Link *l;
500 assert(m);
501
502 /* Try to read updates resolv.conf */
503 manager_read_resolv_conf(m);
504
505 /* If no DNS server was chose so far, pick the first one */
506 if (!m->current_dns_server)
507 manager_set_dns_server(m, m->dns_servers);
508
509 if (!m->current_dns_server) {
510 bool found = false;
511 Iterator i;
512
513 /* No DNS servers configured, let's see if there are
514 * any on any links. If not, we use the fallback
515 * servers */
516
517 HASHMAP_FOREACH(l, m->links, i)
518 if (l->dns_servers) {
519 found = true;
520 break;
521 }
522
523 if (!found)
524 manager_set_dns_server(m, m->fallback_dns_servers);
525 }
526
527 return m->current_dns_server;
528}
529
530void manager_next_dns_server(Manager *m) {
531 assert(m);
532
533 /* If there's currently no DNS server set, then the next
534 * manager_get_dns_server() will find one */
535 if (!m->current_dns_server)
536 return;
537
538 /* Change to the next one, but make sure to follow the linked
539 * list only if the server is still linked. */
540 if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
541 manager_set_dns_server(m, m->current_dns_server->servers_next);
542 return;
543 }
544
545 /* If there was no next one, then start from the beginning of
546 * the list */
547 if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
548 manager_set_dns_server(m, m->fallback_dns_servers);
549 else
550 manager_set_dns_server(m, m->dns_servers);
551}
552
553static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = {
554 [DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP",
555 [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
556 [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
557 [DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
558 [DNS_SERVER_FEATURE_LEVEL_LARGE] = "UDP+EDNS0+DO+LARGE",
559};
560DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);