]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-query.c
machine-id-setup: don't try to read UUID from VM/container manager if we operate...
[thirdparty/systemd.git] / src / resolve / resolved-dns-query.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
a2a416f7
LP
22#include "af-list.h"
23
74b2466e
LP
24#include "resolved-dns-query.h"
25#include "resolved-dns-domain.h"
26
0c903ae7 27/* How long to wait for the query in total */
74b2466e 28#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
0c903ae7 29
8ba9fd9c 30#define CNAME_MAX 8
39762fdf 31#define QUERIES_MAX 2048
8ba9fd9c 32
ec2c5e43
LP
33static void dns_query_stop(DnsQuery *q) {
34 DnsTransaction *t;
74b2466e 35
faa133f3 36 assert(q);
74b2466e 37
ec2c5e43 38 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
74b2466e 39
ec2c5e43
LP
40 while ((t = set_steal_first(q->transactions))) {
41 set_remove(t->queries, q);
42 dns_transaction_gc(t);
74b2466e 43 }
74b2466e
LP
44}
45
46DnsQuery *dns_query_free(DnsQuery *q) {
74b2466e
LP
47 if (!q)
48 return NULL;
49
ec2c5e43
LP
50 dns_query_stop(q);
51 set_free(q->transactions);
322345fd 52
faa133f3
LP
53 dns_question_unref(q->question);
54 dns_answer_unref(q->answer);
322345fd 55
ec2c5e43 56 sd_bus_message_unref(q->request);
82bd6ddd 57 sd_bus_track_unref(q->bus_track);
74b2466e 58
39762fdf 59 if (q->manager) {
74b2466e 60 LIST_REMOVE(queries, q->manager->dns_queries, q);
39762fdf
LP
61 q->manager->n_dns_queries--;
62 }
74b2466e 63
74b2466e
LP
64 free(q);
65
66 return NULL;
67}
68
51323288 69int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) {
74b2466e 70 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
faa133f3
LP
71 unsigned i;
72 int r;
74b2466e
LP
73
74 assert(m);
faa133f3 75 assert(question);
74b2466e 76
faa133f3
LP
77 r = dns_question_is_valid(question);
78 if (r < 0)
79 return r;
74b2466e 80
39762fdf
LP
81 if (m->n_dns_queries >= QUERIES_MAX)
82 return -EBUSY;
83
74b2466e
LP
84 q = new0(DnsQuery, 1);
85 if (!q)
86 return -ENOMEM;
87
faa133f3 88 q->question = dns_question_ref(question);
51323288
LP
89 q->ifindex = ifindex;
90 q->flags = flags;
322345fd 91
faa133f3 92 for (i = 0; i < question->n_keys; i++) {
e4501ed4
LP
93 _cleanup_free_ char *p;
94
95 r = dns_resource_key_to_string(question->keys[i], &p);
96 if (r < 0)
97 return r;
98
99 log_debug("Looking up RR for %s", p);
74b2466e
LP
100 }
101
102 LIST_PREPEND(queries, m->dns_queries, q);
39762fdf 103 m->n_dns_queries++;
74b2466e
LP
104 q->manager = m;
105
8ba9fd9c
LP
106 if (ret)
107 *ret = q;
108 q = NULL;
109
110 return 0;
111}
112
ec2c5e43 113static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
8ba9fd9c 114 assert(q);
ec2c5e43
LP
115 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
116 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
8ba9fd9c 117
322345fd
LP
118 /* Note that this call might invalidate the query. Callers
119 * should hence not attempt to access the query or transaction
120 * after calling this function. */
8ba9fd9c 121
8ba9fd9c
LP
122 q->state = state;
123
322345fd
LP
124 dns_query_stop(q);
125 if (q->complete)
126 q->complete(q);
8ba9fd9c
LP
127}
128
129static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
130 DnsQuery *q = userdata;
131
132 assert(s);
133 assert(q);
134
ec2c5e43 135 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
8ba9fd9c
LP
136 return 0;
137}
138
934e9b10
LP
139static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
140 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
ec2c5e43 141 DnsTransaction *t;
faa133f3
LP
142 int r;
143
144 assert(q);
ec2c5e43 145 assert(s);
faa133f3
LP
146
147 r = set_ensure_allocated(&q->transactions, NULL, NULL);
148 if (r < 0)
149 return r;
150
934e9b10
LP
151 if (key) {
152 question = dns_question_new(1);
153 if (!question)
154 return -ENOMEM;
155
156 r = dns_question_add(question, key);
157 if (r < 0)
158 return r;
159 } else
160 question = dns_question_ref(q->question);
161
dc4d47e2 162 t = dns_scope_find_transaction(s, question, true);
faa133f3 163 if (!t) {
ec2c5e43 164 r = dns_transaction_new(&t, s, question);
faa133f3
LP
165 if (r < 0)
166 return r;
167 }
168
169 r = set_ensure_allocated(&t->queries, NULL, NULL);
170 if (r < 0)
ec2c5e43 171 goto gc;
faa133f3
LP
172
173 r = set_put(t->queries, q);
174 if (r < 0)
ec2c5e43 175 goto gc;
faa133f3
LP
176
177 r = set_put(q->transactions, t);
178 if (r < 0) {
179 set_remove(t->queries, q);
ec2c5e43 180 goto gc;
faa133f3
LP
181 }
182
183 return 0;
184
ec2c5e43
LP
185gc:
186 dns_transaction_gc(t);
faa133f3
LP
187 return r;
188}
189
934e9b10
LP
190static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
191 int r;
192
193 assert(q);
194 assert(s);
195
196 if (s->protocol == DNS_PROTOCOL_MDNS) {
197 r = dns_query_add_transaction(q, s, NULL);
198 if (r < 0)
199 return r;
200 } else {
201 unsigned i;
202
203 /* On DNS and LLMNR we can only send a single
204 * question per datagram, hence issue multiple
205 * transactions. */
206
207 for (i = 0; i < q->question->n_keys; i++) {
208 r = dns_query_add_transaction(q, s, q->question->keys[i]);
209 if (r < 0)
210 return r;
211 }
212 }
213
214 return 0;
215}
216
322345fd 217int dns_query_go(DnsQuery *q) {
8ba9fd9c
LP
218 DnsScopeMatch found = DNS_SCOPE_NO;
219 DnsScope *s, *first = NULL;
ec2c5e43 220 DnsTransaction *t;
faa133f3
LP
221 const char *name;
222 Iterator i;
8ba9fd9c
LP
223 int r;
224
225 assert(q);
226
ec2c5e43 227 if (q->state != DNS_TRANSACTION_NULL)
8ba9fd9c
LP
228 return 0;
229
faa133f3
LP
230 assert(q->question);
231 assert(q->question->n_keys > 0);
232
233 name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
8ba9fd9c
LP
234
235 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
74b2466e
LP
236 DnsScopeMatch match;
237
51323288 238 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e
LP
239 if (match < 0)
240 return match;
241
242 if (match == DNS_SCOPE_NO)
243 continue;
244
245 found = match;
246
247 if (match == DNS_SCOPE_YES) {
248 first = s;
249 break;
250 } else {
251 assert(match == DNS_SCOPE_MAYBE);
252
253 if (!first)
254 first = s;
255 }
256 }
257
258 if (found == DNS_SCOPE_NO)
8ba9fd9c 259 return -ESRCH;
74b2466e 260
934e9b10 261 r = dns_query_add_transaction_split(q, first);
74b2466e 262 if (r < 0)
ec2c5e43 263 goto fail;
74b2466e 264
74b2466e
LP
265 LIST_FOREACH(scopes, s, first->scopes_next) {
266 DnsScopeMatch match;
267
51323288 268 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e 269 if (match < 0)
ec2c5e43 270 goto fail;
74b2466e
LP
271
272 if (match != found)
273 continue;
274
934e9b10 275 r = dns_query_add_transaction_split(q, s);
74b2466e 276 if (r < 0)
ec2c5e43 277 goto fail;
74b2466e
LP
278 }
279
faa133f3
LP
280 q->answer = dns_answer_unref(q->answer);
281 q->answer_ifindex = 0;
282 q->answer_rcode = 0;
51323288
LP
283 q->answer_family = AF_UNSPEC;
284 q->answer_protocol = _DNS_PROTOCOL_INVALID;
74b2466e 285
9a015429
LP
286 r = sd_event_add_time(
287 q->manager->event,
288 &q->timeout_event_source,
289 clock_boottime_or_monotonic(),
290 now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
291 on_query_timeout, q);
74b2466e
LP
292 if (r < 0)
293 goto fail;
294
ec2c5e43 295 q->state = DNS_TRANSACTION_PENDING;
faa133f3 296 q->block_ready++;
74b2466e 297
ec2c5e43 298 /* Start the transactions that are not started yet */
faa133f3 299 SET_FOREACH(t, q->transactions, i) {
ec2c5e43
LP
300 if (t->state != DNS_TRANSACTION_NULL)
301 continue;
302
303 r = dns_transaction_go(t);
304 if (r < 0)
305 goto fail;
74b2466e
LP
306 }
307
faa133f3
LP
308 q->block_ready--;
309 dns_query_ready(q);
322345fd 310
8ba9fd9c 311 return 1;
74b2466e
LP
312
313fail:
8ba9fd9c 314 dns_query_stop(q);
74b2466e
LP
315 return r;
316}
317
faa133f3 318void dns_query_ready(DnsQuery *q) {
ec2c5e43
LP
319 DnsTransaction *t;
320 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
934e9b10
LP
321 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
322 int rcode = 0;
323 DnsScope *scope = NULL;
e4501ed4 324 bool pending = false;
faa133f3 325 Iterator i;
74b2466e
LP
326
327 assert(q);
ec2c5e43 328 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
74b2466e 329
322345fd
LP
330 /* Note that this call might invalidate the query. Callers
331 * should hence not attempt to access the query or transaction
faa133f3 332 * after calling this function, unless the block_ready
322345fd
LP
333 * counter was explicitly bumped before doing so. */
334
faa133f3 335 if (q->block_ready > 0)
74b2466e
LP
336 return;
337
faa133f3 338 SET_FOREACH(t, q->transactions, i) {
74b2466e 339
934e9b10 340 /* If we found a successful answer, ignore all answers from other scopes */
ec2c5e43 341 if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
934e9b10
LP
342 continue;
343
e4501ed4 344 /* One of the transactions is still going on, let's maybe wait for it */
ec2c5e43 345 if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
e4501ed4
LP
346 pending = true;
347 continue;
348 }
74b2466e 349
322345fd
LP
350 /* One of the transactions is successful, let's use
351 * it, and copy its data out */
ec2c5e43 352 if (t->state == DNS_TRANSACTION_SUCCESS) {
934e9b10
LP
353 DnsAnswer *a;
354
faa133f3 355 if (t->received) {
934e9b10
LP
356 rcode = DNS_PACKET_RCODE(t->received);
357 a = t->received->answer;
faa133f3 358 } else {
934e9b10
LP
359 rcode = t->cached_rcode;
360 a = t->cached;
faa133f3 361 }
322345fd 362
ec2c5e43 363 if (state == DNS_TRANSACTION_SUCCESS) {
934e9b10
LP
364 DnsAnswer *merged;
365
366 merged = dns_answer_merge(answer, a);
367 if (!merged) {
ec2c5e43 368 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
934e9b10
LP
369 return;
370 }
371
372 dns_answer_unref(answer);
373 answer = merged;
374 } else {
375 dns_answer_unref(answer);
376 answer = dns_answer_ref(a);
377 }
378
379 scope = t->scope;
ec2c5e43 380 state = DNS_TRANSACTION_SUCCESS;
934e9b10 381 continue;
74b2466e
LP
382 }
383
ad867662
LP
384 /* One of the transactions has failed, let's see
385 * whether we find anything better, but if not, return
934e9b10 386 * its response data */
ec2c5e43 387 if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
934e9b10
LP
388 DnsAnswer *a;
389
7e8e0422 390 if (t->received) {
934e9b10
LP
391 rcode = DNS_PACKET_RCODE(t->received);
392 a = t->received->answer;
7e8e0422 393 } else {
934e9b10
LP
394 rcode = t->cached_rcode;
395 a = t->cached;
7e8e0422
LP
396 }
397
934e9b10
LP
398 dns_answer_unref(answer);
399 answer = dns_answer_ref(a);
400
401 scope = t->scope;
ec2c5e43 402 state = DNS_TRANSACTION_FAILURE;
ad867662
LP
403 continue;
404 }
74b2466e 405
ec2c5e43 406 if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
74b2466e
LP
407 state = t->state;
408 }
409
e4501ed4
LP
410 if (pending) {
411
412 /* If so far we weren't successful, and there's
413 * something still pending, then wait for it */
ec2c5e43 414 if (state != DNS_TRANSACTION_SUCCESS)
e4501ed4
LP
415 return;
416
417 /* If we already were successful, then only wait for
418 * other transactions on the same scope to finish. */
419 SET_FOREACH(t, q->transactions, i) {
ec2c5e43 420 if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
e4501ed4
LP
421 return;
422 }
423 }
424
ec2c5e43 425 if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
934e9b10
LP
426 q->answer = dns_answer_ref(answer);
427 q->answer_rcode = rcode;
428 q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
51323288
LP
429 q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
430 q->answer_family = scope ? scope->family : AF_UNSPEC;
faa133f3 431 }
74b2466e 432
322345fd 433 dns_query_complete(q, state);
74b2466e 434}
8ba9fd9c 435
322345fd 436int dns_query_cname_redirect(DnsQuery *q, const char *name) {
faa133f3
LP
437 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
438 int r;
8ba9fd9c
LP
439
440 assert(q);
441
faa133f3 442 if (q->n_cname_redirects > CNAME_MAX)
8ba9fd9c
LP
443 return -ELOOP;
444
faa133f3
LP
445 r = dns_question_cname_redirect(q->question, name, &nq);
446 if (r < 0)
447 return r;
8ba9fd9c 448
faa133f3
LP
449 dns_question_unref(q->question);
450 q->question = nq;
451 nq = NULL;
8ba9fd9c 452
faa133f3 453 q->n_cname_redirects++;
8ba9fd9c 454
322345fd 455 dns_query_stop(q);
ec2c5e43 456 q->state = DNS_TRANSACTION_NULL;
322345fd 457
8ba9fd9c
LP
458 return 0;
459}
82bd6ddd
LP
460
461static int on_bus_track(sd_bus_track *t, void *userdata) {
462 DnsQuery *q = userdata;
463
464 assert(t);
465 assert(q);
466
467 log_debug("Client of active query vanished, aborting query.");
468 dns_query_complete(q, DNS_TRANSACTION_ABORTED);
469 return 0;
470}
471
472int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) {
473 int r;
474
475 assert(q);
476 assert(m);
477
478 if (!q->bus_track) {
479 r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q);
480 if (r < 0)
481 return r;
482 }
483
484 r = sd_bus_track_add_sender(q->bus_track, m);
485 if (r < 0)
486 return r;
487
488 return 0;
489}