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);
59 LIST_REMOVE(queries, q->manager->dns_queries, q);
60 q->manager->n_dns_queries--;
68 int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question) {
69 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
76 r = dns_question_is_valid(question);
80 if (m->n_dns_queries >= QUERIES_MAX)
83 q = new0(DnsQuery, 1);
87 q->question = dns_question_ref(question);
89 for (i = 0; i < question->n_keys; i++) {
90 _cleanup_free_ char *p;
92 r = dns_resource_key_to_string(question->keys[i], &p);
96 log_debug("Looking up RR for %s", p);
99 LIST_PREPEND(queries, m->dns_queries, q);
110 static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
112 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
113 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
115 /* Note that this call might invalidate the query. Callers
116 * should hence not attempt to access the query or transaction
117 * after calling this function. */
126 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
127 DnsQuery *q = userdata;
132 dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
136 static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
137 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
144 r = set_ensure_allocated(&q->transactions, NULL, NULL);
149 question = dns_question_new(1);
153 r = dns_question_add(question, key);
157 question = dns_question_ref(q->question);
159 t = dns_scope_find_transaction(s, question);
161 r = dns_transaction_new(&t, s, question);
166 r = set_ensure_allocated(&t->queries, NULL, NULL);
170 r = set_put(t->queries, q);
174 r = set_put(q->transactions, t);
176 set_remove(t->queries, q);
183 dns_transaction_gc(t);
187 static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
193 if (s->protocol == DNS_PROTOCOL_MDNS) {
194 r = dns_query_add_transaction(q, s, NULL);
200 /* On DNS and LLMNR we can only send a single
201 * question per datagram, hence issue multiple
204 for (i = 0; i < q->question->n_keys; i++) {
205 r = dns_query_add_transaction(q, s, q->question->keys[i]);
214 int dns_query_go(DnsQuery *q) {
215 DnsScopeMatch found = DNS_SCOPE_NO;
216 DnsScope *s, *first = NULL;
224 if (q->state != DNS_TRANSACTION_NULL)
228 assert(q->question->n_keys > 0);
230 name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
232 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
235 match = dns_scope_good_domain(s, name);
239 if (match == DNS_SCOPE_NO)
244 if (match == DNS_SCOPE_YES) {
248 assert(match == DNS_SCOPE_MAYBE);
255 if (found == DNS_SCOPE_NO)
258 r = dns_query_add_transaction_split(q, first);
262 LIST_FOREACH(scopes, s, first->scopes_next) {
265 match = dns_scope_good_domain(s, name);
272 r = dns_query_add_transaction_split(q, s);
277 q->answer = dns_answer_unref(q->answer);
278 q->answer_ifindex = 0;
281 r = sd_event_add_time(
283 &q->timeout_event_source,
284 clock_boottime_or_monotonic(),
285 now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
286 on_query_timeout, q);
290 q->state = DNS_TRANSACTION_PENDING;
293 /* Start the transactions that are not started yet */
294 SET_FOREACH(t, q->transactions, i) {
295 if (t->state != DNS_TRANSACTION_NULL)
298 r = dns_transaction_go(t);
313 void dns_query_ready(DnsQuery *q) {
315 DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
316 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
318 DnsScope *scope = NULL;
319 bool pending = false;
323 assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
325 /* Note that this call might invalidate the query. Callers
326 * should hence not attempt to access the query or transaction
327 * after calling this function, unless the block_ready
328 * counter was explicitly bumped before doing so. */
330 if (q->block_ready > 0)
333 SET_FOREACH(t, q->transactions, i) {
335 /* If we found a successful answer, ignore all answers from other scopes */
336 if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
339 /* One of the transactions is still going on, let's maybe wait for it */
340 if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
345 /* One of the transactions is successful, let's use
346 * it, and copy its data out */
347 if (t->state == DNS_TRANSACTION_SUCCESS) {
351 rcode = DNS_PACKET_RCODE(t->received);
352 a = t->received->answer;
354 rcode = t->cached_rcode;
358 if (state == DNS_TRANSACTION_SUCCESS) {
361 merged = dns_answer_merge(answer, a);
363 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
367 dns_answer_unref(answer);
370 dns_answer_unref(answer);
371 answer = dns_answer_ref(a);
375 state = DNS_TRANSACTION_SUCCESS;
379 /* One of the transactions has failed, let's see
380 * whether we find anything better, but if not, return
381 * its response data */
382 if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
386 rcode = DNS_PACKET_RCODE(t->received);
387 a = t->received->answer;
389 rcode = t->cached_rcode;
393 dns_answer_unref(answer);
394 answer = dns_answer_ref(a);
397 state = DNS_TRANSACTION_FAILURE;
401 if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
407 /* If so far we weren't successful, and there's
408 * something still pending, then wait for it */
409 if (state != DNS_TRANSACTION_SUCCESS)
412 /* If we already were successful, then only wait for
413 * other transactions on the same scope to finish. */
414 SET_FOREACH(t, q->transactions, i) {
415 if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
420 if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
421 q->answer = dns_answer_ref(answer);
422 q->answer_rcode = rcode;
423 q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
426 dns_query_complete(q, state);
429 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
430 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
435 if (q->n_cname_redirects > CNAME_MAX)
438 r = dns_question_cname_redirect(q->question, name, &nq);
442 dns_question_unref(q->question);
446 q->n_cname_redirects++;
449 q->state = DNS_TRANSACTION_NULL;