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 if( be16toh(DNS_PACKET_HEADER(p)->ancount) > 0)
112 dns_query_transaction_set_state(t, DNS_QUERY_SUCCESS);
114 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
116 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
121 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
122 DnsQueryTransaction *t = userdata;
128 /* Timeout reached? Try again, with a new server */
129 dns_scope_next_dns_server(t->scope);
131 r = dns_query_transaction_start(t);
133 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
138 int dns_query_transaction_start(DnsQueryTransaction *t) {
139 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
145 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
147 if (t->n_attempts >= ATTEMPTS_MAX) {
148 dns_query_transaction_set_state(t, DNS_QUERY_ATTEMPTS_MAX);
154 r = dns_packet_new_query(&p, 0);
158 for (n = 0; n < t->query->n_keys; n++) {
159 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
164 DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
165 DNS_PACKET_HEADER(p)->id = t->id;
167 r = dns_scope_send(t->scope, p);
169 /* Couldn't send? Try immediately again, with a new server */
170 dns_scope_next_dns_server(t->scope);
172 return dns_query_transaction_start(t);
178 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);
182 dns_query_transaction_set_state(t, DNS_QUERY_SENT);
184 dns_query_transaction_set_state(t, DNS_QUERY_SKIPPED);
189 DnsQuery *dns_query_free(DnsQuery *q) {
195 sd_bus_message_unref(q->request);
196 dns_packet_unref(q->packet);
197 sd_event_source_unref(q->timeout_event_source);
199 while (q->transactions)
200 dns_query_transaction_free(q->transactions);
203 LIST_REMOVE(queries, q->manager->dns_queries, q);
205 for (n = 0; n < q->n_keys; n++)
206 free(q->keys[n].name);
213 int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
214 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
215 DnsScope *s, *first = NULL;
216 DnsScopeMatch found = DNS_SCOPE_NO;
217 const char *name = NULL;
222 if (n_keys <= 0 || n_keys >= 65535)
227 q = new0(DnsQuery, 1);
231 q->keys = new(DnsResourceKey, n_keys);
235 for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
236 q->keys[q->n_keys].class = keys[q->n_keys].class;
237 q->keys[q->n_keys].type = keys[q->n_keys].type;
238 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
239 if (!q->keys[q->n_keys].name)
243 name = q->keys[q->n_keys].name;
244 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
248 LIST_PREPEND(queries, m->dns_queries, q);
251 LIST_FOREACH(scopes, s, m->dns_scopes) {
254 match = dns_scope_test(s, name);
258 if (match == DNS_SCOPE_NO)
263 if (match == DNS_SCOPE_YES) {
267 assert(match == DNS_SCOPE_MAYBE);
274 if (found == DNS_SCOPE_NO)
277 r = dns_query_transaction_new(q, NULL, first);
282 LIST_FOREACH(scopes, s, first->scopes_next) {
285 match = dns_scope_test(s, name);
292 r = dns_query_transaction_new(q, NULL, s);
306 static void dns_query_set_state(DnsQuery *q, DnsQueryState state) {
309 if (q->state == state)
314 if (state == DNS_QUERY_SENT)
317 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
319 while (q->transactions)
320 dns_query_transaction_free(q->transactions);
326 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
327 DnsQuery *q = userdata;
332 dns_query_set_state(q, DNS_QUERY_TIMEOUT);
336 int dns_query_start(DnsQuery *q) {
337 DnsQueryTransaction *t;
341 assert(q->state == DNS_QUERY_NULL);
343 if (!q->transactions)
346 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);
350 dns_query_set_state(q, DNS_QUERY_SENT);
352 LIST_FOREACH(transactions_by_query, t, q->transactions) {
354 r = dns_query_transaction_start(t);
358 if (q->state != DNS_QUERY_SENT)
365 while (q->transactions)
366 dns_query_transaction_free(q->transactions);
371 void dns_query_finish(DnsQuery *q) {
372 DnsQueryTransaction *t;
373 DnsQueryState state = DNS_QUERY_SKIPPED;
378 if (q->state != DNS_QUERY_SENT)
381 LIST_FOREACH(transactions_by_query, t, q->transactions) {
383 /* One of the transactions is still going on, let's wait for it */
384 if (t->state == DNS_QUERY_SENT || t->state == DNS_QUERY_NULL)
387 /* One of the transactions is sucecssful, let's use it */
388 if (t->state == DNS_QUERY_SUCCESS) {
389 q->packet = dns_packet_ref(t->packet);
390 dns_query_set_state(q, DNS_QUERY_SUCCESS);
394 if (t->state == DNS_QUERY_FAILURE) {
395 state = DNS_QUERY_FAILURE;
397 if (rcode == 0 && t->packet)
398 rcode = DNS_PACKET_RCODE(t->packet);
400 } else if (state == DNS_QUERY_SKIPPED && t->state != DNS_QUERY_SKIPPED)
404 if (state == DNS_QUERY_FAILURE)
407 dns_query_set_state(q, state);