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