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(q
->manager
->event
, &q
->timeout_event_source
, CLOCK_MONOTONIC
, now(CLOCK_MONOTONIC
) + QUERY_TIMEOUT_USEC
, 0, on_query_timeout
, q
);
285 q
->state
= DNS_TRANSACTION_PENDING
;
288 /* Start the transactions that are not started yet */
289 SET_FOREACH(t
, q
->transactions
, i
) {
290 if (t
->state
!= DNS_TRANSACTION_NULL
)
293 r
= dns_transaction_go(t
);
308 void dns_query_ready(DnsQuery
*q
) {
310 DnsTransactionState state
= DNS_TRANSACTION_NO_SERVERS
;
311 _cleanup_(dns_answer_unrefp
) DnsAnswer
*answer
= NULL
;
313 DnsScope
*scope
= NULL
;
314 bool pending
= false;
318 assert(IN_SET(q
->state
, DNS_TRANSACTION_NULL
, DNS_TRANSACTION_PENDING
));
320 /* Note that this call might invalidate the query. Callers
321 * should hence not attempt to access the query or transaction
322 * after calling this function, unless the block_ready
323 * counter was explicitly bumped before doing so. */
325 if (q
->block_ready
> 0)
328 SET_FOREACH(t
, q
->transactions
, i
) {
330 /* If we found a successful answer, ignore all answers from other scopes */
331 if (state
== DNS_TRANSACTION_SUCCESS
&& t
->scope
!= scope
)
334 /* One of the transactions is still going on, let's maybe wait for it */
335 if (IN_SET(t
->state
, DNS_TRANSACTION_PENDING
, DNS_TRANSACTION_NULL
)) {
340 /* One of the transactions is successful, let's use
341 * it, and copy its data out */
342 if (t
->state
== DNS_TRANSACTION_SUCCESS
) {
346 rcode
= DNS_PACKET_RCODE(t
->received
);
347 a
= t
->received
->answer
;
349 rcode
= t
->cached_rcode
;
353 if (state
== DNS_TRANSACTION_SUCCESS
) {
356 merged
= dns_answer_merge(answer
, a
);
358 dns_query_complete(q
, DNS_TRANSACTION_RESOURCES
);
362 dns_answer_unref(answer
);
365 dns_answer_unref(answer
);
366 answer
= dns_answer_ref(a
);
370 state
= DNS_TRANSACTION_SUCCESS
;
374 /* One of the transactions has failed, let's see
375 * whether we find anything better, but if not, return
376 * its response data */
377 if (state
!= DNS_TRANSACTION_SUCCESS
&& t
->state
== DNS_TRANSACTION_FAILURE
) {
381 rcode
= DNS_PACKET_RCODE(t
->received
);
382 a
= t
->received
->answer
;
384 rcode
= t
->cached_rcode
;
388 dns_answer_unref(answer
);
389 answer
= dns_answer_ref(a
);
392 state
= DNS_TRANSACTION_FAILURE
;
396 if (state
== DNS_TRANSACTION_NO_SERVERS
&& t
->state
!= DNS_TRANSACTION_NO_SERVERS
)
402 /* If so far we weren't successful, and there's
403 * something still pending, then wait for it */
404 if (state
!= DNS_TRANSACTION_SUCCESS
)
407 /* If we already were successful, then only wait for
408 * other transactions on the same scope to finish. */
409 SET_FOREACH(t
, q
->transactions
, i
) {
410 if (t
->scope
== scope
&& IN_SET(t
->state
, DNS_TRANSACTION_PENDING
, DNS_TRANSACTION_NULL
))
415 if (IN_SET(state
, DNS_TRANSACTION_SUCCESS
, DNS_TRANSACTION_FAILURE
)) {
416 q
->answer
= dns_answer_ref(answer
);
417 q
->answer_rcode
= rcode
;
418 q
->answer_ifindex
= (scope
&& scope
->link
) ? scope
->link
->ifindex
: 0;
421 dns_query_complete(q
, state
);
424 int dns_query_cname_redirect(DnsQuery
*q
, const char *name
) {
425 _cleanup_(dns_question_unrefp
) DnsQuestion
*nq
= NULL
;
430 if (q
->n_cname_redirects
> CNAME_MAX
)
433 r
= dns_question_cname_redirect(q
->question
, name
, &nq
);
437 dns_question_unref(q
->question
);
441 q
->n_cname_redirects
++;
444 q
->state
= DNS_TRANSACTION_NULL
;