]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-server.c
Merge pull request #1937 from evverx/fix-stdout-parsing
[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-util.h"
27
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
32 int dns_server_new(
33 Manager *m,
34 DnsServer **ret,
35 DnsServerType type,
36 Link *l,
37 int family,
38 const union in_addr_union *in_addr) {
39
40 DnsServer *s;
41
42 assert(m);
43 assert((type == DNS_SERVER_LINK) == !!l);
44 assert(in_addr);
45
46 if (!IN_SET(family, AF_INET, AF_INET6))
47 return -EAFNOSUPPORT;
48
49 if (l) {
50 if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
51 return -E2BIG;
52 } else {
53 if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
54 return -E2BIG;
55 }
56
57 s = new0(DnsServer, 1);
58 if (!s)
59 return -ENOMEM;
60
61 s->n_ref = 1;
62 s->manager = m;
63 s->type = type;
64 s->family = family;
65 s->address = *in_addr;
66 s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
67
68 switch (type) {
69
70 case DNS_SERVER_LINK:
71 s->link = l;
72 LIST_APPEND(servers, l->dns_servers, s);
73 l->n_dns_servers++;
74 break;
75
76 case DNS_SERVER_SYSTEM:
77 LIST_APPEND(servers, m->dns_servers, s);
78 m->n_dns_servers++;
79 break;
80
81 case DNS_SERVER_FALLBACK:
82 LIST_APPEND(servers, m->fallback_dns_servers, s);
83 m->n_dns_servers++;
84 break;
85
86 default:
87 assert_not_reached("Unknown server type");
88 }
89
90 s->linked = true;
91
92 /* A new DNS server that isn't fallback is added and the one
93 * we used so far was a fallback one? Then let's try to pick
94 * the new one */
95 if (type != DNS_SERVER_FALLBACK &&
96 m->current_dns_server &&
97 m->current_dns_server->type == DNS_SERVER_FALLBACK)
98 manager_set_dns_server(m, NULL);
99
100 if (ret)
101 *ret = s;
102
103 return 0;
104 }
105
106 DnsServer* dns_server_ref(DnsServer *s) {
107 if (!s)
108 return NULL;
109
110 assert(s->n_ref > 0);
111 s->n_ref ++;
112
113 return s;
114 }
115
116 DnsServer* dns_server_unref(DnsServer *s) {
117 if (!s)
118 return NULL;
119
120 assert(s->n_ref > 0);
121 s->n_ref --;
122
123 if (s->n_ref > 0)
124 return NULL;
125
126 free(s);
127 return NULL;
128 }
129
130 void dns_server_unlink(DnsServer *s) {
131 assert(s);
132 assert(s->manager);
133
134 /* This removes the specified server from the linked list of
135 * servers, but any server might still stay around if it has
136 * refs, for example from an ongoing transaction. */
137
138 if (!s->linked)
139 return;
140
141 switch (s->type) {
142
143 case DNS_SERVER_LINK:
144 assert(s->link);
145 assert(s->link->n_dns_servers > 0);
146 LIST_REMOVE(servers, s->link->dns_servers, s);
147 break;
148
149 case DNS_SERVER_SYSTEM:
150 assert(s->manager->n_dns_servers > 0);
151 LIST_REMOVE(servers, s->manager->dns_servers, s);
152 s->manager->n_dns_servers--;
153 break;
154
155 case DNS_SERVER_FALLBACK:
156 assert(s->manager->n_dns_servers > 0);
157 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
158 s->manager->n_dns_servers--;
159 break;
160 }
161
162 s->linked = false;
163
164 if (s->link && s->link->current_dns_server == s)
165 link_set_dns_server(s->link, NULL);
166
167 if (s->manager->current_dns_server == s)
168 manager_set_dns_server(s->manager, NULL);
169
170 dns_server_unref(s);
171 }
172
173 void dns_server_move_back_and_unmark(DnsServer *s) {
174 DnsServer *tail;
175
176 assert(s);
177
178 if (!s->marked)
179 return;
180
181 s->marked = false;
182
183 if (!s->linked || !s->servers_next)
184 return;
185
186 /* Move us to the end of the list, so that the order is
187 * strictly kept, if we are not at the end anyway. */
188
189 switch (s->type) {
190
191 case DNS_SERVER_LINK:
192 assert(s->link);
193 LIST_FIND_TAIL(servers, s, tail);
194 LIST_REMOVE(servers, s->link->dns_servers, s);
195 LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
196 break;
197
198 case DNS_SERVER_SYSTEM:
199 LIST_FIND_TAIL(servers, s, tail);
200 LIST_REMOVE(servers, s->manager->dns_servers, s);
201 LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
202 break;
203
204 case DNS_SERVER_FALLBACK:
205 LIST_FIND_TAIL(servers, s, tail);
206 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
207 LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
208 break;
209
210 default:
211 assert_not_reached("Unknown server type");
212 }
213 }
214
215 void dns_server_packet_received(DnsServer *s, usec_t rtt) {
216 assert(s);
217
218 if (rtt <= s->max_rtt)
219 return;
220
221 s->max_rtt = rtt;
222 s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
223 }
224
225 void dns_server_packet_lost(DnsServer *s, usec_t usec) {
226 assert(s);
227
228 if (s->resend_timeout > usec)
229 return;
230
231 s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
232 }
233
234 static void dns_server_hash_func(const void *p, struct siphash *state) {
235 const DnsServer *s = p;
236
237 assert(s);
238
239 siphash24_compress(&s->family, sizeof(s->family), state);
240 siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
241 }
242
243 static int dns_server_compare_func(const void *a, const void *b) {
244 const DnsServer *x = a, *y = b;
245
246 if (x->family < y->family)
247 return -1;
248 if (x->family > y->family)
249 return 1;
250
251 return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
252 }
253
254 const struct hash_ops dns_server_hash_ops = {
255 .hash = dns_server_hash_func,
256 .compare = dns_server_compare_func
257 };
258
259 void dns_server_unlink_all(DnsServer *first) {
260 DnsServer *next;
261
262 if (!first)
263 return;
264
265 next = first->servers_next;
266 dns_server_unlink(first);
267
268 dns_server_unlink_all(next);
269 }
270
271 void dns_server_unlink_marked(DnsServer *first) {
272 DnsServer *next;
273
274 if (!first)
275 return;
276
277 next = first->servers_next;
278
279 if (first->marked)
280 dns_server_unlink(first);
281
282 dns_server_unlink_marked(next);
283 }
284
285 void dns_server_mark_all(DnsServer *first) {
286 if (!first)
287 return;
288
289 first->marked = true;
290 dns_server_mark_all(first->servers_next);
291 }
292
293 DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
294 DnsServer *s;
295
296 LIST_FOREACH(servers, s, first)
297 if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
298 return s;
299
300 return NULL;
301 }
302
303 DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
304 assert(m);
305
306 switch (t) {
307
308 case DNS_SERVER_SYSTEM:
309 return m->dns_servers;
310
311 case DNS_SERVER_FALLBACK:
312 return m->fallback_dns_servers;
313
314 default:
315 return NULL;
316 }
317 }
318
319 DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
320 assert(m);
321
322 if (m->current_dns_server == s)
323 return s;
324
325 if (s) {
326 _cleanup_free_ char *ip = NULL;
327
328 in_addr_to_string(s->family, &s->address, &ip);
329 log_info("Switching to system DNS server %s.", strna(ip));
330 }
331
332 dns_server_unref(m->current_dns_server);
333 m->current_dns_server = dns_server_ref(s);
334
335 if (m->unicast_scope)
336 dns_cache_flush(&m->unicast_scope->cache);
337
338 return s;
339 }
340
341 DnsServer *manager_get_dns_server(Manager *m) {
342 Link *l;
343 assert(m);
344
345 /* Try to read updates resolv.conf */
346 manager_read_resolv_conf(m);
347
348 /* If no DNS server was chose so far, pick the first one */
349 if (!m->current_dns_server)
350 manager_set_dns_server(m, m->dns_servers);
351
352 if (!m->current_dns_server) {
353 bool found = false;
354 Iterator i;
355
356 /* No DNS servers configured, let's see if there are
357 * any on any links. If not, we use the fallback
358 * servers */
359
360 HASHMAP_FOREACH(l, m->links, i)
361 if (l->dns_servers) {
362 found = true;
363 break;
364 }
365
366 if (!found)
367 manager_set_dns_server(m, m->fallback_dns_servers);
368 }
369
370 return m->current_dns_server;
371 }
372
373 void manager_next_dns_server(Manager *m) {
374 assert(m);
375
376 /* If there's currently no DNS server set, then the next
377 * manager_get_dns_server() will find one */
378 if (!m->current_dns_server)
379 return;
380
381 /* Change to the next one, but make sure to follow the linked
382 * list only if the server is still linked. */
383 if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
384 manager_set_dns_server(m, m->current_dns_server->servers_next);
385 return;
386 }
387
388 /* If there was no next one, then start from the beginning of
389 * the list */
390 if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
391 manager_set_dns_server(m, m->fallback_dns_servers);
392 else
393 manager_set_dns_server(m, m->dns_servers);
394 }