]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-query.c
resolved: only cache answer RRs, never additional or authoritative RRs of responses
[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
27#define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
28#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
29#define ATTEMPTS_MAX 8
8ba9fd9c 30#define CNAME_MAX 8
39762fdf 31#define QUERIES_MAX 2048
8ba9fd9c 32
322345fd 33static int dns_query_transaction_go(DnsQueryTransaction *t);
74b2466e
LP
34
35DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
faa133f3
LP
36 DnsQuery *q;
37
74b2466e
LP
38 if (!t)
39 return NULL;
40
41 sd_event_source_unref(t->timeout_event_source);
ad867662 42
faa133f3 43 dns_question_unref(t->question);
ad867662
LP
44 dns_packet_unref(t->sent);
45 dns_packet_unref(t->received);
faa133f3 46 dns_answer_unref(t->cached);
322345fd 47
623a4c97 48 dns_stream_free(t->stream);
74b2466e 49
faa133f3
LP
50 if (t->scope) {
51 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
52
53 if (t->id != 0)
54 hashmap_remove(t->scope->manager->dns_query_transactions, UINT_TO_PTR(t->id));
74b2466e
LP
55 }
56
faa133f3
LP
57 while ((q = set_steal_first(t->queries)))
58 set_remove(q->transactions, t);
59
60 set_free(t->queries);
74b2466e
LP
61
62 free(t);
63 return NULL;
64}
65
66DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
67
faa133f3
LP
68static void dns_query_transaction_gc(DnsQueryTransaction *t) {
69 assert(t);
70
71 if (t->block_gc > 0)
72 return;
73
74 if (set_isempty(t->queries))
75 dns_query_transaction_free(t);
76}
77
78static int dns_query_transaction_new(DnsQueryTransaction **ret, DnsScope *s, DnsQuestion *q) {
74b2466e
LP
79 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
80 int r;
81
faa133f3 82 assert(ret);
74b2466e 83 assert(s);
faa133f3 84 assert(q);
74b2466e 85
faa133f3 86 r = hashmap_ensure_allocated(&s->manager->dns_query_transactions, NULL, NULL);
74b2466e
LP
87 if (r < 0)
88 return r;
89
90 t = new0(DnsQueryTransaction, 1);
91 if (!t)
92 return -ENOMEM;
93
faa133f3 94 t->question = dns_question_ref(q);
ad867662 95
74b2466e
LP
96 do
97 random_bytes(&t->id, sizeof(t->id));
98 while (t->id == 0 ||
faa133f3 99 hashmap_get(s->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
74b2466e 100
faa133f3 101 r = hashmap_put(s->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
74b2466e
LP
102 if (r < 0) {
103 t->id = 0;
104 return r;
105 }
106
74b2466e
LP
107 LIST_PREPEND(transactions_by_scope, s->transactions, t);
108 t->scope = s;
109
110 if (ret)
111 *ret = t;
112
113 t = NULL;
114
115 return 0;
116}
117
ad867662
LP
118static void dns_query_transaction_stop(DnsQueryTransaction *t) {
119 assert(t);
120
121 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
623a4c97 122 t->stream = dns_stream_free(t->stream);
ad867662
LP
123}
124
faa133f3
LP
125void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state) {
126 DnsQuery *q;
127 Iterator i;
128
74b2466e 129 assert(t);
322345fd
LP
130 assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
131 assert(IN_SET(t->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
74b2466e 132
322345fd
LP
133 /* Note that this call might invalidate the query. Callers
134 * should hence not attempt to access the query or transaction
135 * after calling this function. */
74b2466e 136
a2a416f7
LP
137 log_debug("Transaction on scope %s on %s/%s now complete with %s",
138 dns_protocol_to_string(t->scope->protocol),
139 t->scope->link ? t->scope->link->name : "*",
140 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
141 dns_query_state_to_string(state));
142
74b2466e
LP
143 t->state = state;
144
322345fd 145 dns_query_transaction_stop(t);
faa133f3
LP
146
147 /* Notify all queries that are interested, but make sure the
148 * transaction isn't freed while we are still looking at it */
149 t->block_gc++;
150 SET_FOREACH(q, t->queries, i)
151 dns_query_ready(q);
152 t->block_gc--;
153
154 dns_query_transaction_gc(t);
ad867662
LP
155}
156
623a4c97
LP
157static int on_stream_complete(DnsStream *s, int error) {
158 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
159 DnsQueryTransaction *t;
ad867662 160
623a4c97
LP
161 assert(s);
162 assert(s->transaction);
ad867662 163
623a4c97
LP
164 /* Copy the data we care about out of the stream before we
165 * destroy it. */
166 t = s->transaction;
167 p = dns_packet_ref(s->read_packet);
ad867662 168
623a4c97 169 t->stream = dns_stream_free(t->stream);
ad867662 170
623a4c97
LP
171 if (error != 0) {
172 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
173 return 0;
ad867662
LP
174 }
175
b914e211 176 t->block_gc++;
623a4c97 177 dns_query_transaction_process_reply(t, p);
b914e211
LP
178 t->block_gc--;
179
180 /* If the response wasn't useful, then complete the transition now */
181 if (t->state == DNS_QUERY_PENDING)
182 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
183
ad867662
LP
184 return 0;
185}
186
322345fd 187static int dns_query_transaction_open_tcp(DnsQueryTransaction *t) {
623a4c97 188 _cleanup_close_ int fd = -1;
ad867662
LP
189 int r;
190
191 assert(t);
192
623a4c97
LP
193 if (t->stream)
194 return 0;
195
1716f6dc 196 if (t->scope->protocol == DNS_PROTOCOL_DNS)
623a4c97
LP
197 fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53);
198 else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
1716f6dc 199
b914e211
LP
200 /* When we already received a query to this (but it was truncated), send to its sender address */
201 if (t->received)
202 fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
203 else {
204 union in_addr_union address;
205 int family;
206
207 /* Otherwise, try to talk to the owner of a
208 * the IP address, in case this is a reverse
209 * PTR lookup */
210 r = dns_question_extract_reverse_address(t->question, &family, &address);
211 if (r < 0)
212 return r;
213 if (r == 0)
214 return -EINVAL;
215
216 fd = dns_scope_tcp_socket(t->scope, family, &address, 5355);
217 }
623a4c97
LP
218 } else
219 return -EAFNOSUPPORT;
ad867662 220
623a4c97
LP
221 if (fd < 0)
222 return fd;
ad867662 223
623a4c97
LP
224 r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
225 if (r < 0)
226 return r;
ad867662 227
623a4c97
LP
228 fd = -1;
229
230 r = dns_stream_write_packet(t->stream, t->sent);
ad867662 231 if (r < 0) {
623a4c97 232 t->stream = dns_stream_free(t->stream);
ad867662
LP
233 return r;
234 }
74b2466e 235
623a4c97
LP
236 t->received = dns_packet_unref(t->received);
237 t->stream->complete = on_stream_complete;
b914e211
LP
238 t->stream->transaction = t;
239
240 /* The interface index is difficult to determine if we are
241 * connecting to the local host, hence fill this in right away
242 * instead of determining it from the socket */
243 if (t->scope->link)
244 t->stream->ifindex = t->scope->link->ifindex;
623a4c97 245
ad867662 246 return 0;
74b2466e
LP
247}
248
faa133f3 249void dns_query_transaction_process_reply(DnsQueryTransaction *t, DnsPacket *p) {
ad867662
LP
250 int r;
251
74b2466e
LP
252 assert(t);
253 assert(p);
322345fd 254 assert(t->state == DNS_QUERY_PENDING);
74b2466e 255
322345fd
LP
256 /* Note that this call might invalidate the query. Callers
257 * should hence not attempt to access the query or transaction
258 * after calling this function. */
ad867662 259
623a4c97
LP
260 if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
261 assert(t->scope->link);
262
263 /* For LLMNR we will not accept any packets from other
264 * interfaces */
265
266 if (p->ifindex != t->scope->link->ifindex)
267 return;
268
269 if (p->family != t->scope->family)
270 return;
271
ea917db9
LP
272 /* Tentative replies shall be discarded, see RFC 4795,
273 * 2.1.1 */
274
275 if (DNS_PACKET_T(p))
276 return;
623a4c97
LP
277 }
278
279 if (t->scope->protocol == DNS_PROTOCOL_DNS) {
280
281 /* For DNS we are fine with accepting packets on any
282 * interface, but the source IP address must be one of
283 * a valid DNS server */
284
285 if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender))
286 return;
287
288 if (p->sender_port != 53)
289 return;
290 }
291
ad867662
LP
292 if (t->received != p) {
293 dns_packet_unref(t->received);
294 t->received = dns_packet_ref(p);
295 }
296
623a4c97 297 if (p->ipproto == IPPROTO_TCP) {
ad867662
LP
298 if (DNS_PACKET_TC(p)) {
299 /* Truncated via TCP? Somebody must be fucking with us */
322345fd 300 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
ad867662
LP
301 return;
302 }
303
304 if (DNS_PACKET_ID(p) != t->id) {
305 /* Not the reply to our query? Somebody must be fucking with us */
322345fd 306 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
ad867662
LP
307 return;
308 }
309 }
310
311 if (DNS_PACKET_TC(p)) {
312 /* Response was truncated, let's try again with good old TCP */
322345fd 313 r = dns_query_transaction_open_tcp(t);
8ba9fd9c
LP
314 if (r == -ESRCH) {
315 /* No servers found? Damn! */
322345fd 316 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
8ba9fd9c
LP
317 return;
318 }
ad867662 319 if (r < 0) {
623a4c97
LP
320 /* On LLMNR, if we cannot connect to the host,
321 * we immediately give up */
322 if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
323 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
324 return;
325 }
326
327 /* On DNS, couldn't send? Try immediately again, with a new server */
8ba9fd9c
LP
328 dns_scope_next_dns_server(t->scope);
329
322345fd 330 r = dns_query_transaction_go(t);
8ba9fd9c 331 if (r < 0) {
322345fd 332 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
8ba9fd9c
LP
333 return;
334 }
335
ad867662
LP
336 return;
337 }
338 }
74b2466e 339
322345fd 340 /* Parse and update the cache */
faa133f3 341 r = dns_packet_extract(p);
322345fd
LP
342 if (r < 0) {
343 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
344 return;
934e9b10
LP
345 }
346
d2f47562
LP
347 /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
348 dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0);
322345fd 349
b9d394ea 350 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
322345fd 351 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
b9d394ea 352 else
322345fd 353 dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
74b2466e
LP
354}
355
356static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
357 DnsQueryTransaction *t = userdata;
358 int r;
359
360 assert(s);
361 assert(t);
362
363 /* Timeout reached? Try again, with a new server */
364 dns_scope_next_dns_server(t->scope);
365
322345fd 366 r = dns_query_transaction_go(t);
74b2466e 367 if (r < 0)
322345fd 368 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
74b2466e
LP
369
370 return 0;
371}
372
ad867662 373static int dns_query_make_packet(DnsQueryTransaction *t) {
74b2466e 374 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
1716f6dc 375 unsigned n, added = 0;
74b2466e
LP
376 int r;
377
378 assert(t);
379
ad867662 380 if (t->sent)
74b2466e 381 return 0;
74b2466e 382
1716f6dc 383 r = dns_packet_new_query(&p, t->scope->protocol, 0);
74b2466e
LP
384 if (r < 0)
385 return r;
386
faa133f3
LP
387 for (n = 0; n < t->question->n_keys; n++) {
388 r = dns_scope_good_key(t->scope, t->question->keys[n]);
1716f6dc
LP
389 if (r < 0)
390 return r;
391 if (r == 0)
392 continue;
393
faa133f3 394 r = dns_packet_append_key(p, t->question->keys[n], NULL);
74b2466e
LP
395 if (r < 0)
396 return r;
1716f6dc
LP
397
398 added++;
74b2466e
LP
399 }
400
1716f6dc
LP
401 if (added <= 0)
402 return -EDOM;
403
404 DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
74b2466e
LP
405 DNS_PACKET_HEADER(p)->id = t->id;
406
ad867662
LP
407 t->sent = p;
408 p = NULL;
409
410 return 0;
411}
412
322345fd 413static int dns_query_transaction_go(DnsQueryTransaction *t) {
ad867662
LP
414 int r;
415
416 assert(t);
417
418 dns_query_transaction_stop(t);
419
a2a416f7
LP
420 log_debug("Beginning transaction on scope %s on %s/%s",
421 dns_protocol_to_string(t->scope->protocol),
422 t->scope->link ? t->scope->link->name : "*",
423 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
424
ad867662 425 if (t->n_attempts >= ATTEMPTS_MAX) {
322345fd 426 dns_query_transaction_complete(t, DNS_QUERY_ATTEMPTS_MAX);
ad867662
LP
427 return 0;
428 }
ad867662 429
322345fd
LP
430 t->n_attempts++;
431 t->received = dns_packet_unref(t->received);
faa133f3 432 t->cached = dns_answer_unref(t->cached);
7e8e0422 433 t->cached_rcode = 0;
322345fd
LP
434
435 /* First, let's try the cache */
436 dns_cache_prune(&t->scope->cache);
7e8e0422 437 r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
ad867662
LP
438 if (r < 0)
439 return r;
322345fd 440 if (r > 0) {
7e8e0422
LP
441 if (t->cached_rcode == DNS_RCODE_SUCCESS)
442 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
443 else
444 dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
322345fd
LP
445 return 0;
446 }
ad867662 447
322345fd
LP
448 /* Otherwise, we need to ask the network */
449 r = dns_query_make_packet(t);
1716f6dc
LP
450 if (r == -EDOM) {
451 /* Not the right request to make on this network?
452 * (i.e. an A request made on IPv6 or an AAAA request
453 * made on IPv4, on LLMNR or mDNS.) */
454 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
455 return 0;
456 }
322345fd
LP
457 if (r < 0)
458 return r;
8ba9fd9c 459
b914e211
LP
460 if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
461 (dns_question_endswith(t->question, "in-addr.arpa") > 0 ||
462 dns_question_endswith(t->question, "ip6.arpa") > 0)) {
463
464 /* RFC 4795, Section 2.4. says reverse lookups shall
465 * always be made via TCP on LLMNR */
322345fd 466 r = dns_query_transaction_open_tcp(t);
b914e211
LP
467 } else {
468 /* Try via UDP, and if that fails due to large size try via TCP */
469 r = dns_scope_send(t->scope, t->sent);
470 if (r == -EMSGSIZE)
471 r = dns_query_transaction_open_tcp(t);
472 }
ad867662 473 if (r == -ESRCH) {
1716f6dc 474 /* No servers to send this to? */
322345fd 475 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
ad867662
LP
476 return 0;
477 }
74b2466e
LP
478 if (r < 0) {
479 /* Couldn't send? Try immediately again, with a new server */
480 dns_scope_next_dns_server(t->scope);
481
322345fd 482 return dns_query_transaction_go(t);
74b2466e
LP
483 }
484
faa133f3 485 r = sd_event_add_time(t->scope->manager->event, &t->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + TRANSACTION_TIMEOUT_USEC, 0, on_transaction_timeout, t);
ad867662
LP
486 if (r < 0)
487 return r;
74b2466e 488
322345fd 489 t->state = DNS_QUERY_PENDING;
ad867662 490 return 1;
74b2466e
LP
491}
492
493DnsQuery *dns_query_free(DnsQuery *q) {
faa133f3 494 DnsQueryTransaction *t;
74b2466e
LP
495
496 if (!q)
497 return NULL;
498
499 sd_bus_message_unref(q->request);
322345fd 500
faa133f3
LP
501 dns_question_unref(q->question);
502 dns_answer_unref(q->answer);
322345fd 503
74b2466e
LP
504 sd_event_source_unref(q->timeout_event_source);
505
faa133f3
LP
506 while ((t = set_steal_first(q->transactions))) {
507 set_remove(t->queries, q);
508 dns_query_transaction_gc(t);
509 }
510
511 set_free(q->transactions);
74b2466e 512
39762fdf 513 if (q->manager) {
74b2466e 514 LIST_REMOVE(queries, q->manager->dns_queries, q);
39762fdf
LP
515 q->manager->n_dns_queries--;
516 }
74b2466e 517
74b2466e
LP
518 free(q);
519
520 return NULL;
521}
522
faa133f3 523int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question) {
74b2466e 524 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
faa133f3
LP
525 unsigned i;
526 int r;
74b2466e
LP
527
528 assert(m);
faa133f3 529 assert(question);
74b2466e 530
faa133f3
LP
531 r = dns_question_is_valid(question);
532 if (r < 0)
533 return r;
74b2466e 534
39762fdf
LP
535 if (m->n_dns_queries >= QUERIES_MAX)
536 return -EBUSY;
537
74b2466e
LP
538 q = new0(DnsQuery, 1);
539 if (!q)
540 return -ENOMEM;
541
faa133f3 542 q->question = dns_question_ref(question);
322345fd 543
faa133f3 544 for (i = 0; i < question->n_keys; i++) {
322345fd 545 log_debug("Looking up RR for %s %s %s",
faa133f3
LP
546 strna(dns_class_to_string(question->keys[i]->class)),
547 strna(dns_type_to_string(question->keys[i]->type)),
548 DNS_RESOURCE_KEY_NAME(question->keys[i]));
74b2466e
LP
549 }
550
551 LIST_PREPEND(queries, m->dns_queries, q);
39762fdf 552 m->n_dns_queries++;
74b2466e
LP
553 q->manager = m;
554
8ba9fd9c
LP
555 if (ret)
556 *ret = q;
557 q = NULL;
558
559 return 0;
560}
561
562static void dns_query_stop(DnsQuery *q) {
faa133f3
LP
563 DnsQueryTransaction *t;
564
8ba9fd9c
LP
565 assert(q);
566
567 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
568
faa133f3
LP
569 while ((t = set_steal_first(q->transactions))) {
570 set_remove(t->queries, q);
571 dns_query_transaction_gc(t);
572 }
8ba9fd9c
LP
573}
574
322345fd 575static void dns_query_complete(DnsQuery *q, DnsQueryState state) {
8ba9fd9c 576 assert(q);
322345fd
LP
577 assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
578 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
8ba9fd9c 579
322345fd
LP
580 /* Note that this call might invalidate the query. Callers
581 * should hence not attempt to access the query or transaction
582 * after calling this function. */
8ba9fd9c 583
8ba9fd9c
LP
584 q->state = state;
585
322345fd
LP
586 dns_query_stop(q);
587 if (q->complete)
588 q->complete(q);
8ba9fd9c
LP
589}
590
591static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
592 DnsQuery *q = userdata;
593
594 assert(s);
595 assert(q);
596
322345fd 597 dns_query_complete(q, DNS_QUERY_TIMEOUT);
8ba9fd9c
LP
598 return 0;
599}
600
934e9b10
LP
601static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
602 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
faa133f3
LP
603 DnsQueryTransaction *t;
604 int r;
605
606 assert(q);
607
608 r = set_ensure_allocated(&q->transactions, NULL, NULL);
609 if (r < 0)
610 return r;
611
934e9b10
LP
612 if (key) {
613 question = dns_question_new(1);
614 if (!question)
615 return -ENOMEM;
616
617 r = dns_question_add(question, key);
618 if (r < 0)
619 return r;
620 } else
621 question = dns_question_ref(q->question);
622
faa133f3 623 LIST_FOREACH(transactions_by_scope, t, s->transactions)
934e9b10 624 if (dns_question_is_superset(t->question, question))
faa133f3
LP
625 break;
626
627 if (!t) {
934e9b10 628 r = dns_query_transaction_new(&t, s, question);
faa133f3
LP
629 if (r < 0)
630 return r;
631 }
632
633 r = set_ensure_allocated(&t->queries, NULL, NULL);
634 if (r < 0)
635 goto fail;
636
637 r = set_put(t->queries, q);
638 if (r < 0)
639 goto fail;
640
641 r = set_put(q->transactions, t);
642 if (r < 0) {
643 set_remove(t->queries, q);
644 goto fail;
645 }
646
647 return 0;
648
649fail:
650 dns_query_transaction_gc(t);
651 return r;
652}
653
934e9b10
LP
654static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
655 int r;
656
657 assert(q);
658 assert(s);
659
660 if (s->protocol == DNS_PROTOCOL_MDNS) {
661 r = dns_query_add_transaction(q, s, NULL);
662 if (r < 0)
663 return r;
664 } else {
665 unsigned i;
666
667 /* On DNS and LLMNR we can only send a single
668 * question per datagram, hence issue multiple
669 * transactions. */
670
671 for (i = 0; i < q->question->n_keys; i++) {
672 r = dns_query_add_transaction(q, s, q->question->keys[i]);
673 if (r < 0)
674 return r;
675 }
676 }
677
678 return 0;
679}
680
322345fd 681int dns_query_go(DnsQuery *q) {
8ba9fd9c
LP
682 DnsScopeMatch found = DNS_SCOPE_NO;
683 DnsScope *s, *first = NULL;
684 DnsQueryTransaction *t;
faa133f3
LP
685 const char *name;
686 Iterator i;
8ba9fd9c
LP
687 int r;
688
689 assert(q);
690
691 if (q->state != DNS_QUERY_NULL)
692 return 0;
693
faa133f3
LP
694 assert(q->question);
695 assert(q->question->n_keys > 0);
696
697 name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
8ba9fd9c
LP
698
699 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
74b2466e
LP
700 DnsScopeMatch match;
701
faa133f3 702 match = dns_scope_good_domain(s, name);
74b2466e
LP
703 if (match < 0)
704 return match;
705
706 if (match == DNS_SCOPE_NO)
707 continue;
708
709 found = match;
710
711 if (match == DNS_SCOPE_YES) {
712 first = s;
713 break;
714 } else {
715 assert(match == DNS_SCOPE_MAYBE);
716
717 if (!first)
718 first = s;
719 }
720 }
721
722 if (found == DNS_SCOPE_NO)
8ba9fd9c 723 return -ESRCH;
74b2466e 724
934e9b10 725 r = dns_query_add_transaction_split(q, first);
74b2466e
LP
726 if (r < 0)
727 return r;
728
74b2466e
LP
729 LIST_FOREACH(scopes, s, first->scopes_next) {
730 DnsScopeMatch match;
731
faa133f3 732 match = dns_scope_good_domain(s, name);
74b2466e
LP
733 if (match < 0)
734 return match;
735
736 if (match != found)
737 continue;
738
934e9b10 739 r = dns_query_add_transaction_split(q, s);
74b2466e
LP
740 if (r < 0)
741 return r;
74b2466e
LP
742 }
743
faa133f3
LP
744 q->answer = dns_answer_unref(q->answer);
745 q->answer_ifindex = 0;
746 q->answer_rcode = 0;
74b2466e
LP
747
748 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);
749 if (r < 0)
750 goto fail;
751
322345fd 752 q->state = DNS_QUERY_PENDING;
faa133f3 753 q->block_ready++;
74b2466e 754
faa133f3
LP
755 SET_FOREACH(t, q->transactions, i) {
756 if (t->state == DNS_QUERY_NULL) {
757 r = dns_query_transaction_go(t);
758 if (r < 0)
759 goto fail;
760 }
74b2466e
LP
761 }
762
faa133f3
LP
763 q->block_ready--;
764 dns_query_ready(q);
322345fd 765
8ba9fd9c 766 return 1;
74b2466e
LP
767
768fail:
8ba9fd9c 769 dns_query_stop(q);
74b2466e
LP
770 return r;
771}
772
faa133f3 773void dns_query_ready(DnsQuery *q) {
74b2466e 774 DnsQueryTransaction *t;
ad867662 775 DnsQueryState state = DNS_QUERY_NO_SERVERS;
934e9b10
LP
776 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
777 int rcode = 0;
778 DnsScope *scope = NULL;
faa133f3 779 Iterator i;
74b2466e
LP
780
781 assert(q);
322345fd 782 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
74b2466e 783
322345fd
LP
784 /* Note that this call might invalidate the query. Callers
785 * should hence not attempt to access the query or transaction
faa133f3 786 * after calling this function, unless the block_ready
322345fd
LP
787 * counter was explicitly bumped before doing so. */
788
faa133f3 789 if (q->block_ready > 0)
74b2466e
LP
790 return;
791
faa133f3 792 SET_FOREACH(t, q->transactions, i) {
74b2466e 793
934e9b10
LP
794 /* If we found a successful answer, ignore all answers from other scopes */
795 if (state == DNS_QUERY_SUCCESS && t->scope != scope)
796 continue;
797
74b2466e 798 /* One of the transactions is still going on, let's wait for it */
8ba9fd9c 799 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
74b2466e
LP
800 return;
801
322345fd
LP
802 /* One of the transactions is successful, let's use
803 * it, and copy its data out */
74b2466e 804 if (t->state == DNS_QUERY_SUCCESS) {
934e9b10
LP
805 DnsAnswer *a;
806
faa133f3 807 if (t->received) {
934e9b10
LP
808 rcode = DNS_PACKET_RCODE(t->received);
809 a = t->received->answer;
faa133f3 810 } else {
934e9b10
LP
811 rcode = t->cached_rcode;
812 a = t->cached;
faa133f3 813 }
322345fd 814
934e9b10
LP
815 if (state == DNS_QUERY_SUCCESS) {
816 DnsAnswer *merged;
817
818 merged = dns_answer_merge(answer, a);
819 if (!merged) {
820 dns_query_complete(q, DNS_QUERY_RESOURCES);
821 return;
822 }
823
824 dns_answer_unref(answer);
825 answer = merged;
826 } else {
827 dns_answer_unref(answer);
828 answer = dns_answer_ref(a);
829 }
830
831 scope = t->scope;
832 state = DNS_QUERY_SUCCESS;
833 continue;
74b2466e
LP
834 }
835
ad867662
LP
836 /* One of the transactions has failed, let's see
837 * whether we find anything better, but if not, return
934e9b10
LP
838 * its response data */
839 if (state != DNS_QUERY_SUCCESS && t->state == DNS_QUERY_FAILURE) {
840 DnsAnswer *a;
841
7e8e0422 842 if (t->received) {
934e9b10
LP
843 rcode = DNS_PACKET_RCODE(t->received);
844 a = t->received->answer;
7e8e0422 845 } else {
934e9b10
LP
846 rcode = t->cached_rcode;
847 a = t->cached;
7e8e0422
LP
848 }
849
934e9b10
LP
850 dns_answer_unref(answer);
851 answer = dns_answer_ref(a);
852
853 scope = t->scope;
74b2466e 854 state = DNS_QUERY_FAILURE;
ad867662
LP
855 continue;
856 }
74b2466e 857
ad867662 858 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
74b2466e
LP
859 state = t->state;
860 }
861
934e9b10
LP
862 if (IN_SET(state, DNS_QUERY_SUCCESS, DNS_QUERY_FAILURE)) {
863 q->answer = dns_answer_ref(answer);
864 q->answer_rcode = rcode;
865 q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
faa133f3 866 }
74b2466e 867
322345fd 868 dns_query_complete(q, state);
74b2466e 869}
8ba9fd9c 870
322345fd 871int dns_query_cname_redirect(DnsQuery *q, const char *name) {
faa133f3
LP
872 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
873 int r;
8ba9fd9c
LP
874
875 assert(q);
876
faa133f3 877 if (q->n_cname_redirects > CNAME_MAX)
8ba9fd9c
LP
878 return -ELOOP;
879
faa133f3
LP
880 r = dns_question_cname_redirect(q->question, name, &nq);
881 if (r < 0)
882 return r;
8ba9fd9c 883
faa133f3
LP
884 dns_question_unref(q->question);
885 q->question = nq;
886 nq = NULL;
8ba9fd9c 887
faa133f3 888 q->n_cname_redirects++;
8ba9fd9c 889
322345fd
LP
890 dns_query_stop(q);
891 q->state = DNS_QUERY_NULL;
892
8ba9fd9c
LP
893 return 0;
894}
a2a416f7
LP
895
896static const char* const dns_query_state_table[_DNS_QUERY_STATE_MAX] = {
897 [DNS_QUERY_NULL] = "null",
898 [DNS_QUERY_PENDING] = "pending",
899 [DNS_QUERY_FAILURE] = "failure",
900 [DNS_QUERY_SUCCESS] = "success",
901 [DNS_QUERY_NO_SERVERS] = "no-servers",
902 [DNS_QUERY_TIMEOUT] = "timeout",
903 [DNS_QUERY_ATTEMPTS_MAX] = "attempts-max",
904 [DNS_QUERY_INVALID_REPLY] = "invalid-reply",
905 [DNS_QUERY_RESOURCES] = "resources",
906 [DNS_QUERY_ABORTED] = "aborted",
907};
908DEFINE_STRING_TABLE_LOOKUP(dns_query_state, DnsQueryState);