]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-query.c
util: introduce fputs_with_space() and make use of it at various places
[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"
dd0bc0f1 28#include "resolved-etc-hosts.h"
23b298bc 29#include "string-util.h"
74b2466e 30
0c903ae7 31/* How long to wait for the query in total */
74b2466e 32#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
0c903ae7 33
8ba9fd9c 34#define CNAME_MAX 8
39762fdf 35#define QUERIES_MAX 2048
45ec7efb 36#define AUXILIARY_QUERIES_MAX 64
8ba9fd9c 37
801ad6a6
LP
38static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
39 DnsQueryCandidate *c;
74b2466e 40
801ad6a6 41 assert(ret);
faa133f3 42 assert(q);
801ad6a6 43 assert(s);
74b2466e 44
801ad6a6
LP
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
59static void dns_query_candidate_stop(DnsQueryCandidate *c) {
60 DnsTransaction *t;
74b2466e 61
801ad6a6
LP
62 assert(c);
63
64 while ((t = set_steal_first(c->transactions))) {
547973de 65 set_remove(t->notify_query_candidates, c);
ec2c5e43 66 dns_transaction_gc(t);
74b2466e 67 }
74b2466e
LP
68}
69
801ad6a6
LP
70DnsQueryCandidate* 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
91static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
801ad6a6
LP
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
6627b7e2
LP
99 if (!next) /* We hit the end of the list */
100 return 0;
801ad6a6
LP
101
102 } else {
801ad6a6
LP
103 next = dns_scope_get_search_domains(c->scope);
104
6627b7e2 105 if (!next) /* OK, there's nothing. */
801ad6a6
LP
106 return 0;
107 }
108
109 dns_search_domain_unref(c->search_domain);
110 c->search_domain = dns_search_domain_ref(next);
6627b7e2 111
801ad6a6
LP
112 return 1;
113}
114
115static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
116 DnsTransaction *t;
117 int r;
118
119 assert(c);
120 assert(key);
121
801ad6a6
LP
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;
547973de
LP
127 } else {
128 if (set_contains(c->transactions, t))
129 return 0;
801ad6a6
LP
130 }
131
547973de
LP
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);
801ad6a6
LP
137 if (r < 0)
138 goto gc;
139
547973de 140 r = set_put(t->notify_query_candidates, c);
801ad6a6
LP
141 if (r < 0)
142 goto gc;
143
144 r = set_put(c->transactions, t);
145 if (r < 0) {
547973de 146 (void) set_remove(t->notify_query_candidates, c);
801ad6a6
LP
147 goto gc;
148 }
149
547973de 150 return 1;
801ad6a6
LP
151
152gc:
153 dns_transaction_gc(t);
154 return r;
155}
156
157static 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
177static 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)
7cc6ed7b 185 return DNS_TRANSACTION_ERRNO;
801ad6a6
LP
186
187 SET_FOREACH(t, c->transactions, i) {
188
189 switch (t->state) {
190
5264131a
LP
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
801ad6a6 199 case DNS_TRANSACTION_PENDING:
547973de
LP
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;
801ad6a6
LP
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
222static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
23b298bc 223 DnsQuestion *question;
801ad6a6
LP
224 DnsResourceKey *key;
225 int n = 0, r;
226
227 assert(c);
228
229 dns_query_candidate_stop(c);
230
23b298bc
LP
231 question = dns_query_question_for_protocol(c->query, c->scope->protocol);
232
801ad6a6 233 /* Create one transaction per question key */
23b298bc 234 DNS_QUESTION_FOREACH(key, question) {
801ad6a6
LP
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
252fail:
253 dns_query_candidate_stop(c);
254 return r;
255}
256
547973de 257void dns_query_candidate_notify(DnsQueryCandidate *c) {
801ad6a6
LP
258 DnsTransactionState state;
259 int r;
260
261 assert(c);
262
263 state = dns_query_candidate_state(c);
264
547973de 265 if (DNS_TRANSACTION_IS_LIVE(state))
801ad6a6
LP
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
297fail:
298 log_warning_errno(r, "Failed to follow search domains: %m");
299 c->error_code = r;
300 dns_query_ready(c->query);
301}
302
303static 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
7820b320
LP
314static void dns_query_free_candidates(DnsQuery *q) {
315 assert(q);
316
317 while (q->candidates)
318 dns_query_candidate_free(q->candidates);
319}
320
321static 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;
7cc6ed7b 327 q->answer_errno = 0;
7820b320
LP
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
74b2466e 334DnsQuery *dns_query_free(DnsQuery *q) {
74b2466e
LP
335 if (!q)
336 return NULL;
337
45ec7efb
LP
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
7820b320 347 dns_query_free_candidates(q);
322345fd 348
23b298bc
LP
349 dns_question_unref(q->question_idna);
350 dns_question_unref(q->question_utf8);
7820b320
LP
351
352 dns_query_reset_answer(q);
322345fd 353
ec2c5e43 354 sd_bus_message_unref(q->request);
82bd6ddd 355 sd_bus_track_unref(q->bus_track);
74b2466e 356
23b298bc
LP
357 free(q->request_address_string);
358
39762fdf 359 if (q->manager) {
74b2466e 360 LIST_REMOVE(queries, q->manager->dns_queries, q);
39762fdf
LP
361 q->manager->n_dns_queries--;
362 }
74b2466e 363
74b2466e
LP
364 free(q);
365
366 return NULL;
367}
368
23b298bc
LP
369int dns_query_new(
370 Manager *m,
371 DnsQuery **ret,
372 DnsQuestion *question_utf8,
373 DnsQuestion *question_idna,
374 int ifindex, uint64_t flags) {
375
74b2466e 376 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
23b298bc
LP
377 DnsResourceKey *key;
378 bool good = false;
faa133f3 379 int r;
74b2466e
LP
380
381 assert(m);
382
23b298bc
LP
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);
faa133f3
LP
395 if (r < 0)
396 return r;
23b298bc
LP
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;
74b2466e 413
39762fdf
LP
414 if (m->n_dns_queries >= QUERIES_MAX)
415 return -EBUSY;
416
74b2466e
LP
417 q = new0(DnsQuery, 1);
418 if (!q)
419 return -ENOMEM;
420
23b298bc
LP
421 q->question_utf8 = dns_question_ref(question_utf8);
422 q->question_idna = dns_question_ref(question_idna);
51323288
LP
423 q->ifindex = ifindex;
424 q->flags = flags;
7820b320 425 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
801ad6a6 426 q->answer_protocol = _DNS_PROTOCOL_INVALID;
7820b320 427 q->answer_family = AF_UNSPEC;
322345fd 428
23b298bc
LP
429 /* First dump UTF8 question */
430 DNS_QUESTION_FOREACH(key, question_utf8) {
431 _cleanup_free_ char *p = NULL;
e4501ed4 432
23b298bc 433 r = dns_resource_key_to_string(key, &p);
e4501ed4
LP
434 if (r < 0)
435 return r;
436
23b298bc
LP
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));
74b2466e
LP
455 }
456
457 LIST_PREPEND(queries, m->dns_queries, q);
39762fdf 458 m->n_dns_queries++;
74b2466e
LP
459 q->manager = m;
460
8ba9fd9c
LP
461 if (ret)
462 *ret = q;
463 q = NULL;
464
465 return 0;
466}
467
45ec7efb
LP
468int 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
ec2c5e43 491static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
8ba9fd9c 492 assert(q);
547973de
LP
493 assert(!DNS_TRANSACTION_IS_LIVE(state));
494 assert(DNS_TRANSACTION_IS_LIVE(q->state));
8ba9fd9c 495
322345fd
LP
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. */
8ba9fd9c 499
8ba9fd9c
LP
500 q->state = state;
501
322345fd
LP
502 dns_query_stop(q);
503 if (q->complete)
504 q->complete(q);
8ba9fd9c
LP
505}
506
507static 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
ec2c5e43 513 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
8ba9fd9c
LP
514 return 0;
515}
516
801ad6a6
LP
517static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
518 DnsQueryCandidate *c;
faa133f3
LP
519 int r;
520
521 assert(q);
ec2c5e43 522 assert(s);
faa133f3 523
801ad6a6 524 r = dns_query_candidate_new(&c, q, s);
faa133f3
LP
525 if (r < 0)
526 return r;
527
801ad6a6 528 /* If this a single-label domain on DNS, we might append a suitable search domain first. */
22f711bb 529 if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
23b298bc 530 r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna));
22f711bb 531 if (r < 0)
801ad6a6 532 goto fail;
22f711bb
LP
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 }
faa133f3
LP
540 }
541
801ad6a6
LP
542 r = dns_query_candidate_setup_transactions(c);
543 if (r < 0)
544 goto fail;
545
faa133f3
LP
546 return 0;
547
801ad6a6
LP
548fail:
549 dns_query_candidate_free(c);
faa133f3
LP
550 return r;
551}
552
78c6a153 553static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
2a1037af 554 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
dd0bc0f1 555 int r;
2a1037af
LP
556
557 assert(q);
558 assert(state);
559
dd0bc0f1
LP
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) */
2a1037af
LP
563
564 if (!IN_SET(*state,
3bbdc31d 565 DNS_TRANSACTION_RCODE_FAILURE,
2a1037af
LP
566 DNS_TRANSACTION_NO_SERVERS,
567 DNS_TRANSACTION_TIMEOUT,
edbcc1fd 568 DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
0791110f
LP
569 DNS_TRANSACTION_NETWORK_DOWN,
570 DNS_TRANSACTION_NOT_FOUND))
78c6a153 571 return 0;
2a1037af 572
839a4a20
LP
573 r = dns_synthesize_answer(
574 q->manager,
575 q->question_utf8,
576 q->ifindex,
dd0bc0f1 577 &answer);
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;
dd0bc0f1
LP
587 q->answer_protocol = dns_synthesize_protocol(q->flags);
588 q->answer_family = dns_synthesize_family(q->flags);
839a4a20 589 q->answer_authenticated = true;
2a1037af
LP
590
591 *state = DNS_TRANSACTION_SUCCESS;
78c6a153
LP
592
593 return 1;
2a1037af
LP
594}
595
dd0bc0f1
LP
596static 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
322345fd 624int dns_query_go(DnsQuery *q) {
8ba9fd9c
LP
625 DnsScopeMatch found = DNS_SCOPE_NO;
626 DnsScope *s, *first = NULL;
801ad6a6 627 DnsQueryCandidate *c;
8ba9fd9c
LP
628 int r;
629
630 assert(q);
631
ec2c5e43 632 if (q->state != DNS_TRANSACTION_NULL)
8ba9fd9c
LP
633 return 0;
634
dd0bc0f1
LP
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
8ba9fd9c 643 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
74b2466e 644 DnsScopeMatch match;
23b298bc
LP
645 const char *name;
646
647 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
648 if (!name)
649 continue;
74b2466e 650
51323288 651 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e
LP
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
2a1037af
LP
671 if (found == DNS_SCOPE_NO) {
672 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
673
7cc6ed7b
LP
674 r = dns_query_synthesize_reply(q, &state);
675 if (r < 0)
676 return r;
677
d634711b
LP
678 dns_query_complete(q, state);
679 return 1;
2a1037af 680 }
74b2466e 681
801ad6a6 682 r = dns_query_add_candidate(q, first);
74b2466e 683 if (r < 0)
ec2c5e43 684 goto fail;
74b2466e 685
74b2466e
LP
686 LIST_FOREACH(scopes, s, first->scopes_next) {
687 DnsScopeMatch match;
23b298bc
LP
688 const char *name;
689
690 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
691 if (!name)
692 continue;
74b2466e 693
51323288 694 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e 695 if (match < 0)
ec2c5e43 696 goto fail;
74b2466e
LP
697
698 if (match != found)
699 continue;
700
801ad6a6 701 r = dns_query_add_candidate(q, s);
74b2466e 702 if (r < 0)
ec2c5e43 703 goto fail;
74b2466e
LP
704 }
705
ab88b6d0 706 dns_query_reset_answer(q);
74b2466e 707
9a015429
LP
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);
74b2466e
LP
714 if (r < 0)
715 goto fail;
716
aa4a9deb
LP
717 (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
718
ec2c5e43 719 q->state = DNS_TRANSACTION_PENDING;
faa133f3 720 q->block_ready++;
74b2466e 721
801ad6a6
LP
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--;
ec2c5e43 727 goto fail;
801ad6a6 728 }
74b2466e
LP
729 }
730
faa133f3
LP
731 q->block_ready--;
732 dns_query_ready(q);
322345fd 733
8ba9fd9c 734 return 1;
74b2466e
LP
735
736fail:
8ba9fd9c 737 dns_query_stop(q);
74b2466e
LP
738 return r;
739}
740
801ad6a6 741static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
ec2c5e43 742 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
547973de 743 bool has_authenticated = false, has_non_authenticated = false;
019036a4 744 DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
801ad6a6 745 DnsTransaction *t;
faa133f3 746 Iterator i;
547973de 747 int r;
74b2466e
LP
748
749 assert(q);
750
801ad6a6 751 if (!c) {
7cc6ed7b
LP
752 r = dns_query_synthesize_reply(q, &state);
753 if (r < 0)
754 goto fail;
755
801ad6a6 756 dns_query_complete(q, state);
74b2466e 757 return;
801ad6a6 758 }
74b2466e 759
a7bf2ada
LP
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
801ad6a6 769 SET_FOREACH(t, c->transactions, i) {
74b2466e 770
801ad6a6 771 switch (t->state) {
934e9b10 772
801ad6a6
LP
773 case DNS_TRANSACTION_SUCCESS: {
774 /* We found a successfuly reply, merge it into the answer */
547973de 775 r = dns_answer_extend(&q->answer, t->answer);
7cc6ed7b
LP
776 if (r < 0)
777 goto fail;
019036a4 778
ae6a4bbf 779 q->answer_rcode = t->answer_rcode;
7cc6ed7b 780 q->answer_errno = 0;
801ad6a6 781
019036a4 782 if (t->answer_authenticated) {
931851e8 783 has_authenticated = true;
019036a4
LP
784 dnssec_result_authenticated = t->answer_dnssec_result;
785 } else {
931851e8 786 has_non_authenticated = true;
019036a4
LP
787 dnssec_result_non_authenticated = t->answer_dnssec_result;
788 }
931851e8 789
801ad6a6
LP
790 state = DNS_TRANSACTION_SUCCESS;
791 break;
792 }
793
801ad6a6 794 case DNS_TRANSACTION_NULL:
547973de
LP
795 case DNS_TRANSACTION_PENDING:
796 case DNS_TRANSACTION_VALIDATING:
801ad6a6
LP
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. */
934e9b10 804
019036a4
LP
805 if (state == DNS_TRANSACTION_SUCCESS)
806 continue;
934e9b10 807
d38d5ca6 808 q->answer = dns_answer_unref(q->answer);
019036a4
LP
809 q->answer_rcode = t->answer_rcode;
810 q->answer_dnssec_result = t->answer_dnssec_result;
7cc6ed7b 811 q->answer_errno = t->answer_errno;
934e9b10 812
019036a4 813 state = t->state;
801ad6a6 814 break;
74b2466e 815 }
801ad6a6 816 }
74b2466e 817
019036a4
LP
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
801ad6a6
LP
823 q->answer_protocol = c->scope->protocol;
824 q->answer_family = c->scope->family;
934e9b10 825
801ad6a6
LP
826 dns_search_domain_unref(q->answer_search_domain);
827 q->answer_search_domain = dns_search_domain_ref(c->search_domain);
7e8e0422 828
7cc6ed7b
LP
829 r = dns_query_synthesize_reply(q, &state);
830 if (r < 0)
831 goto fail;
832
801ad6a6 833 dns_query_complete(q, state);
7cc6ed7b
LP
834 return;
835
836fail:
837 q->answer_errno = -r;
838 dns_query_complete(q, DNS_TRANSACTION_ERRNO);
801ad6a6 839}
934e9b10 840
801ad6a6 841void dns_query_ready(DnsQuery *q) {
74b2466e 842
801ad6a6
LP
843 DnsQueryCandidate *bad = NULL, *c;
844 bool pending = false;
74b2466e 845
801ad6a6 846 assert(q);
547973de 847 assert(DNS_TRANSACTION_IS_LIVE(q->state));
e4501ed4 848
801ad6a6
LP
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:
547973de 864 /* One of the candidates is successful,
801ad6a6
LP
865 * let's use it, and copy its data out */
866 dns_query_accept(q, c);
e4501ed4
LP
867 return;
868
801ad6a6 869 case DNS_TRANSACTION_NULL:
547973de
LP
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 */
801ad6a6
LP
874 pending = true;
875 break;
e4501ed4 876
801ad6a6
LP
877 default:
878 /* Any kind of failure */
879 bad = c;
880 break;
881 }
faa133f3 882 }
74b2466e 883
801ad6a6
LP
884 if (pending)
885 return;
2a1037af 886
801ad6a6 887 dns_query_accept(q, bad);
74b2466e 888}
8ba9fd9c 889
45ec7efb 890static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
23b298bc
LP
891 _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
892 int r, k;
8ba9fd9c
LP
893
894 assert(q);
895
45ec7efb 896 q->n_cname_redirects ++;
faa133f3 897 if (q->n_cname_redirects > CNAME_MAX)
8ba9fd9c
LP
898 return -ELOOP;
899
23b298bc 900 r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
faa133f3
LP
901 if (r < 0)
902 return r;
23b298bc 903 else if (r > 0)
8ec76e6a 904 log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
23b298bc
LP
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)
8ec76e6a 918 log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
23b298bc 919 }
8ba9fd9c 920
23b298bc
LP
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;
bc7669cf 927
23b298bc
LP
928 dns_question_unref(q->question_utf8);
929 q->question_utf8 = nq_utf8;
930 nq_utf8 = NULL;
8ba9fd9c 931
7820b320
LP
932 dns_query_free_candidates(q);
933 dns_query_reset_answer(q);
ec2c5e43 934 q->state = DNS_TRANSACTION_NULL;
322345fd 935
59a89990
LP
936 /* Turn off searching for the new name */
937 q->flags |= SD_RESOLVED_NO_SEARCH;
938
8ba9fd9c
LP
939 return 0;
940}
82bd6ddd 941
45ec7efb
LP
942int dns_query_process_cname(DnsQuery *q) {
943 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
23b298bc 944 DnsQuestion *question;
45ec7efb
LP
945 DnsResourceRecord *rr;
946 int r;
947
948 assert(q);
949
7588460a
TG
950 if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
951 return DNS_QUERY_NOMATCH;
45ec7efb 952
23b298bc 953 question = dns_query_question_for_protocol(q, q->answer_protocol);
45ec7efb 954
23b298bc
LP
955 DNS_ANSWER_FOREACH(rr, q->answer) {
956 r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
45ec7efb
LP
957 if (r < 0)
958 return r;
959 if (r > 0)
7588460a 960 return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
45ec7efb 961
542e0c84 962 r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
45ec7efb
LP
963 if (r < 0)
964 return r;
965 if (r > 0 && !cname)
966 cname = dns_resource_record_ref(rr);
967 }
968
969 if (!cname)
7588460a 970 return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
45ec7efb
LP
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 */
7588460a
TG
982 r = dns_query_process_cname(q);
983 if (r != DNS_QUERY_NOMATCH)
984 return r;
45ec7efb
LP
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
7588460a 991 return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */
45ec7efb
LP
992}
993
82bd6ddd
LP
994static 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
966c66e3 1005int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
82bd6ddd
LP
1006 int r;
1007
1008 assert(q);
1009 assert(m);
1010
1011 if (!q->bus_track) {
966c66e3 1012 r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q);
82bd6ddd
LP
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}
23b298bc
LP
1023
1024DnsQuestion* 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
1041const 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}