chiark / gitweb /
6d77c109b4ec5002c7b4edf16152f2fe9b48b0d0
[elogind.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 "af-list.h"
23
24 #include "resolved-dns-query.h"
25 #include "resolved-dns-domain.h"
26
27 /* How long to wait for the query in total */
28 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
29
30 #define CNAME_MAX 8
31 #define QUERIES_MAX 2048
32
33 static void dns_query_stop(DnsQuery *q) {
34         DnsTransaction *t;
35
36         assert(q);
37
38         q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
39
40         while ((t = set_steal_first(q->transactions))) {
41                 set_remove(t->queries, q);
42                 dns_transaction_gc(t);
43         }
44 }
45
46 DnsQuery *dns_query_free(DnsQuery *q) {
47         if (!q)
48                 return NULL;
49
50         dns_query_stop(q);
51         set_free(q->transactions);
52
53         dns_question_unref(q->question);
54         dns_answer_unref(q->answer);
55
56         sd_bus_message_unref(q->request);
57         sd_bus_track_unref(q->bus_track);
58
59         if (q->manager) {
60                 LIST_REMOVE(queries, q->manager->dns_queries, q);
61                 q->manager->n_dns_queries--;
62         }
63
64         free(q);
65
66         return NULL;
67 }
68
69 int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question) {
70         _cleanup_(dns_query_freep) DnsQuery *q = NULL;
71         unsigned i;
72         int r;
73
74         assert(m);
75         assert(question);
76
77         r = dns_question_is_valid(question);
78         if (r < 0)
79                 return r;
80
81         if (m->n_dns_queries >= QUERIES_MAX)
82                 return -EBUSY;
83
84         q = new0(DnsQuery, 1);
85         if (!q)
86                 return -ENOMEM;
87
88         q->question = dns_question_ref(question);
89
90         for (i = 0; i < question->n_keys; i++) {
91                 _cleanup_free_ char *p;
92
93                 r = dns_resource_key_to_string(question->keys[i], &p);
94                 if (r < 0)
95                         return r;
96
97                 log_debug("Looking up RR for %s", p);
98         }
99
100         LIST_PREPEND(queries, m->dns_queries, q);
101         m->n_dns_queries++;
102         q->manager = m;
103
104         if (ret)
105                 *ret = q;
106         q = NULL;
107
108         return 0;
109 }
110
111 static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
112         assert(q);
113         assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
114         assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
115
116         /* Note that this call might invalidate the query. Callers
117          * should hence not attempt to access the query or transaction
118          * after calling this function. */
119
120         q->state = state;
121
122         dns_query_stop(q);
123         if (q->complete)
124                 q->complete(q);
125 }
126
127 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
128         DnsQuery *q = userdata;
129
130         assert(s);
131         assert(q);
132
133         dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
134         return 0;
135 }
136
137 static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
138         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
139         DnsTransaction *t;
140         int r;
141
142         assert(q);
143         assert(s);
144
145         r = set_ensure_allocated(&q->transactions, NULL, NULL);
146         if (r < 0)
147                 return r;
148
149         if (key) {
150                 question = dns_question_new(1);
151                 if (!question)
152                         return -ENOMEM;
153
154                 r = dns_question_add(question, key);
155                 if (r < 0)
156                         return r;
157         } else
158                 question = dns_question_ref(q->question);
159
160         t = dns_scope_find_transaction(s, question, true);
161         if (!t) {
162                 r = dns_transaction_new(&t, s, question);
163                 if (r < 0)
164                         return r;
165         }
166
167         r = set_ensure_allocated(&t->queries, NULL, NULL);
168         if (r < 0)
169                 goto gc;
170
171         r = set_put(t->queries, q);
172         if (r < 0)
173                 goto gc;
174
175         r = set_put(q->transactions, t);
176         if (r < 0) {
177                 set_remove(t->queries, q);
178                 goto gc;
179         }
180
181         return 0;
182
183 gc:
184         dns_transaction_gc(t);
185         return r;
186 }
187
188 static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
189         int r;
190
191         assert(q);
192         assert(s);
193
194         if (s->protocol == DNS_PROTOCOL_MDNS) {
195                 r = dns_query_add_transaction(q, s, NULL);
196                 if (r < 0)
197                         return r;
198         } else {
199                 unsigned i;
200
201                 /* On DNS and LLMNR we can only send a single
202                  * question per datagram, hence issue multiple
203                  * transactions. */
204
205                 for (i = 0; i < q->question->n_keys; i++) {
206                         r = dns_query_add_transaction(q, s, q->question->keys[i]);
207                         if (r < 0)
208                                 return r;
209                 }
210         }
211
212         return 0;
213 }
214
215 int dns_query_go(DnsQuery *q) {
216         DnsScopeMatch found = DNS_SCOPE_NO;
217         DnsScope *s, *first = NULL;
218         DnsTransaction *t;
219         const char *name;
220         Iterator i;
221         int r;
222
223         assert(q);
224
225         if (q->state != DNS_TRANSACTION_NULL)
226                 return 0;
227
228         assert(q->question);
229         assert(q->question->n_keys > 0);
230
231         name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
232
233         LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
234                 DnsScopeMatch match;
235
236                 match = dns_scope_good_domain(s, name);
237                 if (match < 0)
238                         return match;
239
240                 if (match == DNS_SCOPE_NO)
241                         continue;
242
243                 found = match;
244
245                 if (match == DNS_SCOPE_YES) {
246                         first = s;
247                         break;
248                 } else {
249                         assert(match == DNS_SCOPE_MAYBE);
250
251                         if (!first)
252                                 first = s;
253                 }
254         }
255
256         if (found == DNS_SCOPE_NO)
257                 return -ESRCH;
258
259         r = dns_query_add_transaction_split(q, first);
260         if (r < 0)
261                 goto fail;
262
263         LIST_FOREACH(scopes, s, first->scopes_next) {
264                 DnsScopeMatch match;
265
266                 match = dns_scope_good_domain(s, name);
267                 if (match < 0)
268                         goto fail;
269
270                 if (match != found)
271                         continue;
272
273                 r = dns_query_add_transaction_split(q, s);
274                 if (r < 0)
275                         goto fail;
276         }
277
278         q->answer = dns_answer_unref(q->answer);
279         q->answer_ifindex = 0;
280         q->answer_rcode = 0;
281
282         r = sd_event_add_time(
283                         q->manager->event,
284                         &q->timeout_event_source,
285                         clock_boottime_or_monotonic(),
286                         now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
287                         on_query_timeout, q);
288         if (r < 0)
289                 goto fail;
290
291         q->state = DNS_TRANSACTION_PENDING;
292         q->block_ready++;
293
294         /* Start the transactions that are not started yet */
295         SET_FOREACH(t, q->transactions, i) {
296                 if (t->state != DNS_TRANSACTION_NULL)
297                         continue;
298
299                 r = dns_transaction_go(t);
300                 if (r < 0)
301                         goto fail;
302         }
303
304         q->block_ready--;
305         dns_query_ready(q);
306
307         return 1;
308
309 fail:
310         dns_query_stop(q);
311         return r;
312 }
313
314 void dns_query_ready(DnsQuery *q) {
315         DnsTransaction *t;
316         DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
317         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
318         int rcode = 0;
319         DnsScope *scope = NULL;
320         bool pending = false;
321         Iterator i;
322
323         assert(q);
324         assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
325
326         /* Note that this call might invalidate the query. Callers
327          * should hence not attempt to access the query or transaction
328          * after calling this function, unless the block_ready
329          * counter was explicitly bumped before doing so. */
330
331         if (q->block_ready > 0)
332                 return;
333
334         SET_FOREACH(t, q->transactions, i) {
335
336                 /* If we found a successful answer, ignore all answers from other scopes */
337                 if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
338                         continue;
339
340                 /* One of the transactions is still going on, let's maybe wait for it */
341                 if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
342                         pending = true;
343                         continue;
344                 }
345
346                 /* One of the transactions is successful, let's use
347                  * it, and copy its data out */
348                 if (t->state == DNS_TRANSACTION_SUCCESS) {
349                         DnsAnswer *a;
350
351                         if (t->received) {
352                                 rcode = DNS_PACKET_RCODE(t->received);
353                                 a = t->received->answer;
354                         } else {
355                                 rcode = t->cached_rcode;
356                                 a = t->cached;
357                         }
358
359                         if (state == DNS_TRANSACTION_SUCCESS) {
360                                 DnsAnswer *merged;
361
362                                 merged = dns_answer_merge(answer, a);
363                                 if (!merged) {
364                                         dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
365                                         return;
366                                 }
367
368                                 dns_answer_unref(answer);
369                                 answer = merged;
370                         } else {
371                                 dns_answer_unref(answer);
372                                 answer = dns_answer_ref(a);
373                         }
374
375                         scope = t->scope;
376                         state = DNS_TRANSACTION_SUCCESS;
377                         continue;
378                 }
379
380                 /* One of the transactions has failed, let's see
381                  * whether we find anything better, but if not, return
382                  * its response data */
383                 if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
384                         DnsAnswer *a;
385
386                         if (t->received) {
387                                 rcode = DNS_PACKET_RCODE(t->received);
388                                 a = t->received->answer;
389                         } else {
390                                 rcode = t->cached_rcode;
391                                 a = t->cached;
392                         }
393
394                         dns_answer_unref(answer);
395                         answer = dns_answer_ref(a);
396
397                         scope = t->scope;
398                         state = DNS_TRANSACTION_FAILURE;
399                         continue;
400                 }
401
402                 if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
403                         state = t->state;
404         }
405
406         if (pending) {
407
408                 /* If so far we weren't successful, and there's
409                  * something still pending, then wait for it */
410                 if (state != DNS_TRANSACTION_SUCCESS)
411                         return;
412
413                 /* If we already were successful, then only wait for
414                  * other transactions on the same scope to finish. */
415                 SET_FOREACH(t, q->transactions, i) {
416                         if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
417                                 return;
418                 }
419         }
420
421         if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
422                 q->answer = dns_answer_ref(answer);
423                 q->answer_rcode = rcode;
424                 q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
425         }
426
427         dns_query_complete(q, state);
428 }
429
430 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
431         _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
432         int r;
433
434         assert(q);
435
436         if (q->n_cname_redirects > CNAME_MAX)
437                 return -ELOOP;
438
439         r = dns_question_cname_redirect(q->question, name, &nq);
440         if (r < 0)
441                 return r;
442
443         dns_question_unref(q->question);
444         q->question = nq;
445         nq = NULL;
446
447         q->n_cname_redirects++;
448
449         dns_query_stop(q);
450         q->state = DNS_TRANSACTION_NULL;
451
452         return 0;
453 }
454
455 static int on_bus_track(sd_bus_track *t, void *userdata) {
456         DnsQuery *q = userdata;
457
458         assert(t);
459         assert(q);
460
461         log_debug("Client of active query vanished, aborting query.");
462         dns_query_complete(q, DNS_TRANSACTION_ABORTED);
463         return 0;
464 }
465
466 int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) {
467         int r;
468
469         assert(q);
470         assert(m);
471
472         if (!q->bus_track) {
473                 r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q);
474                 if (r < 0)
475                         return r;
476         }
477
478         r = sd_bus_track_add_sender(q->bus_track, m);
479         if (r < 0)
480                 return r;
481
482         return 0;
483 }