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