]> git.proxmox.com Git - systemd.git/blame - src/resolve/resolved-dns-query.c
New upstream version 249~rc1
[systemd.git] / src / resolve / resolved-dns-query.c
CommitLineData
a032b68d 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
5eef597e 2
db2df898 3#include "alloc-util.h"
13d276d0 4#include "dns-domain.h"
4c89c718 5#include "dns-type.h"
db2df898 6#include "hostname-util.h"
13d276d0 7#include "local-addresses.h"
5eef597e 8#include "resolved-dns-query.h"
4c89c718
MP
9#include "resolved-dns-synthesize.h"
10#include "resolved-etc-hosts.h"
11#include "string-util.h"
5eef597e 12
5eef597e 13#define QUERIES_MAX 2048
4c89c718 14#define AUXILIARY_QUERIES_MAX 64
8b3d4ff0
MB
15#define CNAME_REDIRECTS_MAX 16
16
17assert_cc(AUXILIARY_QUERIES_MAX < UINT8_MAX);
18assert_cc(CNAME_REDIRECTS_MAX < UINT8_MAX);
5eef597e 19
4c89c718
MP
20static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
21 DnsQueryCandidate *c;
5eef597e 22
4c89c718 23 assert(ret);
5eef597e 24 assert(q);
4c89c718 25 assert(s);
5eef597e 26
a032b68d 27 c = new(DnsQueryCandidate, 1);
4c89c718
MP
28 if (!c)
29 return -ENOMEM;
5eef597e 30
a032b68d 31 *c = (DnsQueryCandidate) {
1ce460ce 32 .n_ref = 1,
a032b68d
MB
33 .query = q,
34 .scope = s,
35 };
5eef597e 36
4c89c718
MP
37 LIST_PREPEND(candidates_by_query, q->candidates, c);
38 LIST_PREPEND(candidates_by_scope, s->query_candidates, c);
5eef597e 39
4c89c718
MP
40 *ret = c;
41 return 0;
42}
5eef597e 43
4c89c718
MP
44static void dns_query_candidate_stop(DnsQueryCandidate *c) {
45 DnsTransaction *t;
5eef597e 46
4c89c718 47 assert(c);
5eef597e 48
874c989e
MB
49 /* Detach all the DnsTransactions attached to this query */
50
4c89c718
MP
51 while ((t = set_steal_first(c->transactions))) {
52 set_remove(t->notify_query_candidates, c);
aa27b158 53 set_remove(t->notify_query_candidates_done, c);
4c89c718 54 dns_transaction_gc(t);
5eef597e 55 }
5eef597e
MP
56}
57
874c989e
MB
58static DnsQueryCandidate* dns_query_candidate_unlink(DnsQueryCandidate *c) {
59 assert(c);
60
61 /* Detach this DnsQueryCandidate from the Query and Scope objects */
62
63 if (c->query) {
64 LIST_REMOVE(candidates_by_query, c->query->candidates, c);
65 c->query = NULL;
66 }
67
68 if (c->scope) {
69 LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
70 c->scope = NULL;
71 }
72
73 return c;
74}
75
1ce460ce 76static DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
4c89c718
MP
77 if (!c)
78 return NULL;
5eef597e 79
4c89c718 80 dns_query_candidate_stop(c);
874c989e 81 dns_query_candidate_unlink(c);
5eef597e 82
4c89c718
MP
83 set_free(c->transactions);
84 dns_search_domain_unref(c->search_domain);
5eef597e 85
8a584da2 86 return mfree(c);
5eef597e
MP
87}
88
1ce460ce
MB
89DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(DnsQueryCandidate, dns_query_candidate, dns_query_candidate_free);
90
4c89c718 91static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
1ce460ce 92 DnsSearchDomain *next;
5eef597e 93
4c89c718 94 assert(c);
5eef597e 95
4c89c718
MP
96 if (c->search_domain && c->search_domain->linked)
97 next = c->search_domain->domains_next;
98 else
99 next = dns_scope_get_search_domains(c->scope);
5eef597e 100
4c89c718
MP
101 for (;;) {
102 if (!next) /* We hit the end of the list */
103 return 0;
5eef597e 104
4c89c718
MP
105 if (!next->route_only)
106 break;
5eef597e 107
4c89c718
MP
108 /* Skip over route-only domains */
109 next = next->domains_next;
110 }
5eef597e 111
4c89c718
MP
112 dns_search_domain_unref(c->search_domain);
113 c->search_domain = dns_search_domain_ref(next);
114
115 return 1;
5eef597e
MP
116}
117
3a6ce677
BR
118static int dns_query_candidate_add_transaction(
119 DnsQueryCandidate *c,
120 DnsResourceKey *key,
121 DnsPacket *bypass) {
122
a10f5d05 123 _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL;
5eef597e
MP
124 int r;
125
4c89c718 126 assert(c);
874c989e 127 assert(c->query); /* We shan't add transactions to a candidate that has been detached already */
5eef597e 128
3a6ce677
BR
129 if (key) {
130 /* Regular lookup with a resource key */
131 assert(!bypass);
132
133 t = dns_scope_find_transaction(c->scope, key, c->query->flags);
134 if (!t) {
135 r = dns_transaction_new(&t, c->scope, key, NULL, c->query->flags);
136 if (r < 0)
137 return r;
138 } else if (set_contains(c->transactions, t))
139 return 0;
140 } else {
141 /* "Bypass" lookup with a query packet */
142 assert(bypass);
143
144 r = dns_transaction_new(&t, c->scope, NULL, bypass, c->query->flags);
5eef597e
MP
145 if (r < 0)
146 return r;
3a6ce677 147 }
5eef597e 148
aa27b158
MP
149 r = set_ensure_allocated(&t->notify_query_candidates_done, NULL);
150 if (r < 0)
a10f5d05 151 return r;
aa27b158 152
a10f5d05 153 r = set_ensure_put(&t->notify_query_candidates, NULL, c);
5eef597e 154 if (r < 0)
a10f5d05 155 return r;
5eef597e 156
a10f5d05 157 r = set_ensure_put(&c->transactions, NULL, t);
5eef597e 158 if (r < 0) {
4c89c718 159 (void) set_remove(t->notify_query_candidates, c);
a10f5d05 160 return r;
5eef597e
MP
161 }
162
a10f5d05 163 TAKE_PTR(t);
4c89c718 164 return 1;
5eef597e
MP
165}
166
4c89c718 167static int dns_query_candidate_go(DnsQueryCandidate *c) {
1ce460ce 168 _cleanup_(dns_query_candidate_unrefp) DnsQueryCandidate *keep_c = NULL;
4c89c718 169 DnsTransaction *t;
5eef597e 170 int r;
4c89c718 171 unsigned n = 0;
5eef597e 172
4c89c718 173 assert(c);
5eef597e 174
1ce460ce
MB
175 /* Let's keep a reference to the query while we're operating */
176 keep_c = dns_query_candidate_ref(c);
177
4c89c718 178 /* Start the transactions that are not started yet */
a032b68d 179 SET_FOREACH(t, c->transactions) {
4c89c718
MP
180 if (t->state != DNS_TRANSACTION_NULL)
181 continue;
13d276d0 182
4c89c718 183 r = dns_transaction_go(t);
5eef597e
MP
184 if (r < 0)
185 return r;
4c89c718
MP
186
187 n++;
13d276d0
MP
188 }
189
4c89c718
MP
190 /* If there was nothing to start, then let's proceed immediately */
191 if (n == 0)
192 dns_query_candidate_notify(c);
193
13d276d0
MP
194 return 0;
195}
5eef597e 196
4c89c718
MP
197static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
198 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
199 DnsTransaction *t;
5eef597e 200
4c89c718 201 assert(c);
13d276d0 202
4c89c718
MP
203 if (c->error_code != 0)
204 return DNS_TRANSACTION_ERRNO;
13d276d0 205
8b3d4ff0 206 SET_FOREACH(t, c->transactions)
4c89c718
MP
207
208 switch (t->state) {
209
210 case DNS_TRANSACTION_NULL:
211 /* If there's a NULL transaction pending, then
212 * this means not all transactions where
213 * started yet, and we were called from within
214 * the stackframe that is supposed to start
215 * remaining transactions. In this case,
216 * simply claim the candidate is pending. */
13d276d0 217
4c89c718
MP
218 case DNS_TRANSACTION_PENDING:
219 case DNS_TRANSACTION_VALIDATING:
220 /* If there's one transaction currently in
221 * VALIDATING state, then this means there's
222 * also one in PENDING state, hence we can
223 * return PENDING immediately. */
224 return DNS_TRANSACTION_PENDING;
225
226 case DNS_TRANSACTION_SUCCESS:
227 state = t->state;
228 break;
13d276d0 229
4c89c718
MP
230 default:
231 if (state != DNS_TRANSACTION_SUCCESS)
232 state = t->state;
13d276d0 233
4c89c718
MP
234 break;
235 }
13d276d0 236
4c89c718 237 return state;
13d276d0
MP
238}
239
4c89c718
MP
240static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
241 DnsQuestion *question;
242 DnsResourceKey *key;
243 int n = 0, r;
13d276d0 244
4c89c718 245 assert(c);
874c989e 246 assert(c->query); /* We shan't add transactions to a candidate that has been detached already */
13d276d0 247
4c89c718 248 dns_query_candidate_stop(c);
13d276d0 249
3a6ce677
BR
250 if (c->query->question_bypass) {
251 /* If this is a bypass query, then pass the original query packet along to the transaction */
252
253 assert(dns_question_size(c->query->question_bypass->question) == 1);
254
5b5a102a 255 if (!dns_scope_good_key(c->scope, dns_question_first_key(c->query->question_bypass->question)))
3a6ce677
BR
256 return 0;
257
258 r = dns_query_candidate_add_transaction(c, NULL, c->query->question_bypass);
259 if (r < 0)
260 goto fail;
261
262 return 1;
263 }
264
4c89c718 265 question = dns_query_question_for_protocol(c->query, c->scope->protocol);
13d276d0 266
4c89c718
MP
267 /* Create one transaction per question key */
268 DNS_QUESTION_FOREACH(key, question) {
269 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
270 DnsResourceKey *qkey;
271
4c89c718
MP
272 if (c->search_domain) {
273 r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
274 if (r < 0)
275 goto fail;
276
277 qkey = new_key;
278 } else
279 qkey = key;
280
281 if (!dns_scope_good_key(c->scope, qkey))
282 continue;
283
3a6ce677 284 r = dns_query_candidate_add_transaction(c, qkey, NULL);
4c89c718
MP
285 if (r < 0)
286 goto fail;
287
288 n++;
13d276d0 289 }
4c89c718
MP
290
291 return n;
292
293fail:
294 dns_query_candidate_stop(c);
295 return r;
13d276d0
MP
296}
297
4c89c718
MP
298void dns_query_candidate_notify(DnsQueryCandidate *c) {
299 DnsTransactionState state;
13d276d0
MP
300 int r;
301
4c89c718 302 assert(c);
13d276d0 303
874c989e
MB
304 if (!c->query) /* This candidate has been abandoned, do nothing. */
305 return;
306
4c89c718 307 state = dns_query_candidate_state(c);
13d276d0 308
4c89c718
MP
309 if (DNS_TRANSACTION_IS_LIVE(state))
310 return;
13d276d0 311
4c89c718 312 if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
13d276d0 313
4c89c718 314 r = dns_query_candidate_next_search_domain(c);
13d276d0 315 if (r < 0)
4c89c718
MP
316 goto fail;
317
318 if (r > 0) {
319 /* OK, there's another search domain to try, let's do so. */
13d276d0 320
4c89c718
MP
321 r = dns_query_candidate_setup_transactions(c);
322 if (r < 0)
323 goto fail;
13d276d0 324
4c89c718
MP
325 if (r > 0) {
326 /* New transactions where queued. Start them and wait */
13d276d0 327
4c89c718
MP
328 r = dns_query_candidate_go(c);
329 if (r < 0)
330 goto fail;
331
332 return;
333 }
334 }
13d276d0 335
13d276d0
MP
336 }
337
4c89c718
MP
338 dns_query_ready(c->query);
339 return;
340
341fail:
9e294e28 342 c->error_code = log_warning_errno(r, "Failed to follow search domains: %m");
4c89c718 343 dns_query_ready(c->query);
13d276d0
MP
344}
345
4c89c718
MP
346static void dns_query_stop(DnsQuery *q) {
347 DnsQueryCandidate *c;
13d276d0 348
4c89c718 349 assert(q);
13d276d0 350
3a6ce677 351 q->timeout_event_source = sd_event_source_disable_unref(q->timeout_event_source);
13d276d0 352
4c89c718
MP
353 LIST_FOREACH(candidates_by_query, c, q->candidates)
354 dns_query_candidate_stop(c);
13d276d0
MP
355}
356
874c989e 357static void dns_query_unlink_candidates(DnsQuery *q) {
13d276d0 358 assert(q);
13d276d0 359
4c89c718 360 while (q->candidates)
874c989e
MB
361 /* Here we drop *our* references to each of the candidates. If we had the only reference, the
362 * DnsQueryCandidate object will be freed. */
363 dns_query_candidate_unref(dns_query_candidate_unlink(q->candidates));
4c89c718 364}
13d276d0 365
4c89c718
MP
366static void dns_query_reset_answer(DnsQuery *q) {
367 assert(q);
13d276d0 368
4c89c718
MP
369 q->answer = dns_answer_unref(q->answer);
370 q->answer_rcode = 0;
371 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
372 q->answer_errno = 0;
3a6ce677 373 q->answer_query_flags = 0;
4c89c718
MP
374 q->answer_protocol = _DNS_PROTOCOL_INVALID;
375 q->answer_family = AF_UNSPEC;
376 q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain);
3a6ce677 377 q->answer_full_packet = dns_packet_unref(q->answer_full_packet);
13d276d0
MP
378}
379
4c89c718
MP
380DnsQuery *dns_query_free(DnsQuery *q) {
381 if (!q)
382 return NULL;
13d276d0 383
4c89c718
MP
384 while (q->auxiliary_queries)
385 dns_query_free(q->auxiliary_queries);
13d276d0 386
4c89c718
MP
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 }
13d276d0 392
874c989e 393 dns_query_unlink_candidates(q);
13d276d0 394
4c89c718
MP
395 dns_question_unref(q->question_idna);
396 dns_question_unref(q->question_utf8);
3a6ce677 397 dns_packet_unref(q->question_bypass);
13d276d0 398
4c89c718 399 dns_query_reset_answer(q);
13d276d0 400
a032b68d 401 sd_bus_message_unref(q->bus_request);
4c89c718
MP
402 sd_bus_track_unref(q->bus_track);
403
a032b68d
MB
404 if (q->varlink_request) {
405 varlink_set_userdata(q->varlink_request, NULL);
406 varlink_unref(q->varlink_request);
407 }
408
3a6ce677
BR
409 if (q->request_packet)
410 hashmap_remove_value(q->stub_listener_extra ?
411 q->stub_listener_extra->queries_by_packet :
412 q->manager->stub_queries_by_packet,
413 q->request_packet,
414 q);
5a920b42 415
3a6ce677
BR
416 dns_packet_unref(q->request_packet);
417 dns_answer_unref(q->reply_answer);
418 dns_answer_unref(q->reply_authoritative);
419 dns_answer_unref(q->reply_additional);
420
421 if (q->request_stream) {
5a920b42 422 /* Detach the stream from our query, in case something else keeps a reference to it. */
3a6ce677
BR
423 (void) set_remove(q->request_stream->queries, q);
424 q->request_stream = dns_stream_unref(q->request_stream);
5a920b42
MP
425 }
426
4c89c718
MP
427 free(q->request_address_string);
428
429 if (q->manager) {
430 LIST_REMOVE(queries, q->manager->dns_queries, q);
431 q->manager->n_dns_queries--;
13d276d0
MP
432 }
433
8a584da2 434 return mfree(q);
13d276d0
MP
435}
436
4c89c718
MP
437int dns_query_new(
438 Manager *m,
439 DnsQuery **ret,
440 DnsQuestion *question_utf8,
441 DnsQuestion *question_idna,
3a6ce677 442 DnsPacket *question_bypass,
5a920b42
MP
443 int ifindex,
444 uint64_t flags) {
13d276d0 445
4c89c718 446 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
3a6ce677 447 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
4c89c718 448 DnsResourceKey *key;
13d276d0
MP
449 int r;
450
4c89c718 451 assert(m);
13d276d0 452
3a6ce677
BR
453 if (question_bypass) {
454 /* It's either a "bypass" query, or a regular one, but can't be both. */
455 if (question_utf8 || question_idna)
4c89c718 456 return -EINVAL;
13d276d0 457
3a6ce677
BR
458 } else {
459 bool good = false;
460
461 /* This (primarily) checks two things:
462 *
463 * 1. That the question is not empty
464 * 2. That all RR keys in the question objects are for the same domain
465 *
466 * Or in other words, a single DnsQuery object may be used to look up A+AAAA combination for
467 * the same domain name, or SRV+TXT (for DNS-SD services), but not for unrelated lookups. */
468
469 if (dns_question_size(question_utf8) > 0) {
470 r = dns_question_is_valid_for_query(question_utf8);
4c89c718
MP
471 if (r < 0)
472 return r;
473 if (r == 0)
474 return -EINVAL;
13d276d0 475
4c89c718 476 good = true;
5eef597e 477 }
4c89c718 478
3a6ce677
BR
479 /* If the IDNA and UTF8 questions are the same, merge their references */
480 r = dns_question_is_equal(question_idna, question_utf8);
481 if (r < 0)
482 return r;
483 if (r > 0)
484 question_idna = question_utf8;
485 else {
486 if (dns_question_size(question_idna) > 0) {
487 r = dns_question_is_valid_for_query(question_idna);
488 if (r < 0)
489 return r;
490 if (r == 0)
491 return -EINVAL;
492
493 good = true;
494 }
495 }
496
497 if (!good) /* don't allow empty queries */
498 return -EINVAL;
499 }
4c89c718
MP
500
501 if (m->n_dns_queries >= QUERIES_MAX)
502 return -EBUSY;
503
a032b68d 504 q = new(DnsQuery, 1);
4c89c718
MP
505 if (!q)
506 return -ENOMEM;
507
a032b68d
MB
508 *q = (DnsQuery) {
509 .question_utf8 = dns_question_ref(question_utf8),
510 .question_idna = dns_question_ref(question_idna),
3a6ce677 511 .question_bypass = dns_packet_ref(question_bypass),
a032b68d
MB
512 .ifindex = ifindex,
513 .flags = flags,
514 .answer_dnssec_result = _DNSSEC_RESULT_INVALID,
515 .answer_protocol = _DNS_PROTOCOL_INVALID,
516 .answer_family = AF_UNSPEC,
517 };
13d276d0 518
3a6ce677
BR
519 if (question_bypass) {
520 DNS_QUESTION_FOREACH(key, question_bypass->question)
521 log_debug("Looking up bypass packet for %s.",
522 dns_resource_key_to_string(key, key_str, sizeof key_str));
523 } else {
8b3d4ff0 524 /* First dump UTF8 question */
3a6ce677
BR
525 DNS_QUESTION_FOREACH(key, question_utf8)
526 log_debug("Looking up RR for %s.",
527 dns_resource_key_to_string(key, key_str, sizeof key_str));
528
529 /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
530 DNS_QUESTION_FOREACH(key, question_idna) {
5b5a102a 531 r = dns_question_contains_key(question_utf8, key);
3a6ce677
BR
532 if (r < 0)
533 return r;
534 if (r > 0)
535 continue;
13d276d0 536
3a6ce677
BR
537 log_debug("Looking up IDNA RR for %s.",
538 dns_resource_key_to_string(key, key_str, sizeof key_str));
539 }
5eef597e
MP
540 }
541
4c89c718
MP
542 LIST_PREPEND(queries, m->dns_queries, q);
543 m->n_dns_queries++;
544 q->manager = m;
545
546 if (ret)
547 *ret = q;
4c89c718 548
3a6ce677 549 TAKE_PTR(q);
5eef597e
MP
550 return 0;
551}
552
4c89c718 553int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
13d276d0 554 assert(q);
4c89c718 555 assert(auxiliary_for);
13d276d0 556
5a920b42 557 /* Ensure that the query is not auxiliary yet, and
4c89c718
MP
558 * nothing else is auxiliary to it either */
559 assert(!q->auxiliary_for);
560 assert(!q->auxiliary_queries);
13d276d0 561
4c89c718
MP
562 /* Ensure that the unit we shall be made auxiliary for isn't
563 * auxiliary itself */
564 assert(!auxiliary_for->auxiliary_for);
13d276d0 565
4c89c718
MP
566 if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
567 return -EAGAIN;
13d276d0 568
4c89c718
MP
569 LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
570 q->auxiliary_for = auxiliary_for;
13d276d0 571
4c89c718
MP
572 auxiliary_for->n_auxiliary_queries++;
573 return 0;
574}
13d276d0 575
a032b68d 576void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
4c89c718
MP
577 assert(q);
578 assert(!DNS_TRANSACTION_IS_LIVE(state));
579 assert(DNS_TRANSACTION_IS_LIVE(q->state));
13d276d0 580
a032b68d
MB
581 /* Note that this call might invalidate the query. Callers should hence not attempt to access the
582 * query or transaction after calling this function. */
13d276d0 583
4c89c718 584 q->state = state;
13d276d0 585
4c89c718
MP
586 dns_query_stop(q);
587 if (q->complete)
588 q->complete(q);
589}
13d276d0 590
4c89c718
MP
591static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
592 DnsQuery *q = userdata;
13d276d0 593
4c89c718
MP
594 assert(s);
595 assert(q);
13d276d0 596
4c89c718
MP
597 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
598 return 0;
13d276d0
MP
599}
600
4c89c718 601static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
1ce460ce 602 _cleanup_(dns_query_candidate_unrefp) DnsQueryCandidate *c = NULL;
4c89c718 603 int r;
13d276d0
MP
604
605 assert(q);
4c89c718 606 assert(s);
13d276d0 607
4c89c718
MP
608 r = dns_query_candidate_new(&c, q, s);
609 if (r < 0)
610 return r;
13d276d0 611
4c89c718 612 /* If this a single-label domain on DNS, we might append a suitable search domain first. */
a10f5d05
MB
613 if (!FLAGS_SET(q->flags, SD_RESOLVED_NO_SEARCH) &&
614 dns_scope_name_wants_search_domain(s, dns_question_first_name(q->question_idna))) {
615 /* OK, we want a search domain now. Let's find one for this scope */
13d276d0 616
6e866b33 617 r = dns_query_candidate_next_search_domain(c);
a10f5d05
MB
618 if (r < 0)
619 return r;
4c89c718 620 }
13d276d0 621
4c89c718
MP
622 r = dns_query_candidate_setup_transactions(c);
623 if (r < 0)
a10f5d05 624 return r;
13d276d0 625
a10f5d05 626 TAKE_PTR(c);
4c89c718 627 return 0;
13d276d0
MP
628}
629
630static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
631 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
13d276d0
MP
632 int r;
633
634 assert(q);
635 assert(state);
636
4c89c718
MP
637 /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the
638 * the normal lookup finished. The data from the network hence takes precedence over the data we
639 * synthesize. (But note that many scopes refuse to resolve certain domain names) */
13d276d0
MP
640
641 if (!IN_SET(*state,
4c89c718 642 DNS_TRANSACTION_RCODE_FAILURE,
13d276d0
MP
643 DNS_TRANSACTION_NO_SERVERS,
644 DNS_TRANSACTION_TIMEOUT,
4c89c718
MP
645 DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
646 DNS_TRANSACTION_NETWORK_DOWN,
647 DNS_TRANSACTION_NOT_FOUND))
13d276d0
MP
648 return 0;
649
3a6ce677
BR
650 if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
651 return 0;
652
4c89c718
MP
653 r = dns_synthesize_answer(
654 q->manager,
3a6ce677 655 q->question_bypass ? q->question_bypass->question : q->question_utf8,
4c89c718
MP
656 q->ifindex,
657 &answer);
f5e65279
MB
658 if (r == -ENXIO) {
659 /* If we get ENXIO this tells us to generate NXDOMAIN unconditionally. */
13d276d0 660
f5e65279
MB
661 dns_query_reset_answer(q);
662 q->answer_rcode = DNS_RCODE_NXDOMAIN;
663 q->answer_protocol = dns_synthesize_protocol(q->flags);
664 q->answer_family = dns_synthesize_family(q->flags);
3a6ce677 665 q->answer_query_flags = SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL|SD_RESOLVED_SYNTHETIC;
f5e65279
MB
666 *state = DNS_TRANSACTION_RCODE_FAILURE;
667
668 return 0;
669 }
4c89c718
MP
670 if (r <= 0)
671 return r;
13d276d0 672
4c89c718 673 dns_query_reset_answer(q);
13d276d0 674
b012e921 675 q->answer = TAKE_PTR(answer);
4c89c718
MP
676 q->answer_rcode = DNS_RCODE_SUCCESS;
677 q->answer_protocol = dns_synthesize_protocol(q->flags);
678 q->answer_family = dns_synthesize_family(q->flags);
3a6ce677 679 q->answer_query_flags = SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL|SD_RESOLVED_SYNTHETIC;
13d276d0 680
4c89c718 681 *state = DNS_TRANSACTION_SUCCESS;
13d276d0 682
4c89c718
MP
683 return 1;
684}
13d276d0 685
4c89c718
MP
686static int dns_query_try_etc_hosts(DnsQuery *q) {
687 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
688 int r;
13d276d0 689
4c89c718 690 assert(q);
13d276d0 691
1ce460ce
MB
692 /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is
693 * done. The data from /etc/hosts hence takes precedence over the network. */
13d276d0 694
3a6ce677
BR
695 if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
696 return 0;
697
4c89c718
MP
698 r = manager_etc_hosts_lookup(
699 q->manager,
3a6ce677 700 q->question_bypass ? q->question_bypass->question : q->question_utf8,
4c89c718
MP
701 &answer);
702 if (r <= 0)
703 return r;
13d276d0 704
4c89c718 705 dns_query_reset_answer(q);
13d276d0 706
b012e921 707 q->answer = TAKE_PTR(answer);
13d276d0 708 q->answer_rcode = DNS_RCODE_SUCCESS;
4c89c718
MP
709 q->answer_protocol = dns_synthesize_protocol(q->flags);
710 q->answer_family = dns_synthesize_family(q->flags);
3a6ce677 711 q->answer_query_flags = SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL|SD_RESOLVED_SYNTHETIC;
13d276d0
MP
712
713 return 1;
714}
715
5eef597e
MP
716int dns_query_go(DnsQuery *q) {
717 DnsScopeMatch found = DNS_SCOPE_NO;
718 DnsScope *s, *first = NULL;
4c89c718 719 DnsQueryCandidate *c;
5eef597e
MP
720 int r;
721
722 assert(q);
723
724 if (q->state != DNS_TRANSACTION_NULL)
725 return 0;
726
4c89c718
MP
727 r = dns_query_try_etc_hosts(q);
728 if (r < 0)
729 return r;
730 if (r > 0) {
731 dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
732 return 1;
733 }
5eef597e
MP
734
735 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
736 DnsScopeMatch match;
4c89c718
MP
737 const char *name;
738
739 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
740 if (!name)
741 continue;
5eef597e
MP
742
743 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
6e866b33
MB
744 if (match < 0) {
745 log_debug("Couldn't check if '%s' matches against scope, ignoring.", name);
5eef597e 746 continue;
6e866b33 747 }
5eef597e 748
6e866b33
MB
749 if (match > found) { /* Does this match better? If so, remember how well it matched, and the first one
750 * that matches this well */
751 found = match;
5eef597e 752 first = s;
5eef597e
MP
753 }
754 }
755
13d276d0
MP
756 if (found == DNS_SCOPE_NO) {
757 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
758
4c89c718
MP
759 r = dns_query_synthesize_reply(q, &state);
760 if (r < 0)
761 return r;
762
13d276d0
MP
763 dns_query_complete(q, state);
764 return 1;
765 }
5eef597e 766
4c89c718 767 r = dns_query_add_candidate(q, first);
5eef597e
MP
768 if (r < 0)
769 goto fail;
770
771 LIST_FOREACH(scopes, s, first->scopes_next) {
772 DnsScopeMatch match;
4c89c718
MP
773 const char *name;
774
775 name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
776 if (!name)
777 continue;
5eef597e
MP
778
779 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
6e866b33 780 if (match < 0) {
7c20daf6 781 log_debug("Couldn't check if '%s' matches against scope, ignoring.", name);
6e866b33
MB
782 continue;
783 }
5eef597e 784
6e866b33 785 if (match < found)
5eef597e
MP
786 continue;
787
4c89c718 788 r = dns_query_add_candidate(q, s);
5eef597e
MP
789 if (r < 0)
790 goto fail;
791 }
792
4c89c718 793 dns_query_reset_answer(q);
5eef597e 794
a032b68d 795 r = sd_event_add_time_relative(
5eef597e
MP
796 q->manager->event,
797 &q->timeout_event_source,
798 clock_boottime_or_monotonic(),
a032b68d 799 SD_RESOLVED_QUERY_TIMEOUT_USEC,
1d42b86d 800 0, on_query_timeout, q);
5eef597e
MP
801 if (r < 0)
802 goto fail;
803
4c89c718
MP
804 (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
805
5eef597e
MP
806 q->state = DNS_TRANSACTION_PENDING;
807 q->block_ready++;
808
4c89c718
MP
809 /* Start the transactions */
810 LIST_FOREACH(candidates_by_query, c, q->candidates) {
811 r = dns_query_candidate_go(c);
812 if (r < 0) {
813 q->block_ready--;
5eef597e 814 goto fail;
4c89c718 815 }
5eef597e
MP
816 }
817
818 q->block_ready--;
819 dns_query_ready(q);
820
821 return 1;
822
823fail:
824 dns_query_stop(q);
825 return r;
826}
827
4c89c718 828static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
5eef597e 829 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
3a6ce677 830 bool has_authenticated = false, has_non_authenticated = false, has_confidential = false, has_non_confidential = false;
4c89c718
MP
831 DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
832 DnsTransaction *t;
4c89c718 833 int r;
5eef597e
MP
834
835 assert(q);
5eef597e 836
4c89c718
MP
837 if (!c) {
838 r = dns_query_synthesize_reply(q, &state);
839 if (r < 0)
840 goto fail;
5eef597e 841
4c89c718 842 dns_query_complete(q, state);
5eef597e 843 return;
4c89c718 844 }
5eef597e 845
4c89c718
MP
846 if (c->error_code != 0) {
847 /* If the candidate had an error condition of its own, start with that. */
848 state = DNS_TRANSACTION_ERRNO;
849 q->answer = dns_answer_unref(q->answer);
850 q->answer_rcode = 0;
851 q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
3a6ce677 852 q->answer_query_flags = 0;
4c89c718 853 q->answer_errno = c->error_code;
3a6ce677 854 q->answer_full_packet = dns_packet_unref(q->answer_full_packet);
4c89c718 855 }
5eef597e 856
a032b68d 857 SET_FOREACH(t, c->transactions) {
5eef597e 858
4c89c718 859 switch (t->state) {
5eef597e 860
4c89c718 861 case DNS_TRANSACTION_SUCCESS: {
3a6ce677
BR
862 /* We found a successful reply, merge it into the answer */
863
864 if (state == DNS_TRANSACTION_SUCCESS) {
865 r = dns_answer_extend(&q->answer, t->answer);
866 if (r < 0)
867 goto fail;
868
869 q->answer_query_flags |= dns_transaction_source_to_query_flags(t->answer_source);
870 } else {
871 /* Override non-successful previous answers */
872 dns_answer_unref(q->answer);
873 q->answer = dns_answer_ref(t->answer);
874
875 q->answer_query_flags = dns_transaction_source_to_query_flags(t->answer_source);
876 }
5eef597e 877
4c89c718
MP
878 q->answer_rcode = t->answer_rcode;
879 q->answer_errno = 0;
5eef597e 880
3a6ce677
BR
881 dns_packet_unref(q->answer_full_packet);
882 q->answer_full_packet = dns_packet_ref(t->received);
883
884 if (FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED)) {
4c89c718
MP
885 has_authenticated = true;
886 dnssec_result_authenticated = t->answer_dnssec_result;
5eef597e 887 } else {
4c89c718
MP
888 has_non_authenticated = true;
889 dnssec_result_non_authenticated = t->answer_dnssec_result;
5eef597e
MP
890 }
891
3a6ce677
BR
892 if (FLAGS_SET(t->answer_query_flags, SD_RESOLVED_CONFIDENTIAL))
893 has_confidential = true;
894 else
895 has_non_confidential = true;
896
5eef597e 897 state = DNS_TRANSACTION_SUCCESS;
4c89c718 898 break;
5eef597e
MP
899 }
900
4c89c718
MP
901 case DNS_TRANSACTION_NULL:
902 case DNS_TRANSACTION_PENDING:
903 case DNS_TRANSACTION_VALIDATING:
904 case DNS_TRANSACTION_ABORTED:
905 /* Ignore transactions that didn't complete */
906 continue;
5eef597e 907
4c89c718 908 default:
2897b343 909 /* Any kind of failure? Store the data away, if there's nothing stored yet. */
4c89c718
MP
910 if (state == DNS_TRANSACTION_SUCCESS)
911 continue;
5eef597e 912
2897b343 913 /* If there's already an authenticated negative reply stored, then prefer that over any unauthenticated one */
3a6ce677
BR
914 if (FLAGS_SET(q->answer_query_flags, SD_RESOLVED_AUTHENTICATED) &&
915 !FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
2897b343
MP
916 continue;
917
3a6ce677
BR
918 dns_answer_unref(q->answer);
919 q->answer = dns_answer_ref(t->answer);
4c89c718
MP
920 q->answer_rcode = t->answer_rcode;
921 q->answer_dnssec_result = t->answer_dnssec_result;
3a6ce677 922 q->answer_query_flags = t->answer_query_flags | dns_transaction_source_to_query_flags(t->answer_source);
4c89c718 923 q->answer_errno = t->answer_errno;
3a6ce677
BR
924 dns_packet_unref(q->answer_full_packet);
925 q->answer_full_packet = dns_packet_ref(t->received);
5eef597e 926
5eef597e 927 state = t->state;
4c89c718
MP
928 break;
929 }
5eef597e
MP
930 }
931
4c89c718 932 if (state == DNS_TRANSACTION_SUCCESS) {
3a6ce677
BR
933 SET_FLAG(q->answer_query_flags, SD_RESOLVED_AUTHENTICATED, has_authenticated && !has_non_authenticated);
934 SET_FLAG(q->answer_query_flags, SD_RESOLVED_CONFIDENTIAL, has_confidential && !has_non_confidential);
935 q->answer_dnssec_result = FLAGS_SET(q->answer_query_flags, SD_RESOLVED_AUTHENTICATED) ? dnssec_result_authenticated : dnssec_result_non_authenticated;
4c89c718
MP
936 }
937
938 q->answer_protocol = c->scope->protocol;
939 q->answer_family = c->scope->family;
5eef597e 940
4c89c718
MP
941 dns_search_domain_unref(q->answer_search_domain);
942 q->answer_search_domain = dns_search_domain_ref(c->search_domain);
943
944 r = dns_query_synthesize_reply(q, &state);
945 if (r < 0)
946 goto fail;
947
948 dns_query_complete(q, state);
949 return;
950
951fail:
952 q->answer_errno = -r;
953 dns_query_complete(q, DNS_TRANSACTION_ERRNO);
954}
955
956void dns_query_ready(DnsQuery *q) {
957
958 DnsQueryCandidate *bad = NULL, *c;
959 bool pending = false;
960
961 assert(q);
962 assert(DNS_TRANSACTION_IS_LIVE(q->state));
963
964 /* Note that this call might invalidate the query. Callers
965 * should hence not attempt to access the query or transaction
966 * after calling this function, unless the block_ready
967 * counter was explicitly bumped before doing so. */
968
969 if (q->block_ready > 0)
970 return;
971
972 LIST_FOREACH(candidates_by_query, c, q->candidates) {
973 DnsTransactionState state;
974
975 state = dns_query_candidate_state(c);
976 switch (state) {
977
978 case DNS_TRANSACTION_SUCCESS:
979 /* One of the candidates is successful,
980 * let's use it, and copy its data out */
981 dns_query_accept(q, c);
5eef597e
MP
982 return;
983
4c89c718
MP
984 case DNS_TRANSACTION_NULL:
985 case DNS_TRANSACTION_PENDING:
986 case DNS_TRANSACTION_VALIDATING:
987 /* One of the candidates is still going on,
988 * let's maybe wait for it */
989 pending = true;
990 break;
5eef597e 991
4c89c718
MP
992 default:
993 /* Any kind of failure */
994 bad = c;
995 break;
996 }
5eef597e
MP
997 }
998
4c89c718
MP
999 if (pending)
1000 return;
13d276d0 1001
4c89c718 1002 dns_query_accept(q, bad);
5eef597e
MP
1003}
1004
4c89c718
MP
1005static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
1006 _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
1007 int r, k;
5eef597e
MP
1008
1009 assert(q);
1010
8b3d4ff0 1011 if (q->n_cname_redirects >= CNAME_REDIRECTS_MAX)
5eef597e 1012 return -ELOOP;
8b3d4ff0 1013 q->n_cname_redirects++;
5eef597e 1014
4c89c718 1015 r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
5eef597e
MP
1016 if (r < 0)
1017 return r;
3a6ce677 1018 if (r > 0)
4c89c718
MP
1019 log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
1020
1021 k = dns_question_is_equal(q->question_idna, q->question_utf8);
1022 if (k < 0)
3a6ce677 1023 return k;
4c89c718
MP
1024 if (k > 0) {
1025 /* Same question? Shortcut new question generation */
1026 nq_utf8 = dns_question_ref(nq_idna);
1027 k = r;
1028 } else {
1029 k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
1030 if (k < 0)
1031 return k;
3a6ce677 1032 if (k > 0)
4c89c718
MP
1033 log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
1034 }
5eef597e 1035
4c89c718
MP
1036 if (r == 0 && k == 0) /* No actual cname happened? */
1037 return -ELOOP;
5eef597e 1038
a032b68d 1039 if (q->answer_protocol == DNS_PROTOCOL_DNS)
4c89c718
MP
1040 /* Don't permit CNAME redirects from unicast DNS to LLMNR or MulticastDNS, so that global resources
1041 * cannot invade the local namespace. The opposite way we permit: local names may redirect to global
1042 * ones. */
4c89c718 1043 q->flags &= ~(SD_RESOLVED_LLMNR|SD_RESOLVED_MDNS); /* mask away the local protocols */
4c89c718
MP
1044
1045 /* Turn off searching for the new name */
1046 q->flags |= SD_RESOLVED_NO_SEARCH;
1047
1048 dns_question_unref(q->question_idna);
b012e921 1049 q->question_idna = TAKE_PTR(nq_idna);
4c89c718
MP
1050
1051 dns_question_unref(q->question_utf8);
b012e921 1052 q->question_utf8 = TAKE_PTR(nq_utf8);
4c89c718 1053
874c989e 1054 dns_query_unlink_candidates(q);
3a6ce677
BR
1055
1056 /* Note that we do *not* reset the answer here, because the answer we previously got might already
1057 * include everything we need, let's check that first */
5eef597e 1058
5eef597e
MP
1059 q->state = DNS_TRANSACTION_NULL;
1060
1061 return 0;
1062}
1063
3a6ce677 1064int dns_query_process_cname_one(DnsQuery *q) {
4c89c718
MP
1065 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
1066 DnsQuestion *question;
1067 DnsResourceRecord *rr;
3a6ce677
BR
1068 bool full_match = true;
1069 DnsResourceKey *k;
4c89c718
MP
1070 int r;
1071
1072 assert(q);
1073
3a6ce677
BR
1074 /* Processes a CNAME redirect if there's one. Returns one of three values:
1075 *
1076 * CNAME_QUERY_MATCH → direct RR match, caller should just use the RRs in this answer (and not
1077 * bother with any CNAME/DNAME stuff)
1078 *
1079 * CNAME_QUERY_NOMATCH → no match at all, neither direct nor CNAME/DNAME, caller might decide to
1080 * restart query or take things as NODATA reply.
1081 *
1082 * CNAME_QUERY_CNAME → no direct RR match, but a CNAME/DNAME match that we now followed for one step.
1083 *
1084 * The function might also return a failure, in particular -ELOOP if we encountered too many
1085 * CNAMEs/DNAMEs in a chain or if following CNAMEs/DNAMEs was turned off.
1086 *
1087 * Note that this function doesn't actually restart the query. The caller can decide to do that in
1088 * case of CNAME_QUERY_CNAME, though. */
1089
4c89c718
MP
1090 if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
1091 return DNS_QUERY_NOMATCH;
1092
1093 question = dns_query_question_for_protocol(q, q->answer_protocol);
1094
3a6ce677
BR
1095 /* Small reminder: our question will consist of one or more RR keys that match in name, but not in
1096 * record type. Specifically, when we do an address lookup the question will typically consist of one
1097 * A and one AAAA key lookup for the same domain name. When we get a response from a server we need
1098 * to check if the answer answers all our questions to use it. Note that a response of CNAME/DNAME
1099 * can answer both an A and the AAAA question for us, but an A/AAAA response only the relevant
1100 * type.
1101 *
1102 * Hence we first check of the answers we collected are sufficient to answer all our questions
1103 * directly. If one question wasn't answered we go on, waiting for more replies. However, if there's
1104 * a CNAME/DNAME response we use it, and redirect to it, regardless if it was a response to the A or
8b3d4ff0 1105 * the AAAA query. */
3a6ce677
BR
1106
1107 DNS_QUESTION_FOREACH(k, question) {
1108 bool match = false;
1109
1110 DNS_ANSWER_FOREACH(rr, q->answer) {
1111 r = dns_resource_key_match_rr(k, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
1112 if (r < 0)
1113 return r;
1114 if (r > 0) {
1115 match = true; /* Yay, we found an RR that matches the key we are looking for */
1116 break;
1117 }
1118 }
1119
1120 if (!match) {
1121 /* Hmm. :-( there's no response for this key. This doesn't match. */
1122 full_match = false;
1123 break;
1124 }
1125 }
1126
1127 if (full_match)
1128 return DNS_QUERY_MATCH; /* The answer can answer our question in full, no need to follow CNAMEs/DNAMEs */
4c89c718 1129
3a6ce677
BR
1130 /* Let's see if there is a CNAME/DNAME to match. This case is simpler: we accept the CNAME/DNAME that
1131 * matches any of our questions. */
1132 DNS_ANSWER_FOREACH(rr, q->answer) {
4c89c718
MP
1133 r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
1134 if (r < 0)
1135 return r;
1136 if (r > 0 && !cname)
1137 cname = dns_resource_record_ref(rr);
1138 }
1139
1140 if (!cname)
3a6ce677 1141 return DNS_QUERY_NOMATCH; /* No match and no CNAME/DNAME to follow */
4c89c718
MP
1142
1143 if (q->flags & SD_RESOLVED_NO_CNAME)
1144 return -ELOOP;
1145
3a6ce677 1146 if (!FLAGS_SET(q->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
2897b343 1147 q->previous_redirect_unauthenticated = true;
3a6ce677
BR
1148 if (!FLAGS_SET(q->answer_query_flags, SD_RESOLVED_CONFIDENTIAL))
1149 q->previous_redirect_non_confidential = true;
1150 if (!FLAGS_SET(q->answer_query_flags, SD_RESOLVED_SYNTHETIC))
1151 q->previous_redirect_non_synthetic = true;
2897b343 1152
4c89c718
MP
1153 /* OK, let's actually follow the CNAME */
1154 r = dns_query_cname_redirect(q, cname);
1155 if (r < 0)
1156 return r;
1157
3a6ce677
BR
1158 return DNS_QUERY_CNAME; /* Tell caller that we did a single CNAME/DNAME redirection step */
1159}
4c89c718 1160
3a6ce677
BR
1161int dns_query_process_cname_many(DnsQuery *q) {
1162 int r;
1163
1164 assert(q);
1165
1166 /* Follows CNAMEs through the current packet: as long as the current packet can fulfill our
1167 * redirected CNAME queries we keep going, and restart the query once the current packet isn't good
1168 * enough anymore. It's a wrapper around dns_query_process_cname_one() and returns the same values,
1169 * but with extended semantics. Specifically:
1170 *
1171 * DNS_QUERY_MATCH → as above
1172 *
1173 * DNS_QUERY_CNAME → we ran into a CNAME/DNAME redirect that we could not answer from the current
1174 * message, and thus restarted the query to resolve it.
1175 *
1176 * DNS_QUERY_NOMATCH → we reached the end of CNAME/DNAME chain, and there are no direct matches nor a
1177 * CNAME/DNAME match. i.e. this is a NODATA case.
1178 *
1179 * Note that this function will restart the query for the caller if needed, and that's the case
1180 * DNS_QUERY_CNAME is returned.
1181 */
1182
1183 r = dns_query_process_cname_one(q);
1184 if (r != DNS_QUERY_CNAME)
1185 return r; /* The first redirect is special: if it doesn't answer the question that's no
1186 * reason to restart the query, we just accept this as a NODATA answer. */
1187
1188 for (;;) {
1189 r = dns_query_process_cname_one(q);
1190 if (r < 0 || r == DNS_QUERY_MATCH)
1191 return r;
1192 if (r == DNS_QUERY_NOMATCH) {
1193 /* OK, so we followed one or more CNAME/DNAME RR but the existing packet can't answer
1194 * this. Let's restart the query hence, with the new question. Why the different
1195 * handling than the first chain element? Because if the server answers a direct
1196 * question with an empty answer then this is a NODATA response. But if it responds
1197 * with a CNAME chain that ultimately is incomplete (i.e. a non-empty but truncated
1198 * CNAME chain) then we better follow up ourselves and ask for the rest of the
1199 * chain. This is particular relevant since our cache will store CNAME/DNAME
1200 * redirects that we learnt about for lookups of certain DNS types, but later on we
1201 * can reuse this data even for other DNS types, but in that case need to follow up
1202 * with the final lookup of the chain ourselves with the RR type we ourselves are
1203 * interested in. */
1204 r = dns_query_go(q);
1205 if (r < 0)
1206 return r;
4c89c718 1207
3a6ce677
BR
1208 return DNS_QUERY_CNAME;
1209 }
1210
1211 /* So we found a CNAME that the existing packet already answers, again via a CNAME, let's
1212 * continue going then. */
1213 assert(r == DNS_QUERY_CNAME);
1214 }
4c89c718
MP
1215}
1216
4c89c718
MP
1217DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
1218 assert(q);
1219
3a6ce677
BR
1220 if (q->question_bypass)
1221 return q->question_bypass->question;
1222
4c89c718
MP
1223 switch (protocol) {
1224
1225 case DNS_PROTOCOL_DNS:
1226 return q->question_idna;
1227
1228 case DNS_PROTOCOL_MDNS:
1229 case DNS_PROTOCOL_LLMNR:
1230 return q->question_utf8;
1231
1232 default:
1233 return NULL;
1234 }
1235}
1236
1237const char *dns_query_string(DnsQuery *q) {
1238 const char *name;
1239 int r;
1240
1241 /* Returns a somewhat useful human-readable lookup key string for this query */
1242
3a6ce677
BR
1243 if (q->question_bypass)
1244 return dns_question_first_name(q->question_bypass->question);
1245
4c89c718
MP
1246 if (q->request_address_string)
1247 return q->request_address_string;
1248
1249 if (q->request_address_valid) {
1250 r = in_addr_to_string(q->request_family, &q->request_address, &q->request_address_string);
1251 if (r >= 0)
1252 return q->request_address_string;
1253 }
1254
1255 name = dns_question_first_name(q->question_utf8);
1256 if (name)
1257 return name;
1258
1259 return dns_question_first_name(q->question_idna);
1260}
2897b343
MP
1261
1262bool dns_query_fully_authenticated(DnsQuery *q) {
1263 assert(q);
1264
3a6ce677
BR
1265 return FLAGS_SET(q->answer_query_flags, SD_RESOLVED_AUTHENTICATED) && !q->previous_redirect_unauthenticated;
1266}
1267
1268bool dns_query_fully_confidential(DnsQuery *q) {
1269 assert(q);
1270
1271 return FLAGS_SET(q->answer_query_flags, SD_RESOLVED_CONFIDENTIAL) && !q->previous_redirect_non_confidential;
1272}
1273
1274bool dns_query_fully_authoritative(DnsQuery *q) {
1275 assert(q);
1276
1277 /* We are authoritative for everything synthetic (except if a previous CNAME/DNAME) wasn't
1278 * synthetic. (Note: SD_RESOLVED_SYNTHETIC is reset on each CNAME/DNAME, hence the explicit check for
8b3d4ff0 1279 * previous synthetic DNAME/CNAME redirections.) */
3a6ce677
BR
1280 if ((q->answer_query_flags & SD_RESOLVED_SYNTHETIC) && !q->previous_redirect_non_synthetic)
1281 return true;
1282
1283 /* We are also authoritative for everything coming only from the trust anchor and the local
1284 * zones. (Note: the SD_RESOLVED_FROM_xyz flags we merge on each redirect, hence no need to
8b3d4ff0 1285 * explicitly check previous redirects here.) */
3a6ce677 1286 return (q->answer_query_flags & SD_RESOLVED_FROM_MASK & ~(SD_RESOLVED_FROM_TRUST_ANCHOR | SD_RESOLVED_FROM_ZONE)) == 0;
2897b343 1287}