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