]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-server.c
resolved: rework dns server lifecycle logic
[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"
f2f1dbe5 26#include "string-util.h"
74b2466e 27
9df3ba6c
TG
28/* After how much time to repeat classic DNS requests */
29#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
30#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
31
74b2466e
LP
32int dns_server_new(
33 Manager *m,
34 DnsServer **ret,
4e945a6f 35 DnsServerType type,
74b2466e 36 Link *l,
0dd25fb9 37 int family,
3c0cf502 38 const union in_addr_union *in_addr) {
74b2466e
LP
39
40 DnsServer *s, *tail;
41
42 assert(m);
4e945a6f 43 assert((type == DNS_SERVER_LINK) == !!l);
74b2466e 44 assert(in_addr);
74b2466e
LP
45
46 s = new0(DnsServer, 1);
47 if (!s)
48 return -ENOMEM;
49
91b14d6f 50 s->n_ref = 1;
4e945a6f 51 s->type = type;
74b2466e
LP
52 s->family = family;
53 s->address = *in_addr;
9df3ba6c 54 s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
74b2466e 55
4e945a6f 56 if (type == DNS_SERVER_LINK) {
6073b6f2
TG
57 LIST_FIND_TAIL(servers, l->dns_servers, tail);
58 LIST_INSERT_AFTER(servers, l->dns_servers, tail, s);
74b2466e 59 s->link = l;
4e945a6f 60 } else if (type == DNS_SERVER_SYSTEM) {
74b2466e
LP
61 LIST_FIND_TAIL(servers, m->dns_servers, tail);
62 LIST_INSERT_AFTER(servers, m->dns_servers, tail, s);
4e945a6f
LP
63 } else if (type == DNS_SERVER_FALLBACK) {
64 LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail);
65 LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s);
66 } else
67 assert_not_reached("Unknown server type");
74b2466e
LP
68
69 s->manager = m;
0eac4623 70 s->linked = true;
74b2466e 71
4e945a6f
LP
72 /* A new DNS server that isn't fallback is added and the one
73 * we used so far was a fallback one? Then let's try to pick
74 * the new one */
75 if (type != DNS_SERVER_FALLBACK &&
3e684349
LP
76 m->current_dns_server &&
77 m->current_dns_server->type == DNS_SERVER_FALLBACK)
78 manager_set_dns_server(m, NULL);
4e945a6f 79
74b2466e
LP
80 if (ret)
81 *ret = s;
82
83 return 0;
84}
85
91b14d6f 86DnsServer* dns_server_ref(DnsServer *s) {
74b2466e
LP
87 if (!s)
88 return NULL;
89
91b14d6f 90 assert(s->n_ref > 0);
91b14d6f 91 s->n_ref ++;
cab5b059 92
91b14d6f
TG
93 return s;
94}
95
0eac4623 96DnsServer* dns_server_unref(DnsServer *s) {
91b14d6f
TG
97 if (!s)
98 return NULL;
3e684349 99
0eac4623
LP
100 assert(s->n_ref > 0);
101 s->n_ref --;
91b14d6f 102
0eac4623
LP
103 if (s->n_ref > 0)
104 return NULL;
74b2466e 105
74b2466e 106 free(s);
74b2466e
LP
107 return NULL;
108}
87f5a193 109
0eac4623
LP
110void dns_server_unlink(DnsServer *s) {
111 assert(s);
112 assert(s->manager);
91b14d6f 113
0eac4623
LP
114 /* This removes the specified server from the linked list of
115 * servers, but any server might still stay around if it has
116 * refs, for example from an ongoing transaction. */
91b14d6f 117
0eac4623
LP
118 if (!s->linked)
119 return;
91b14d6f 120
0eac4623
LP
121 switch (s->type) {
122
123 case DNS_SERVER_LINK:
124 assert(s->link);
125 LIST_REMOVE(servers, s->link->dns_servers, s);
126 break;
127
128 case DNS_SERVER_SYSTEM:
129 LIST_REMOVE(servers, s->manager->dns_servers, s);
130 break;
131
132 case DNS_SERVER_FALLBACK:
133 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
134 break;
135 }
136
137 s->linked = false;
138
139 if (s->link && s->link->current_dns_server == s)
140 link_set_dns_server(s->link, NULL);
141
142 if (s->manager->current_dns_server == s)
143 manager_set_dns_server(s->manager, NULL);
144
145 dns_server_unref(s);
91b14d6f
TG
146}
147
9df3ba6c
TG
148void dns_server_packet_received(DnsServer *s, usec_t rtt) {
149 assert(s);
150
84129d46
LP
151 if (rtt <= s->max_rtt)
152 return;
153
154 s->max_rtt = rtt;
155 s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
9df3ba6c
TG
156}
157
158void dns_server_packet_lost(DnsServer *s, usec_t usec) {
159 assert(s);
160
84129d46
LP
161 if (s->resend_timeout > usec)
162 return;
163
164 s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
9df3ba6c
TG
165}
166
b826ab58 167static void dns_server_hash_func(const void *p, struct siphash *state) {
87f5a193 168 const DnsServer *s = p;
87f5a193 169
b826ab58 170 assert(s);
87f5a193 171
b826ab58
TG
172 siphash24_compress(&s->family, sizeof(s->family), state);
173 siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
87f5a193
LP
174}
175
d5099efc 176static int dns_server_compare_func(const void *a, const void *b) {
87f5a193
LP
177 const DnsServer *x = a, *y = b;
178
179 if (x->family < y->family)
180 return -1;
181 if (x->family > y->family)
182 return 1;
183
184 return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
185}
d5099efc
MS
186
187const struct hash_ops dns_server_hash_ops = {
188 .hash = dns_server_hash_func,
189 .compare = dns_server_compare_func
190};
636e813d 191
0eac4623
LP
192DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
193 assert(m);
194
195 switch (t) {
196
197 case DNS_SERVER_SYSTEM:
198 return m->dns_servers;
199
200 case DNS_SERVER_FALLBACK:
201 return m->fallback_dns_servers;
636e813d 202
0eac4623
LP
203 default:
204 return NULL;
205 }
206}
207
208void manager_flush_dns_servers(Manager *m, DnsServerType type) {
636e813d
LP
209 assert(m);
210
0eac4623
LP
211 for (;;) {
212 DnsServer *first;
636e813d 213
0eac4623
LP
214 first = manager_get_first_dns_server(m, type);
215 if (!first)
216 break;
636e813d 217
0eac4623 218 dns_server_unlink(first);
636e813d
LP
219 }
220}
221
222void manager_flush_marked_dns_servers(Manager *m, DnsServerType type) {
0eac4623 223 DnsServer *first, *s, *next;
636e813d
LP
224
225 assert(m);
226
0eac4623 227 first = manager_get_first_dns_server(m, type);
636e813d 228
0eac4623 229 LIST_FOREACH_SAFE(servers, s, next, first) {
636e813d
LP
230 if (!s->marked)
231 continue;
232
0eac4623 233 dns_server_unlink(s);
636e813d
LP
234 }
235}
236
237void manager_mark_dns_servers(Manager *m, DnsServerType type) {
238 DnsServer *first, *s;
239
240 assert(m);
241
0eac4623 242 first = manager_get_first_dns_server(m, type);
636e813d
LP
243 LIST_FOREACH(servers, s, first)
244 s->marked = true;
245}
f2f1dbe5 246
0eac4623
LP
247DnsServer* manager_find_dns_server(Manager *m, DnsServerType type, int family, const union in_addr_union *in_addr) {
248 DnsServer *first, *s;
f2f1dbe5
LP
249
250 assert(m);
251 assert(in_addr);
252
0eac4623 253 first = manager_get_first_dns_server(m, type);
f2f1dbe5 254
0eac4623 255 LIST_FOREACH(servers, s, first)
f2f1dbe5
LP
256 if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
257 return s;
258
259 return NULL;
260}
261
262DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
263 assert(m);
264
265 if (m->current_dns_server == s)
266 return s;
267
268 if (s) {
269 _cleanup_free_ char *ip = NULL;
270
271 in_addr_to_string(s->family, &s->address, &ip);
272 log_info("Switching to system DNS server %s.", strna(ip));
273 }
274
0eac4623
LP
275 dns_server_unref(m->current_dns_server);
276 m->current_dns_server = dns_server_ref(s);
f2f1dbe5
LP
277
278 if (m->unicast_scope)
279 dns_cache_flush(&m->unicast_scope->cache);
280
281 return s;
282}
283
284DnsServer *manager_get_dns_server(Manager *m) {
285 Link *l;
286 assert(m);
287
288 /* Try to read updates resolv.conf */
289 manager_read_resolv_conf(m);
290
291 /* If no DNS server was chose so far, pick the first one */
292 if (!m->current_dns_server)
293 manager_set_dns_server(m, m->dns_servers);
294
295 if (!m->current_dns_server) {
296 bool found = false;
297 Iterator i;
298
299 /* No DNS servers configured, let's see if there are
300 * any on any links. If not, we use the fallback
301 * servers */
302
303 HASHMAP_FOREACH(l, m->links, i)
304 if (l->dns_servers) {
305 found = true;
306 break;
307 }
308
309 if (!found)
310 manager_set_dns_server(m, m->fallback_dns_servers);
311 }
312
313 return m->current_dns_server;
314}
315
316void manager_next_dns_server(Manager *m) {
317 assert(m);
318
319 /* If there's currently no DNS server set, then the next
320 * manager_get_dns_server() will find one */
321 if (!m->current_dns_server)
322 return;
323
0eac4623
LP
324 /* Change to the next one, but make sure to follow the linked
325 * list only if the server is still linked. */
326 if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
f2f1dbe5
LP
327 manager_set_dns_server(m, m->current_dns_server->servers_next);
328 return;
329 }
330
331 /* If there was no next one, then start from the beginning of
332 * the list */
333 if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
334 manager_set_dns_server(m, m->fallback_dns_servers);
335 else
336 manager_set_dns_server(m, m->dns_servers);
337}