]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-query.c
resolved: properly handle LLMNR/TCP connection errors
[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 "string-util.h"
29
30 /* How long to wait for the query in total */
31 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
32
33 #define CNAME_MAX 8
34 #define QUERIES_MAX 2048
35 #define AUXILIARY_QUERIES_MAX 64
36
37 static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
38 DnsQueryCandidate *c;
39
40 assert(ret);
41 assert(q);
42 assert(s);
43
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
58 static void dns_query_candidate_stop(DnsQueryCandidate *c) {
59 DnsTransaction *t;
60
61 assert(c);
62
63 while ((t = set_steal_first(c->transactions))) {
64 set_remove(t->notify_query_candidates, c);
65 dns_transaction_gc(t);
66 }
67 }
68
69 DnsQueryCandidate* 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
90 static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
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
98 if (!next) /* We hit the end of the list */
99 return 0;
100
101 } else {
102 next = dns_scope_get_search_domains(c->scope);
103
104 if (!next) /* OK, there's nothing. */
105 return 0;
106 }
107
108 dns_search_domain_unref(c->search_domain);
109 c->search_domain = dns_search_domain_ref(next);
110
111 return 1;
112 }
113
114 static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
115 DnsTransaction *t;
116 int r;
117
118 assert(c);
119 assert(key);
120
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;
126 } else {
127 if (set_contains(c->transactions, t))
128 return 0;
129 }
130
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);
136 if (r < 0)
137 goto gc;
138
139 r = set_put(t->notify_query_candidates, c);
140 if (r < 0)
141 goto gc;
142
143 r = set_put(c->transactions, t);
144 if (r < 0) {
145 (void) set_remove(t->notify_query_candidates, c);
146 goto gc;
147 }
148
149 return 1;
150
151 gc:
152 dns_transaction_gc(t);
153 return r;
154 }
155
156 static 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
176 static 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
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
198 case DNS_TRANSACTION_PENDING:
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;
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
221 static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
222 DnsQuestion *question;
223 DnsResourceKey *key;
224 int n = 0, r;
225
226 assert(c);
227
228 dns_query_candidate_stop(c);
229
230 question = dns_query_question_for_protocol(c->query, c->scope->protocol);
231
232 /* Create one transaction per question key */
233 DNS_QUESTION_FOREACH(key, question) {
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
251 fail:
252 dns_query_candidate_stop(c);
253 return r;
254 }
255
256 void dns_query_candidate_notify(DnsQueryCandidate *c) {
257 DnsTransactionState state;
258 int r;
259
260 assert(c);
261
262 state = dns_query_candidate_state(c);
263
264 if (DNS_TRANSACTION_IS_LIVE(state))
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
296 fail:
297 log_warning_errno(r, "Failed to follow search domains: %m");
298 c->error_code = r;
299 dns_query_ready(c->query);
300 }
301
302 static 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
313 static void dns_query_free_candidates(DnsQuery *q) {
314 assert(q);
315
316 while (q->candidates)
317 dns_query_candidate_free(q->candidates);
318 }
319
320 static 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
332 DnsQuery *dns_query_free(DnsQuery *q) {
333 if (!q)
334 return NULL;
335
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
345 dns_query_free_candidates(q);
346
347 dns_question_unref(q->question_idna);
348 dns_question_unref(q->question_utf8);
349
350 dns_query_reset_answer(q);
351
352 sd_bus_message_unref(q->request);
353 sd_bus_track_unref(q->bus_track);
354
355 free(q->request_address_string);
356
357 if (q->manager) {
358 LIST_REMOVE(queries, q->manager->dns_queries, q);
359 q->manager->n_dns_queries--;
360 }
361
362 free(q);
363
364 return NULL;
365 }
366
367 int dns_query_new(
368 Manager *m,
369 DnsQuery **ret,
370 DnsQuestion *question_utf8,
371 DnsQuestion *question_idna,
372 int ifindex, uint64_t flags) {
373
374 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
375 DnsResourceKey *key;
376 bool good = false;
377 int r;
378
379 assert(m);
380
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);
393 if (r < 0)
394 return r;
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;
411
412 if (m->n_dns_queries >= QUERIES_MAX)
413 return -EBUSY;
414
415 q = new0(DnsQuery, 1);
416 if (!q)
417 return -ENOMEM;
418
419 q->question_utf8 = dns_question_ref(question_utf8);
420 q->question_idna = dns_question_ref(question_idna);
421 q->ifindex = ifindex;
422 q->flags = flags;
423 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
424 q->answer_protocol = _DNS_PROTOCOL_INVALID;
425 q->answer_family = AF_UNSPEC;
426
427 /* First dump UTF8 question */
428 DNS_QUESTION_FOREACH(key, question_utf8) {
429 _cleanup_free_ char *p = NULL;
430
431 r = dns_resource_key_to_string(key, &p);
432 if (r < 0)
433 return r;
434
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));
453 }
454
455 LIST_PREPEND(queries, m->dns_queries, q);
456 m->n_dns_queries++;
457 q->manager = m;
458
459 if (ret)
460 *ret = q;
461 q = NULL;
462
463 return 0;
464 }
465
466 int 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
489 static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
490 assert(q);
491 assert(!DNS_TRANSACTION_IS_LIVE(state));
492 assert(DNS_TRANSACTION_IS_LIVE(q->state));
493
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. */
497
498 q->state = state;
499
500 dns_query_stop(q);
501 if (q->complete)
502 q->complete(q);
503 }
504
505 static 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
511 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
512 return 0;
513 }
514
515 static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
516 DnsQueryCandidate *c;
517 int r;
518
519 assert(q);
520 assert(s);
521
522 r = dns_query_candidate_new(&c, q, s);
523 if (r < 0)
524 return r;
525
526 /* If this a single-label domain on DNS, we might append a suitable search domain first. */
527 if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
528 r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna));
529 if (r < 0)
530 goto fail;
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 }
538 }
539
540 r = dns_query_candidate_setup_transactions(c);
541 if (r < 0)
542 goto fail;
543
544 return 0;
545
546 fail:
547 dns_query_candidate_free(c);
548 return r;
549 }
550
551 static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
552 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
553 DnsProtocol protocol;
554 int family, r;
555
556 assert(q);
557 assert(state);
558
559 /* Tries to synthesize localhost RR replies (and others) where appropriate */
560
561 if (!IN_SET(*state,
562 DNS_TRANSACTION_RCODE_FAILURE,
563 DNS_TRANSACTION_NO_SERVERS,
564 DNS_TRANSACTION_TIMEOUT,
565 DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
566 DNS_TRANSACTION_NETWORK_DOWN,
567 DNS_TRANSACTION_NOT_FOUND))
568 return 0;
569
570 r = dns_synthesize_answer(
571 q->manager,
572 q->question_utf8,
573 q->ifindex,
574 q->flags,
575 &answer,
576 &protocol,
577 &family);
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 = protocol;
588 q->answer_family = family;
589 q->answer_authenticated = true;
590
591 *state = DNS_TRANSACTION_SUCCESS;
592
593 return 1;
594 }
595
596 int dns_query_go(DnsQuery *q) {
597 DnsScopeMatch found = DNS_SCOPE_NO;
598 DnsScope *s, *first = NULL;
599 DnsQueryCandidate *c;
600 int r;
601
602 assert(q);
603
604 if (q->state != DNS_TRANSACTION_NULL)
605 return 0;
606
607 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
608 DnsScopeMatch match;
609 const char *name;
610
611 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
612 if (!name)
613 continue;
614
615 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
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
635 if (found == DNS_SCOPE_NO) {
636 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
637
638 dns_query_synthesize_reply(q, &state);
639 dns_query_complete(q, state);
640 return 1;
641 }
642
643 r = dns_query_add_candidate(q, first);
644 if (r < 0)
645 goto fail;
646
647 LIST_FOREACH(scopes, s, first->scopes_next) {
648 DnsScopeMatch match;
649 const char *name;
650
651 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
652 if (!name)
653 continue;
654
655 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
656 if (match < 0)
657 goto fail;
658
659 if (match != found)
660 continue;
661
662 r = dns_query_add_candidate(q, s);
663 if (r < 0)
664 goto fail;
665 }
666
667 q->answer = dns_answer_unref(q->answer);
668 q->answer_rcode = 0;
669 q->answer_family = AF_UNSPEC;
670 q->answer_protocol = _DNS_PROTOCOL_INVALID;
671
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);
678 if (r < 0)
679 goto fail;
680
681 (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
682
683 q->state = DNS_TRANSACTION_PENDING;
684 q->block_ready++;
685
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--;
691 goto fail;
692 }
693 }
694
695 q->block_ready--;
696 dns_query_ready(q);
697
698 return 1;
699
700 fail:
701 dns_query_stop(q);
702 return r;
703 }
704
705 static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
706 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
707 bool has_authenticated = false, has_non_authenticated = false;
708 DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
709 DnsTransaction *t;
710 Iterator i;
711 int r;
712
713 assert(q);
714
715 if (!c) {
716 dns_query_synthesize_reply(q, &state);
717 dns_query_complete(q, state);
718 return;
719 }
720
721 SET_FOREACH(t, c->transactions, i) {
722
723 switch (t->state) {
724
725 case DNS_TRANSACTION_SUCCESS: {
726 /* We found a successfuly reply, merge it into the answer */
727 r = dns_answer_extend(&q->answer, t->answer);
728 if (r < 0) {
729 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
730 return;
731 }
732
733 q->answer_rcode = t->answer_rcode;
734
735 if (t->answer_authenticated) {
736 has_authenticated = true;
737 dnssec_result_authenticated = t->answer_dnssec_result;
738 } else {
739 has_non_authenticated = true;
740 dnssec_result_non_authenticated = t->answer_dnssec_result;
741 }
742
743 state = DNS_TRANSACTION_SUCCESS;
744 break;
745 }
746
747 case DNS_TRANSACTION_NULL:
748 case DNS_TRANSACTION_PENDING:
749 case DNS_TRANSACTION_VALIDATING:
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. */
757
758 if (state == DNS_TRANSACTION_SUCCESS)
759 continue;
760
761 q->answer = dns_answer_unref(q->answer);
762 q->answer_rcode = t->answer_rcode;
763 q->answer_dnssec_result = t->answer_dnssec_result;
764
765 state = t->state;
766 break;
767 }
768 }
769
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
775 q->answer_protocol = c->scope->protocol;
776 q->answer_family = c->scope->family;
777
778 dns_search_domain_unref(q->answer_search_domain);
779 q->answer_search_domain = dns_search_domain_ref(c->search_domain);
780
781 dns_query_synthesize_reply(q, &state);
782 dns_query_complete(q, state);
783 }
784
785 void dns_query_ready(DnsQuery *q) {
786
787 DnsQueryCandidate *bad = NULL, *c;
788 bool pending = false;
789
790 assert(q);
791 assert(DNS_TRANSACTION_IS_LIVE(q->state));
792
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:
808 /* One of the candidates is successful,
809 * let's use it, and copy its data out */
810 dns_query_accept(q, c);
811 return;
812
813 case DNS_TRANSACTION_NULL:
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 */
818 pending = true;
819 break;
820
821 default:
822 /* Any kind of failure */
823 bad = c;
824 break;
825 }
826 }
827
828 if (pending)
829 return;
830
831 dns_query_accept(q, bad);
832 }
833
834 static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
835 _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
836 int r, k;
837
838 assert(q);
839
840 q->n_cname_redirects ++;
841 if (q->n_cname_redirects > CNAME_MAX)
842 return -ELOOP;
843
844 r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
845 if (r < 0)
846 return r;
847 else if (r > 0)
848 log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
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)
862 log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
863 }
864
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;
871
872 dns_question_unref(q->question_utf8);
873 q->question_utf8 = nq_utf8;
874 nq_utf8 = NULL;
875
876 dns_query_free_candidates(q);
877 dns_query_reset_answer(q);
878 q->state = DNS_TRANSACTION_NULL;
879
880 /* Turn off searching for the new name */
881 q->flags |= SD_RESOLVED_NO_SEARCH;
882
883 return 0;
884 }
885
886 int dns_query_process_cname(DnsQuery *q) {
887 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
888 DnsQuestion *question;
889 DnsResourceRecord *rr;
890 int r;
891
892 assert(q);
893
894 if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
895 return DNS_QUERY_NOMATCH;
896
897 question = dns_query_question_for_protocol(q, q->answer_protocol);
898
899 DNS_ANSWER_FOREACH(rr, q->answer) {
900 r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
901 if (r < 0)
902 return r;
903 if (r > 0)
904 return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
905
906 r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
907 if (r < 0)
908 return r;
909 if (r > 0 && !cname)
910 cname = dns_resource_record_ref(rr);
911 }
912
913 if (!cname)
914 return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
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 */
926 r = dns_query_process_cname(q);
927 if (r != DNS_QUERY_NOMATCH)
928 return r;
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
935 return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */
936 }
937
938 static 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
949 int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
950 int r;
951
952 assert(q);
953 assert(m);
954
955 if (!q->bus_track) {
956 r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q);
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 }
967
968 DnsQuestion* 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
985 const 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 }