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/>.
22 #include "resolved-dns-query.h"
23 #include "resolved-dns-domain.h"
25 #define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
26 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
27 #define ATTEMPTS_MAX 8
29 DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
33 sd_event_source_unref(t->timeout_event_source);
34 dns_packet_unref(t->packet);
37 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
38 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
42 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
48 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
50 static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
51 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
57 r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
61 t = new0(DnsQueryTransaction, 1);
66 random_bytes(&t->id, sizeof(t->id));
68 hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
70 r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
76 LIST_PREPEND(transactions_by_query, q->transactions, t);
79 LIST_PREPEND(transactions_by_scope, s->transactions, t);
90 static void dns_query_transaction_set_state(DnsQueryTransaction *t, DnsQueryState state) {
93 if (t->state == state)
98 if (state != DNS_QUERY_SENT)
99 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
101 dns_query_finish(t->query);
104 int dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
108 t->packet = dns_packet_ref(p);
110 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
111 dns_query_transaction_set_state(t, DNS_QUERY_SUCCESS);
113 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
118 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
119 DnsQueryTransaction *t = userdata;
125 /* Timeout reached? Try again, with a new server */
126 dns_scope_next_dns_server(t->scope);
128 r = dns_query_transaction_start(t);
130 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
135 int dns_query_transaction_start(DnsQueryTransaction *t) {
136 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
142 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
144 if (t->n_attempts >= ATTEMPTS_MAX) {
145 dns_query_transaction_set_state(t, DNS_QUERY_ATTEMPTS_MAX);
151 r = dns_packet_new_query(&p, 0);
155 for (n = 0; n < t->query->n_keys; n++) {
156 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
161 DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
162 DNS_PACKET_HEADER(p)->id = t->id;
164 r = dns_scope_send(t->scope, p);
166 /* Couldn't send? Try immediately again, with a new server */
167 dns_scope_next_dns_server(t->scope);
169 return dns_query_transaction_start(t);
175 q = sd_event_add_time(t->query->manager->event, &t->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + TRANSACTION_TIMEOUT_USEC, 0, on_transaction_timeout, t);
179 dns_query_transaction_set_state(t, DNS_QUERY_SENT);
181 dns_query_transaction_set_state(t, DNS_QUERY_SKIPPED);
186 DnsQuery *dns_query_free(DnsQuery *q) {
192 sd_bus_message_unref(q->request);
193 dns_packet_unref(q->packet);
194 sd_event_source_unref(q->timeout_event_source);
196 while (q->transactions)
197 dns_query_transaction_free(q->transactions);
200 LIST_REMOVE(queries, q->manager->dns_queries, q);
202 for (n = 0; n < q->n_keys; n++)
203 free(q->keys[n].name);
210 int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
211 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
212 DnsScope *s, *first = NULL;
213 DnsScopeMatch found = DNS_SCOPE_NO;
214 const char *name = NULL;
219 if (n_keys <= 0 || n_keys >= 65535)
224 q = new0(DnsQuery, 1);
228 q->keys = new(DnsResourceKey, n_keys);
232 for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
233 q->keys[q->n_keys].class = keys[q->n_keys].class;
234 q->keys[q->n_keys].type = keys[q->n_keys].type;
235 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
236 if (!q->keys[q->n_keys].name)
240 name = q->keys[q->n_keys].name;
241 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
245 LIST_PREPEND(queries, m->dns_queries, q);
248 LIST_FOREACH(scopes, s, m->dns_scopes) {
251 match = dns_scope_test(s, name);
255 if (match == DNS_SCOPE_NO)
260 if (match == DNS_SCOPE_YES) {
264 assert(match == DNS_SCOPE_MAYBE);
271 if (found == DNS_SCOPE_NO)
274 r = dns_query_transaction_new(q, NULL, first);
279 LIST_FOREACH(scopes, s, first->scopes_next) {
282 match = dns_scope_test(s, name);
289 r = dns_query_transaction_new(q, NULL, s);
303 static void dns_query_set_state(DnsQuery *q, DnsQueryState state) {
306 if (q->state == state)
311 if (state == DNS_QUERY_SENT)
314 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
316 while (q->transactions)
317 dns_query_transaction_free(q->transactions);
323 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
324 DnsQuery *q = userdata;
329 dns_query_set_state(q, DNS_QUERY_TIMEOUT);
333 int dns_query_start(DnsQuery *q) {
334 DnsQueryTransaction *t;
338 assert(q->state == DNS_QUERY_NULL);
340 if (!q->transactions)
343 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);
347 dns_query_set_state(q, DNS_QUERY_SENT);
349 LIST_FOREACH(transactions_by_query, t, q->transactions) {
351 r = dns_query_transaction_start(t);
355 if (q->state != DNS_QUERY_SENT)
362 while (q->transactions)
363 dns_query_transaction_free(q->transactions);
368 void dns_query_finish(DnsQuery *q) {
369 DnsQueryTransaction *t;
370 DnsQueryState state = DNS_QUERY_SKIPPED;
375 if (q->state != DNS_QUERY_SENT)
378 LIST_FOREACH(transactions_by_query, t, q->transactions) {
380 /* One of the transactions is still going on, let's wait for it */
381 if (t->state == DNS_QUERY_SENT || t->state == DNS_QUERY_NULL)
384 /* One of the transactions is sucecssful, let's use it */
385 if (t->state == DNS_QUERY_SUCCESS) {
386 q->packet = dns_packet_ref(t->packet);
387 dns_query_set_state(q, DNS_QUERY_SUCCESS);
391 if (t->state == DNS_QUERY_FAILURE) {
392 state = DNS_QUERY_FAILURE;
394 if (rcode == 0 && t->packet)
395 rcode = DNS_PACKET_RCODE(t->packet);
397 } else if (state == DNS_QUERY_SKIPPED && t->state != DNS_QUERY_SKIPPED)
401 if (state == DNS_QUERY_FAILURE)
404 dns_query_set_state(q, state);