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