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_start(DnsQueryTransaction *t);
33 DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
37 sd_event_source_unref(t->timeout_event_source);
39 dns_packet_unref(t->sent);
40 dns_packet_unref(t->received);
42 sd_event_source_unref(t->tcp_event_source);
43 safe_close(t->tcp_fd);
46 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
47 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
51 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
57 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
59 static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
60 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
66 r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
70 t = new0(DnsQueryTransaction, 1);
77 random_bytes(&t->id, sizeof(t->id));
79 hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
81 r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
87 LIST_PREPEND(transactions_by_query, q->transactions, t);
90 LIST_PREPEND(transactions_by_scope, s->transactions, t);
101 static void dns_query_transaction_stop(DnsQueryTransaction *t) {
104 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
105 t->tcp_event_source = sd_event_source_unref(t->tcp_event_source);
106 t->tcp_fd = safe_close(t->tcp_fd);
109 static void dns_query_transaction_set_state(DnsQueryTransaction *t, DnsQueryState state) {
112 if (t->state == state)
117 if (state != DNS_QUERY_PENDING) {
118 dns_query_transaction_stop(t);
119 dns_query_finish(t->query);
123 static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
124 DnsQueryTransaction *t = userdata;
129 if (revents & EPOLLOUT) {
134 sz = htobe16(t->sent->size);
136 iov[0].iov_base = &sz;
137 iov[0].iov_len = sizeof(sz);
138 iov[1].iov_base = DNS_PACKET_DATA(t->sent);
139 iov[1].iov_len = t->sent->size;
141 IOVEC_INCREMENT(iov, 2, t->tcp_written);
143 ss = writev(fd, iov, 2);
145 if (errno != EINTR && errno != EAGAIN) {
146 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
150 t->tcp_written += ss;
152 /* Are we done? If so, disable the event source for EPOLLOUT */
153 if (t->tcp_written >= sizeof(sz) + t->sent->size) {
154 r = sd_event_source_set_io_events(s, EPOLLIN);
156 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
162 if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
164 if (t->tcp_read < sizeof(t->tcp_read_size)) {
167 ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
169 if (errno != EINTR && errno != EAGAIN) {
170 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
173 } else if (ss == 0) {
174 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
180 if (t->tcp_read >= sizeof(t->tcp_read_size)) {
182 if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
183 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
187 if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
191 r = dns_packet_new(&t->received, be16toh(t->tcp_read_size));
193 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
199 (uint8_t*) DNS_PACKET_DATA(t->received) + t->tcp_read - sizeof(t->tcp_read_size),
200 sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size) - t->tcp_read);
202 if (errno != EINTR && errno != EAGAIN) {
203 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
206 } else if (ss == 0) {
207 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
213 if (t->tcp_read >= sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
214 t->received->size = be16toh(t->tcp_read_size);
215 dns_query_transaction_reply(t, t->received);
224 static int dns_query_transaction_start_tcp(DnsQueryTransaction *t) {
234 t->received = dns_packet_unref(t->received);
236 t->tcp_fd = dns_scope_tcp_socket(t->scope);
240 r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
242 t->tcp_fd = safe_close(t->tcp_fd);
249 void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
255 if (t->state != DNS_QUERY_PENDING)
258 if (t->received != p) {
259 dns_packet_unref(t->received);
260 t->received = dns_packet_ref(p);
263 if (t->tcp_fd >= 0) {
264 if (DNS_PACKET_TC(p)) {
265 /* Truncated via TCP? Somebody must be fucking with us */
266 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
270 if (DNS_PACKET_ID(p) != t->id) {
271 /* Not the reply to our query? Somebody must be fucking with us */
272 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
277 if (DNS_PACKET_TC(p)) {
278 /* Response was truncated, let's try again with good old TCP */
279 r = dns_query_transaction_start_tcp(t);
281 /* No servers found? Damn! */
282 dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS);
286 /* Couldn't send? Try immediately again, with a new server */
287 dns_scope_next_dns_server(t->scope);
289 r = dns_query_transaction_start(t);
291 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
299 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
300 dns_query_transaction_set_state(t, DNS_QUERY_SUCCESS);
302 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
305 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
306 DnsQueryTransaction *t = userdata;
312 /* Timeout reached? Try again, with a new server */
313 dns_scope_next_dns_server(t->scope);
315 r = dns_query_transaction_start(t);
317 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
322 static int dns_query_make_packet(DnsQueryTransaction *t) {
323 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
332 r = dns_packet_new_query(&p, 0);
336 for (n = 0; n < t->query->n_keys; n++) {
337 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
342 DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
343 DNS_PACKET_HEADER(p)->id = t->id;
351 static int dns_query_transaction_start(DnsQueryTransaction *t) {
356 dns_query_transaction_stop(t);
358 if (t->n_attempts >= ATTEMPTS_MAX) {
359 dns_query_transaction_set_state(t, DNS_QUERY_ATTEMPTS_MAX);
363 r = dns_query_make_packet(t);
368 t->received = dns_packet_unref(t->received);
370 /* Try via UDP, and if that fails due to large size try via TCP */
371 r = dns_scope_send(t->scope, t->sent);
373 r = dns_query_transaction_start_tcp(t);
376 dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS);
380 /* Couldn't send? Try immediately again, with a new server */
381 dns_scope_next_dns_server(t->scope);
383 return dns_query_transaction_start(t);
386 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);
390 dns_query_transaction_set_state(t, DNS_QUERY_PENDING);
394 DnsQuery *dns_query_free(DnsQuery *q) {
400 sd_bus_message_unref(q->request);
401 dns_packet_unref(q->received);
402 sd_event_source_unref(q->timeout_event_source);
404 while (q->transactions)
405 dns_query_transaction_free(q->transactions);
408 LIST_REMOVE(queries, q->manager->dns_queries, q);
409 q->manager->n_dns_queries--;
412 for (n = 0; n < q->n_keys; n++)
413 free(q->keys[n].name);
420 int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
421 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
422 const char *name = NULL;
426 if (n_keys <= 0 || n_keys >= 65535)
429 if (m->n_dns_queries >= QUERIES_MAX)
434 q = new0(DnsQuery, 1);
438 q->keys = new(DnsResourceKey, n_keys);
442 for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
443 q->keys[q->n_keys].class = keys[q->n_keys].class;
444 q->keys[q->n_keys].type = keys[q->n_keys].type;
445 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
446 if (!q->keys[q->n_keys].name)
450 name = q->keys[q->n_keys].name;
451 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
455 LIST_PREPEND(queries, m->dns_queries, q);
466 static void dns_query_stop(DnsQuery *q) {
469 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
471 while (q->transactions)
472 dns_query_transaction_free(q->transactions);
475 static void dns_query_set_state(DnsQuery *q, DnsQueryState state) {
476 DnsQueryState old_state;
479 if (q->state == state)
482 old_state = q->state;
485 if (!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING)) {
488 if (old_state == DNS_QUERY_PENDING && q->complete)
493 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
494 DnsQuery *q = userdata;
499 dns_query_set_state(q, DNS_QUERY_TIMEOUT);
503 int dns_query_start(DnsQuery *q) {
504 DnsScopeMatch found = DNS_SCOPE_NO;
505 DnsScope *s, *first = NULL;
506 DnsQueryTransaction *t;
511 if (q->state != DNS_QUERY_NULL)
514 assert(q->n_keys > 0);
516 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
519 match = dns_scope_test(s, q->keys[0].name);
523 if (match == DNS_SCOPE_NO)
528 if (match == DNS_SCOPE_YES) {
532 assert(match == DNS_SCOPE_MAYBE);
539 if (found == DNS_SCOPE_NO)
542 r = dns_query_transaction_new(q, NULL, first);
546 LIST_FOREACH(scopes, s, first->scopes_next) {
549 match = dns_scope_test(s, q->keys[0].name);
556 r = dns_query_transaction_new(q, NULL, s);
561 q->received = dns_packet_unref(q->received);
563 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);
567 dns_query_set_state(q, DNS_QUERY_PENDING);
569 LIST_FOREACH(transactions_by_query, t, q->transactions) {
571 r = dns_query_transaction_start(t);
575 if (q->state != DNS_QUERY_PENDING)
586 void dns_query_finish(DnsQuery *q) {
587 DnsQueryTransaction *t;
588 DnsQueryState state = DNS_QUERY_NO_SERVERS;
589 DnsPacket *received = NULL;
593 if (q->state != DNS_QUERY_PENDING)
596 LIST_FOREACH(transactions_by_query, t, q->transactions) {
598 /* One of the transactions is still going on, let's wait for it */
599 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
602 /* One of the transactions is successful, let's use it */
603 if (t->state == DNS_QUERY_SUCCESS) {
604 q->received = dns_packet_ref(t->received);
605 dns_query_set_state(q, DNS_QUERY_SUCCESS);
609 /* One of the transactions has failed, let's see
610 * whether we find anything better, but if not, return
611 * its response packet */
612 if (t->state == DNS_QUERY_FAILURE) {
613 received = t->received;
614 state = DNS_QUERY_FAILURE;
618 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
622 if (state == DNS_QUERY_FAILURE)
623 q->received = dns_packet_ref(received);
625 dns_query_set_state(q, state);
628 int dns_query_follow_cname(DnsQuery *q, const char *name) {
629 DnsResourceKey *keys;
634 if (q->n_cname > CNAME_MAX)
637 keys = new(DnsResourceKey, q->n_keys);
641 for (i = 0; i < q->n_keys; i++) {
642 keys[i].class = q->keys[i].class;
643 keys[i].type = q->keys[i].type;
644 keys[i].name = strdup(name);
648 free(keys[i-1].name);
654 for (i = 0; i < q->n_keys; i++)
655 free(q->keys[i].name);
662 dns_query_set_state(q, DNS_QUERY_NULL);
666 int dns_query_matches_rr(DnsQuery *q, DnsResourceRecord *rr) {
673 for (i = 0; i < q->n_keys; i++) {
675 if (rr->key.class != q->keys[i].class)
678 if (rr->key.type != q->keys[i].type &&
679 q->keys[i].type != DNS_TYPE_ANY)
682 r = dns_name_equal(rr->key.name, q->keys[i].name);
690 int dns_query_matches_cname(DnsQuery *q, DnsResourceRecord *rr) {
697 for (i = 0; i < q->n_keys; i++) {
699 if (rr->key.class != q->keys[i].class)
702 if (rr->key.type != DNS_TYPE_CNAME)
705 r = dns_name_equal(rr->key.name, q->keys[i].name);