]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-query.c
nss-myhostname: don't include assert.h twice
[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
22#include "resolved-dns-query.h"
23#include "resolved-dns-domain.h"
24
25#define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
26#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
27#define ATTEMPTS_MAX 8
8ba9fd9c 28#define CNAME_MAX 8
39762fdf 29#define QUERIES_MAX 2048
8ba9fd9c 30
322345fd 31static int dns_query_transaction_go(DnsQueryTransaction *t);
74b2466e
LP
32
33DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
34 if (!t)
35 return NULL;
36
37 sd_event_source_unref(t->timeout_event_source);
ad867662
LP
38
39 dns_packet_unref(t->sent);
40 dns_packet_unref(t->received);
41
322345fd
LP
42 dns_resource_record_freev(t->cached_rrs, t->n_cached_rrs);
43
ad867662
LP
44 sd_event_source_unref(t->tcp_event_source);
45 safe_close(t->tcp_fd);
74b2466e
LP
46
47 if (t->query) {
48 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
49 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
50 }
51
52 if (t->scope)
53 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
54
55 free(t);
56 return NULL;
57}
58
59DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
60
61static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
62 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
63 int r;
64
65 assert(q);
66 assert(s);
67
68 r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
69 if (r < 0)
70 return r;
71
72 t = new0(DnsQueryTransaction, 1);
73 if (!t)
74 return -ENOMEM;
75
ad867662
LP
76 t->tcp_fd = -1;
77
74b2466e
LP
78 do
79 random_bytes(&t->id, sizeof(t->id));
80 while (t->id == 0 ||
81 hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
82
83 r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
84 if (r < 0) {
85 t->id = 0;
86 return r;
87 }
88
89 LIST_PREPEND(transactions_by_query, q->transactions, t);
90 t->query = q;
91
92 LIST_PREPEND(transactions_by_scope, s->transactions, t);
93 t->scope = s;
94
95 if (ret)
96 *ret = t;
97
98 t = NULL;
99
100 return 0;
101}
102
ad867662
LP
103static void dns_query_transaction_stop(DnsQueryTransaction *t) {
104 assert(t);
105
106 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
107 t->tcp_event_source = sd_event_source_unref(t->tcp_event_source);
108 t->tcp_fd = safe_close(t->tcp_fd);
109}
110
322345fd 111static void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state) {
74b2466e 112 assert(t);
322345fd
LP
113 assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
114 assert(IN_SET(t->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
74b2466e 115
322345fd
LP
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. */
74b2466e
LP
119
120 t->state = state;
121
322345fd
LP
122 dns_query_transaction_stop(t);
123 dns_query_finish(t->query);
ad867662
LP
124}
125
126static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
127 DnsQueryTransaction *t = userdata;
128 int r;
129
130 assert(t);
131
132 if (revents & EPOLLOUT) {
133 struct iovec iov[2];
134 be16_t sz;
135 ssize_t ss;
136
137 sz = htobe16(t->sent->size);
138
139 iov[0].iov_base = &sz;
140 iov[0].iov_len = sizeof(sz);
141 iov[1].iov_base = DNS_PACKET_DATA(t->sent);
142 iov[1].iov_len = t->sent->size;
143
144 IOVEC_INCREMENT(iov, 2, t->tcp_written);
145
146 ss = writev(fd, iov, 2);
147 if (ss < 0) {
148 if (errno != EINTR && errno != EAGAIN) {
322345fd 149 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
ad867662
LP
150 return -errno;
151 }
152 } else
153 t->tcp_written += ss;
154
155 /* Are we done? If so, disable the event source for EPOLLOUT */
156 if (t->tcp_written >= sizeof(sz) + t->sent->size) {
157 r = sd_event_source_set_io_events(s, EPOLLIN);
158 if (r < 0) {
322345fd 159 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
ad867662
LP
160 return r;
161 }
162 }
163 }
164
165 if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
166
167 if (t->tcp_read < sizeof(t->tcp_read_size)) {
168 ssize_t ss;
169
170 ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
171 if (ss < 0) {
172 if (errno != EINTR && errno != EAGAIN) {
322345fd 173 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
ad867662
LP
174 return -errno;
175 }
176 } else if (ss == 0) {
322345fd 177 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
ad867662
LP
178 return -EIO;
179 } else
180 t->tcp_read += ss;
181 }
182
183 if (t->tcp_read >= sizeof(t->tcp_read_size)) {
184
185 if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
322345fd 186 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
ad867662
LP
187 return -EBADMSG;
188 }
189
190 if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
191 ssize_t ss;
192
193 if (!t->received) {
1716f6dc 194 r = dns_packet_new(&t->received, t->scope->protocol, be16toh(t->tcp_read_size));
ad867662 195 if (r < 0) {
322345fd 196 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
ad867662
LP
197 return r;
198 }
199 }
200
201 ss = read(fd,
202 (uint8_t*) DNS_PACKET_DATA(t->received) + t->tcp_read - sizeof(t->tcp_read_size),
203 sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size) - t->tcp_read);
204 if (ss < 0) {
205 if (errno != EINTR && errno != EAGAIN) {
322345fd 206 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
ad867662
LP
207 return -errno;
208 }
209 } else if (ss == 0) {
322345fd 210 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
ad867662
LP
211 return -EIO;
212 } else
213 t->tcp_read += ss;
214 }
215
216 if (t->tcp_read >= sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
217 t->received->size = be16toh(t->tcp_read_size);
218 dns_query_transaction_reply(t, t->received);
219 return 0;
220 }
221 }
222 }
223
224 return 0;
225}
226
322345fd 227static int dns_query_transaction_open_tcp(DnsQueryTransaction *t) {
ad867662
LP
228 int r;
229
230 assert(t);
231
1716f6dc
LP
232 if (t->scope->protocol == DNS_PROTOCOL_DNS)
233 return -ENOTSUP;
234
ad867662
LP
235 if (t->tcp_fd >= 0)
236 return 0;
237
238 t->tcp_written = 0;
239 t->tcp_read = 0;
240 t->received = dns_packet_unref(t->received);
241
242 t->tcp_fd = dns_scope_tcp_socket(t->scope);
243 if (t->tcp_fd < 0)
244 return t->tcp_fd;
245
246 r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
247 if (r < 0) {
248 t->tcp_fd = safe_close(t->tcp_fd);
249 return r;
250 }
74b2466e 251
ad867662 252 return 0;
74b2466e
LP
253}
254
ad867662
LP
255void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
256 int r;
257
74b2466e
LP
258 assert(t);
259 assert(p);
322345fd 260 assert(t->state == DNS_QUERY_PENDING);
74b2466e 261
322345fd
LP
262 /* Note that this call might invalidate the query. Callers
263 * should hence not attempt to access the query or transaction
264 * after calling this function. */
ad867662
LP
265
266 if (t->received != p) {
267 dns_packet_unref(t->received);
268 t->received = dns_packet_ref(p);
269 }
270
271 if (t->tcp_fd >= 0) {
272 if (DNS_PACKET_TC(p)) {
273 /* Truncated via TCP? Somebody must be fucking with us */
322345fd 274 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
ad867662
LP
275 return;
276 }
277
278 if (DNS_PACKET_ID(p) != t->id) {
279 /* Not the reply to our query? Somebody must be fucking with us */
322345fd 280 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
ad867662
LP
281 return;
282 }
283 }
284
285 if (DNS_PACKET_TC(p)) {
286 /* Response was truncated, let's try again with good old TCP */
322345fd 287 r = dns_query_transaction_open_tcp(t);
8ba9fd9c
LP
288 if (r == -ESRCH) {
289 /* No servers found? Damn! */
322345fd 290 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
8ba9fd9c
LP
291 return;
292 }
ad867662 293 if (r < 0) {
8ba9fd9c
LP
294 /* Couldn't send? Try immediately again, with a new server */
295 dns_scope_next_dns_server(t->scope);
296
322345fd 297 r = dns_query_transaction_go(t);
8ba9fd9c 298 if (r < 0) {
322345fd 299 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
8ba9fd9c
LP
300 return;
301 }
302
ad867662
LP
303 return;
304 }
305 }
74b2466e 306
322345fd
LP
307 /* Parse and update the cache */
308 r = dns_packet_extract_rrs(p);
309 if (r < 0) {
310 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
311 return;
312 } else if (r > 0)
313 dns_cache_put_rrs(&t->scope->cache, p->rrs, r, 0);
314
b9d394ea 315 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
322345fd 316 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
b9d394ea 317 else
322345fd 318 dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
74b2466e
LP
319}
320
321static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
322 DnsQueryTransaction *t = userdata;
323 int r;
324
325 assert(s);
326 assert(t);
327
328 /* Timeout reached? Try again, with a new server */
329 dns_scope_next_dns_server(t->scope);
330
322345fd 331 r = dns_query_transaction_go(t);
74b2466e 332 if (r < 0)
322345fd 333 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
74b2466e
LP
334
335 return 0;
336}
337
ad867662 338static int dns_query_make_packet(DnsQueryTransaction *t) {
74b2466e 339 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
1716f6dc 340 unsigned n, added = 0;
74b2466e
LP
341 int r;
342
343 assert(t);
344
ad867662 345 if (t->sent)
74b2466e 346 return 0;
74b2466e 347
1716f6dc 348 r = dns_packet_new_query(&p, t->scope->protocol, 0);
74b2466e
LP
349 if (r < 0)
350 return r;
351
352 for (n = 0; n < t->query->n_keys; n++) {
1716f6dc
LP
353 r = dns_scope_good_key(t->scope, &t->query->keys[n]);
354 if (r < 0)
355 return r;
356 if (r == 0)
357 continue;
358
74b2466e
LP
359 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
360 if (r < 0)
361 return r;
1716f6dc
LP
362
363 added++;
74b2466e
LP
364 }
365
1716f6dc
LP
366 if (added <= 0)
367 return -EDOM;
368
369 DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
74b2466e
LP
370 DNS_PACKET_HEADER(p)->id = t->id;
371
ad867662
LP
372 t->sent = p;
373 p = NULL;
374
375 return 0;
376}
377
322345fd 378static int dns_query_transaction_go(DnsQueryTransaction *t) {
ad867662
LP
379 int r;
380
381 assert(t);
382
383 dns_query_transaction_stop(t);
384
385 if (t->n_attempts >= ATTEMPTS_MAX) {
322345fd 386 dns_query_transaction_complete(t, DNS_QUERY_ATTEMPTS_MAX);
ad867662
LP
387 return 0;
388 }
ad867662 389
322345fd
LP
390 t->n_attempts++;
391 t->received = dns_packet_unref(t->received);
392 t->cached_rrs = dns_resource_record_freev(t->cached_rrs, t->n_cached_rrs);
393 t->n_cached_rrs = 0;
394
395 /* First, let's try the cache */
396 dns_cache_prune(&t->scope->cache);
397 r = dns_cache_lookup_many(&t->scope->cache, t->query->keys, t->query->n_keys, &t->cached_rrs);
ad867662
LP
398 if (r < 0)
399 return r;
322345fd
LP
400 if (r > 0) {
401 t->n_cached_rrs = r;
402 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
403 return 0;
404 }
ad867662 405
322345fd
LP
406 /* Otherwise, we need to ask the network */
407 r = dns_query_make_packet(t);
1716f6dc
LP
408 if (r == -EDOM) {
409 /* Not the right request to make on this network?
410 * (i.e. an A request made on IPv6 or an AAAA request
411 * made on IPv4, on LLMNR or mDNS.) */
412 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
413 return 0;
414 }
322345fd
LP
415 if (r < 0)
416 return r;
8ba9fd9c 417
ad867662
LP
418 /* Try via UDP, and if that fails due to large size try via TCP */
419 r = dns_scope_send(t->scope, t->sent);
420 if (r == -EMSGSIZE)
322345fd 421 r = dns_query_transaction_open_tcp(t);
ad867662 422 if (r == -ESRCH) {
1716f6dc 423 /* No servers to send this to? */
322345fd 424 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
ad867662
LP
425 return 0;
426 }
74b2466e
LP
427 if (r < 0) {
428 /* Couldn't send? Try immediately again, with a new server */
429 dns_scope_next_dns_server(t->scope);
430
322345fd 431 return dns_query_transaction_go(t);
74b2466e
LP
432 }
433
ad867662
LP
434 r = sd_event_add_time(t->query->manager->event, &t->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + TRANSACTION_TIMEOUT_USEC, 0, on_transaction_timeout, t);
435 if (r < 0)
436 return r;
74b2466e 437
322345fd 438 t->state = DNS_QUERY_PENDING;
ad867662 439 return 1;
74b2466e
LP
440}
441
442DnsQuery *dns_query_free(DnsQuery *q) {
443 unsigned n;
444
445 if (!q)
446 return NULL;
447
448 sd_bus_message_unref(q->request);
ad867662 449 dns_packet_unref(q->received);
322345fd
LP
450
451 dns_resource_record_freev(q->cached_rrs, q->n_cached_rrs);
452
74b2466e
LP
453 sd_event_source_unref(q->timeout_event_source);
454
455 while (q->transactions)
456 dns_query_transaction_free(q->transactions);
457
39762fdf 458 if (q->manager) {
74b2466e 459 LIST_REMOVE(queries, q->manager->dns_queries, q);
39762fdf
LP
460 q->manager->n_dns_queries--;
461 }
74b2466e
LP
462
463 for (n = 0; n < q->n_keys; n++)
464 free(q->keys[n].name);
465 free(q->keys);
466 free(q);
467
468 return NULL;
469}
470
471int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
472 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
74b2466e 473 const char *name = NULL;
74b2466e
LP
474
475 assert(m);
476
477 if (n_keys <= 0 || n_keys >= 65535)
478 return -EINVAL;
479
39762fdf
LP
480 if (m->n_dns_queries >= QUERIES_MAX)
481 return -EBUSY;
482
74b2466e
LP
483 assert(keys);
484
485 q = new0(DnsQuery, 1);
486 if (!q)
487 return -ENOMEM;
488
489 q->keys = new(DnsResourceKey, n_keys);
490 if (!q->keys)
491 return -ENOMEM;
492
493 for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
494 q->keys[q->n_keys].class = keys[q->n_keys].class;
495 q->keys[q->n_keys].type = keys[q->n_keys].type;
496 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
497 if (!q->keys[q->n_keys].name)
498 return -ENOMEM;
499
500 if (!name)
501 name = q->keys[q->n_keys].name;
502 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
503 return -EINVAL;
322345fd
LP
504
505 log_debug("Looking up RR for %s %s %s",
506 strna(dns_class_to_string(keys[q->n_keys].class)),
507 strna(dns_type_to_string(keys[q->n_keys].type)),
508 keys[q->n_keys].name);
74b2466e
LP
509 }
510
511 LIST_PREPEND(queries, m->dns_queries, q);
39762fdf 512 m->n_dns_queries++;
74b2466e
LP
513 q->manager = m;
514
8ba9fd9c
LP
515 if (ret)
516 *ret = q;
517 q = NULL;
518
519 return 0;
520}
521
522static void dns_query_stop(DnsQuery *q) {
523 assert(q);
524
525 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
526
527 while (q->transactions)
528 dns_query_transaction_free(q->transactions);
529}
530
322345fd 531static void dns_query_complete(DnsQuery *q, DnsQueryState state) {
8ba9fd9c 532 assert(q);
322345fd
LP
533 assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
534 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
8ba9fd9c 535
322345fd
LP
536 /* Note that this call might invalidate the query. Callers
537 * should hence not attempt to access the query or transaction
538 * after calling this function. */
8ba9fd9c 539
8ba9fd9c
LP
540 q->state = state;
541
322345fd
LP
542 dns_query_stop(q);
543 if (q->complete)
544 q->complete(q);
8ba9fd9c
LP
545}
546
547static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
548 DnsQuery *q = userdata;
549
550 assert(s);
551 assert(q);
552
322345fd 553 dns_query_complete(q, DNS_QUERY_TIMEOUT);
8ba9fd9c
LP
554 return 0;
555}
556
322345fd 557int dns_query_go(DnsQuery *q) {
8ba9fd9c
LP
558 DnsScopeMatch found = DNS_SCOPE_NO;
559 DnsScope *s, *first = NULL;
560 DnsQueryTransaction *t;
561 int r;
562
563 assert(q);
564
565 if (q->state != DNS_QUERY_NULL)
566 return 0;
567
568 assert(q->n_keys > 0);
569
570 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
74b2466e
LP
571 DnsScopeMatch match;
572
1716f6dc 573 match = dns_scope_good_domain(s, q->keys[0].name);
74b2466e
LP
574 if (match < 0)
575 return match;
576
577 if (match == DNS_SCOPE_NO)
578 continue;
579
580 found = match;
581
582 if (match == DNS_SCOPE_YES) {
583 first = s;
584 break;
585 } else {
586 assert(match == DNS_SCOPE_MAYBE);
587
588 if (!first)
589 first = s;
590 }
591 }
592
593 if (found == DNS_SCOPE_NO)
8ba9fd9c 594 return -ESRCH;
74b2466e
LP
595
596 r = dns_query_transaction_new(q, NULL, first);
597 if (r < 0)
598 return r;
599
74b2466e
LP
600 LIST_FOREACH(scopes, s, first->scopes_next) {
601 DnsScopeMatch match;
602
1716f6dc 603 match = dns_scope_good_domain(s, q->keys[0].name);
74b2466e
LP
604 if (match < 0)
605 return match;
606
607 if (match != found)
608 continue;
609
610 r = dns_query_transaction_new(q, NULL, s);
611 if (r < 0)
612 return r;
74b2466e
LP
613 }
614
8ba9fd9c 615 q->received = dns_packet_unref(q->received);
74b2466e
LP
616
617 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);
618 if (r < 0)
619 goto fail;
620
322345fd
LP
621 q->state = DNS_QUERY_PENDING;
622 q->block_finish++;
74b2466e
LP
623
624 LIST_FOREACH(transactions_by_query, t, q->transactions) {
322345fd 625 r = dns_query_transaction_go(t);
74b2466e
LP
626 if (r < 0)
627 goto fail;
74b2466e
LP
628 }
629
322345fd
LP
630 q->block_finish--;
631 dns_query_finish(q);
632
8ba9fd9c 633 return 1;
74b2466e
LP
634
635fail:
8ba9fd9c 636 dns_query_stop(q);
74b2466e
LP
637 return r;
638}
639
640void dns_query_finish(DnsQuery *q) {
641 DnsQueryTransaction *t;
ad867662
LP
642 DnsQueryState state = DNS_QUERY_NO_SERVERS;
643 DnsPacket *received = NULL;
74b2466e
LP
644
645 assert(q);
322345fd 646 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
74b2466e 647
322345fd
LP
648 /* Note that this call might invalidate the query. Callers
649 * should hence not attempt to access the query or transaction
650 * after calling this function, unless the block_finish
651 * counter was explicitly bumped before doing so. */
652
653 if (q->block_finish > 0)
74b2466e
LP
654 return;
655
656 LIST_FOREACH(transactions_by_query, t, q->transactions) {
657
658 /* One of the transactions is still going on, let's wait for it */
8ba9fd9c 659 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
74b2466e
LP
660 return;
661
322345fd
LP
662 /* One of the transactions is successful, let's use
663 * it, and copy its data out */
74b2466e 664 if (t->state == DNS_QUERY_SUCCESS) {
ad867662 665 q->received = dns_packet_ref(t->received);
322345fd
LP
666
667 /* We simply steal the cached RRs array */
668 q->cached_rrs = t->cached_rrs;
669 q->n_cached_rrs = t->n_cached_rrs;
670 t->cached_rrs = NULL;
671 t->n_cached_rrs = 0;
672
673 dns_query_complete(q, DNS_QUERY_SUCCESS);
74b2466e
LP
674 return;
675 }
676
ad867662
LP
677 /* One of the transactions has failed, let's see
678 * whether we find anything better, but if not, return
679 * its response packet */
74b2466e 680 if (t->state == DNS_QUERY_FAILURE) {
ad867662 681 received = t->received;
74b2466e 682 state = DNS_QUERY_FAILURE;
ad867662
LP
683 continue;
684 }
74b2466e 685
ad867662 686 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
74b2466e
LP
687 state = t->state;
688 }
689
690 if (state == DNS_QUERY_FAILURE)
ad867662 691 q->received = dns_packet_ref(received);
74b2466e 692
322345fd 693 dns_query_complete(q, state);
74b2466e 694}
8ba9fd9c 695
322345fd 696int dns_query_cname_redirect(DnsQuery *q, const char *name) {
8ba9fd9c
LP
697 DnsResourceKey *keys;
698 unsigned i;
699
700 assert(q);
701
702 if (q->n_cname > CNAME_MAX)
703 return -ELOOP;
704
705 keys = new(DnsResourceKey, q->n_keys);
706 if (!keys)
707 return -ENOMEM;
708
709 for (i = 0; i < q->n_keys; i++) {
710 keys[i].class = q->keys[i].class;
711 keys[i].type = q->keys[i].type;
712 keys[i].name = strdup(name);
713 if (!keys[i].name) {
714
715 for (; i > 0; i--)
716 free(keys[i-1].name);
717 free(keys);
718 return -ENOMEM;
719 }
720 }
721
722 for (i = 0; i < q->n_keys; i++)
723 free(q->keys[i].name);
724 free(q->keys);
725
726 q->keys = keys;
727
728 q->n_cname++;
729
322345fd
LP
730 dns_query_stop(q);
731 q->state = DNS_QUERY_NULL;
732
8ba9fd9c
LP
733 return 0;
734}
735
736int dns_query_matches_rr(DnsQuery *q, DnsResourceRecord *rr) {
737 unsigned i;
738 int r;
739
740 assert(q);
741 assert(rr);
742
743 for (i = 0; i < q->n_keys; i++) {
744
745 if (rr->key.class != q->keys[i].class)
746 continue;
747
748 if (rr->key.type != q->keys[i].type &&
749 q->keys[i].type != DNS_TYPE_ANY)
750 continue;
751
752 r = dns_name_equal(rr->key.name, q->keys[i].name);
753 if (r != 0)
754 return r;
755 }
756
757 return 0;
758}
759
760int dns_query_matches_cname(DnsQuery *q, DnsResourceRecord *rr) {
761 unsigned i;
762 int r;
763
764 assert(q);
765 assert(rr);
766
767 for (i = 0; i < q->n_keys; i++) {
768
769 if (rr->key.class != q->keys[i].class)
770 continue;
771
772 if (rr->key.type != DNS_TYPE_CNAME)
773 continue;
774
775 r = dns_name_equal(rr->key.name, q->keys[i].name);
776 if (r != 0)
777 return r;
778 }
779
780 return 0;
781}
322345fd
LP
782
783int dns_query_get_rrs(DnsQuery *q, DnsResourceRecord ***rrs) {
784 int r;
785
786 assert(q);
787 assert(rrs);
788
789 if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
790 return -EBUSY;
791
792 if (q->received) {
793 r = dns_packet_extract_rrs(q->received);
794 if (r < 0)
795 return r;
796 if (r == 0) {
797 *rrs = NULL;
798 return r;
799 }
800
801 *rrs = q->received->rrs;
802 return r;
803 }
804
805 if (q->cached_rrs) {
806 *rrs = q->cached_rrs;
807 return q->n_cached_rrs;
808 }
809
810 return -ESRCH;
811}
812
813int dns_query_get_rcode(DnsQuery *q) {
814 assert(q);
815
816 if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
817 return -EBUSY;
818
819 if (!q->received)
820 return -ESRCH;
821
822 return DNS_PACKET_RCODE(q->received);
823}
824
825int dns_query_get_ifindex(DnsQuery *q) {
826 assert(q);
827
828 if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
829 return -EBUSY;
830
831 if (!q->received)
832 return -ESRCH;
833
834 return q->received->ifindex;
835}