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
30 static int dns_query_transaction_start(DnsQueryTransaction *t);
32 DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
36 sd_event_source_unref(t->timeout_event_source);
38 dns_packet_unref(t->sent);
39 dns_packet_unref(t->received);
41 sd_event_source_unref(t->tcp_event_source);
42 safe_close(t->tcp_fd);
45 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
46 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
50 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
56 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
58 static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
59 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
65 r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
69 t = new0(DnsQueryTransaction, 1);
76 random_bytes(&t->id, sizeof(t->id));
78 hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
80 r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
86 LIST_PREPEND(transactions_by_query, q->transactions, t);
89 LIST_PREPEND(transactions_by_scope, s->transactions, t);
100 static void dns_query_transaction_stop(DnsQueryTransaction *t) {
103 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
104 t->tcp_event_source = sd_event_source_unref(t->tcp_event_source);
105 t->tcp_fd = safe_close(t->tcp_fd);
108 static void dns_query_transaction_set_state(DnsQueryTransaction *t, DnsQueryState state) {
111 if (t->state == state)
116 if (state != DNS_QUERY_PENDING) {
117 dns_query_transaction_stop(t);
118 dns_query_finish(t->query);
122 static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
123 DnsQueryTransaction *t = userdata;
128 if (revents & EPOLLOUT) {
133 sz = htobe16(t->sent->size);
135 iov[0].iov_base = &sz;
136 iov[0].iov_len = sizeof(sz);
137 iov[1].iov_base = DNS_PACKET_DATA(t->sent);
138 iov[1].iov_len = t->sent->size;
140 IOVEC_INCREMENT(iov, 2, t->tcp_written);
142 ss = writev(fd, iov, 2);
144 if (errno != EINTR && errno != EAGAIN) {
145 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
149 t->tcp_written += ss;
151 /* Are we done? If so, disable the event source for EPOLLOUT */
152 if (t->tcp_written >= sizeof(sz) + t->sent->size) {
153 r = sd_event_source_set_io_events(s, EPOLLIN);
155 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
161 if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
163 if (t->tcp_read < sizeof(t->tcp_read_size)) {
166 ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
168 if (errno != EINTR && errno != EAGAIN) {
169 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
172 } else if (ss == 0) {
173 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
179 if (t->tcp_read >= sizeof(t->tcp_read_size)) {
181 if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
182 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
186 if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
190 r = dns_packet_new(&t->received, be16toh(t->tcp_read_size));
192 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
198 (uint8_t*) DNS_PACKET_DATA(t->received) + t->tcp_read - sizeof(t->tcp_read_size),
199 sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size) - t->tcp_read);
201 if (errno != EINTR && errno != EAGAIN) {
202 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
205 } else if (ss == 0) {
206 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
212 if (t->tcp_read >= sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
213 t->received->size = be16toh(t->tcp_read_size);
214 dns_query_transaction_reply(t, t->received);
223 static int dns_query_transaction_start_tcp(DnsQueryTransaction *t) {
233 t->received = dns_packet_unref(t->received);
235 t->tcp_fd = dns_scope_tcp_socket(t->scope);
239 r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
241 t->tcp_fd = safe_close(t->tcp_fd);
248 void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
254 if (t->state != DNS_QUERY_PENDING)
257 if (t->received != p) {
258 dns_packet_unref(t->received);
259 t->received = dns_packet_ref(p);
262 if (t->tcp_fd >= 0) {
263 if (DNS_PACKET_TC(p)) {
264 /* Truncated via TCP? Somebody must be fucking with us */
265 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
269 if (DNS_PACKET_ID(p) != t->id) {
270 /* Not the reply to our query? Somebody must be fucking with us */
271 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
276 if (DNS_PACKET_TC(p)) {
277 /* Response was truncated, let's try again with good old TCP */
278 r = dns_query_transaction_start_tcp(t);
280 /* No servers found? Damn! */
281 dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS);
285 /* Couldn't send? Try immediately again, with a new server */
286 dns_scope_next_dns_server(t->scope);
288 r = dns_query_transaction_start(t);
290 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
298 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
299 dns_query_transaction_set_state(t, DNS_QUERY_SUCCESS);
301 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
304 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
305 DnsQueryTransaction *t = userdata;
311 /* Timeout reached? Try again, with a new server */
312 dns_scope_next_dns_server(t->scope);
314 r = dns_query_transaction_start(t);
316 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
321 static int dns_query_make_packet(DnsQueryTransaction *t) {
322 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
331 r = dns_packet_new_query(&p, 0);
335 for (n = 0; n < t->query->n_keys; n++) {
336 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
341 DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
342 DNS_PACKET_HEADER(p)->id = t->id;
350 static int dns_query_transaction_start(DnsQueryTransaction *t) {
355 dns_query_transaction_stop(t);
357 if (t->n_attempts >= ATTEMPTS_MAX) {
358 dns_query_transaction_set_state(t, DNS_QUERY_ATTEMPTS_MAX);
362 r = dns_query_make_packet(t);
367 t->received = dns_packet_unref(t->received);
369 /* Try via UDP, and if that fails due to large size try via TCP */
370 r = dns_scope_send(t->scope, t->sent);
372 r = dns_query_transaction_start_tcp(t);
375 dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS);
379 /* Couldn't send? Try immediately again, with a new server */
380 dns_scope_next_dns_server(t->scope);
382 return dns_query_transaction_start(t);
385 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);
389 dns_query_transaction_set_state(t, DNS_QUERY_PENDING);
393 DnsQuery *dns_query_free(DnsQuery *q) {
399 sd_bus_message_unref(q->request);
400 dns_packet_unref(q->received);
401 sd_event_source_unref(q->timeout_event_source);
403 while (q->transactions)
404 dns_query_transaction_free(q->transactions);
407 LIST_REMOVE(queries, q->manager->dns_queries, q);
409 for (n = 0; n < q->n_keys; n++)
410 free(q->keys[n].name);
417 int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
418 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
419 const char *name = NULL;
423 if (n_keys <= 0 || n_keys >= 65535)
428 q = new0(DnsQuery, 1);
432 q->keys = new(DnsResourceKey, n_keys);
436 for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
437 q->keys[q->n_keys].class = keys[q->n_keys].class;
438 q->keys[q->n_keys].type = keys[q->n_keys].type;
439 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
440 if (!q->keys[q->n_keys].name)
444 name = q->keys[q->n_keys].name;
445 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
449 LIST_PREPEND(queries, m->dns_queries, q);
459 static void dns_query_stop(DnsQuery *q) {
462 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
464 while (q->transactions)
465 dns_query_transaction_free(q->transactions);
468 static void dns_query_set_state(DnsQuery *q, DnsQueryState state) {
469 DnsQueryState old_state;
472 if (q->state == state)
475 old_state = q->state;
478 if (!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING)) {
481 if (old_state == DNS_QUERY_PENDING && q->complete)
486 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
487 DnsQuery *q = userdata;
492 dns_query_set_state(q, DNS_QUERY_TIMEOUT);
496 int dns_query_start(DnsQuery *q) {
497 DnsScopeMatch found = DNS_SCOPE_NO;
498 DnsScope *s, *first = NULL;
499 DnsQueryTransaction *t;
504 if (q->state != DNS_QUERY_NULL)
507 assert(q->n_keys > 0);
509 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
512 match = dns_scope_test(s, q->keys[0].name);
516 if (match == DNS_SCOPE_NO)
521 if (match == DNS_SCOPE_YES) {
525 assert(match == DNS_SCOPE_MAYBE);
532 if (found == DNS_SCOPE_NO)
535 r = dns_query_transaction_new(q, NULL, first);
539 LIST_FOREACH(scopes, s, first->scopes_next) {
542 match = dns_scope_test(s, q->keys[0].name);
549 r = dns_query_transaction_new(q, NULL, s);
554 q->received = dns_packet_unref(q->received);
556 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);
560 dns_query_set_state(q, DNS_QUERY_PENDING);
562 LIST_FOREACH(transactions_by_query, t, q->transactions) {
564 r = dns_query_transaction_start(t);
568 if (q->state != DNS_QUERY_PENDING)
579 void dns_query_finish(DnsQuery *q) {
580 DnsQueryTransaction *t;
581 DnsQueryState state = DNS_QUERY_NO_SERVERS;
582 DnsPacket *received = NULL;
586 if (q->state != DNS_QUERY_PENDING)
589 LIST_FOREACH(transactions_by_query, t, q->transactions) {
591 /* One of the transactions is still going on, let's wait for it */
592 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
595 /* One of the transactions is successful, let's use it */
596 if (t->state == DNS_QUERY_SUCCESS) {
597 q->received = dns_packet_ref(t->received);
598 dns_query_set_state(q, DNS_QUERY_SUCCESS);
602 /* One of the transactions has failed, let's see
603 * whether we find anything better, but if not, return
604 * its response packet */
605 if (t->state == DNS_QUERY_FAILURE) {
606 received = t->received;
607 state = DNS_QUERY_FAILURE;
611 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
615 if (state == DNS_QUERY_FAILURE)
616 q->received = dns_packet_ref(received);
618 dns_query_set_state(q, state);
621 int dns_query_follow_cname(DnsQuery *q, const char *name) {
622 DnsResourceKey *keys;
627 if (q->n_cname > CNAME_MAX)
630 keys = new(DnsResourceKey, q->n_keys);
634 for (i = 0; i < q->n_keys; i++) {
635 keys[i].class = q->keys[i].class;
636 keys[i].type = q->keys[i].type;
637 keys[i].name = strdup(name);
641 free(keys[i-1].name);
647 for (i = 0; i < q->n_keys; i++)
648 free(q->keys[i].name);
655 dns_query_set_state(q, DNS_QUERY_NULL);
659 int dns_query_matches_rr(DnsQuery *q, DnsResourceRecord *rr) {
666 for (i = 0; i < q->n_keys; i++) {
668 if (rr->key.class != q->keys[i].class)
671 if (rr->key.type != q->keys[i].type &&
672 q->keys[i].type != DNS_TYPE_ANY)
675 r = dns_name_equal(rr->key.name, q->keys[i].name);
683 int dns_query_matches_cname(DnsQuery *q, DnsResourceRecord *rr) {
690 for (i = 0; i < q->n_keys; i++) {
692 if (rr->key.class != q->keys[i].class)
695 if (rr->key.type != DNS_TYPE_CNAME)
698 r = dns_name_equal(rr->key.name, q->keys[i].name);