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