1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
24 #include "resolved-dns-query.h"
25 #include "resolved-dns-domain.h"
27 /* How long to wait for the query in total */
28 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
31 #define QUERIES_MAX 2048
33 static void dns_query_stop(DnsQuery *q) {
38 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
40 while ((t = set_steal_first(q->transactions))) {
41 set_remove(t->queries, q);
42 dns_transaction_gc(t);
46 DnsQuery *dns_query_free(DnsQuery *q) {
51 set_free(q->transactions);
53 dns_question_unref(q->question);
54 dns_answer_unref(q->answer);
56 sd_bus_message_unref(q->request);
57 sd_bus_track_unref(q->bus_track);
60 LIST_REMOVE(queries, q->manager->dns_queries, q);
61 q->manager->n_dns_queries--;
69 int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) {
70 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
77 r = dns_question_is_valid(question);
81 if (m->n_dns_queries >= QUERIES_MAX)
84 q = new0(DnsQuery, 1);
88 q->question = dns_question_ref(question);
92 for (i = 0; i < question->n_keys; i++) {
93 _cleanup_free_ char *p;
95 r = dns_resource_key_to_string(question->keys[i], &p);
99 log_debug("Looking up RR for %s", p);
102 LIST_PREPEND(queries, m->dns_queries, q);
113 static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
115 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
116 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
118 /* Note that this call might invalidate the query. Callers
119 * should hence not attempt to access the query or transaction
120 * after calling this function. */
129 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
130 DnsQuery *q = userdata;
135 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
139 static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
140 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
147 r = set_ensure_allocated(&q->transactions, NULL);
152 question = dns_question_new(1);
156 r = dns_question_add(question, key);
160 question = dns_question_ref(q->question);
162 t = dns_scope_find_transaction(s, question, true);
164 r = dns_transaction_new(&t, s, question);
169 r = set_ensure_allocated(&t->queries, NULL);
173 r = set_put(t->queries, q);
177 r = set_put(q->transactions, t);
179 set_remove(t->queries, q);
186 dns_transaction_gc(t);
190 static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
196 if (s->protocol == DNS_PROTOCOL_MDNS) {
197 r = dns_query_add_transaction(q, s, NULL);
203 /* On DNS and LLMNR we can only send a single
204 * question per datagram, hence issue multiple
207 for (i = 0; i < q->question->n_keys; i++) {
208 r = dns_query_add_transaction(q, s, q->question->keys[i]);
217 int dns_query_go(DnsQuery *q) {
218 DnsScopeMatch found = DNS_SCOPE_NO;
219 DnsScope *s, *first = NULL;
227 if (q->state != DNS_TRANSACTION_NULL)
231 assert(q->question->n_keys > 0);
233 name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
235 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
238 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
242 if (match == DNS_SCOPE_NO)
247 if (match == DNS_SCOPE_YES) {
251 assert(match == DNS_SCOPE_MAYBE);
258 if (found == DNS_SCOPE_NO)
261 r = dns_query_add_transaction_split(q, first);
265 LIST_FOREACH(scopes, s, first->scopes_next) {
268 match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
275 r = dns_query_add_transaction_split(q, s);
280 q->answer = dns_answer_unref(q->answer);
281 q->answer_ifindex = 0;
283 q->answer_family = AF_UNSPEC;
284 q->answer_protocol = _DNS_PROTOCOL_INVALID;
286 r = sd_event_add_time(
288 &q->timeout_event_source,
289 clock_boottime_or_monotonic(),
290 now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
291 on_query_timeout, q);
295 q->state = DNS_TRANSACTION_PENDING;
298 /* Start the transactions that are not started yet */
299 SET_FOREACH(t, q->transactions, i) {
300 if (t->state != DNS_TRANSACTION_NULL)
303 r = dns_transaction_go(t);
318 void dns_query_ready(DnsQuery *q) {
320 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
321 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
323 DnsScope *scope = NULL;
324 bool pending = false;
328 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
330 /* Note that this call might invalidate the query. Callers
331 * should hence not attempt to access the query or transaction
332 * after calling this function, unless the block_ready
333 * counter was explicitly bumped before doing so. */
335 if (q->block_ready > 0)
338 SET_FOREACH(t, q->transactions, i) {
340 /* If we found a successful answer, ignore all answers from other scopes */
341 if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
344 /* One of the transactions is still going on, let's maybe wait for it */
345 if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
350 /* One of the transactions is successful, let's use
351 * it, and copy its data out */
352 if (t->state == DNS_TRANSACTION_SUCCESS) {
356 rcode = DNS_PACKET_RCODE(t->received);
357 a = t->received->answer;
359 rcode = t->cached_rcode;
363 if (state == DNS_TRANSACTION_SUCCESS) {
366 merged = dns_answer_merge(answer, a);
368 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
372 dns_answer_unref(answer);
375 dns_answer_unref(answer);
376 answer = dns_answer_ref(a);
380 state = DNS_TRANSACTION_SUCCESS;
384 /* One of the transactions has failed, let's see
385 * whether we find anything better, but if not, return
386 * its response data */
387 if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
391 rcode = DNS_PACKET_RCODE(t->received);
392 a = t->received->answer;
394 rcode = t->cached_rcode;
398 dns_answer_unref(answer);
399 answer = dns_answer_ref(a);
402 state = DNS_TRANSACTION_FAILURE;
406 if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
412 /* If so far we weren't successful, and there's
413 * something still pending, then wait for it */
414 if (state != DNS_TRANSACTION_SUCCESS)
417 /* If we already were successful, then only wait for
418 * other transactions on the same scope to finish. */
419 SET_FOREACH(t, q->transactions, i) {
420 if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
425 if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
426 q->answer = dns_answer_ref(answer);
427 q->answer_rcode = rcode;
428 q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
429 q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
430 q->answer_family = scope ? scope->family : AF_UNSPEC;
433 dns_query_complete(q, state);
436 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
437 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
442 if (q->n_cname_redirects > CNAME_MAX)
445 r = dns_question_cname_redirect(q->question, name, &nq);
449 dns_question_unref(q->question);
453 q->n_cname_redirects++;
456 q->state = DNS_TRANSACTION_NULL;
461 static int on_bus_track(sd_bus_track *t, void *userdata) {
462 DnsQuery *q = userdata;
467 log_debug("Client of active query vanished, aborting query.");
468 dns_query_complete(q, DNS_TRANSACTION_ABORTED);
472 int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) {
479 r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q);
484 r = sd_bus_track_add_sender(q->bus_track, m);