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(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), 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);
421 /* First, let's try the cache */
422 dns_cache_prune(&t->scope->cache);
423 r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
427 if (t->cached_rcode == DNS_RCODE_SUCCESS)
428 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
430 dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
434 /* Otherwise, we need to ask the network */
435 r = dns_query_make_packet(t);
437 /* Not the right request to make on this network?
438 * (i.e. an A request made on IPv6 or an AAAA request
439 * made on IPv4, on LLMNR or mDNS.) */
440 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
446 /* Try via UDP, and if that fails due to large size try via TCP */
447 r = dns_scope_send(t->scope, t->sent);
449 r = dns_query_transaction_open_tcp(t);
451 /* No servers to send this to? */
452 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
456 /* Couldn't send? Try immediately again, with a new server */
457 dns_scope_next_dns_server(t->scope);
459 return dns_query_transaction_go(t);
462 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);
466 t->state = DNS_QUERY_PENDING;
470 DnsQuery *dns_query_free(DnsQuery *q) {
471 DnsQueryTransaction *t;
476 sd_bus_message_unref(q->request);
478 dns_question_unref(q->question);
479 dns_answer_unref(q->answer);
481 sd_event_source_unref(q->timeout_event_source);
483 while ((t = set_steal_first(q->transactions))) {
484 set_remove(t->queries, q);
485 dns_query_transaction_gc(t);
488 set_free(q->transactions);
491 LIST_REMOVE(queries, q->manager->dns_queries, q);
492 q->manager->n_dns_queries--;
500 int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question) {
501 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
508 r = dns_question_is_valid(question);
512 if (m->n_dns_queries >= QUERIES_MAX)
515 q = new0(DnsQuery, 1);
519 q->question = dns_question_ref(question);
521 for (i = 0; i < question->n_keys; i++) {
522 log_debug("Looking up RR for %s %s %s",
523 strna(dns_class_to_string(question->keys[i]->class)),
524 strna(dns_type_to_string(question->keys[i]->type)),
525 DNS_RESOURCE_KEY_NAME(question->keys[i]));
528 LIST_PREPEND(queries, m->dns_queries, q);
539 static void dns_query_stop(DnsQuery *q) {
540 DnsQueryTransaction *t;
544 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
546 while ((t = set_steal_first(q->transactions))) {
547 set_remove(t->queries, q);
548 dns_query_transaction_gc(t);
552 static void dns_query_complete(DnsQuery *q, DnsQueryState state) {
554 assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
555 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
557 /* Note that this call might invalidate the query. Callers
558 * should hence not attempt to access the query or transaction
559 * after calling this function. */
568 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
569 DnsQuery *q = userdata;
574 dns_query_complete(q, DNS_QUERY_TIMEOUT);
578 static int dns_query_add_transaction(DnsQuery *q, DnsScope *s) {
579 DnsQueryTransaction *t;
584 r = set_ensure_allocated(&q->transactions, NULL, NULL);
588 LIST_FOREACH(transactions_by_scope, t, s->transactions)
589 if (dns_question_is_superset(t->question, q->question))
593 r = dns_query_transaction_new(&t, s, q->question);
598 r = set_ensure_allocated(&t->queries, NULL, NULL);
602 r = set_put(t->queries, q);
606 r = set_put(q->transactions, t);
608 set_remove(t->queries, q);
615 dns_query_transaction_gc(t);
619 int dns_query_go(DnsQuery *q) {
620 DnsScopeMatch found = DNS_SCOPE_NO;
621 DnsScope *s, *first = NULL;
622 DnsQueryTransaction *t;
629 if (q->state != DNS_QUERY_NULL)
633 assert(q->question->n_keys > 0);
635 name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
637 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
640 match = dns_scope_good_domain(s, name);
644 if (match == DNS_SCOPE_NO)
649 if (match == DNS_SCOPE_YES) {
653 assert(match == DNS_SCOPE_MAYBE);
660 if (found == DNS_SCOPE_NO)
663 r = dns_query_add_transaction(q, first);
667 LIST_FOREACH(scopes, s, first->scopes_next) {
670 match = dns_scope_good_domain(s, name);
677 r = dns_query_add_transaction(q, s);
682 q->answer = dns_answer_unref(q->answer);
683 q->answer_ifindex = 0;
686 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);
690 q->state = DNS_QUERY_PENDING;
693 SET_FOREACH(t, q->transactions, i) {
694 if (t->state == DNS_QUERY_NULL) {
695 r = dns_query_transaction_go(t);
711 void dns_query_ready(DnsQuery *q) {
712 DnsQueryTransaction *t;
713 DnsQueryState state = DNS_QUERY_NO_SERVERS;
714 DnsAnswer *failure_answer = NULL;
715 int failure_rcode = 0, failure_ifindex = 0;
719 assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
721 /* Note that this call might invalidate the query. Callers
722 * should hence not attempt to access the query or transaction
723 * after calling this function, unless the block_ready
724 * counter was explicitly bumped before doing so. */
726 if (q->block_ready > 0)
729 SET_FOREACH(t, q->transactions, i) {
731 /* One of the transactions is still going on, let's wait for it */
732 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
735 /* One of the transactions is successful, let's use
736 * it, and copy its data out */
737 if (t->state == DNS_QUERY_SUCCESS) {
739 q->answer = dns_answer_ref(t->received->answer);
740 q->answer_ifindex = t->received->ifindex;
741 q->answer_rcode = DNS_PACKET_RCODE(t->received);
743 q->answer = dns_answer_ref(t->cached);
744 q->answer_ifindex = t->scope->link ? t->scope->link->ifindex : 0;
745 q->answer_rcode = t->cached_rcode;
748 dns_query_complete(q, DNS_QUERY_SUCCESS);
752 /* One of the transactions has failed, let's see
753 * whether we find anything better, but if not, return
754 * its response packet */
755 if (t->state == DNS_QUERY_FAILURE) {
757 failure_answer = t->received->answer;
758 failure_ifindex = t->received->ifindex;
759 failure_rcode = DNS_PACKET_RCODE(t->received);
761 failure_answer = t->cached;
762 failure_ifindex = t->scope->link ? t->scope->link->ifindex : 0;
763 failure_rcode = t->cached_rcode;
766 state = DNS_QUERY_FAILURE;
770 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
774 if (state == DNS_QUERY_FAILURE) {
775 q->answer = dns_answer_ref(failure_answer);
776 q->answer_ifindex = failure_ifindex;
777 q->answer_rcode = failure_rcode;
780 dns_query_complete(q, state);
783 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
784 _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
789 if (q->n_cname_redirects > CNAME_MAX)
792 r = dns_question_cname_redirect(q->question, name, &nq);
796 dns_question_unref(q->question);
800 q->n_cname_redirects++;
803 q->state = DNS_QUERY_NULL;