]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-query.c
resolved: add a DNS client stub resolver
[thirdparty/systemd.git] / src / resolve / resolved-dns-query.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include "resolved-dns-query.h"
23 #include "resolved-dns-domain.h"
24
25 #define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
26 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
27 #define ATTEMPTS_MAX 8
28
29 DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
30 if (!t)
31 return NULL;
32
33 sd_event_source_unref(t->timeout_event_source);
34 dns_packet_unref(t->packet);
35
36 if (t->query) {
37 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
38 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
39 }
40
41 if (t->scope)
42 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
43
44 free(t);
45 return NULL;
46 }
47
48 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
49
50 static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
51 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
52 int r;
53
54 assert(q);
55 assert(s);
56
57 r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
58 if (r < 0)
59 return r;
60
61 t = new0(DnsQueryTransaction, 1);
62 if (!t)
63 return -ENOMEM;
64
65 do
66 random_bytes(&t->id, sizeof(t->id));
67 while (t->id == 0 ||
68 hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
69
70 r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
71 if (r < 0) {
72 t->id = 0;
73 return r;
74 }
75
76 LIST_PREPEND(transactions_by_query, q->transactions, t);
77 t->query = q;
78
79 LIST_PREPEND(transactions_by_scope, s->transactions, t);
80 t->scope = s;
81
82 if (ret)
83 *ret = t;
84
85 t = NULL;
86
87 return 0;
88 }
89
90 static void dns_query_transaction_set_state(DnsQueryTransaction *t, DnsQueryState state) {
91 assert(t);
92
93 if (t->state == state)
94 return;
95
96 t->state = state;
97
98 if (state != DNS_QUERY_SENT)
99 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
100
101 dns_query_finish(t->query);
102 }
103
104 int dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
105 assert(t);
106 assert(p);
107
108 t->packet = dns_packet_ref(p);
109
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);
113 else
114 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
115 } else
116 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
117
118 return 0;
119 }
120
121 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
122 DnsQueryTransaction *t = userdata;
123 int r;
124
125 assert(s);
126 assert(t);
127
128 /* Timeout reached? Try again, with a new server */
129 dns_scope_next_dns_server(t->scope);
130
131 r = dns_query_transaction_start(t);
132 if (r < 0)
133 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
134
135 return 0;
136 }
137
138 int dns_query_transaction_start(DnsQueryTransaction *t) {
139 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
140 unsigned n;
141 int r;
142
143 assert(t);
144
145 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
146
147 if (t->n_attempts >= ATTEMPTS_MAX) {
148 dns_query_transaction_set_state(t, DNS_QUERY_ATTEMPTS_MAX);
149 return 0;
150 }
151
152 t->n_attempts++;
153
154 r = dns_packet_new_query(&p, 0);
155 if (r < 0)
156 return r;
157
158 for (n = 0; n < t->query->n_keys; n++) {
159 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
160 if (r < 0)
161 return r;
162 }
163
164 DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
165 DNS_PACKET_HEADER(p)->id = t->id;
166
167 r = dns_scope_send(t->scope, p);
168 if (r < 0) {
169 /* Couldn't send? Try immediately again, with a new server */
170 dns_scope_next_dns_server(t->scope);
171
172 return dns_query_transaction_start(t);
173 }
174
175 if (r > 0) {
176 int q;
177
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);
179 if (q < 0)
180 return q;
181
182 dns_query_transaction_set_state(t, DNS_QUERY_SENT);
183 } else
184 dns_query_transaction_set_state(t, DNS_QUERY_SKIPPED);
185
186 return r;
187 }
188
189 DnsQuery *dns_query_free(DnsQuery *q) {
190 unsigned n;
191
192 if (!q)
193 return NULL;
194
195 sd_bus_message_unref(q->request);
196 dns_packet_unref(q->packet);
197 sd_event_source_unref(q->timeout_event_source);
198
199 while (q->transactions)
200 dns_query_transaction_free(q->transactions);
201
202 if (q->manager)
203 LIST_REMOVE(queries, q->manager->dns_queries, q);
204
205 for (n = 0; n < q->n_keys; n++)
206 free(q->keys[n].name);
207 free(q->keys);
208 free(q);
209
210 return NULL;
211 }
212
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;
218 int n, r;
219
220 assert(m);
221
222 if (n_keys <= 0 || n_keys >= 65535)
223 return -EINVAL;
224
225 assert(keys);
226
227 q = new0(DnsQuery, 1);
228 if (!q)
229 return -ENOMEM;
230
231 q->keys = new(DnsResourceKey, n_keys);
232 if (!q->keys)
233 return -ENOMEM;
234
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)
240 return -ENOMEM;
241
242 if (!name)
243 name = q->keys[q->n_keys].name;
244 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
245 return -EINVAL;
246 }
247
248 LIST_PREPEND(queries, m->dns_queries, q);
249 q->manager = m;
250
251 LIST_FOREACH(scopes, s, m->dns_scopes) {
252 DnsScopeMatch match;
253
254 match = dns_scope_test(s, name);
255 if (match < 0)
256 return match;
257
258 if (match == DNS_SCOPE_NO)
259 continue;
260
261 found = match;
262
263 if (match == DNS_SCOPE_YES) {
264 first = s;
265 break;
266 } else {
267 assert(match == DNS_SCOPE_MAYBE);
268
269 if (!first)
270 first = s;
271 }
272 }
273
274 if (found == DNS_SCOPE_NO)
275 return -ENETDOWN;
276
277 r = dns_query_transaction_new(q, NULL, first);
278 if (r < 0)
279 return r;
280
281 n = 1;
282 LIST_FOREACH(scopes, s, first->scopes_next) {
283 DnsScopeMatch match;
284
285 match = dns_scope_test(s, name);
286 if (match < 0)
287 return match;
288
289 if (match != found)
290 continue;
291
292 r = dns_query_transaction_new(q, NULL, s);
293 if (r < 0)
294 return r;
295
296 n++;
297 }
298
299 if (ret)
300 *ret = q;
301 q = NULL;
302
303 return n;
304 }
305
306 static void dns_query_set_state(DnsQuery *q, DnsQueryState state) {
307 assert(q);
308
309 if (q->state == state)
310 return;
311
312 q->state = state;
313
314 if (state == DNS_QUERY_SENT)
315 return;
316
317 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
318
319 while (q->transactions)
320 dns_query_transaction_free(q->transactions);
321
322 if (q->complete)
323 q->complete(q);
324 }
325
326 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
327 DnsQuery *q = userdata;
328
329 assert(s);
330 assert(q);
331
332 dns_query_set_state(q, DNS_QUERY_TIMEOUT);
333 return 0;
334 }
335
336 int dns_query_start(DnsQuery *q) {
337 DnsQueryTransaction *t;
338 int r;
339
340 assert(q);
341 assert(q->state == DNS_QUERY_NULL);
342
343 if (!q->transactions)
344 return -ENETDOWN;
345
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);
347 if (r < 0)
348 goto fail;
349
350 dns_query_set_state(q, DNS_QUERY_SENT);
351
352 LIST_FOREACH(transactions_by_query, t, q->transactions) {
353
354 r = dns_query_transaction_start(t);
355 if (r < 0)
356 goto fail;
357
358 if (q->state != DNS_QUERY_SENT)
359 break;
360 }
361
362 return 0;
363
364 fail:
365 while (q->transactions)
366 dns_query_transaction_free(q->transactions);
367
368 return r;
369 }
370
371 void dns_query_finish(DnsQuery *q) {
372 DnsQueryTransaction *t;
373 DnsQueryState state = DNS_QUERY_SKIPPED;
374 uint16_t rcode = 0;
375
376 assert(q);
377
378 if (q->state != DNS_QUERY_SENT)
379 return;
380
381 LIST_FOREACH(transactions_by_query, t, q->transactions) {
382
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)
385 return;
386
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);
391 return;
392 }
393
394 if (t->state == DNS_QUERY_FAILURE) {
395 state = DNS_QUERY_FAILURE;
396
397 if (rcode == 0 && t->packet)
398 rcode = DNS_PACKET_RCODE(t->packet);
399
400 } else if (state == DNS_QUERY_SKIPPED && t->state != DNS_QUERY_SKIPPED)
401 state = t->state;
402 }
403
404 if (state == DNS_QUERY_FAILURE)
405 q->rcode = rcode;
406
407 dns_query_set_state(q, state);
408 }