]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-server.c
resolved: fallback to TCP if UDP fails
[thirdparty/systemd.git] / src / resolve / resolved-dns-server.c
CommitLineData
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
40int 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;
4e945a6f 74 s->type = type;
74b2466e
LP
75 s->family = family;
76 s->address = *in_addr;
9df3ba6c 77 s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
74b2466e 78
0b58db65
LP
79 switch (type) {
80
81 case DNS_SERVER_LINK:
82 s->link = l;
eed857b7
LP
83 LIST_APPEND(servers, l->dns_servers, s);
84 l->n_dns_servers++;
0b58db65
LP
85 break;
86
87 case DNS_SERVER_SYSTEM:
eed857b7
LP
88 LIST_APPEND(servers, m->dns_servers, s);
89 m->n_dns_servers++;
0b58db65
LP
90 break;
91
92 case DNS_SERVER_FALLBACK:
eed857b7
LP
93 LIST_APPEND(servers, m->fallback_dns_servers, s);
94 m->n_dns_servers++;
0b58db65
LP
95 break;
96
97 default:
4e945a6f 98 assert_not_reached("Unknown server type");
0b58db65 99 }
74b2466e 100
0eac4623 101 s->linked = true;
74b2466e 102
4e945a6f
LP
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
105 * the new one */
106 if (type != DNS_SERVER_FALLBACK &&
3e684349
LP
107 m->current_dns_server &&
108 m->current_dns_server->type == DNS_SERVER_FALLBACK)
109 manager_set_dns_server(m, NULL);
4e945a6f 110
74b2466e
LP
111 if (ret)
112 *ret = s;
113
114 return 0;
115}
116
91b14d6f 117DnsServer* dns_server_ref(DnsServer *s) {
74b2466e
LP
118 if (!s)
119 return NULL;
120
91b14d6f 121 assert(s->n_ref > 0);
91b14d6f 122 s->n_ref ++;
cab5b059 123
91b14d6f
TG
124 return s;
125}
126
0eac4623 127DnsServer* dns_server_unref(DnsServer *s) {
91b14d6f
TG
128 if (!s)
129 return NULL;
3e684349 130
0eac4623
LP
131 assert(s->n_ref > 0);
132 s->n_ref --;
91b14d6f 133
0eac4623
LP
134 if (s->n_ref > 0)
135 return NULL;
74b2466e 136
74b2466e 137 free(s);
74b2466e
LP
138 return NULL;
139}
87f5a193 140
0eac4623
LP
141void dns_server_unlink(DnsServer *s) {
142 assert(s);
143 assert(s->manager);
91b14d6f 144
0eac4623
LP
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. */
91b14d6f 148
0eac4623
LP
149 if (!s->linked)
150 return;
91b14d6f 151
0eac4623
LP
152 switch (s->type) {
153
154 case DNS_SERVER_LINK:
155 assert(s->link);
eed857b7 156 assert(s->link->n_dns_servers > 0);
0eac4623
LP
157 LIST_REMOVE(servers, s->link->dns_servers, s);
158 break;
159
160 case DNS_SERVER_SYSTEM:
eed857b7 161 assert(s->manager->n_dns_servers > 0);
0eac4623 162 LIST_REMOVE(servers, s->manager->dns_servers, s);
eed857b7 163 s->manager->n_dns_servers--;
0eac4623
LP
164 break;
165
166 case DNS_SERVER_FALLBACK:
eed857b7 167 assert(s->manager->n_dns_servers > 0);
0eac4623 168 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
eed857b7 169 s->manager->n_dns_servers--;
0eac4623
LP
170 break;
171 }
172
173 s->linked = false;
174
175 if (s->link && s->link->current_dns_server == s)
176 link_set_dns_server(s->link, NULL);
177
178 if (s->manager->current_dns_server == s)
179 manager_set_dns_server(s->manager, NULL);
180
181 dns_server_unref(s);
91b14d6f
TG
182}
183
0b58db65
LP
184void dns_server_move_back_and_unmark(DnsServer *s) {
185 DnsServer *tail;
186
187 assert(s);
188
189 if (!s->marked)
190 return;
191
192 s->marked = false;
193
194 if (!s->linked || !s->servers_next)
195 return;
196
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. */
199
200 switch (s->type) {
201
202 case DNS_SERVER_LINK:
203 assert(s->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);
207 break;
208
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);
213 break;
214
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);
219 break;
220
221 default:
222 assert_not_reached("Unknown server type");
223 }
224}
225
be808ea0 226void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt) {
9df3ba6c
TG
227 assert(s);
228
be808ea0
TG
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);
232 }
233
234 if (s->possible_features == features)
235 s->n_failed_attempts = 0;
84129d46 236
be808ea0
TG
237 if (s->max_rtt < rtt) {
238 s->max_rtt = rtt;
239 s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
240 }
9df3ba6c
TG
241}
242
be808ea0 243void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) {
9df3ba6c 244 assert(s);
be808ea0
TG
245 assert(s->manager);
246
247 if (s->possible_features == features)
248 s->n_failed_attempts ++;
9df3ba6c 249
84129d46
LP
250 if (s->resend_timeout > usec)
251 return;
252
253 s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
9df3ba6c
TG
254}
255
be808ea0
TG
256static bool dns_server_grace_period_expired(DnsServer *s) {
257 usec_t ts;
258
259 assert(s);
260 assert(s->manager);
261
262 if (s->verified_usec == 0)
263 return false;
264
265 assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
266
267 if (s->verified_usec + s->features_grace_period_usec > ts)
268 return false;
269
270 s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC);
271
272 return true;
273}
274
275DnsServerFeatureLevel dns_server_possible_features(DnsServer *s) {
276 assert(s);
277
278 if (s->possible_features != DNS_SERVER_FEATURE_LEVEL_BEST &&
279 dns_server_grace_period_expired(s)) {
280 _cleanup_free_ char *ip = NULL;
281
282 s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
283 s->n_failed_attempts = 0;
284 s->verified_usec = 0;
285
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;
293
294 s->possible_features --;
295 s->n_failed_attempts = 0;
296 s->verified_usec = 0;
297
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));
301 }
302
303 return s->possible_features;
304}
305
b826ab58 306static void dns_server_hash_func(const void *p, struct siphash *state) {
87f5a193 307 const DnsServer *s = p;
87f5a193 308
b826ab58 309 assert(s);
87f5a193 310
b826ab58
TG
311 siphash24_compress(&s->family, sizeof(s->family), state);
312 siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
87f5a193
LP
313}
314
d5099efc 315static int dns_server_compare_func(const void *a, const void *b) {
87f5a193
LP
316 const DnsServer *x = a, *y = b;
317
318 if (x->family < y->family)
319 return -1;
320 if (x->family > y->family)
321 return 1;
322
323 return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
324}
d5099efc
MS
325
326const struct hash_ops dns_server_hash_ops = {
327 .hash = dns_server_hash_func,
328 .compare = dns_server_compare_func
329};
636e813d 330
4b95f179
LP
331void dns_server_unlink_all(DnsServer *first) {
332 DnsServer *next;
0eac4623 333
4b95f179
LP
334 if (!first)
335 return;
0eac4623 336
4b95f179
LP
337 next = first->servers_next;
338 dns_server_unlink(first);
636e813d 339
4b95f179 340 dns_server_unlink_all(next);
0eac4623
LP
341}
342
4b95f179
LP
343void dns_server_unlink_marked(DnsServer *first) {
344 DnsServer *next;
636e813d 345
4b95f179
LP
346 if (!first)
347 return;
636e813d 348
4b95f179 349 next = first->servers_next;
636e813d 350
4b95f179 351 if (first->marked)
0eac4623 352 dns_server_unlink(first);
636e813d 353
4b95f179
LP
354 dns_server_unlink_marked(next);
355}
636e813d 356
4b95f179
LP
357void dns_server_mark_all(DnsServer *first) {
358 if (!first)
359 return;
636e813d 360
4b95f179
LP
361 first->marked = true;
362 dns_server_mark_all(first->servers_next);
636e813d
LP
363}
364
4b95f179
LP
365DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
366 DnsServer *s;
636e813d 367
636e813d 368 LIST_FOREACH(servers, s, first)
4b95f179
LP
369 if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
370 return s;
f2f1dbe5 371
4b95f179
LP
372 return NULL;
373}
f2f1dbe5 374
4b95f179 375DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
f2f1dbe5 376 assert(m);
f2f1dbe5 377
4b95f179 378 switch (t) {
f2f1dbe5 379
4b95f179
LP
380 case DNS_SERVER_SYSTEM:
381 return m->dns_servers;
f2f1dbe5 382
4b95f179
LP
383 case DNS_SERVER_FALLBACK:
384 return m->fallback_dns_servers;
385
386 default:
387 return NULL;
388 }
f2f1dbe5
LP
389}
390
391DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
392 assert(m);
393
394 if (m->current_dns_server == s)
395 return s;
396
397 if (s) {
398 _cleanup_free_ char *ip = NULL;
399
400 in_addr_to_string(s->family, &s->address, &ip);
401 log_info("Switching to system DNS server %s.", strna(ip));
402 }
403
0eac4623
LP
404 dns_server_unref(m->current_dns_server);
405 m->current_dns_server = dns_server_ref(s);
f2f1dbe5
LP
406
407 if (m->unicast_scope)
408 dns_cache_flush(&m->unicast_scope->cache);
409
410 return s;
411}
412
413DnsServer *manager_get_dns_server(Manager *m) {
414 Link *l;
415 assert(m);
416
417 /* Try to read updates resolv.conf */
418 manager_read_resolv_conf(m);
419
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);
423
424 if (!m->current_dns_server) {
425 bool found = false;
426 Iterator i;
427
428 /* No DNS servers configured, let's see if there are
429 * any on any links. If not, we use the fallback
430 * servers */
431
432 HASHMAP_FOREACH(l, m->links, i)
433 if (l->dns_servers) {
434 found = true;
435 break;
436 }
437
438 if (!found)
439 manager_set_dns_server(m, m->fallback_dns_servers);
440 }
441
442 return m->current_dns_server;
443}
444
445void manager_next_dns_server(Manager *m) {
446 assert(m);
447
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)
451 return;
452
0eac4623
LP
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) {
f2f1dbe5
LP
456 manager_set_dns_server(m, m->current_dns_server->servers_next);
457 return;
458 }
459
460 /* If there was no next one, then start from the beginning of
461 * the list */
462 if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
463 manager_set_dns_server(m, m->fallback_dns_servers);
464 else
465 manager_set_dns_server(m, m->dns_servers);
466}
be808ea0
TG
467
468static 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",
471};
472DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);