]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-query.c
resolved: when synthesizing RR responses, own the name fully
[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
b5efdb8a 22#include "alloc-util.h"
2a1037af 23#include "dns-domain.h"
b5efdb8a 24#include "hostname-util.h"
78c6a153 25#include "local-addresses.h"
74b2466e 26#include "resolved-dns-query.h"
839a4a20 27#include "resolved-dns-synthesize.h"
23b298bc 28#include "string-util.h"
74b2466e 29
0c903ae7 30/* How long to wait for the query in total */
74b2466e 31#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
0c903ae7 32
8ba9fd9c 33#define CNAME_MAX 8
39762fdf 34#define QUERIES_MAX 2048
45ec7efb 35#define AUXILIARY_QUERIES_MAX 64
8ba9fd9c 36
801ad6a6
LP
37static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
38 DnsQueryCandidate *c;
74b2466e 39
801ad6a6 40 assert(ret);
faa133f3 41 assert(q);
801ad6a6 42 assert(s);
74b2466e 43
801ad6a6
LP
44 c = new0(DnsQueryCandidate, 1);
45 if (!c)
46 return -ENOMEM;
47
48 c->query = q;
49 c->scope = s;
50
51 LIST_PREPEND(candidates_by_query, q->candidates, c);
52 LIST_PREPEND(candidates_by_scope, s->query_candidates, c);
53
54 *ret = c;
55 return 0;
56}
57
58static void dns_query_candidate_stop(DnsQueryCandidate *c) {
59 DnsTransaction *t;
74b2466e 60
801ad6a6
LP
61 assert(c);
62
63 while ((t = set_steal_first(c->transactions))) {
547973de 64 set_remove(t->notify_query_candidates, c);
ec2c5e43 65 dns_transaction_gc(t);
74b2466e 66 }
74b2466e
LP
67}
68
801ad6a6
LP
69DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
70
71 if (!c)
72 return NULL;
73
74 dns_query_candidate_stop(c);
75
76 set_free(c->transactions);
77 dns_search_domain_unref(c->search_domain);
78
79 if (c->query)
80 LIST_REMOVE(candidates_by_query, c->query->candidates, c);
81
82 if (c->scope)
83 LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
84
85 free(c);
86
87 return NULL;
88}
89
90static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
801ad6a6
LP
91 DnsSearchDomain *next = NULL;
92
93 assert(c);
94
95 if (c->search_domain && c->search_domain->linked) {
96 next = c->search_domain->domains_next;
97
6627b7e2
LP
98 if (!next) /* We hit the end of the list */
99 return 0;
801ad6a6
LP
100
101 } else {
801ad6a6
LP
102 next = dns_scope_get_search_domains(c->scope);
103
6627b7e2 104 if (!next) /* OK, there's nothing. */
801ad6a6
LP
105 return 0;
106 }
107
108 dns_search_domain_unref(c->search_domain);
109 c->search_domain = dns_search_domain_ref(next);
6627b7e2 110
801ad6a6
LP
111 return 1;
112}
113
114static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
115 DnsTransaction *t;
116 int r;
117
118 assert(c);
119 assert(key);
120
801ad6a6
LP
121 t = dns_scope_find_transaction(c->scope, key, true);
122 if (!t) {
123 r = dns_transaction_new(&t, c->scope, key);
124 if (r < 0)
125 return r;
547973de
LP
126 } else {
127 if (set_contains(c->transactions, t))
128 return 0;
801ad6a6
LP
129 }
130
547973de
LP
131 r = set_ensure_allocated(&c->transactions, NULL);
132 if (r < 0)
133 goto gc;
134
135 r = set_ensure_allocated(&t->notify_query_candidates, NULL);
801ad6a6
LP
136 if (r < 0)
137 goto gc;
138
547973de 139 r = set_put(t->notify_query_candidates, c);
801ad6a6
LP
140 if (r < 0)
141 goto gc;
142
143 r = set_put(c->transactions, t);
144 if (r < 0) {
547973de 145 (void) set_remove(t->notify_query_candidates, c);
801ad6a6
LP
146 goto gc;
147 }
148
547973de 149 return 1;
801ad6a6
LP
150
151gc:
152 dns_transaction_gc(t);
153 return r;
154}
155
156static int dns_query_candidate_go(DnsQueryCandidate *c) {
157 DnsTransaction *t;
158 Iterator i;
159 int r;
160
161 assert(c);
162
163 /* Start the transactions that are not started yet */
164 SET_FOREACH(t, c->transactions, i) {
165 if (t->state != DNS_TRANSACTION_NULL)
166 continue;
167
168 r = dns_transaction_go(t);
169 if (r < 0)
170 return r;
171 }
172
173 return 0;
174}
175
176static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
177 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
178 DnsTransaction *t;
179 Iterator i;
180
181 assert(c);
182
183 if (c->error_code != 0)
184 return DNS_TRANSACTION_RESOURCES;
185
186 SET_FOREACH(t, c->transactions, i) {
187
188 switch (t->state) {
189
5264131a
LP
190 case DNS_TRANSACTION_NULL:
191 /* If there's a NULL transaction pending, then
192 * this means not all transactions where
193 * started yet, and we were called from within
194 * the stackframe that is supposed to start
195 * remaining transactions. In this case,
196 * simply claim the candidate is pending. */
197
801ad6a6 198 case DNS_TRANSACTION_PENDING:
547973de
LP
199 case DNS_TRANSACTION_VALIDATING:
200 /* If there's one transaction currently in
201 * VALIDATING state, then this means there's
202 * also one in PENDING state, hence we can
203 * return PENDING immediately. */
204 return DNS_TRANSACTION_PENDING;
801ad6a6
LP
205
206 case DNS_TRANSACTION_SUCCESS:
207 state = t->state;
208 break;
209
210 default:
211 if (state != DNS_TRANSACTION_SUCCESS)
212 state = t->state;
213
214 break;
215 }
216 }
217
218 return state;
219}
220
221static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
23b298bc 222 DnsQuestion *question;
801ad6a6
LP
223 DnsResourceKey *key;
224 int n = 0, r;
225
226 assert(c);
227
228 dns_query_candidate_stop(c);
229
23b298bc
LP
230 question = dns_query_question_for_protocol(c->query, c->scope->protocol);
231
801ad6a6 232 /* Create one transaction per question key */
23b298bc 233 DNS_QUESTION_FOREACH(key, question) {
801ad6a6
LP
234 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
235
236 if (c->search_domain) {
237 r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
238 if (r < 0)
239 goto fail;
240 }
241
242 r = dns_query_candidate_add_transaction(c, new_key ?: key);
243 if (r < 0)
244 goto fail;
245
246 n++;
247 }
248
249 return n;
250
251fail:
252 dns_query_candidate_stop(c);
253 return r;
254}
255
547973de 256void dns_query_candidate_notify(DnsQueryCandidate *c) {
801ad6a6
LP
257 DnsTransactionState state;
258 int r;
259
260 assert(c);
261
262 state = dns_query_candidate_state(c);
263
547973de 264 if (DNS_TRANSACTION_IS_LIVE(state))
801ad6a6
LP
265 return;
266
267 if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
268
269 r = dns_query_candidate_next_search_domain(c);
270 if (r < 0)
271 goto fail;
272
273 if (r > 0) {
274 /* OK, there's another search domain to try, let's do so. */
275
276 r = dns_query_candidate_setup_transactions(c);
277 if (r < 0)
278 goto fail;
279
280 if (r > 0) {
281 /* New transactions where queued. Start them and wait */
282
283 r = dns_query_candidate_go(c);
284 if (r < 0)
285 goto fail;
286
287 return;
288 }
289 }
290
291 }
292
293 dns_query_ready(c->query);
294 return;
295
296fail:
297 log_warning_errno(r, "Failed to follow search domains: %m");
298 c->error_code = r;
299 dns_query_ready(c->query);
300}
301
302static void dns_query_stop(DnsQuery *q) {
303 DnsQueryCandidate *c;
304
305 assert(q);
306
307 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
308
309 LIST_FOREACH(candidates_by_query, c, q->candidates)
310 dns_query_candidate_stop(c);
311}
312
7820b320
LP
313static void dns_query_free_candidates(DnsQuery *q) {
314 assert(q);
315
316 while (q->candidates)
317 dns_query_candidate_free(q->candidates);
318}
319
320static void dns_query_reset_answer(DnsQuery *q) {
321 assert(q);
322
323 q->answer = dns_answer_unref(q->answer);
324 q->answer_rcode = 0;
325 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
326 q->answer_authenticated = false;
327 q->answer_protocol = _DNS_PROTOCOL_INVALID;
328 q->answer_family = AF_UNSPEC;
329 q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain);
330}
331
74b2466e 332DnsQuery *dns_query_free(DnsQuery *q) {
74b2466e
LP
333 if (!q)
334 return NULL;
335
45ec7efb
LP
336 while (q->auxiliary_queries)
337 dns_query_free(q->auxiliary_queries);
338
339 if (q->auxiliary_for) {
340 assert(q->auxiliary_for->n_auxiliary_queries > 0);
341 q->auxiliary_for->n_auxiliary_queries--;
342 LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
343 }
344
7820b320 345 dns_query_free_candidates(q);
322345fd 346
23b298bc
LP
347 dns_question_unref(q->question_idna);
348 dns_question_unref(q->question_utf8);
7820b320
LP
349
350 dns_query_reset_answer(q);
322345fd 351
ec2c5e43 352 sd_bus_message_unref(q->request);
82bd6ddd 353 sd_bus_track_unref(q->bus_track);
74b2466e 354
23b298bc
LP
355 free(q->request_address_string);
356
39762fdf 357 if (q->manager) {
74b2466e 358 LIST_REMOVE(queries, q->manager->dns_queries, q);
39762fdf
LP
359 q->manager->n_dns_queries--;
360 }
74b2466e 361
74b2466e
LP
362 free(q);
363
364 return NULL;
365}
366
23b298bc
LP
367int dns_query_new(
368 Manager *m,
369 DnsQuery **ret,
370 DnsQuestion *question_utf8,
371 DnsQuestion *question_idna,
372 int ifindex, uint64_t flags) {
373
74b2466e 374 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
23b298bc
LP
375 DnsResourceKey *key;
376 bool good = false;
faa133f3 377 int r;
74b2466e
LP
378
379 assert(m);
380
23b298bc
LP
381 if (dns_question_size(question_utf8) > 0) {
382 r = dns_question_is_valid_for_query(question_utf8);
383 if (r < 0)
384 return r;
385 if (r == 0)
386 return -EINVAL;
387
388 good = true;
389 }
390
391 /* If the IDNA and UTF8 questions are the same, merge their references */
392 r = dns_question_is_equal(question_idna, question_utf8);
faa133f3
LP
393 if (r < 0)
394 return r;
23b298bc
LP
395 if (r > 0)
396 question_idna = question_utf8;
397 else {
398 if (dns_question_size(question_idna) > 0) {
399 r = dns_question_is_valid_for_query(question_idna);
400 if (r < 0)
401 return r;
402 if (r == 0)
403 return -EINVAL;
404
405 good = true;
406 }
407 }
408
409 if (!good) /* don't allow empty queries */
410 return -EINVAL;
74b2466e 411
39762fdf
LP
412 if (m->n_dns_queries >= QUERIES_MAX)
413 return -EBUSY;
414
74b2466e
LP
415 q = new0(DnsQuery, 1);
416 if (!q)
417 return -ENOMEM;
418
23b298bc
LP
419 q->question_utf8 = dns_question_ref(question_utf8);
420 q->question_idna = dns_question_ref(question_idna);
51323288
LP
421 q->ifindex = ifindex;
422 q->flags = flags;
7820b320 423 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
801ad6a6 424 q->answer_protocol = _DNS_PROTOCOL_INVALID;
7820b320 425 q->answer_family = AF_UNSPEC;
322345fd 426
23b298bc
LP
427 /* First dump UTF8 question */
428 DNS_QUESTION_FOREACH(key, question_utf8) {
429 _cleanup_free_ char *p = NULL;
e4501ed4 430
23b298bc 431 r = dns_resource_key_to_string(key, &p);
e4501ed4
LP
432 if (r < 0)
433 return r;
434
23b298bc
LP
435 log_debug("Looking up RR for %s.", strstrip(p));
436 }
437
438 /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
439 DNS_QUESTION_FOREACH(key, question_idna) {
440 _cleanup_free_ char *p = NULL;
441
442 r = dns_question_contains(question_utf8, key);
443 if (r < 0)
444 return r;
445 if (r > 0)
446 continue;
447
448 r = dns_resource_key_to_string(key, &p);
449 if (r < 0)
450 return r;
451
452 log_debug("Looking up IDNA RR for %s.", strstrip(p));
74b2466e
LP
453 }
454
455 LIST_PREPEND(queries, m->dns_queries, q);
39762fdf 456 m->n_dns_queries++;
74b2466e
LP
457 q->manager = m;
458
8ba9fd9c
LP
459 if (ret)
460 *ret = q;
461 q = NULL;
462
463 return 0;
464}
465
45ec7efb
LP
466int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
467 assert(q);
468 assert(auxiliary_for);
469
470 /* Ensure that that the query is not auxiliary yet, and
471 * nothing else is auxiliary to it either */
472 assert(!q->auxiliary_for);
473 assert(!q->auxiliary_queries);
474
475 /* Ensure that the unit we shall be made auxiliary for isn't
476 * auxiliary itself */
477 assert(!auxiliary_for->auxiliary_for);
478
479 if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
480 return -EAGAIN;
481
482 LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
483 q->auxiliary_for = auxiliary_for;
484
485 auxiliary_for->n_auxiliary_queries++;
486 return 0;
487}
488
ec2c5e43 489static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
8ba9fd9c 490 assert(q);
547973de
LP
491 assert(!DNS_TRANSACTION_IS_LIVE(state));
492 assert(DNS_TRANSACTION_IS_LIVE(q->state));
8ba9fd9c 493
322345fd
LP
494 /* Note that this call might invalidate the query. Callers
495 * should hence not attempt to access the query or transaction
496 * after calling this function. */
8ba9fd9c 497
8ba9fd9c
LP
498 q->state = state;
499
322345fd
LP
500 dns_query_stop(q);
501 if (q->complete)
502 q->complete(q);
8ba9fd9c
LP
503}
504
505static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
506 DnsQuery *q = userdata;
507
508 assert(s);
509 assert(q);
510
ec2c5e43 511 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
8ba9fd9c
LP
512 return 0;
513}
514
801ad6a6
LP
515static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
516 DnsQueryCandidate *c;
faa133f3
LP
517 int r;
518
519 assert(q);
ec2c5e43 520 assert(s);
faa133f3 521
801ad6a6 522 r = dns_query_candidate_new(&c, q, s);
faa133f3
LP
523 if (r < 0)
524 return r;
525
801ad6a6 526 /* If this a single-label domain on DNS, we might append a suitable search domain first. */
22f711bb 527 if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
23b298bc 528 r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna));
22f711bb 529 if (r < 0)
801ad6a6 530 goto fail;
22f711bb
LP
531 if (r > 0) {
532 /* OK, we need a search domain now. Let's find one for this scope */
533
534 r = dns_query_candidate_next_search_domain(c);
535 if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
536 goto fail;
537 }
faa133f3
LP
538 }
539
801ad6a6
LP
540 r = dns_query_candidate_setup_transactions(c);
541 if (r < 0)
542 goto fail;
543
faa133f3
LP
544 return 0;
545
801ad6a6
LP
546fail:
547 dns_query_candidate_free(c);
faa133f3
LP
548 return r;
549}
550
78c6a153 551static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
2a1037af 552 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
839a4a20
LP
553 DnsProtocol protocol;
554 int family, r;
2a1037af
LP
555
556 assert(q);
557 assert(state);
558
839a4a20 559 /* Tries to synthesize localhost RR replies (and others) where appropriate */
2a1037af
LP
560
561 if (!IN_SET(*state,
3bbdc31d 562 DNS_TRANSACTION_RCODE_FAILURE,
2a1037af
LP
563 DNS_TRANSACTION_NO_SERVERS,
564 DNS_TRANSACTION_TIMEOUT,
edbcc1fd 565 DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
0791110f
LP
566 DNS_TRANSACTION_NETWORK_DOWN,
567 DNS_TRANSACTION_NOT_FOUND))
78c6a153 568 return 0;
2a1037af 569
839a4a20
LP
570 r = dns_synthesize_answer(
571 q->manager,
572 q->question_utf8,
573 q->ifindex,
574 q->flags,
575 &answer,
576 &protocol,
577 &family);
2a1037af 578
839a4a20
LP
579 if (r <= 0)
580 return r;
2a1037af 581
839a4a20 582 dns_query_reset_answer(q);
2a1037af 583
2a1037af
LP
584 q->answer = answer;
585 answer = NULL;
2a1037af 586 q->answer_rcode = DNS_RCODE_SUCCESS;
839a4a20
LP
587 q->answer_protocol = protocol;
588 q->answer_family = family;
589 q->answer_authenticated = true;
2a1037af
LP
590
591 *state = DNS_TRANSACTION_SUCCESS;
78c6a153
LP
592
593 return 1;
2a1037af
LP
594}
595
322345fd 596int dns_query_go(DnsQuery *q) {
8ba9fd9c
LP
597 DnsScopeMatch found = DNS_SCOPE_NO;
598 DnsScope *s, *first = NULL;
801ad6a6 599 DnsQueryCandidate *c;
8ba9fd9c
LP
600 int r;
601
602 assert(q);
603
ec2c5e43 604 if (q->state != DNS_TRANSACTION_NULL)
8ba9fd9c
LP
605 return 0;
606
8ba9fd9c 607 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
74b2466e 608 DnsScopeMatch match;
23b298bc
LP
609 const char *name;
610
611 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
612 if (!name)
613 continue;
74b2466e 614
51323288 615 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e
LP
616 if (match < 0)
617 return match;
618
619 if (match == DNS_SCOPE_NO)
620 continue;
621
622 found = match;
623
624 if (match == DNS_SCOPE_YES) {
625 first = s;
626 break;
627 } else {
628 assert(match == DNS_SCOPE_MAYBE);
629
630 if (!first)
631 first = s;
632 }
633 }
634
2a1037af
LP
635 if (found == DNS_SCOPE_NO) {
636 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
637
638 dns_query_synthesize_reply(q, &state);
d634711b
LP
639 dns_query_complete(q, state);
640 return 1;
2a1037af 641 }
74b2466e 642
801ad6a6 643 r = dns_query_add_candidate(q, first);
74b2466e 644 if (r < 0)
ec2c5e43 645 goto fail;
74b2466e 646
74b2466e
LP
647 LIST_FOREACH(scopes, s, first->scopes_next) {
648 DnsScopeMatch match;
23b298bc
LP
649 const char *name;
650
651 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
652 if (!name)
653 continue;
74b2466e 654
51323288 655 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e 656 if (match < 0)
ec2c5e43 657 goto fail;
74b2466e
LP
658
659 if (match != found)
660 continue;
661
801ad6a6 662 r = dns_query_add_candidate(q, s);
74b2466e 663 if (r < 0)
ec2c5e43 664 goto fail;
74b2466e
LP
665 }
666
faa133f3 667 q->answer = dns_answer_unref(q->answer);
faa133f3 668 q->answer_rcode = 0;
51323288
LP
669 q->answer_family = AF_UNSPEC;
670 q->answer_protocol = _DNS_PROTOCOL_INVALID;
74b2466e 671
9a015429
LP
672 r = sd_event_add_time(
673 q->manager->event,
674 &q->timeout_event_source,
675 clock_boottime_or_monotonic(),
676 now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
677 on_query_timeout, q);
74b2466e
LP
678 if (r < 0)
679 goto fail;
680
aa4a9deb
LP
681 (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
682
ec2c5e43 683 q->state = DNS_TRANSACTION_PENDING;
faa133f3 684 q->block_ready++;
74b2466e 685
801ad6a6
LP
686 /* Start the transactions */
687 LIST_FOREACH(candidates_by_query, c, q->candidates) {
688 r = dns_query_candidate_go(c);
689 if (r < 0) {
690 q->block_ready--;
ec2c5e43 691 goto fail;
801ad6a6 692 }
74b2466e
LP
693 }
694
faa133f3
LP
695 q->block_ready--;
696 dns_query_ready(q);
322345fd 697
8ba9fd9c 698 return 1;
74b2466e
LP
699
700fail:
8ba9fd9c 701 dns_query_stop(q);
74b2466e
LP
702 return r;
703}
704
801ad6a6 705static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
ec2c5e43 706 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
547973de 707 bool has_authenticated = false, has_non_authenticated = false;
019036a4 708 DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
801ad6a6 709 DnsTransaction *t;
faa133f3 710 Iterator i;
547973de 711 int r;
74b2466e
LP
712
713 assert(q);
714
801ad6a6
LP
715 if (!c) {
716 dns_query_synthesize_reply(q, &state);
717 dns_query_complete(q, state);
74b2466e 718 return;
801ad6a6 719 }
74b2466e 720
801ad6a6 721 SET_FOREACH(t, c->transactions, i) {
74b2466e 722
801ad6a6 723 switch (t->state) {
934e9b10 724
801ad6a6
LP
725 case DNS_TRANSACTION_SUCCESS: {
726 /* We found a successfuly reply, merge it into the answer */
547973de
LP
727 r = dns_answer_extend(&q->answer, t->answer);
728 if (r < 0) {
801ad6a6
LP
729 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
730 return;
731 }
019036a4 732
ae6a4bbf 733 q->answer_rcode = t->answer_rcode;
801ad6a6 734
019036a4 735 if (t->answer_authenticated) {
931851e8 736 has_authenticated = true;
019036a4
LP
737 dnssec_result_authenticated = t->answer_dnssec_result;
738 } else {
931851e8 739 has_non_authenticated = true;
019036a4
LP
740 dnssec_result_non_authenticated = t->answer_dnssec_result;
741 }
931851e8 742
801ad6a6
LP
743 state = DNS_TRANSACTION_SUCCESS;
744 break;
745 }
746
801ad6a6 747 case DNS_TRANSACTION_NULL:
547973de
LP
748 case DNS_TRANSACTION_PENDING:
749 case DNS_TRANSACTION_VALIDATING:
801ad6a6
LP
750 case DNS_TRANSACTION_ABORTED:
751 /* Ignore transactions that didn't complete */
752 continue;
753
754 default:
755 /* Any kind of failure? Store the data away,
756 * if there's nothing stored yet. */
934e9b10 757
019036a4
LP
758 if (state == DNS_TRANSACTION_SUCCESS)
759 continue;
934e9b10 760
d38d5ca6 761 q->answer = dns_answer_unref(q->answer);
019036a4
LP
762 q->answer_rcode = t->answer_rcode;
763 q->answer_dnssec_result = t->answer_dnssec_result;
934e9b10 764
019036a4 765 state = t->state;
801ad6a6 766 break;
74b2466e 767 }
801ad6a6 768 }
74b2466e 769
019036a4
LP
770 if (state == DNS_TRANSACTION_SUCCESS) {
771 q->answer_authenticated = has_authenticated && !has_non_authenticated;
772 q->answer_dnssec_result = q->answer_authenticated ? dnssec_result_authenticated : dnssec_result_non_authenticated;
773 }
774
801ad6a6
LP
775 q->answer_protocol = c->scope->protocol;
776 q->answer_family = c->scope->family;
934e9b10 777
801ad6a6
LP
778 dns_search_domain_unref(q->answer_search_domain);
779 q->answer_search_domain = dns_search_domain_ref(c->search_domain);
7e8e0422 780
801ad6a6
LP
781 dns_query_synthesize_reply(q, &state);
782 dns_query_complete(q, state);
783}
934e9b10 784
801ad6a6 785void dns_query_ready(DnsQuery *q) {
74b2466e 786
801ad6a6
LP
787 DnsQueryCandidate *bad = NULL, *c;
788 bool pending = false;
74b2466e 789
801ad6a6 790 assert(q);
547973de 791 assert(DNS_TRANSACTION_IS_LIVE(q->state));
e4501ed4 792
801ad6a6
LP
793 /* Note that this call might invalidate the query. Callers
794 * should hence not attempt to access the query or transaction
795 * after calling this function, unless the block_ready
796 * counter was explicitly bumped before doing so. */
797
798 if (q->block_ready > 0)
799 return;
800
801 LIST_FOREACH(candidates_by_query, c, q->candidates) {
802 DnsTransactionState state;
803
804 state = dns_query_candidate_state(c);
805 switch (state) {
806
807 case DNS_TRANSACTION_SUCCESS:
547973de 808 /* One of the candidates is successful,
801ad6a6
LP
809 * let's use it, and copy its data out */
810 dns_query_accept(q, c);
e4501ed4
LP
811 return;
812
801ad6a6 813 case DNS_TRANSACTION_NULL:
547973de
LP
814 case DNS_TRANSACTION_PENDING:
815 case DNS_TRANSACTION_VALIDATING:
816 /* One of the candidates is still going on,
817 * let's maybe wait for it */
801ad6a6
LP
818 pending = true;
819 break;
e4501ed4 820
801ad6a6
LP
821 default:
822 /* Any kind of failure */
823 bad = c;
824 break;
825 }
faa133f3 826 }
74b2466e 827
801ad6a6
LP
828 if (pending)
829 return;
2a1037af 830
801ad6a6 831 dns_query_accept(q, bad);
74b2466e 832}
8ba9fd9c 833
45ec7efb 834static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
23b298bc
LP
835 _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
836 int r, k;
8ba9fd9c
LP
837
838 assert(q);
839
45ec7efb 840 q->n_cname_redirects ++;
faa133f3 841 if (q->n_cname_redirects > CNAME_MAX)
8ba9fd9c
LP
842 return -ELOOP;
843
23b298bc 844 r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
faa133f3
LP
845 if (r < 0)
846 return r;
23b298bc 847 else if (r > 0)
8ec76e6a 848 log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
23b298bc
LP
849
850 k = dns_question_is_equal(q->question_idna, q->question_utf8);
851 if (k < 0)
852 return r;
853 if (k > 0) {
854 /* Same question? Shortcut new question generation */
855 nq_utf8 = dns_question_ref(nq_idna);
856 k = r;
857 } else {
858 k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
859 if (k < 0)
860 return k;
861 else if (k > 0)
8ec76e6a 862 log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
23b298bc 863 }
8ba9fd9c 864
23b298bc
LP
865 if (r == 0 && k == 0) /* No actual cname happened? */
866 return -ELOOP;
867
868 dns_question_unref(q->question_idna);
869 q->question_idna = nq_idna;
870 nq_idna = NULL;
bc7669cf 871
23b298bc
LP
872 dns_question_unref(q->question_utf8);
873 q->question_utf8 = nq_utf8;
874 nq_utf8 = NULL;
8ba9fd9c 875
7820b320
LP
876 dns_query_free_candidates(q);
877 dns_query_reset_answer(q);
ec2c5e43 878 q->state = DNS_TRANSACTION_NULL;
322345fd 879
59a89990
LP
880 /* Turn off searching for the new name */
881 q->flags |= SD_RESOLVED_NO_SEARCH;
882
8ba9fd9c
LP
883 return 0;
884}
82bd6ddd 885
45ec7efb
LP
886int dns_query_process_cname(DnsQuery *q) {
887 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
23b298bc 888 DnsQuestion *question;
45ec7efb
LP
889 DnsResourceRecord *rr;
890 int r;
891
892 assert(q);
893
7588460a
TG
894 if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
895 return DNS_QUERY_NOMATCH;
45ec7efb 896
23b298bc 897 question = dns_query_question_for_protocol(q, q->answer_protocol);
45ec7efb 898
23b298bc
LP
899 DNS_ANSWER_FOREACH(rr, q->answer) {
900 r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
45ec7efb
LP
901 if (r < 0)
902 return r;
903 if (r > 0)
7588460a 904 return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
45ec7efb 905
542e0c84 906 r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
45ec7efb
LP
907 if (r < 0)
908 return r;
909 if (r > 0 && !cname)
910 cname = dns_resource_record_ref(rr);
911 }
912
913 if (!cname)
7588460a 914 return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
45ec7efb
LP
915
916 if (q->flags & SD_RESOLVED_NO_CNAME)
917 return -ELOOP;
918
919 /* OK, let's actually follow the CNAME */
920 r = dns_query_cname_redirect(q, cname);
921 if (r < 0)
922 return r;
923
924 /* Let's see if the answer can already answer the new
925 * redirected question */
7588460a
TG
926 r = dns_query_process_cname(q);
927 if (r != DNS_QUERY_NOMATCH)
928 return r;
45ec7efb
LP
929
930 /* OK, it cannot, let's begin with the new query */
931 r = dns_query_go(q);
932 if (r < 0)
933 return r;
934
7588460a 935 return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */
45ec7efb
LP
936}
937
82bd6ddd
LP
938static int on_bus_track(sd_bus_track *t, void *userdata) {
939 DnsQuery *q = userdata;
940
941 assert(t);
942 assert(q);
943
944 log_debug("Client of active query vanished, aborting query.");
945 dns_query_complete(q, DNS_TRANSACTION_ABORTED);
946 return 0;
947}
948
966c66e3 949int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
82bd6ddd
LP
950 int r;
951
952 assert(q);
953 assert(m);
954
955 if (!q->bus_track) {
966c66e3 956 r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q);
82bd6ddd
LP
957 if (r < 0)
958 return r;
959 }
960
961 r = sd_bus_track_add_sender(q->bus_track, m);
962 if (r < 0)
963 return r;
964
965 return 0;
966}
23b298bc
LP
967
968DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
969 assert(q);
970
971 switch (protocol) {
972
973 case DNS_PROTOCOL_DNS:
974 return q->question_idna;
975
976 case DNS_PROTOCOL_MDNS:
977 case DNS_PROTOCOL_LLMNR:
978 return q->question_utf8;
979
980 default:
981 return NULL;
982 }
983}
984
985const char *dns_query_string(DnsQuery *q) {
986 const char *name;
987 int r;
988
989 /* Returns a somewhat useful human-readable lookup key string for this query */
990
991 if (q->request_address_string)
992 return q->request_address_string;
993
994 if (q->request_address_valid) {
995 r = in_addr_to_string(q->request_family, &q->request_address, &q->request_address_string);
996 if (r >= 0)
997 return q->request_address_string;
998 }
999
1000 name = dns_question_first_name(q->question_utf8);
1001 if (name)
1002 return name;
1003
1004 return dns_question_first_name(q->question_idna);
1005}