]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-query.c
resolved: add code to map DNSSEC digest types to strings and back
[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"
74b2466e 27
0c903ae7 28/* How long to wait for the query in total */
74b2466e 29#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
0c903ae7 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))) {
62 set_remove(t->query_candidates, c);
ec2c5e43 63 dns_transaction_gc(t);
74b2466e 64 }
74b2466e
LP
65}
66
801ad6a6
LP
67DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
68
69 if (!c)
70 return NULL;
71
72 dns_query_candidate_stop(c);
73
74 set_free(c->transactions);
75 dns_search_domain_unref(c->search_domain);
76
77 if (c->query)
78 LIST_REMOVE(candidates_by_query, c->query->candidates, c);
79
80 if (c->scope)
81 LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
82
83 free(c);
84
85 return NULL;
86}
87
88static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
801ad6a6
LP
89 DnsSearchDomain *next = NULL;
90
91 assert(c);
92
93 if (c->search_domain && c->search_domain->linked) {
94 next = c->search_domain->domains_next;
95
6627b7e2
LP
96 if (!next) /* We hit the end of the list */
97 return 0;
801ad6a6
LP
98
99 } else {
801ad6a6
LP
100 next = dns_scope_get_search_domains(c->scope);
101
6627b7e2 102 if (!next) /* OK, there's nothing. */
801ad6a6
LP
103 return 0;
104 }
105
106 dns_search_domain_unref(c->search_domain);
107 c->search_domain = dns_search_domain_ref(next);
6627b7e2 108
801ad6a6
LP
109 return 1;
110}
111
112static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
113 DnsTransaction *t;
114 int r;
115
116 assert(c);
117 assert(key);
118
119 r = set_ensure_allocated(&c->transactions, NULL);
120 if (r < 0)
121 return r;
122
123 t = dns_scope_find_transaction(c->scope, key, true);
124 if (!t) {
125 r = dns_transaction_new(&t, c->scope, key);
126 if (r < 0)
127 return r;
128 }
129
130 r = set_ensure_allocated(&t->query_candidates, NULL);
131 if (r < 0)
132 goto gc;
133
134 r = set_put(t->query_candidates, c);
135 if (r < 0)
136 goto gc;
137
138 r = set_put(c->transactions, t);
139 if (r < 0) {
140 set_remove(t->query_candidates, c);
141 goto gc;
142 }
143
144 return 0;
145
146gc:
147 dns_transaction_gc(t);
148 return r;
149}
150
151static int dns_query_candidate_go(DnsQueryCandidate *c) {
152 DnsTransaction *t;
153 Iterator i;
154 int r;
155
156 assert(c);
157
158 /* Start the transactions that are not started yet */
159 SET_FOREACH(t, c->transactions, i) {
160 if (t->state != DNS_TRANSACTION_NULL)
161 continue;
162
163 r = dns_transaction_go(t);
164 if (r < 0)
165 return r;
166 }
167
168 return 0;
169}
170
171static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
172 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
173 DnsTransaction *t;
174 Iterator i;
175
176 assert(c);
177
178 if (c->error_code != 0)
179 return DNS_TRANSACTION_RESOURCES;
180
181 SET_FOREACH(t, c->transactions, i) {
182
183 switch (t->state) {
184
185 case DNS_TRANSACTION_PENDING:
186 case DNS_TRANSACTION_NULL:
187 return t->state;
188
189 case DNS_TRANSACTION_SUCCESS:
190 state = t->state;
191 break;
192
193 default:
194 if (state != DNS_TRANSACTION_SUCCESS)
195 state = t->state;
196
197 break;
198 }
199 }
200
201 return state;
202}
203
204static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
205 DnsResourceKey *key;
206 int n = 0, r;
207
208 assert(c);
209
210 dns_query_candidate_stop(c);
211
212 /* Create one transaction per question key */
213 DNS_QUESTION_FOREACH(key, c->query->question) {
214 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
215
216 if (c->search_domain) {
217 r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
218 if (r < 0)
219 goto fail;
220 }
221
222 r = dns_query_candidate_add_transaction(c, new_key ?: key);
223 if (r < 0)
224 goto fail;
225
226 n++;
227 }
228
229 return n;
230
231fail:
232 dns_query_candidate_stop(c);
233 return r;
234}
235
236void dns_query_candidate_ready(DnsQueryCandidate *c) {
237 DnsTransactionState state;
238 int r;
239
240 assert(c);
241
242 state = dns_query_candidate_state(c);
243
244 if (IN_SET(state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
245 return;
246
247 if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
248
249 r = dns_query_candidate_next_search_domain(c);
250 if (r < 0)
251 goto fail;
252
253 if (r > 0) {
254 /* OK, there's another search domain to try, let's do so. */
255
256 r = dns_query_candidate_setup_transactions(c);
257 if (r < 0)
258 goto fail;
259
260 if (r > 0) {
261 /* New transactions where queued. Start them and wait */
262
263 r = dns_query_candidate_go(c);
264 if (r < 0)
265 goto fail;
266
267 return;
268 }
269 }
270
271 }
272
273 dns_query_ready(c->query);
274 return;
275
276fail:
277 log_warning_errno(r, "Failed to follow search domains: %m");
278 c->error_code = r;
279 dns_query_ready(c->query);
280}
281
282static void dns_query_stop(DnsQuery *q) {
283 DnsQueryCandidate *c;
284
285 assert(q);
286
287 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
288
289 LIST_FOREACH(candidates_by_query, c, q->candidates)
290 dns_query_candidate_stop(c);
291}
292
74b2466e 293DnsQuery *dns_query_free(DnsQuery *q) {
74b2466e
LP
294 if (!q)
295 return NULL;
296
45ec7efb
LP
297 while (q->auxiliary_queries)
298 dns_query_free(q->auxiliary_queries);
299
300 if (q->auxiliary_for) {
301 assert(q->auxiliary_for->n_auxiliary_queries > 0);
302 q->auxiliary_for->n_auxiliary_queries--;
303 LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
304 }
305
801ad6a6
LP
306 while (q->candidates)
307 dns_query_candidate_free(q->candidates);
322345fd 308
faa133f3
LP
309 dns_question_unref(q->question);
310 dns_answer_unref(q->answer);
801ad6a6 311 dns_search_domain_unref(q->answer_search_domain);
322345fd 312
ec2c5e43 313 sd_bus_message_unref(q->request);
82bd6ddd 314 sd_bus_track_unref(q->bus_track);
74b2466e 315
39762fdf 316 if (q->manager) {
74b2466e 317 LIST_REMOVE(queries, q->manager->dns_queries, q);
39762fdf
LP
318 q->manager->n_dns_queries--;
319 }
74b2466e 320
74b2466e
LP
321 free(q);
322
323 return NULL;
324}
325
51323288 326int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) {
74b2466e 327 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
faa133f3
LP
328 unsigned i;
329 int r;
74b2466e
LP
330
331 assert(m);
faa133f3 332 assert(question);
74b2466e 333
703e4f5e 334 r = dns_question_is_valid_for_query(question);
faa133f3
LP
335 if (r < 0)
336 return r;
74b2466e 337
39762fdf
LP
338 if (m->n_dns_queries >= QUERIES_MAX)
339 return -EBUSY;
340
74b2466e
LP
341 q = new0(DnsQuery, 1);
342 if (!q)
343 return -ENOMEM;
344
faa133f3 345 q->question = dns_question_ref(question);
51323288
LP
346 q->ifindex = ifindex;
347 q->flags = flags;
801ad6a6
LP
348 q->answer_family = AF_UNSPEC;
349 q->answer_protocol = _DNS_PROTOCOL_INVALID;
322345fd 350
faa133f3 351 for (i = 0; i < question->n_keys; i++) {
e4501ed4
LP
352 _cleanup_free_ char *p;
353
354 r = dns_resource_key_to_string(question->keys[i], &p);
355 if (r < 0)
356 return r;
357
358 log_debug("Looking up RR for %s", p);
74b2466e
LP
359 }
360
361 LIST_PREPEND(queries, m->dns_queries, q);
39762fdf 362 m->n_dns_queries++;
74b2466e
LP
363 q->manager = m;
364
8ba9fd9c
LP
365 if (ret)
366 *ret = q;
367 q = NULL;
368
369 return 0;
370}
371
45ec7efb
LP
372int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
373 assert(q);
374 assert(auxiliary_for);
375
376 /* Ensure that that the query is not auxiliary yet, and
377 * nothing else is auxiliary to it either */
378 assert(!q->auxiliary_for);
379 assert(!q->auxiliary_queries);
380
381 /* Ensure that the unit we shall be made auxiliary for isn't
382 * auxiliary itself */
383 assert(!auxiliary_for->auxiliary_for);
384
385 if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
386 return -EAGAIN;
387
388 LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
389 q->auxiliary_for = auxiliary_for;
390
391 auxiliary_for->n_auxiliary_queries++;
392 return 0;
393}
394
ec2c5e43 395static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
8ba9fd9c 396 assert(q);
ec2c5e43
LP
397 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
398 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
8ba9fd9c 399
322345fd
LP
400 /* Note that this call might invalidate the query. Callers
401 * should hence not attempt to access the query or transaction
402 * after calling this function. */
8ba9fd9c 403
8ba9fd9c
LP
404 q->state = state;
405
322345fd
LP
406 dns_query_stop(q);
407 if (q->complete)
408 q->complete(q);
8ba9fd9c
LP
409}
410
411static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
412 DnsQuery *q = userdata;
413
414 assert(s);
415 assert(q);
416
ec2c5e43 417 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
8ba9fd9c
LP
418 return 0;
419}
420
801ad6a6
LP
421static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
422 DnsQueryCandidate *c;
faa133f3
LP
423 int r;
424
425 assert(q);
ec2c5e43 426 assert(s);
faa133f3 427
801ad6a6 428 r = dns_query_candidate_new(&c, q, s);
faa133f3
LP
429 if (r < 0)
430 return r;
431
801ad6a6
LP
432 /* If this a single-label domain on DNS, we might append a suitable search domain first. */
433 r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question));
faa133f3 434 if (r < 0)
801ad6a6
LP
435 goto fail;
436 if (r > 0) {
437 /* OK, we need a search domain now. Let's find one for this scope */
faa133f3 438
801ad6a6
LP
439 r = dns_query_candidate_next_search_domain(c);
440 if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
441 goto fail;
faa133f3
LP
442 }
443
801ad6a6
LP
444 r = dns_query_candidate_setup_transactions(c);
445 if (r < 0)
446 goto fail;
447
faa133f3
LP
448 return 0;
449
801ad6a6
LP
450fail:
451 dns_query_candidate_free(c);
faa133f3
LP
452 return r;
453}
454
2a1037af
LP
455static int SYNTHESIZE_IFINDEX(int ifindex) {
456
78c6a153
LP
457 /* When the caller asked for resolving on a specific
458 * interface, we synthesize the answer for that
459 * interface. However, if nothing specific was claimed and we
460 * only return localhost RRs, we synthesize the answer for
2a1037af
LP
461 * localhost. */
462
463 if (ifindex > 0)
464 return ifindex;
465
466 return LOOPBACK_IFINDEX;
467}
468
469static int SYNTHESIZE_FAMILY(uint64_t flags) {
470
471 /* Picks an address family depending on set flags. This is
472 * purely for synthesized answers, where the family we return
473 * for the reply should match what was requested in the
474 * question, even though we are synthesizing the answer
475 * here. */
476
477 if (!(flags & SD_RESOLVED_DNS)) {
478 if (flags & SD_RESOLVED_LLMNR_IPV4)
479 return AF_INET;
480 if (flags & SD_RESOLVED_LLMNR_IPV6)
481 return AF_INET6;
482 }
483
484 return AF_UNSPEC;
485}
486
487static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) {
488
489 /* Similar as SYNTHESIZE_FAMILY() but does this for the
490 * protocol. If resolving via DNS was requested, we claim it
491 * was DNS. Similar, if nothing specific was
492 * requested. However, if only resolving via LLMNR was
493 * requested we return that. */
494
495 if (flags & SD_RESOLVED_DNS)
496 return DNS_PROTOCOL_DNS;
497 if (flags & SD_RESOLVED_LLMNR)
498 return DNS_PROTOCOL_LLMNR;
499
500 return DNS_PROTOCOL_DNS;
501}
502
78c6a153
LP
503static int dns_type_to_af(uint16_t t) {
504 switch (t) {
505
506 case DNS_TYPE_A:
507 return AF_INET;
508
509 case DNS_TYPE_AAAA:
510 return AF_INET6;
511
512 case DNS_TYPE_ANY:
513 return AF_UNSPEC;
514
515 default:
516 return -EINVAL;
517 }
518}
519
520static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
521 int r;
522
523 assert(q);
524 assert(key);
525 assert(answer);
526
527 r = dns_answer_reserve(answer, 2);
528 if (r < 0)
529 return r;
530
531 if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
532 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
533
534 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, DNS_RESOURCE_KEY_NAME(key));
535 if (!rr)
536 return -ENOMEM;
537
538 rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
539
540 r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
541 if (r < 0)
542 return r;
543 }
544
545 if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
546 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
547
548 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key));
549 if (!rr)
550 return -ENOMEM;
551
552 rr->aaaa.in6_addr = in6addr_loopback;
553
554 r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
555 if (r < 0)
556 return r;
557 }
558
559 return 0;
560}
561
562static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex) {
563 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
564
565 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
566 if (!rr)
567 return -ENOMEM;
568
569 rr->ptr.name = strdup(to);
570 if (!rr->ptr.name)
571 return -ENOMEM;
572
573 return dns_answer_add(*answer, rr, ifindex);
574}
575
576static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
577 int r;
578
579 assert(q);
580 assert(key);
581 assert(answer);
582
583 r = dns_answer_reserve(answer, 1);
584 if (r < 0)
585 return r;
586
587 if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
588 r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
589 if (r < 0)
590 return r;
591 }
592
593 return 0;
594}
595
596static int answer_add_addresses_rr(
597 DnsAnswer **answer,
598 const char *name,
599 struct local_address *addresses,
600 unsigned n_addresses) {
601
602 unsigned j;
603 int r;
604
605 assert(answer);
606 assert(name);
607
608 r = dns_answer_reserve(answer, n_addresses);
609 if (r < 0)
610 return r;
611
612 for (j = 0; j < n_addresses; j++) {
613 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
614
615 r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
616 if (r < 0)
617 return r;
618
619 r = dns_answer_add(*answer, rr, addresses[j].ifindex);
620 if (r < 0)
621 return r;
622 }
623
624 return 0;
625}
626
627static int answer_add_addresses_ptr(
628 DnsAnswer **answer,
629 const char *name,
630 struct local_address *addresses,
631 unsigned n_addresses,
632 int af, const union in_addr_union *match) {
633
634 unsigned j;
635 int r;
636
637 assert(answer);
638 assert(name);
639
640 for (j = 0; j < n_addresses; j++) {
641 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
642
643 if (af != AF_UNSPEC) {
644
645 if (addresses[j].family != af)
646 continue;
647
648 if (match && !in_addr_equal(af, match, &addresses[j].address))
649 continue;
650 }
651
652 r = dns_answer_reserve(answer, 1);
653 if (r < 0)
654 return r;
655
656 r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
657 if (r < 0)
658 return r;
659
660 r = dns_answer_add(*answer, rr, addresses[j].ifindex);
661 if (r < 0)
662 return r;
663 }
664
665 return 0;
666}
667
668static int synthesize_system_hostname_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
669 _cleanup_free_ struct local_address *addresses = NULL;
670 int n = 0, af;
671
672 assert(q);
673 assert(key);
674 assert(answer);
675
676 af = dns_type_to_af(key->type);
677 if (af >= 0) {
678 n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
679 if (n < 0)
680 return n;
681
682 if (n == 0) {
683 struct local_address buffer[2];
684
685 /* If we have no local addresses then use ::1
686 * and 127.0.0.2 as local ones. */
687
688 if (af == AF_INET || af == AF_UNSPEC)
689 buffer[n++] = (struct local_address) {
690 .family = AF_INET,
691 .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
692 .address.in.s_addr = htobe32(0x7F000002),
693 };
694
695 if (af == AF_INET6 || af == AF_UNSPEC)
696 buffer[n++] = (struct local_address) {
697 .family = AF_INET6,
698 .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
699 .address.in6 = in6addr_loopback,
700 };
701
702 return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n);
703 }
704 }
705
706 return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
707}
708
709static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
710 _cleanup_free_ struct local_address *addresses = NULL;
711 int n, r;
712
713 assert(q);
714 assert(address);
715 assert(answer);
716
717 if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
718
719 /* Always map the IPv4 address 127.0.0.2 to the local
720 * hostname, in addition to "localhost": */
721
722 r = dns_answer_reserve(answer, 3);
723 if (r < 0)
724 return r;
725
726 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
727 if (r < 0)
728 return r;
729
730 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
731 if (r < 0)
732 return r;
733
734 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
735 if (r < 0)
736 return r;
737
738 return 0;
739 }
740
741 n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
742 if (n < 0)
743 return n;
744
745 r = answer_add_addresses_ptr(answer, q->manager->llmnr_hostname, addresses, n, af, address);
746 if (r < 0)
747 return r;
748
749 return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address);
750}
751
752static int synthesize_gateway_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
753 _cleanup_free_ struct local_address *addresses = NULL;
754 int n = 0, af;
755
756 assert(q);
757 assert(key);
758 assert(answer);
759
760 af = dns_type_to_af(key->type);
761 if (af >= 0) {
762 n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
763 if (n < 0)
764 return n;
765 }
766
767 return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
768}
769
770static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
771 _cleanup_free_ struct local_address *addresses = NULL;
772 int n;
773
774 assert(q);
775 assert(address);
776 assert(answer);
777
778 n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
779 if (n < 0)
780 return n;
781
782 return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
783}
784
785static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
2a1037af
LP
786 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
787 unsigned i;
788 int r;
789
790 assert(q);
791 assert(state);
792
793 /* Tries to synthesize localhost RR replies where appropriate */
794
795 if (!IN_SET(*state,
796 DNS_TRANSACTION_FAILURE,
797 DNS_TRANSACTION_NO_SERVERS,
798 DNS_TRANSACTION_TIMEOUT,
799 DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
78c6a153 800 return 0;
2a1037af
LP
801
802 for (i = 0; i < q->question->n_keys; i++) {
78c6a153 803 union in_addr_union address;
2a1037af 804 const char *name;
78c6a153 805 int af;
2a1037af
LP
806
807 if (q->question->keys[i]->class != DNS_CLASS_IN &&
808 q->question->keys[i]->class != DNS_CLASS_ANY)
809 continue;
810
811 name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]);
812
813 if (is_localhost(name)) {
814
78c6a153
LP
815 r = synthesize_localhost_rr(q, q->question->keys[i], &answer);
816 if (r < 0)
817 return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
2a1037af 818
78c6a153 819 } else if (manager_is_own_hostname(q->manager, name)) {
2a1037af 820
78c6a153
LP
821 r = synthesize_system_hostname_rr(q, q->question->keys[i], &answer);
822 if (r < 0)
823 return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
2a1037af 824
78c6a153 825 } else if (is_gateway_hostname(name)) {
2a1037af 826
78c6a153
LP
827 r = synthesize_gateway_rr(q, q->question->keys[i], &answer);
828 if (r < 0)
829 return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
2a1037af 830
78c6a153
LP
831 } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
832 dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
2a1037af 833
78c6a153
LP
834 r = synthesize_localhost_ptr(q, q->question->keys[i], &answer);
835 if (r < 0)
836 return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
2a1037af 837
78c6a153 838 } else if (dns_name_address(name, &af, &address) > 0) {
2a1037af 839
78c6a153
LP
840 r = synthesize_system_hostname_ptr(q, af, &address, &answer);
841 if (r < 0)
842 return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
2a1037af 843
78c6a153
LP
844 r = synthesize_gateway_ptr(q, af, &address, &answer);
845 if (r < 0)
846 return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
2a1037af
LP
847 }
848 }
849
850 if (!answer)
78c6a153 851 return 0;
2a1037af
LP
852
853 dns_answer_unref(q->answer);
854 q->answer = answer;
855 answer = NULL;
856
2a1037af 857 q->answer_rcode = DNS_RCODE_SUCCESS;
801ad6a6
LP
858 q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
859 q->answer_family = SYNTHESIZE_FAMILY(q->flags);
2a1037af
LP
860
861 *state = DNS_TRANSACTION_SUCCESS;
78c6a153
LP
862
863 return 1;
2a1037af
LP
864}
865
322345fd 866int dns_query_go(DnsQuery *q) {
8ba9fd9c
LP
867 DnsScopeMatch found = DNS_SCOPE_NO;
868 DnsScope *s, *first = NULL;
801ad6a6 869 DnsQueryCandidate *c;
faa133f3 870 const char *name;
8ba9fd9c
LP
871 int r;
872
873 assert(q);
874
ec2c5e43 875 if (q->state != DNS_TRANSACTION_NULL)
8ba9fd9c
LP
876 return 0;
877
faa133f3
LP
878 assert(q->question);
879 assert(q->question->n_keys > 0);
880
703e4f5e 881 name = dns_question_first_name(q->question);
8ba9fd9c
LP
882
883 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
74b2466e
LP
884 DnsScopeMatch match;
885
51323288 886 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e
LP
887 if (match < 0)
888 return match;
889
890 if (match == DNS_SCOPE_NO)
891 continue;
892
893 found = match;
894
895 if (match == DNS_SCOPE_YES) {
896 first = s;
897 break;
898 } else {
899 assert(match == DNS_SCOPE_MAYBE);
900
901 if (!first)
902 first = s;
903 }
904 }
905
2a1037af
LP
906 if (found == DNS_SCOPE_NO) {
907 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
908
909 dns_query_synthesize_reply(q, &state);
d634711b
LP
910 dns_query_complete(q, state);
911 return 1;
2a1037af 912 }
74b2466e 913
801ad6a6 914 r = dns_query_add_candidate(q, first);
74b2466e 915 if (r < 0)
ec2c5e43 916 goto fail;
74b2466e 917
74b2466e
LP
918 LIST_FOREACH(scopes, s, first->scopes_next) {
919 DnsScopeMatch match;
920
51323288 921 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
74b2466e 922 if (match < 0)
ec2c5e43 923 goto fail;
74b2466e
LP
924
925 if (match != found)
926 continue;
927
801ad6a6 928 r = dns_query_add_candidate(q, s);
74b2466e 929 if (r < 0)
ec2c5e43 930 goto fail;
74b2466e
LP
931 }
932
faa133f3 933 q->answer = dns_answer_unref(q->answer);
faa133f3 934 q->answer_rcode = 0;
51323288
LP
935 q->answer_family = AF_UNSPEC;
936 q->answer_protocol = _DNS_PROTOCOL_INVALID;
74b2466e 937
9a015429
LP
938 r = sd_event_add_time(
939 q->manager->event,
940 &q->timeout_event_source,
941 clock_boottime_or_monotonic(),
942 now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
943 on_query_timeout, q);
74b2466e
LP
944 if (r < 0)
945 goto fail;
946
ec2c5e43 947 q->state = DNS_TRANSACTION_PENDING;
faa133f3 948 q->block_ready++;
74b2466e 949
801ad6a6
LP
950 /* Start the transactions */
951 LIST_FOREACH(candidates_by_query, c, q->candidates) {
952 r = dns_query_candidate_go(c);
953 if (r < 0) {
954 q->block_ready--;
ec2c5e43 955 goto fail;
801ad6a6 956 }
74b2466e
LP
957 }
958
faa133f3
LP
959 q->block_ready--;
960 dns_query_ready(q);
322345fd 961
8ba9fd9c 962 return 1;
74b2466e
LP
963
964fail:
8ba9fd9c 965 dns_query_stop(q);
74b2466e
LP
966 return r;
967}
968
801ad6a6 969static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
ec2c5e43 970 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
801ad6a6 971 DnsTransaction *t;
faa133f3 972 Iterator i;
74b2466e
LP
973
974 assert(q);
975
801ad6a6
LP
976 if (!c) {
977 dns_query_synthesize_reply(q, &state);
978 dns_query_complete(q, state);
74b2466e 979 return;
801ad6a6 980 }
74b2466e 981
801ad6a6 982 SET_FOREACH(t, c->transactions, i) {
74b2466e 983
801ad6a6 984 switch (t->state) {
934e9b10 985
801ad6a6
LP
986 case DNS_TRANSACTION_SUCCESS: {
987 /* We found a successfuly reply, merge it into the answer */
ae6a4bbf 988 DnsAnswer *merged;
322345fd 989
ae6a4bbf 990 merged = dns_answer_merge(q->answer, t->answer);
801ad6a6
LP
991 if (!merged) {
992 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
993 return;
994 }
995
996 dns_answer_unref(q->answer);
997 q->answer = merged;
ae6a4bbf 998 q->answer_rcode = t->answer_rcode;
801ad6a6
LP
999
1000 state = DNS_TRANSACTION_SUCCESS;
1001 break;
1002 }
1003
1004 case DNS_TRANSACTION_PENDING:
1005 case DNS_TRANSACTION_NULL:
1006 case DNS_TRANSACTION_ABORTED:
1007 /* Ignore transactions that didn't complete */
1008 continue;
1009
1010 default:
1011 /* Any kind of failure? Store the data away,
1012 * if there's nothing stored yet. */
934e9b10 1013
801ad6a6
LP
1014 if (state != DNS_TRANSACTION_SUCCESS) {
1015
1016 dns_answer_unref(q->answer);
ae6a4bbf
LP
1017 q->answer = dns_answer_ref(t->answer);
1018 q->answer_rcode = t->answer_rcode;
934e9b10 1019
801ad6a6 1020 state = t->state;
934e9b10
LP
1021 }
1022
801ad6a6 1023 break;
74b2466e 1024 }
801ad6a6 1025 }
74b2466e 1026
801ad6a6
LP
1027 q->answer_protocol = c->scope->protocol;
1028 q->answer_family = c->scope->family;
934e9b10 1029
801ad6a6
LP
1030 dns_search_domain_unref(q->answer_search_domain);
1031 q->answer_search_domain = dns_search_domain_ref(c->search_domain);
7e8e0422 1032
801ad6a6
LP
1033 dns_query_synthesize_reply(q, &state);
1034 dns_query_complete(q, state);
1035}
934e9b10 1036
801ad6a6 1037void dns_query_ready(DnsQuery *q) {
74b2466e 1038
801ad6a6
LP
1039 DnsQueryCandidate *bad = NULL, *c;
1040 bool pending = false;
74b2466e 1041
801ad6a6
LP
1042 assert(q);
1043 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
e4501ed4 1044
801ad6a6
LP
1045 /* Note that this call might invalidate the query. Callers
1046 * should hence not attempt to access the query or transaction
1047 * after calling this function, unless the block_ready
1048 * counter was explicitly bumped before doing so. */
1049
1050 if (q->block_ready > 0)
1051 return;
1052
1053 LIST_FOREACH(candidates_by_query, c, q->candidates) {
1054 DnsTransactionState state;
1055
1056 state = dns_query_candidate_state(c);
1057 switch (state) {
1058
1059 case DNS_TRANSACTION_SUCCESS:
1060 /* One of the transactions is successful,
1061 * let's use it, and copy its data out */
1062 dns_query_accept(q, c);
e4501ed4
LP
1063 return;
1064
801ad6a6
LP
1065 case DNS_TRANSACTION_PENDING:
1066 case DNS_TRANSACTION_NULL:
1067 /* One of the transactions is still going on, let's maybe wait for it */
1068 pending = true;
1069 break;
e4501ed4 1070
801ad6a6
LP
1071 default:
1072 /* Any kind of failure */
1073 bad = c;
1074 break;
1075 }
faa133f3 1076 }
74b2466e 1077
801ad6a6
LP
1078 if (pending)
1079 return;
2a1037af 1080
801ad6a6 1081 dns_query_accept(q, bad);
74b2466e 1082}
8ba9fd9c 1083
45ec7efb 1084static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
faa133f3
LP
1085 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
1086 int r;
8ba9fd9c
LP
1087
1088 assert(q);
1089
45ec7efb 1090 q->n_cname_redirects ++;
faa133f3 1091 if (q->n_cname_redirects > CNAME_MAX)
8ba9fd9c
LP
1092 return -ELOOP;
1093
36d9205d 1094 r = dns_question_cname_redirect(q->question, cname, &nq);
faa133f3
LP
1095 if (r < 0)
1096 return r;
8ba9fd9c 1097
faa133f3
LP
1098 dns_question_unref(q->question);
1099 q->question = nq;
1100 nq = NULL;
8ba9fd9c 1101
322345fd 1102 dns_query_stop(q);
ec2c5e43 1103 q->state = DNS_TRANSACTION_NULL;
322345fd 1104
8ba9fd9c
LP
1105 return 0;
1106}
82bd6ddd 1107
45ec7efb
LP
1108int dns_query_process_cname(DnsQuery *q) {
1109 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
1110 DnsResourceRecord *rr;
1111 int r;
1112
1113 assert(q);
1114
1115 if (q->state != DNS_TRANSACTION_SUCCESS)
1116 return 0;
1117
1118 DNS_ANSWER_FOREACH(rr, q->answer) {
1119
801ad6a6 1120 r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
45ec7efb
LP
1121 if (r < 0)
1122 return r;
1123 if (r > 0)
1124 return 0; /* The answer matches directly, no need to follow cnames */
1125
801ad6a6 1126 r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
45ec7efb
LP
1127 if (r < 0)
1128 return r;
1129 if (r > 0 && !cname)
1130 cname = dns_resource_record_ref(rr);
1131 }
1132
1133 if (!cname)
1134 return 0; /* No cname to follow */
1135
1136 if (q->flags & SD_RESOLVED_NO_CNAME)
1137 return -ELOOP;
1138
1139 /* OK, let's actually follow the CNAME */
1140 r = dns_query_cname_redirect(q, cname);
1141 if (r < 0)
1142 return r;
1143
1144 /* Let's see if the answer can already answer the new
1145 * redirected question */
1146 DNS_ANSWER_FOREACH(rr, q->answer) {
801ad6a6 1147 r = dns_question_matches_rr(q->question, rr, NULL);
45ec7efb
LP
1148 if (r < 0)
1149 return r;
1150 if (r > 0)
1151 return 0; /* It can answer it, yay! */
1152 }
1153
1154 /* OK, it cannot, let's begin with the new query */
1155 r = dns_query_go(q);
1156 if (r < 0)
1157 return r;
1158
1159 return 1; /* We return > 0, if we restarted the query for a new cname */
1160}
1161
82bd6ddd
LP
1162static int on_bus_track(sd_bus_track *t, void *userdata) {
1163 DnsQuery *q = userdata;
1164
1165 assert(t);
1166 assert(q);
1167
1168 log_debug("Client of active query vanished, aborting query.");
1169 dns_query_complete(q, DNS_TRANSACTION_ABORTED);
1170 return 0;
1171}
1172
966c66e3 1173int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
82bd6ddd
LP
1174 int r;
1175
1176 assert(q);
1177 assert(m);
1178
1179 if (!q->bus_track) {
966c66e3 1180 r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q);
82bd6ddd
LP
1181 if (r < 0)
1182 return r;
1183 }
1184
1185 r = sd_bus_track_add_sender(q->bus_track, m);
1186 if (r < 0)
1187 return r;
1188
1189 return 0;
1190}