1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
22 #include "resolved-dns-query.h"
23 #include "resolved-dns-domain.h"
25 #define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
26 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
27 #define ATTEMPTS_MAX 8
29 #define QUERIES_MAX 2048
31 static int dns_query_transaction_go(DnsQueryTransaction *t);
33 DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
39 sd_event_source_unref(t->timeout_event_source);
41 dns_question_unref(t->question);
42 dns_packet_unref(t->sent);
43 dns_packet_unref(t->received);
44 dns_answer_unref(t->cached);
46 sd_event_source_unref(t->tcp_event_source);
47 safe_close(t->tcp_fd);
50 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
53 hashmap_remove(t->scope->manager->dns_query_transactions, UINT_TO_PTR(t->id));
56 while ((q = set_steal_first(t->queries)))
57 set_remove(q->transactions, t);
65 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
67 static void dns_query_transaction_gc(DnsQueryTransaction *t) {
73 if (set_isempty(t->queries))
74 dns_query_transaction_free(t);
77 static int dns_query_transaction_new(DnsQueryTransaction **ret, DnsScope *s, DnsQuestion *q) {
78 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
85 r = hashmap_ensure_allocated(&s->manager->dns_query_transactions, NULL, NULL);
89 t = new0(DnsQueryTransaction, 1);
94 t->question = dns_question_ref(q);
97 random_bytes(&t->id, sizeof(t->id));
99 hashmap_get(s->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
101 r = hashmap_put(s->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
107 LIST_PREPEND(transactions_by_scope, s->transactions, t);
118 static void dns_query_transaction_stop(DnsQueryTransaction *t) {
121 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
122 t->tcp_event_source = sd_event_source_unref(t->tcp_event_source);
123 t->tcp_fd = safe_close(t->tcp_fd);
126 void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state) {
131 assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
132 assert(IN_SET(t->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
134 /* Note that this call might invalidate the query. Callers
135 * should hence not attempt to access the query or transaction
136 * after calling this function. */
140 dns_query_transaction_stop(t);
142 /* Notify all queries that are interested, but make sure the
143 * transaction isn't freed while we are still looking at it */
145 SET_FOREACH(q, t->queries, i)
149 dns_query_transaction_gc(t);
152 static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
153 DnsQueryTransaction *t = userdata;
158 if (revents & EPOLLOUT) {
163 sz = htobe16(t->sent->size);
165 iov[0].iov_base = &sz;
166 iov[0].iov_len = sizeof(sz);
167 iov[1].iov_base = DNS_PACKET_DATA(t->sent);
168 iov[1].iov_len = t->sent->size;
170 IOVEC_INCREMENT(iov, 2, t->tcp_written);
172 ss = writev(fd, iov, 2);
174 if (errno != EINTR && errno != EAGAIN) {
175 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
179 t->tcp_written += ss;
181 /* Are we done? If so, disable the event source for EPOLLOUT */
182 if (t->tcp_written >= sizeof(sz) + t->sent->size) {
183 r = sd_event_source_set_io_events(s, EPOLLIN);
185 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
191 if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
193 if (t->tcp_read < sizeof(t->tcp_read_size)) {
196 ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
198 if (errno != EINTR && errno != EAGAIN) {
199 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
202 } else if (ss == 0) {
203 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
209 if (t->tcp_read >= sizeof(t->tcp_read_size)) {
211 if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
212 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
216 if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
220 r = dns_packet_new(&t->received, t->scope->protocol, be16toh(t->tcp_read_size));
222 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
228 (uint8_t*) DNS_PACKET_DATA(t->received) + t->tcp_read - sizeof(t->tcp_read_size),
229 sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size) - t->tcp_read);
231 if (errno != EINTR && errno != EAGAIN) {
232 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
235 } else if (ss == 0) {
236 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
242 if (t->tcp_read >= sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
243 t->received->size = be16toh(t->tcp_read_size);
244 dns_query_transaction_process_reply(t, t->received);
253 static int dns_query_transaction_open_tcp(DnsQueryTransaction *t) {
258 if (t->scope->protocol == DNS_PROTOCOL_DNS)
266 t->received = dns_packet_unref(t->received);
268 t->tcp_fd = dns_scope_tcp_socket(t->scope);
272 r = sd_event_add_io(t->scope->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
274 t->tcp_fd = safe_close(t->tcp_fd);
281 void dns_query_transaction_process_reply(DnsQueryTransaction *t, DnsPacket *p) {
286 assert(t->state == DNS_QUERY_PENDING);
288 /* Note that this call might invalidate the query. Callers
289 * should hence not attempt to access the query or transaction
290 * after calling this function. */
292 if (t->received != p) {
293 dns_packet_unref(t->received);
294 t->received = dns_packet_ref(p);
297 if (t->tcp_fd >= 0) {
298 if (DNS_PACKET_TC(p)) {
299 /* Truncated via TCP? Somebody must be fucking with us */
300 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
304 if (DNS_PACKET_ID(p) != t->id) {
305 /* Not the reply to our query? Somebody must be fucking with us */
306 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
311 if (DNS_PACKET_TC(p)) {
312 /* Response was truncated, let's try again with good old TCP */
313 r = dns_query_transaction_open_tcp(t);
315 /* No servers found? Damn! */
316 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
320 /* Couldn't send? Try immediately again, with a new server */
321 dns_scope_next_dns_server(t->scope);
323 r = dns_query_transaction_go(t);
325 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
333 /* Parse and update the cache */
334 r = dns_packet_extract(p);
336 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
339 dns_cache_put_answer(&t->scope->cache, p->answer, 0);
341 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
342 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
344 dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
347 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
348 DnsQueryTransaction *t = userdata;
354 /* Timeout reached? Try again, with a new server */
355 dns_scope_next_dns_server(t->scope);
357 r = dns_query_transaction_go(t);
359 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
364 static int dns_query_make_packet(DnsQueryTransaction *t) {
365 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
366 unsigned n, added = 0;
374 r = dns_packet_new_query(&p, t->scope->protocol, 0);
378 for (n = 0; n < t->question->n_keys; n++) {
379 r = dns_scope_good_key(t->scope, t->question->keys[n]);
385 r = dns_packet_append_key(p, t->question->keys[n], NULL);
395 DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
396 DNS_PACKET_HEADER(p)->id = t->id;
404 static int dns_query_transaction_go(DnsQueryTransaction *t) {
409 dns_query_transaction_stop(t);
411 if (t->n_attempts >= ATTEMPTS_MAX) {
412 dns_query_transaction_complete(t, DNS_QUERY_ATTEMPTS_MAX);
417 t->received = dns_packet_unref(t->received);
418 t->cached = dns_answer_unref(t->cached);
420 /* First, let's try the cache */
421 dns_cache_prune(&t->scope->cache);
422 r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached);
426 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
430 /* Otherwise, we need to ask the network */
431 r = dns_query_make_packet(t);
433 /* Not the right request to make on this network?
434 * (i.e. an A request made on IPv6 or an AAAA request
435 * made on IPv4, on LLMNR or mDNS.) */
436 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
442 /* Try via UDP, and if that fails due to large size try via TCP */
443 r = dns_scope_send(t->scope, t->sent);
445 r = dns_query_transaction_open_tcp(t);
447 /* No servers to send this to? */
448 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
452 /* Couldn't send? Try immediately again, with a new server */
453 dns_scope_next_dns_server(t->scope);
455 return dns_query_transaction_go(t);
458 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);
462 t->state = DNS_QUERY_PENDING;
466 DnsQuery *dns_query_free(DnsQuery *q) {
467 DnsQueryTransaction *t;
472 sd_bus_message_unref(q->request);
474 dns_question_unref(q->question);
475 dns_answer_unref(q->answer);
477 sd_event_source_unref(q->timeout_event_source);
479 while ((t = set_steal_first(q->transactions))) {
480 set_remove(t->queries, q);
481 dns_query_transaction_gc(t);
484 set_free(q->transactions);
487 LIST_REMOVE(queries, q->manager->dns_queries, q);
488 q->manager->n_dns_queries--;
496 int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question) {
497 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
504 r = dns_question_is_valid(question);
508 if (m->n_dns_queries >= QUERIES_MAX)
511 q = new0(DnsQuery, 1);
515 q->question = dns_question_ref(question);
517 for (i = 0; i < question->n_keys; i++) {
518 log_debug("Looking up RR for %s %s %s",
519 strna(dns_class_to_string(question->keys[i]->class)),
520 strna(dns_type_to_string(question->keys[i]->type)),
521 DNS_RESOURCE_KEY_NAME(question->keys[i]));
524 LIST_PREPEND(queries, m->dns_queries, q);
535 static void dns_query_stop(DnsQuery *q) {
536 DnsQueryTransaction *t;
540 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
542 while ((t = set_steal_first(q->transactions))) {
543 set_remove(t->queries, q);
544 dns_query_transaction_gc(t);
548 static void dns_query_complete(DnsQuery *q, DnsQueryState state) {
550 assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
551 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
553 /* Note that this call might invalidate the query. Callers
554 * should hence not attempt to access the query or transaction
555 * after calling this function. */
564 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
565 DnsQuery *q = userdata;
570 dns_query_complete(q, DNS_QUERY_TIMEOUT);
574 static int dns_query_add_transaction(DnsQuery *q, DnsScope *s) {
575 DnsQueryTransaction *t;
580 r = set_ensure_allocated(&q->transactions, NULL, NULL);
584 LIST_FOREACH(transactions_by_scope, t, s->transactions)
585 if (dns_question_is_superset(t->question, q->question))
589 r = dns_query_transaction_new(&t, s, q->question);
594 r = set_ensure_allocated(&t->queries, NULL, NULL);
598 r = set_put(t->queries, q);
602 r = set_put(q->transactions, t);
604 set_remove(t->queries, q);
611 dns_query_transaction_gc(t);
615 int dns_query_go(DnsQuery *q) {
616 DnsScopeMatch found = DNS_SCOPE_NO;
617 DnsScope *s, *first = NULL;
618 DnsQueryTransaction *t;
625 if (q->state != DNS_QUERY_NULL)
629 assert(q->question->n_keys > 0);
631 name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
633 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
636 match = dns_scope_good_domain(s, name);
640 if (match == DNS_SCOPE_NO)
645 if (match == DNS_SCOPE_YES) {
649 assert(match == DNS_SCOPE_MAYBE);
656 if (found == DNS_SCOPE_NO)
659 r = dns_query_add_transaction(q, first);
663 LIST_FOREACH(scopes, s, first->scopes_next) {
666 match = dns_scope_good_domain(s, name);
673 r = dns_query_add_transaction(q, s);
678 q->answer = dns_answer_unref(q->answer);
679 q->answer_ifindex = 0;
682 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);
686 q->state = DNS_QUERY_PENDING;
689 SET_FOREACH(t, q->transactions, i) {
690 if (t->state == DNS_QUERY_NULL) {
691 r = dns_query_transaction_go(t);
707 void dns_query_ready(DnsQuery *q) {
708 DnsQueryTransaction *t;
709 DnsQueryState state = DNS_QUERY_NO_SERVERS;
710 DnsPacket *received = NULL;
714 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
716 /* Note that this call might invalidate the query. Callers
717 * should hence not attempt to access the query or transaction
718 * after calling this function, unless the block_ready
719 * counter was explicitly bumped before doing so. */
721 if (q->block_ready > 0)
724 SET_FOREACH(t, q->transactions, i) {
726 /* One of the transactions is still going on, let's wait for it */
727 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
730 /* One of the transactions is successful, let's use
731 * it, and copy its data out */
732 if (t->state == DNS_QUERY_SUCCESS) {
734 q->answer = dns_answer_ref(t->received->answer);
735 q->answer_ifindex = t->received->ifindex;
736 q->answer_rcode = DNS_PACKET_RCODE(t->received);
738 q->answer = dns_answer_ref(t->cached);
739 q->answer_ifindex = t->scope->link ? t->scope->link->ifindex : 0;
743 dns_query_complete(q, DNS_QUERY_SUCCESS);
747 /* One of the transactions has failed, let's see
748 * whether we find anything better, but if not, return
749 * its response packet */
750 if (t->state == DNS_QUERY_FAILURE) {
751 received = t->received;
752 state = DNS_QUERY_FAILURE;
756 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
760 if (state == DNS_QUERY_FAILURE) {
761 q->answer = dns_answer_ref(received->answer);
762 q->answer_ifindex = received->ifindex;
763 q->answer_rcode = DNS_PACKET_RCODE(received);
766 dns_query_complete(q, state);
769 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
770 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
775 if (q->n_cname_redirects > CNAME_MAX)
778 r = dns_question_cname_redirect(q->question, name, &nq);
782 dns_question_unref(q->question);
786 q->n_cname_redirects++;
789 q->state = DNS_QUERY_NULL;