chiark / gitweb /
remove unused includes
[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
23 #include "resolved-dns-query.h"
24
25 /* How long to wait for the query in total */
26 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
27
28 #define CNAME_MAX 8
29 #define QUERIES_MAX 2048
30
31 static void dns_query_stop(DnsQuery *q) {
32         DnsTransaction *t;
33
34         assert(q);
35
36         q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
37
38         while ((t = set_steal_first(q->transactions))) {
39                 set_remove(t->queries, q);
40                 dns_transaction_gc(t);
41         }
42 }
43
44 DnsQuery *dns_query_free(DnsQuery *q) {
45         if (!q)
46                 return NULL;
47
48         dns_query_stop(q);
49         set_free(q->transactions);
50
51         dns_question_unref(q->question);
52         dns_answer_unref(q->answer);
53
54         sd_bus_message_unref(q->request);
55         sd_bus_track_unref(q->bus_track);
56
57         if (q->manager) {
58                 LIST_REMOVE(queries, q->manager->dns_queries, q);
59                 q->manager->n_dns_queries--;
60         }
61
62         free(q);
63
64         return NULL;
65 }
66
67 int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) {
68         _cleanup_(dns_query_freep) DnsQuery *q = NULL;
69         unsigned i;
70         int r;
71
72         assert(m);
73         assert(question);
74
75         r = dns_question_is_valid(question);
76         if (r < 0)
77                 return r;
78
79         if (m->n_dns_queries >= QUERIES_MAX)
80                 return -EBUSY;
81
82         q = new0(DnsQuery, 1);
83         if (!q)
84                 return -ENOMEM;
85
86         q->question = dns_question_ref(question);
87         q->ifindex = ifindex;
88         q->flags = flags;
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);
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);
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, q->ifindex, q->flags, 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, q->ifindex, q->flags, 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         q->answer_family = AF_UNSPEC;
282         q->answer_protocol = _DNS_PROTOCOL_INVALID;
283
284         r = sd_event_add_time(
285                         q->manager->event,
286                         &q->timeout_event_source,
287                         clock_boottime_or_monotonic(),
288                         now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
289                         on_query_timeout, q);
290         if (r < 0)
291                 goto fail;
292
293         q->state = DNS_TRANSACTION_PENDING;
294         q->block_ready++;
295
296         /* Start the transactions that are not started yet */
297         SET_FOREACH(t, q->transactions, i) {
298                 if (t->state != DNS_TRANSACTION_NULL)
299                         continue;
300
301                 r = dns_transaction_go(t);
302                 if (r < 0)
303                         goto fail;
304         }
305
306         q->block_ready--;
307         dns_query_ready(q);
308
309         return 1;
310
311 fail:
312         dns_query_stop(q);
313         return r;
314 }
315
316 void dns_query_ready(DnsQuery *q) {
317         DnsTransaction *t;
318         DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
319         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
320         int rcode = 0;
321         DnsScope *scope = NULL;
322         bool pending = false;
323         Iterator i;
324
325         assert(q);
326         assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
327
328         /* Note that this call might invalidate the query. Callers
329          * should hence not attempt to access the query or transaction
330          * after calling this function, unless the block_ready
331          * counter was explicitly bumped before doing so. */
332
333         if (q->block_ready > 0)
334                 return;
335
336         SET_FOREACH(t, q->transactions, i) {
337
338                 /* If we found a successful answer, ignore all answers from other scopes */
339                 if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
340                         continue;
341
342                 /* One of the transactions is still going on, let's maybe wait for it */
343                 if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
344                         pending = true;
345                         continue;
346                 }
347
348                 /* One of the transactions is successful, let's use
349                  * it, and copy its data out */
350                 if (t->state == DNS_TRANSACTION_SUCCESS) {
351                         DnsAnswer *a;
352
353                         if (t->received) {
354                                 rcode = DNS_PACKET_RCODE(t->received);
355                                 a = t->received->answer;
356                         } else {
357                                 rcode = t->cached_rcode;
358                                 a = t->cached;
359                         }
360
361                         if (state == DNS_TRANSACTION_SUCCESS) {
362                                 DnsAnswer *merged;
363
364                                 merged = dns_answer_merge(answer, a);
365                                 if (!merged) {
366                                         dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
367                                         return;
368                                 }
369
370                                 dns_answer_unref(answer);
371                                 answer = merged;
372                         } else {
373                                 dns_answer_unref(answer);
374                                 answer = dns_answer_ref(a);
375                         }
376
377                         scope = t->scope;
378                         state = DNS_TRANSACTION_SUCCESS;
379                         continue;
380                 }
381
382                 /* One of the transactions has failed, let's see
383                  * whether we find anything better, but if not, return
384                  * its response data */
385                 if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
386                         DnsAnswer *a;
387
388                         if (t->received) {
389                                 rcode = DNS_PACKET_RCODE(t->received);
390                                 a = t->received->answer;
391                         } else {
392                                 rcode = t->cached_rcode;
393                                 a = t->cached;
394                         }
395
396                         dns_answer_unref(answer);
397                         answer = dns_answer_ref(a);
398
399                         scope = t->scope;
400                         state = DNS_TRANSACTION_FAILURE;
401                         continue;
402                 }
403
404                 if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
405                         state = t->state;
406         }
407
408         if (pending) {
409
410                 /* If so far we weren't successful, and there's
411                  * something still pending, then wait for it */
412                 if (state != DNS_TRANSACTION_SUCCESS)
413                         return;
414
415                 /* If we already were successful, then only wait for
416                  * other transactions on the same scope to finish. */
417                 SET_FOREACH(t, q->transactions, i) {
418                         if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
419                                 return;
420                 }
421         }
422
423         if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
424                 q->answer = dns_answer_ref(answer);
425                 q->answer_rcode = rcode;
426                 q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
427                 q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
428                 q->answer_family = scope ? scope->family : AF_UNSPEC;
429         }
430
431         dns_query_complete(q, state);
432 }
433
434 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
435         _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
436         int r;
437
438         assert(q);
439
440         if (q->n_cname_redirects > CNAME_MAX)
441                 return -ELOOP;
442
443         r = dns_question_cname_redirect(q->question, name, &nq);
444         if (r < 0)
445                 return r;
446
447         dns_question_unref(q->question);
448         q->question = nq;
449         nq = NULL;
450
451         q->n_cname_redirects++;
452
453         dns_query_stop(q);
454         q->state = DNS_TRANSACTION_NULL;
455
456         return 0;
457 }
458
459 static int on_bus_track(sd_bus_track *t, void *userdata) {
460         DnsQuery *q = userdata;
461
462         assert(t);
463         assert(q);
464
465         log_debug("Client of active query vanished, aborting query.");
466         dns_query_complete(q, DNS_TRANSACTION_ABORTED);
467         return 0;
468 }
469
470 int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) {
471         int r;
472
473         assert(q);
474         assert(m);
475
476         if (!q->bus_track) {
477                 r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q);
478                 if (r < 0)
479                         return r;
480         }
481
482         r = sd_bus_track_add_sender(q->bus_track, m);
483         if (r < 0)
484                 return r;
485
486         return 0;
487 }