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) {
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);
90 for (i = 0; i < question->n_keys; i++) {
91 _cleanup_free_ char *p;
93 r = dns_resource_key_to_string(question->keys[i], &p);
97 log_debug("Looking up RR for %s", p);
100 LIST_PREPEND(queries, m->dns_queries, q);
111 static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
113 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
114 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
116 /* Note that this call might invalidate the query. Callers
117 * should hence not attempt to access the query or transaction
118 * after calling this function. */
127 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
128 DnsQuery *q = userdata;
133 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
137 static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
138 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
145 r = set_ensure_allocated(&q->transactions, NULL, NULL);
150 question = dns_question_new(1);
154 r = dns_question_add(question, key);
158 question = dns_question_ref(q->question);
160 t = dns_scope_find_transaction(s, question, true);
162 r = dns_transaction_new(&t, s, question);
167 r = set_ensure_allocated(&t->queries, NULL, NULL);
171 r = set_put(t->queries, q);
175 r = set_put(q->transactions, t);
177 set_remove(t->queries, q);
184 dns_transaction_gc(t);
188 static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
194 if (s->protocol == DNS_PROTOCOL_MDNS) {
195 r = dns_query_add_transaction(q, s, NULL);
201 /* On DNS and LLMNR we can only send a single
202 * question per datagram, hence issue multiple
205 for (i = 0; i < q->question->n_keys; i++) {
206 r = dns_query_add_transaction(q, s, q->question->keys[i]);
215 int dns_query_go(DnsQuery *q) {
216 DnsScopeMatch found = DNS_SCOPE_NO;
217 DnsScope *s, *first = NULL;
225 if (q->state != DNS_TRANSACTION_NULL)
229 assert(q->question->n_keys > 0);
231 name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
233 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
236 match = dns_scope_good_domain(s, name);
240 if (match == DNS_SCOPE_NO)
245 if (match == DNS_SCOPE_YES) {
249 assert(match == DNS_SCOPE_MAYBE);
256 if (found == DNS_SCOPE_NO)
259 r = dns_query_add_transaction_split(q, first);
263 LIST_FOREACH(scopes, s, first->scopes_next) {
266 match = dns_scope_good_domain(s, name);
273 r = dns_query_add_transaction_split(q, s);
278 q->answer = dns_answer_unref(q->answer);
279 q->answer_ifindex = 0;
282 r = sd_event_add_time(
284 &q->timeout_event_source,
285 clock_boottime_or_monotonic(),
286 now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
287 on_query_timeout, q);
291 q->state = DNS_TRANSACTION_PENDING;
294 /* Start the transactions that are not started yet */
295 SET_FOREACH(t, q->transactions, i) {
296 if (t->state != DNS_TRANSACTION_NULL)
299 r = dns_transaction_go(t);
314 void dns_query_ready(DnsQuery *q) {
316 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
317 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
319 DnsScope *scope = NULL;
320 bool pending = false;
324 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
326 /* Note that this call might invalidate the query. Callers
327 * should hence not attempt to access the query or transaction
328 * after calling this function, unless the block_ready
329 * counter was explicitly bumped before doing so. */
331 if (q->block_ready > 0)
334 SET_FOREACH(t, q->transactions, i) {
336 /* If we found a successful answer, ignore all answers from other scopes */
337 if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
340 /* One of the transactions is still going on, let's maybe wait for it */
341 if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
346 /* One of the transactions is successful, let's use
347 * it, and copy its data out */
348 if (t->state == DNS_TRANSACTION_SUCCESS) {
352 rcode = DNS_PACKET_RCODE(t->received);
353 a = t->received->answer;
355 rcode = t->cached_rcode;
359 if (state == DNS_TRANSACTION_SUCCESS) {
362 merged = dns_answer_merge(answer, a);
364 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
368 dns_answer_unref(answer);
371 dns_answer_unref(answer);
372 answer = dns_answer_ref(a);
376 state = DNS_TRANSACTION_SUCCESS;
380 /* One of the transactions has failed, let's see
381 * whether we find anything better, but if not, return
382 * its response data */
383 if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
387 rcode = DNS_PACKET_RCODE(t->received);
388 a = t->received->answer;
390 rcode = t->cached_rcode;
394 dns_answer_unref(answer);
395 answer = dns_answer_ref(a);
398 state = DNS_TRANSACTION_FAILURE;
402 if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
408 /* If so far we weren't successful, and there's
409 * something still pending, then wait for it */
410 if (state != DNS_TRANSACTION_SUCCESS)
413 /* If we already were successful, then only wait for
414 * other transactions on the same scope to finish. */
415 SET_FOREACH(t, q->transactions, i) {
416 if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
421 if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
422 q->answer = dns_answer_ref(answer);
423 q->answer_rcode = rcode;
424 q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
427 dns_query_complete(q, state);
430 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
431 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
436 if (q->n_cname_redirects > CNAME_MAX)
439 r = dns_question_cname_redirect(q->question, name, &nq);
443 dns_question_unref(q->question);
447 q->n_cname_redirects++;
450 q->state = DNS_TRANSACTION_NULL;
455 static int on_bus_track(sd_bus_track *t, void *userdata) {
456 DnsQuery *q = userdata;
461 log_debug("Client of active query vanished, aborting query.");
462 dns_query_complete(q, DNS_TRANSACTION_ABORTED);
466 int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) {
473 r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q);
478 r = sd_bus_track_add_sender(q->bus_track, m);