]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-server.c
5fc7c3187f36d224095f7190e633edcd3d7a14d6
[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->type = type;
75 s->family = family;
76 s->address = *in_addr;
77 s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
78
79 switch (type) {
80
81 case DNS_SERVER_LINK:
82 s->link = l;
83 LIST_APPEND(servers, l->dns_servers, s);
84 l->n_dns_servers++;
85 break;
86
87 case DNS_SERVER_SYSTEM:
88 LIST_APPEND(servers, m->dns_servers, s);
89 m->n_dns_servers++;
90 break;
91
92 case DNS_SERVER_FALLBACK:
93 LIST_APPEND(servers, m->fallback_dns_servers, s);
94 m->n_dns_servers++;
95 break;
96
97 default:
98 assert_not_reached("Unknown server type");
99 }
100
101 s->linked = true;
102
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 &&
107 m->current_dns_server &&
108 m->current_dns_server->type == DNS_SERVER_FALLBACK)
109 manager_set_dns_server(m, NULL);
110
111 if (ret)
112 *ret = s;
113
114 return 0;
115 }
116
117 DnsServer* dns_server_ref(DnsServer *s) {
118 if (!s)
119 return NULL;
120
121 assert(s->n_ref > 0);
122 s->n_ref ++;
123
124 return s;
125 }
126
127 DnsServer* dns_server_unref(DnsServer *s) {
128 if (!s)
129 return NULL;
130
131 assert(s->n_ref > 0);
132 s->n_ref --;
133
134 if (s->n_ref > 0)
135 return NULL;
136
137 free(s);
138 return NULL;
139 }
140
141 void dns_server_unlink(DnsServer *s) {
142 assert(s);
143 assert(s->manager);
144
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. */
148
149 if (!s->linked)
150 return;
151
152 switch (s->type) {
153
154 case DNS_SERVER_LINK:
155 assert(s->link);
156 assert(s->link->n_dns_servers > 0);
157 LIST_REMOVE(servers, s->link->dns_servers, s);
158 break;
159
160 case DNS_SERVER_SYSTEM:
161 assert(s->manager->n_dns_servers > 0);
162 LIST_REMOVE(servers, s->manager->dns_servers, s);
163 s->manager->n_dns_servers--;
164 break;
165
166 case DNS_SERVER_FALLBACK:
167 assert(s->manager->n_dns_servers > 0);
168 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
169 s->manager->n_dns_servers--;
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);
182 }
183
184 void 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
226 void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt) {
227 assert(s);
228
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;
236
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 }
241 }
242
243 void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) {
244 assert(s);
245 assert(s->manager);
246
247 if (s->possible_features == features)
248 s->n_failed_attempts ++;
249
250 if (s->resend_timeout > usec)
251 return;
252
253 s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
254 }
255
256 static 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
275 DnsServerFeatureLevel 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
306 static void dns_server_hash_func(const void *p, struct siphash *state) {
307 const DnsServer *s = p;
308
309 assert(s);
310
311 siphash24_compress(&s->family, sizeof(s->family), state);
312 siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
313 }
314
315 static int dns_server_compare_func(const void *a, const void *b) {
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 }
325
326 const struct hash_ops dns_server_hash_ops = {
327 .hash = dns_server_hash_func,
328 .compare = dns_server_compare_func
329 };
330
331 void dns_server_unlink_all(DnsServer *first) {
332 DnsServer *next;
333
334 if (!first)
335 return;
336
337 next = first->servers_next;
338 dns_server_unlink(first);
339
340 dns_server_unlink_all(next);
341 }
342
343 void dns_server_unlink_marked(DnsServer *first) {
344 DnsServer *next;
345
346 if (!first)
347 return;
348
349 next = first->servers_next;
350
351 if (first->marked)
352 dns_server_unlink(first);
353
354 dns_server_unlink_marked(next);
355 }
356
357 void dns_server_mark_all(DnsServer *first) {
358 if (!first)
359 return;
360
361 first->marked = true;
362 dns_server_mark_all(first->servers_next);
363 }
364
365 DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
366 DnsServer *s;
367
368 LIST_FOREACH(servers, s, first)
369 if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
370 return s;
371
372 return NULL;
373 }
374
375 DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
376 assert(m);
377
378 switch (t) {
379
380 case DNS_SERVER_SYSTEM:
381 return m->dns_servers;
382
383 case DNS_SERVER_FALLBACK:
384 return m->fallback_dns_servers;
385
386 default:
387 return NULL;
388 }
389 }
390
391 DnsServer *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
404 dns_server_unref(m->current_dns_server);
405 m->current_dns_server = dns_server_ref(s);
406
407 if (m->unicast_scope)
408 dns_cache_flush(&m->unicast_scope->cache);
409
410 return s;
411 }
412
413 DnsServer *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
445 void 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
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) {
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 }
467
468 static 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 };
472 DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);