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