]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-server.c
resolved: cache stringified transaction key once per transaction
[thirdparty/systemd.git] / src / resolve / resolved-dns-server.c
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
40 int 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_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;
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
118 DnsServer* 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
128 DnsServer* 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
142 void 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
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
227 void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size) {
228 assert(s);
229
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) {
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;
245
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
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 }
256 }
257
258 void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) {
259 assert(s);
260 assert(s->manager);
261
262 if (s->possible_features == features)
263 s->n_failed_attempts ++;
264
265 if (s->resend_timeout > usec)
266 return;
267
268 s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
269 }
270
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
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
331 static void dns_server_hash_func(const void *p, struct siphash *state) {
332 const DnsServer *s = p;
333
334 assert(s);
335
336 siphash24_compress(&s->family, sizeof(s->family), state);
337 siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
338 }
339
340 static int dns_server_compare_func(const void *a, const void *b) {
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 }
350
351 const struct hash_ops dns_server_hash_ops = {
352 .hash = dns_server_hash_func,
353 .compare = dns_server_compare_func
354 };
355
356 void dns_server_unlink_all(DnsServer *first) {
357 DnsServer *next;
358
359 if (!first)
360 return;
361
362 next = first->servers_next;
363 dns_server_unlink(first);
364
365 dns_server_unlink_all(next);
366 }
367
368 void dns_server_unlink_marked(DnsServer *first) {
369 DnsServer *next;
370
371 if (!first)
372 return;
373
374 next = first->servers_next;
375
376 if (first->marked)
377 dns_server_unlink(first);
378
379 dns_server_unlink_marked(next);
380 }
381
382 void dns_server_mark_all(DnsServer *first) {
383 if (!first)
384 return;
385
386 first->marked = true;
387 dns_server_mark_all(first->servers_next);
388 }
389
390 DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
391 DnsServer *s;
392
393 LIST_FOREACH(servers, s, first)
394 if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
395 return s;
396
397 return NULL;
398 }
399
400 DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
401 assert(m);
402
403 switch (t) {
404
405 case DNS_SERVER_SYSTEM:
406 return m->dns_servers;
407
408 case DNS_SERVER_FALLBACK:
409 return m->fallback_dns_servers;
410
411 default:
412 return NULL;
413 }
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
429 dns_server_unref(m->current_dns_server);
430 m->current_dns_server = dns_server_ref(s);
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
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) {
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 }
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",
496 [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
497 [DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
498 [DNS_SERVER_FEATURE_LEVEL_LARGE] = "UDP+EDNS0+DO+LARGE",
499 };
500 DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);