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