]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-query.c
Merge pull request #18481 from keszybz/rpm-restart-post-trans
[thirdparty/systemd.git] / src / resolve / resolved-dns-query.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "dns-domain.h"
5 #include "dns-type.h"
6 #include "hostname-util.h"
7 #include "local-addresses.h"
8 #include "resolved-dns-query.h"
9 #include "resolved-dns-synthesize.h"
10 #include "resolved-etc-hosts.h"
11 #include "string-util.h"
12
13 #define CNAME_MAX 8
14 #define QUERIES_MAX 2048
15 #define AUXILIARY_QUERIES_MAX 64
16
17 static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
18 DnsQueryCandidate *c;
19
20 assert(ret);
21 assert(q);
22 assert(s);
23
24 c = new(DnsQueryCandidate, 1);
25 if (!c)
26 return -ENOMEM;
27
28 *c = (DnsQueryCandidate) {
29 .n_ref = 1,
30 .query = q,
31 .scope = s,
32 };
33
34 LIST_PREPEND(candidates_by_query, q->candidates, c);
35 LIST_PREPEND(candidates_by_scope, s->query_candidates, c);
36
37 *ret = c;
38 return 0;
39 }
40
41 static void dns_query_candidate_stop(DnsQueryCandidate *c) {
42 DnsTransaction *t;
43
44 assert(c);
45
46 while ((t = set_steal_first(c->transactions))) {
47 set_remove(t->notify_query_candidates, c);
48 set_remove(t->notify_query_candidates_done, c);
49 dns_transaction_gc(t);
50 }
51 }
52
53 static DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
54 if (!c)
55 return NULL;
56
57 dns_query_candidate_stop(c);
58
59 set_free(c->transactions);
60 dns_search_domain_unref(c->search_domain);
61
62 if (c->query)
63 LIST_REMOVE(candidates_by_query, c->query->candidates, c);
64
65 if (c->scope)
66 LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
67
68 return mfree(c);
69 }
70
71 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(DnsQueryCandidate, dns_query_candidate, dns_query_candidate_free);
72
73 static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
74 DnsSearchDomain *next;
75
76 assert(c);
77
78 if (c->search_domain && c->search_domain->linked)
79 next = c->search_domain->domains_next;
80 else
81 next = dns_scope_get_search_domains(c->scope);
82
83 for (;;) {
84 if (!next) /* We hit the end of the list */
85 return 0;
86
87 if (!next->route_only)
88 break;
89
90 /* Skip over route-only domains */
91 next = next->domains_next;
92 }
93
94 dns_search_domain_unref(c->search_domain);
95 c->search_domain = dns_search_domain_ref(next);
96
97 return 1;
98 }
99
100 static int dns_query_candidate_add_transaction(
101 DnsQueryCandidate *c,
102 DnsResourceKey *key,
103 DnsPacket *bypass) {
104
105 _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL;
106 int r;
107
108 assert(c);
109
110 if (key) {
111 /* Regular lookup with a resource key */
112 assert(!bypass);
113
114 t = dns_scope_find_transaction(c->scope, key, c->query->flags);
115 if (!t) {
116 r = dns_transaction_new(&t, c->scope, key, NULL, c->query->flags);
117 if (r < 0)
118 return r;
119 } else if (set_contains(c->transactions, t))
120 return 0;
121 } else {
122 /* "Bypass" lookup with a query packet */
123 assert(bypass);
124
125 r = dns_transaction_new(&t, c->scope, NULL, bypass, c->query->flags);
126 if (r < 0)
127 return r;
128 }
129
130 r = set_ensure_allocated(&t->notify_query_candidates_done, NULL);
131 if (r < 0)
132 return r;
133
134 r = set_ensure_put(&t->notify_query_candidates, NULL, c);
135 if (r < 0)
136 return r;
137
138 r = set_ensure_put(&c->transactions, NULL, t);
139 if (r < 0) {
140 (void) set_remove(t->notify_query_candidates, c);
141 return r;
142 }
143
144 TAKE_PTR(t);
145 return 1;
146 }
147
148 static int dns_query_candidate_go(DnsQueryCandidate *c) {
149 _cleanup_(dns_query_candidate_unrefp) DnsQueryCandidate *keep_c = NULL;
150 DnsTransaction *t;
151 int r;
152 unsigned n = 0;
153
154 assert(c);
155
156 /* Let's keep a reference to the query while we're operating */
157 keep_c = dns_query_candidate_ref(c);
158
159 /* Start the transactions that are not started yet */
160 SET_FOREACH(t, c->transactions) {
161 if (t->state != DNS_TRANSACTION_NULL)
162 continue;
163
164 r = dns_transaction_go(t);
165 if (r < 0)
166 return r;
167
168 n++;
169 }
170
171 /* If there was nothing to start, then let's proceed immediately */
172 if (n == 0)
173 dns_query_candidate_notify(c);
174
175 return 0;
176 }
177
178 static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
179 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
180 DnsTransaction *t;
181
182 assert(c);
183
184 if (c->error_code != 0)
185 return DNS_TRANSACTION_ERRNO;
186
187 SET_FOREACH(t, c->transactions) {
188
189 switch (t->state) {
190
191 case DNS_TRANSACTION_NULL:
192 /* If there's a NULL transaction pending, then
193 * this means not all transactions where
194 * started yet, and we were called from within
195 * the stackframe that is supposed to start
196 * remaining transactions. In this case,
197 * simply claim the candidate is pending. */
198
199 case DNS_TRANSACTION_PENDING:
200 case DNS_TRANSACTION_VALIDATING:
201 /* If there's one transaction currently in
202 * VALIDATING state, then this means there's
203 * also one in PENDING state, hence we can
204 * return PENDING immediately. */
205 return DNS_TRANSACTION_PENDING;
206
207 case DNS_TRANSACTION_SUCCESS:
208 state = t->state;
209 break;
210
211 default:
212 if (state != DNS_TRANSACTION_SUCCESS)
213 state = t->state;
214
215 break;
216 }
217 }
218
219 return state;
220 }
221
222 static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
223 DnsQuestion *question;
224 DnsResourceKey *key;
225 int n = 0, r;
226
227 assert(c);
228
229 dns_query_candidate_stop(c);
230
231 if (c->query->question_bypass) {
232 /* If this is a bypass query, then pass the original query packet along to the transaction */
233
234 assert(dns_question_size(c->query->question_bypass->question) == 1);
235
236 if (!dns_scope_good_key(c->scope, c->query->question_bypass->question->keys[0]))
237 return 0;
238
239 r = dns_query_candidate_add_transaction(c, NULL, c->query->question_bypass);
240 if (r < 0)
241 goto fail;
242
243 return 1;
244 }
245
246 question = dns_query_question_for_protocol(c->query, c->scope->protocol);
247
248 /* Create one transaction per question key */
249 DNS_QUESTION_FOREACH(key, question) {
250 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
251 DnsResourceKey *qkey;
252
253 if (c->search_domain) {
254 r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
255 if (r < 0)
256 goto fail;
257
258 qkey = new_key;
259 } else
260 qkey = key;
261
262 if (!dns_scope_good_key(c->scope, qkey))
263 continue;
264
265 r = dns_query_candidate_add_transaction(c, qkey, NULL);
266 if (r < 0)
267 goto fail;
268
269 n++;
270 }
271
272 return n;
273
274 fail:
275 dns_query_candidate_stop(c);
276 return r;
277 }
278
279 void dns_query_candidate_notify(DnsQueryCandidate *c) {
280 DnsTransactionState state;
281 int r;
282
283 assert(c);
284
285 state = dns_query_candidate_state(c);
286
287 if (DNS_TRANSACTION_IS_LIVE(state))
288 return;
289
290 if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
291
292 r = dns_query_candidate_next_search_domain(c);
293 if (r < 0)
294 goto fail;
295
296 if (r > 0) {
297 /* OK, there's another search domain to try, let's do so. */
298
299 r = dns_query_candidate_setup_transactions(c);
300 if (r < 0)
301 goto fail;
302
303 if (r > 0) {
304 /* New transactions where queued. Start them and wait */
305
306 r = dns_query_candidate_go(c);
307 if (r < 0)
308 goto fail;
309
310 return;
311 }
312 }
313
314 }
315
316 dns_query_ready(c->query);
317 return;
318
319 fail:
320 c->error_code = log_warning_errno(r, "Failed to follow search domains: %m");
321 dns_query_ready(c->query);
322 }
323
324 static void dns_query_stop(DnsQuery *q) {
325 DnsQueryCandidate *c;
326
327 assert(q);
328
329 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
330
331 LIST_FOREACH(candidates_by_query, c, q->candidates)
332 dns_query_candidate_stop(c);
333 }
334
335 static void dns_query_unref_candidates(DnsQuery *q) {
336 assert(q);
337
338 while (q->candidates)
339 dns_query_candidate_unref(q->candidates);
340 }
341
342 static void dns_query_reset_answer(DnsQuery *q) {
343 assert(q);
344
345 q->answer = dns_answer_unref(q->answer);
346 q->answer_rcode = 0;
347 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
348 q->answer_errno = 0;
349 q->answer_authenticated = false;
350 q->answer_protocol = _DNS_PROTOCOL_INVALID;
351 q->answer_family = AF_UNSPEC;
352 q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain);
353 q->answer_full_packet = dns_packet_unref(q->answer_full_packet);
354 }
355
356 DnsQuery *dns_query_free(DnsQuery *q) {
357 if (!q)
358 return NULL;
359
360 while (q->auxiliary_queries)
361 dns_query_free(q->auxiliary_queries);
362
363 if (q->auxiliary_for) {
364 assert(q->auxiliary_for->n_auxiliary_queries > 0);
365 q->auxiliary_for->n_auxiliary_queries--;
366 LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
367 }
368
369 dns_query_unref_candidates(q);
370
371 dns_question_unref(q->question_idna);
372 dns_question_unref(q->question_utf8);
373 dns_packet_unref(q->question_bypass);
374
375 dns_query_reset_answer(q);
376
377 sd_bus_message_unref(q->bus_request);
378 sd_bus_track_unref(q->bus_track);
379
380 if (q->varlink_request) {
381 varlink_set_userdata(q->varlink_request, NULL);
382 varlink_unref(q->varlink_request);
383 }
384
385 if (q->request_packet)
386 hashmap_remove_value(q->stub_listener_extra ?
387 q->stub_listener_extra->queries_by_packet :
388 q->manager->stub_queries_by_packet,
389 q->request_packet,
390 q);
391
392 dns_packet_unref(q->request_packet);
393 dns_answer_unref(q->reply_answer);
394 dns_answer_unref(q->reply_authoritative);
395 dns_answer_unref(q->reply_additional);
396
397 if (q->request_stream) {
398 /* Detach the stream from our query, in case something else keeps a reference to it. */
399 (void) set_remove(q->request_stream->queries, q);
400 q->request_stream = dns_stream_unref(q->request_stream);
401 }
402
403 free(q->request_address_string);
404
405 if (q->manager) {
406 LIST_REMOVE(queries, q->manager->dns_queries, q);
407 q->manager->n_dns_queries--;
408 }
409
410 return mfree(q);
411 }
412
413 int dns_query_new(
414 Manager *m,
415 DnsQuery **ret,
416 DnsQuestion *question_utf8,
417 DnsQuestion *question_idna,
418 DnsPacket *question_bypass,
419 int ifindex,
420 uint64_t flags) {
421
422 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
423 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
424 DnsResourceKey *key;
425 int r;
426
427 assert(m);
428
429 if (question_bypass) {
430 /* It's either a "bypass" query, or a regular one, but can't be both. */
431 if (question_utf8 || question_idna)
432 return -EINVAL;
433
434 } else {
435 bool good = false;
436
437 if (dns_question_size(question_utf8) > 0) {
438 r = dns_question_is_valid_for_query(question_utf8);
439 if (r < 0)
440 return r;
441 if (r == 0)
442 return -EINVAL;
443
444 good = true;
445 }
446
447 /* If the IDNA and UTF8 questions are the same, merge their references */
448 r = dns_question_is_equal(question_idna, question_utf8);
449 if (r < 0)
450 return r;
451 if (r > 0)
452 question_idna = question_utf8;
453 else {
454 if (dns_question_size(question_idna) > 0) {
455 r = dns_question_is_valid_for_query(question_idna);
456 if (r < 0)
457 return r;
458 if (r == 0)
459 return -EINVAL;
460
461 good = true;
462 }
463 }
464
465 if (!good) /* don't allow empty queries */
466 return -EINVAL;
467 }
468
469 if (m->n_dns_queries >= QUERIES_MAX)
470 return -EBUSY;
471
472 q = new(DnsQuery, 1);
473 if (!q)
474 return -ENOMEM;
475
476 *q = (DnsQuery) {
477 .question_utf8 = dns_question_ref(question_utf8),
478 .question_idna = dns_question_ref(question_idna),
479 .question_bypass = dns_packet_ref(question_bypass),
480 .ifindex = ifindex,
481 .flags = flags,
482 .answer_dnssec_result = _DNSSEC_RESULT_INVALID,
483 .answer_protocol = _DNS_PROTOCOL_INVALID,
484 .answer_family = AF_UNSPEC,
485 };
486
487 if (question_bypass) {
488 DNS_QUESTION_FOREACH(key, question_bypass->question)
489 log_debug("Looking up bypass packet for %s.",
490 dns_resource_key_to_string(key, key_str, sizeof key_str));
491 } else {
492 /* First dump UTF8 question */
493 DNS_QUESTION_FOREACH(key, question_utf8)
494 log_debug("Looking up RR for %s.",
495 dns_resource_key_to_string(key, key_str, sizeof key_str));
496
497 /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
498 DNS_QUESTION_FOREACH(key, question_idna) {
499 r = dns_question_contains(question_utf8, key);
500 if (r < 0)
501 return r;
502 if (r > 0)
503 continue;
504
505 log_debug("Looking up IDNA RR for %s.",
506 dns_resource_key_to_string(key, key_str, sizeof key_str));
507 }
508 }
509
510 LIST_PREPEND(queries, m->dns_queries, q);
511 m->n_dns_queries++;
512 q->manager = m;
513
514 if (ret)
515 *ret = q;
516
517 TAKE_PTR(q);
518 return 0;
519 }
520
521 int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
522 assert(q);
523 assert(auxiliary_for);
524
525 /* Ensure that the query is not auxiliary yet, and
526 * nothing else is auxiliary to it either */
527 assert(!q->auxiliary_for);
528 assert(!q->auxiliary_queries);
529
530 /* Ensure that the unit we shall be made auxiliary for isn't
531 * auxiliary itself */
532 assert(!auxiliary_for->auxiliary_for);
533
534 if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
535 return -EAGAIN;
536
537 LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
538 q->auxiliary_for = auxiliary_for;
539
540 auxiliary_for->n_auxiliary_queries++;
541 return 0;
542 }
543
544 void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
545 assert(q);
546 assert(!DNS_TRANSACTION_IS_LIVE(state));
547 assert(DNS_TRANSACTION_IS_LIVE(q->state));
548
549 /* Note that this call might invalidate the query. Callers should hence not attempt to access the
550 * query or transaction after calling this function. */
551
552 q->state = state;
553
554 dns_query_stop(q);
555 if (q->complete)
556 q->complete(q);
557 }
558
559 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
560 DnsQuery *q = userdata;
561
562 assert(s);
563 assert(q);
564
565 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
566 return 0;
567 }
568
569 static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
570 _cleanup_(dns_query_candidate_unrefp) DnsQueryCandidate *c = NULL;
571 int r;
572
573 assert(q);
574 assert(s);
575
576 r = dns_query_candidate_new(&c, q, s);
577 if (r < 0)
578 return r;
579
580 /* If this a single-label domain on DNS, we might append a suitable search domain first. */
581 if (!FLAGS_SET(q->flags, SD_RESOLVED_NO_SEARCH) &&
582 dns_scope_name_wants_search_domain(s, dns_question_first_name(q->question_idna))) {
583 /* OK, we want a search domain now. Let's find one for this scope */
584
585 r = dns_query_candidate_next_search_domain(c);
586 if (r < 0)
587 return r;
588 }
589
590 r = dns_query_candidate_setup_transactions(c);
591 if (r < 0)
592 return r;
593
594 TAKE_PTR(c);
595 return 0;
596 }
597
598 static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
599 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
600 int r;
601
602 assert(q);
603 assert(state);
604
605 /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the
606 * the normal lookup finished. The data from the network hence takes precedence over the data we
607 * synthesize. (But note that many scopes refuse to resolve certain domain names) */
608
609 if (!IN_SET(*state,
610 DNS_TRANSACTION_RCODE_FAILURE,
611 DNS_TRANSACTION_NO_SERVERS,
612 DNS_TRANSACTION_TIMEOUT,
613 DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
614 DNS_TRANSACTION_NETWORK_DOWN,
615 DNS_TRANSACTION_NOT_FOUND))
616 return 0;
617
618 if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
619 return 0;
620
621 r = dns_synthesize_answer(
622 q->manager,
623 q->question_bypass ? q->question_bypass->question : q->question_utf8,
624 q->ifindex,
625 &answer);
626 if (r == -ENXIO) {
627 /* If we get ENXIO this tells us to generate NXDOMAIN unconditionally. */
628
629 dns_query_reset_answer(q);
630 q->answer_rcode = DNS_RCODE_NXDOMAIN;
631 q->answer_protocol = dns_synthesize_protocol(q->flags);
632 q->answer_family = dns_synthesize_family(q->flags);
633 q->answer_authenticated = true;
634 *state = DNS_TRANSACTION_RCODE_FAILURE;
635
636 return 0;
637 }
638 if (r <= 0)
639 return r;
640
641 dns_query_reset_answer(q);
642
643 q->answer = TAKE_PTR(answer);
644 q->answer_rcode = DNS_RCODE_SUCCESS;
645 q->answer_protocol = dns_synthesize_protocol(q->flags);
646 q->answer_family = dns_synthesize_family(q->flags);
647 q->answer_authenticated = true;
648
649 *state = DNS_TRANSACTION_SUCCESS;
650
651 return 1;
652 }
653
654 static int dns_query_try_etc_hosts(DnsQuery *q) {
655 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
656 int r;
657
658 assert(q);
659
660 /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is
661 * done. The data from /etc/hosts hence takes precedence over the network. */
662
663 if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
664 return 0;
665
666 r = manager_etc_hosts_lookup(
667 q->manager,
668 q->question_bypass ? q->question_bypass->question : q->question_utf8,
669 &answer);
670 if (r <= 0)
671 return r;
672
673 dns_query_reset_answer(q);
674
675 q->answer = TAKE_PTR(answer);
676 q->answer_rcode = DNS_RCODE_SUCCESS;
677 q->answer_protocol = dns_synthesize_protocol(q->flags);
678 q->answer_family = dns_synthesize_family(q->flags);
679 q->answer_authenticated = true;
680
681 return 1;
682 }
683
684 int dns_query_go(DnsQuery *q) {
685 DnsScopeMatch found = DNS_SCOPE_NO;
686 DnsScope *s, *first = NULL;
687 DnsQueryCandidate *c;
688 int r;
689
690 assert(q);
691
692 if (q->state != DNS_TRANSACTION_NULL)
693 return 0;
694
695 r = dns_query_try_etc_hosts(q);
696 if (r < 0)
697 return r;
698 if (r > 0) {
699 dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
700 return 1;
701 }
702
703 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
704 DnsScopeMatch match;
705 const char *name;
706
707 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
708 if (!name)
709 continue;
710
711 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
712 if (match < 0) {
713 log_debug("Couldn't check if '%s' matches against scope, ignoring.", name);
714 continue;
715 }
716
717 if (match > found) { /* Does this match better? If so, remember how well it matched, and the first one
718 * that matches this well */
719 found = match;
720 first = s;
721 }
722 }
723
724 if (found == DNS_SCOPE_NO) {
725 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
726
727 r = dns_query_synthesize_reply(q, &state);
728 if (r < 0)
729 return r;
730
731 dns_query_complete(q, state);
732 return 1;
733 }
734
735 r = dns_query_add_candidate(q, first);
736 if (r < 0)
737 goto fail;
738
739 LIST_FOREACH(scopes, s, first->scopes_next) {
740 DnsScopeMatch match;
741 const char *name;
742
743 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
744 if (!name)
745 continue;
746
747 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
748 if (match < 0) {
749 log_debug("Couldn't check if '%s' matches against scope, ignoring.", name);
750 continue;
751 }
752
753 if (match < found)
754 continue;
755
756 r = dns_query_add_candidate(q, s);
757 if (r < 0)
758 goto fail;
759 }
760
761 dns_query_reset_answer(q);
762
763 r = sd_event_add_time_relative(
764 q->manager->event,
765 &q->timeout_event_source,
766 clock_boottime_or_monotonic(),
767 SD_RESOLVED_QUERY_TIMEOUT_USEC,
768 0, on_query_timeout, q);
769 if (r < 0)
770 goto fail;
771
772 (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
773
774 q->state = DNS_TRANSACTION_PENDING;
775 q->block_ready++;
776
777 /* Start the transactions */
778 LIST_FOREACH(candidates_by_query, c, q->candidates) {
779 r = dns_query_candidate_go(c);
780 if (r < 0) {
781 q->block_ready--;
782 goto fail;
783 }
784 }
785
786 q->block_ready--;
787 dns_query_ready(q);
788
789 return 1;
790
791 fail:
792 dns_query_stop(q);
793 return r;
794 }
795
796 static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
797 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
798 bool has_authenticated = false, has_non_authenticated = false;
799 DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
800 DnsTransaction *t;
801 int r;
802
803 assert(q);
804
805 if (!c) {
806 r = dns_query_synthesize_reply(q, &state);
807 if (r < 0)
808 goto fail;
809
810 dns_query_complete(q, state);
811 return;
812 }
813
814 if (c->error_code != 0) {
815 /* If the candidate had an error condition of its own, start with that. */
816 state = DNS_TRANSACTION_ERRNO;
817 q->answer = dns_answer_unref(q->answer);
818 q->answer_rcode = 0;
819 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
820 q->answer_authenticated = false;
821 q->answer_errno = c->error_code;
822 q->answer_full_packet = dns_packet_unref(q->answer_full_packet);
823 }
824
825 SET_FOREACH(t, c->transactions) {
826
827 switch (t->state) {
828
829 case DNS_TRANSACTION_SUCCESS: {
830 /* We found a successful reply, merge it into the answer */
831
832 if (state == DNS_TRANSACTION_SUCCESS) {
833 r = dns_answer_extend(&q->answer, t->answer);
834 if (r < 0)
835 goto fail;
836 } else {
837 /* Override non-successful previous answers */
838 dns_answer_unref(q->answer);
839 q->answer = dns_answer_ref(t->answer);
840 }
841
842 q->answer_rcode = t->answer_rcode;
843 q->answer_errno = 0;
844
845 dns_packet_unref(q->answer_full_packet);
846 q->answer_full_packet = dns_packet_ref(t->received);
847
848 if (t->answer_authenticated) {
849 has_authenticated = true;
850 dnssec_result_authenticated = t->answer_dnssec_result;
851 } else {
852 has_non_authenticated = true;
853 dnssec_result_non_authenticated = t->answer_dnssec_result;
854 }
855
856 state = DNS_TRANSACTION_SUCCESS;
857 break;
858 }
859
860 case DNS_TRANSACTION_NULL:
861 case DNS_TRANSACTION_PENDING:
862 case DNS_TRANSACTION_VALIDATING:
863 case DNS_TRANSACTION_ABORTED:
864 /* Ignore transactions that didn't complete */
865 continue;
866
867 default:
868 /* Any kind of failure? Store the data away, if there's nothing stored yet. */
869 if (state == DNS_TRANSACTION_SUCCESS)
870 continue;
871
872 /* If there's already an authenticated negative reply stored, then prefer that over any unauthenticated one */
873 if (q->answer_authenticated && !t->answer_authenticated)
874 continue;
875
876 dns_answer_unref(q->answer);
877 q->answer = dns_answer_ref(t->answer);
878 q->answer_rcode = t->answer_rcode;
879 q->answer_dnssec_result = t->answer_dnssec_result;
880 q->answer_authenticated = t->answer_authenticated;
881 q->answer_errno = t->answer_errno;
882 dns_packet_unref(q->answer_full_packet);
883 q->answer_full_packet = dns_packet_ref(t->received);
884
885 state = t->state;
886 break;
887 }
888 }
889
890 if (state == DNS_TRANSACTION_SUCCESS) {
891 q->answer_authenticated = has_authenticated && !has_non_authenticated;
892 q->answer_dnssec_result = q->answer_authenticated ? dnssec_result_authenticated : dnssec_result_non_authenticated;
893 }
894
895 q->answer_protocol = c->scope->protocol;
896 q->answer_family = c->scope->family;
897
898 dns_search_domain_unref(q->answer_search_domain);
899 q->answer_search_domain = dns_search_domain_ref(c->search_domain);
900
901 r = dns_query_synthesize_reply(q, &state);
902 if (r < 0)
903 goto fail;
904
905 dns_query_complete(q, state);
906 return;
907
908 fail:
909 q->answer_errno = -r;
910 dns_query_complete(q, DNS_TRANSACTION_ERRNO);
911 }
912
913 void dns_query_ready(DnsQuery *q) {
914
915 DnsQueryCandidate *bad = NULL, *c;
916 bool pending = false;
917
918 assert(q);
919 assert(DNS_TRANSACTION_IS_LIVE(q->state));
920
921 /* Note that this call might invalidate the query. Callers
922 * should hence not attempt to access the query or transaction
923 * after calling this function, unless the block_ready
924 * counter was explicitly bumped before doing so. */
925
926 if (q->block_ready > 0)
927 return;
928
929 LIST_FOREACH(candidates_by_query, c, q->candidates) {
930 DnsTransactionState state;
931
932 state = dns_query_candidate_state(c);
933 switch (state) {
934
935 case DNS_TRANSACTION_SUCCESS:
936 /* One of the candidates is successful,
937 * let's use it, and copy its data out */
938 dns_query_accept(q, c);
939 return;
940
941 case DNS_TRANSACTION_NULL:
942 case DNS_TRANSACTION_PENDING:
943 case DNS_TRANSACTION_VALIDATING:
944 /* One of the candidates is still going on,
945 * let's maybe wait for it */
946 pending = true;
947 break;
948
949 default:
950 /* Any kind of failure */
951 bad = c;
952 break;
953 }
954 }
955
956 if (pending)
957 return;
958
959 dns_query_accept(q, bad);
960 }
961
962 static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
963 _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
964 int r, k;
965
966 assert(q);
967
968 q->n_cname_redirects++;
969 if (q->n_cname_redirects > CNAME_MAX)
970 return -ELOOP;
971
972 r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
973 if (r < 0)
974 return r;
975 else if (r > 0)
976 log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
977
978 k = dns_question_is_equal(q->question_idna, q->question_utf8);
979 if (k < 0)
980 return r;
981 if (k > 0) {
982 /* Same question? Shortcut new question generation */
983 nq_utf8 = dns_question_ref(nq_idna);
984 k = r;
985 } else {
986 k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
987 if (k < 0)
988 return k;
989 else if (k > 0)
990 log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
991 }
992
993 if (r == 0 && k == 0) /* No actual cname happened? */
994 return -ELOOP;
995
996 if (q->answer_protocol == DNS_PROTOCOL_DNS)
997 /* Don't permit CNAME redirects from unicast DNS to LLMNR or MulticastDNS, so that global resources
998 * cannot invade the local namespace. The opposite way we permit: local names may redirect to global
999 * ones. */
1000 q->flags &= ~(SD_RESOLVED_LLMNR|SD_RESOLVED_MDNS); /* mask away the local protocols */
1001
1002 /* Turn off searching for the new name */
1003 q->flags |= SD_RESOLVED_NO_SEARCH;
1004
1005 dns_question_unref(q->question_idna);
1006 q->question_idna = TAKE_PTR(nq_idna);
1007
1008 dns_question_unref(q->question_utf8);
1009 q->question_utf8 = TAKE_PTR(nq_utf8);
1010
1011 dns_query_unref_candidates(q);
1012 dns_query_reset_answer(q);
1013
1014 q->state = DNS_TRANSACTION_NULL;
1015
1016 return 0;
1017 }
1018
1019 int dns_query_process_cname(DnsQuery *q) {
1020 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
1021 DnsQuestion *question;
1022 DnsResourceRecord *rr;
1023 int r;
1024
1025 assert(q);
1026
1027 if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
1028 return DNS_QUERY_NOMATCH;
1029
1030 question = dns_query_question_for_protocol(q, q->answer_protocol);
1031
1032 DNS_ANSWER_FOREACH(rr, q->answer) {
1033 r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
1034 if (r < 0)
1035 return r;
1036 if (r > 0)
1037 return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
1038
1039 r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
1040 if (r < 0)
1041 return r;
1042 if (r > 0 && !cname)
1043 cname = dns_resource_record_ref(rr);
1044 }
1045
1046 if (!cname)
1047 return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
1048
1049 if (q->flags & SD_RESOLVED_NO_CNAME)
1050 return -ELOOP;
1051
1052 if (!q->answer_authenticated)
1053 q->previous_redirect_unauthenticated = true;
1054
1055 /* OK, let's actually follow the CNAME */
1056 r = dns_query_cname_redirect(q, cname);
1057 if (r < 0)
1058 return r;
1059
1060 /* Let's see if the answer can already answer the new
1061 * redirected question */
1062 r = dns_query_process_cname(q);
1063 if (r != DNS_QUERY_NOMATCH)
1064 return r;
1065
1066 /* OK, it cannot, let's begin with the new query */
1067 r = dns_query_go(q);
1068 if (r < 0)
1069 return r;
1070
1071 return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */
1072 }
1073
1074 DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
1075 assert(q);
1076
1077 if (q->question_bypass)
1078 return q->question_bypass->question;
1079
1080 switch (protocol) {
1081
1082 case DNS_PROTOCOL_DNS:
1083 return q->question_idna;
1084
1085 case DNS_PROTOCOL_MDNS:
1086 case DNS_PROTOCOL_LLMNR:
1087 return q->question_utf8;
1088
1089 default:
1090 return NULL;
1091 }
1092 }
1093
1094 const char *dns_query_string(DnsQuery *q) {
1095 const char *name;
1096 int r;
1097
1098 /* Returns a somewhat useful human-readable lookup key string for this query */
1099
1100 if (q->question_bypass)
1101 return dns_question_first_name(q->question_bypass->question);
1102
1103 if (q->request_address_string)
1104 return q->request_address_string;
1105
1106 if (q->request_address_valid) {
1107 r = in_addr_to_string(q->request_family, &q->request_address, &q->request_address_string);
1108 if (r >= 0)
1109 return q->request_address_string;
1110 }
1111
1112 name = dns_question_first_name(q->question_utf8);
1113 if (name)
1114 return name;
1115
1116 return dns_question_first_name(q->question_idna);
1117 }
1118
1119 bool dns_query_fully_authenticated(DnsQuery *q) {
1120 assert(q);
1121
1122 return q->answer_authenticated && !q->previous_redirect_unauthenticated;
1123 }