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 DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
33 sd_event_source_unref(t->timeout_event_source);
35 dns_packet_unref(t->sent);
36 dns_packet_unref(t->received);
38 sd_event_source_unref(t->tcp_event_source);
39 safe_close(t->tcp_fd);
42 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
43 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
47 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
53 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
55 static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
56 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
62 r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
66 t = new0(DnsQueryTransaction, 1);
73 random_bytes(&t->id, sizeof(t->id));
75 hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
77 r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
83 LIST_PREPEND(transactions_by_query, q->transactions, t);
86 LIST_PREPEND(transactions_by_scope, s->transactions, t);
97 static void dns_query_transaction_stop(DnsQueryTransaction *t) {
100 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
101 t->tcp_event_source = sd_event_source_unref(t->tcp_event_source);
102 t->tcp_fd = safe_close(t->tcp_fd);
105 static void dns_query_transaction_set_state(DnsQueryTransaction *t, DnsQueryState state) {
108 if (t->state == state)
113 if (state != DNS_QUERY_SENT) {
114 dns_query_transaction_stop(t);
115 dns_query_finish(t->query);
119 static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
120 DnsQueryTransaction *t = userdata;
125 if (revents & EPOLLOUT) {
130 sz = htobe16(t->sent->size);
132 iov[0].iov_base = &sz;
133 iov[0].iov_len = sizeof(sz);
134 iov[1].iov_base = DNS_PACKET_DATA(t->sent);
135 iov[1].iov_len = t->sent->size;
137 IOVEC_INCREMENT(iov, 2, t->tcp_written);
139 ss = writev(fd, iov, 2);
141 if (errno != EINTR && errno != EAGAIN) {
142 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
146 t->tcp_written += ss;
148 /* Are we done? If so, disable the event source for EPOLLOUT */
149 if (t->tcp_written >= sizeof(sz) + t->sent->size) {
150 r = sd_event_source_set_io_events(s, EPOLLIN);
152 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
158 if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
160 if (t->tcp_read < sizeof(t->tcp_read_size)) {
163 ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
165 if (errno != EINTR && errno != EAGAIN) {
166 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
169 } else if (ss == 0) {
170 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
176 if (t->tcp_read >= sizeof(t->tcp_read_size)) {
178 if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
179 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
183 if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
187 r = dns_packet_new(&t->received, be16toh(t->tcp_read_size));
189 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
195 (uint8_t*) DNS_PACKET_DATA(t->received) + t->tcp_read - sizeof(t->tcp_read_size),
196 sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size) - t->tcp_read);
198 if (errno != EINTR && errno != EAGAIN) {
199 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
202 } else if (ss == 0) {
203 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
209 if (t->tcp_read >= sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
210 t->received->size = be16toh(t->tcp_read_size);
211 dns_query_transaction_reply(t, t->received);
220 static int dns_query_transaction_start_tcp(DnsQueryTransaction *t) {
230 t->received = dns_packet_unref(t->received);
232 t->tcp_fd = dns_scope_tcp_socket(t->scope);
236 r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
238 t->tcp_fd = safe_close(t->tcp_fd);
245 void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
251 if (t->state != DNS_QUERY_SENT)
254 if (t->received != p) {
255 dns_packet_unref(t->received);
256 t->received = dns_packet_ref(p);
259 if (t->tcp_fd >= 0) {
260 if (DNS_PACKET_TC(p)) {
261 /* Truncated via TCP? Somebody must be fucking with us */
262 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
266 if (DNS_PACKET_ID(p) != t->id) {
267 /* Not the reply to our query? Somebody must be fucking with us */
268 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
273 if (DNS_PACKET_TC(p)) {
274 /* Response was truncated, let's try again with good old TCP */
275 r = dns_query_transaction_start_tcp(t);
277 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
282 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
283 dns_query_transaction_set_state(t, DNS_QUERY_SUCCESS);
285 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
288 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
289 DnsQueryTransaction *t = userdata;
295 /* Timeout reached? Try again, with a new server */
296 dns_scope_next_dns_server(t->scope);
298 r = dns_query_transaction_start(t);
300 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
305 static int dns_query_make_packet(DnsQueryTransaction *t) {
306 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
315 r = dns_packet_new_query(&p, 0);
319 for (n = 0; n < t->query->n_keys; n++) {
320 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
325 DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
326 DNS_PACKET_HEADER(p)->id = t->id;
334 int dns_query_transaction_start(DnsQueryTransaction *t) {
339 dns_query_transaction_stop(t);
341 if (t->n_attempts >= ATTEMPTS_MAX) {
342 dns_query_transaction_set_state(t, DNS_QUERY_ATTEMPTS_MAX);
347 r = dns_query_make_packet(t);
351 /* Try via UDP, and if that fails due to large size try via TCP */
352 r = dns_scope_send(t->scope, t->sent);
354 r = dns_query_transaction_start_tcp(t);
357 dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS);
361 /* Couldn't send? Try immediately again, with a new server */
362 dns_scope_next_dns_server(t->scope);
364 return dns_query_transaction_start(t);
367 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);
371 dns_query_transaction_set_state(t, DNS_QUERY_SENT);
375 DnsQuery *dns_query_free(DnsQuery *q) {
381 sd_bus_message_unref(q->request);
382 dns_packet_unref(q->received);
383 sd_event_source_unref(q->timeout_event_source);
385 while (q->transactions)
386 dns_query_transaction_free(q->transactions);
389 LIST_REMOVE(queries, q->manager->dns_queries, q);
391 for (n = 0; n < q->n_keys; n++)
392 free(q->keys[n].name);
399 int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
400 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
401 DnsScope *s, *first = NULL;
402 DnsScopeMatch found = DNS_SCOPE_NO;
403 const char *name = NULL;
408 if (n_keys <= 0 || n_keys >= 65535)
413 q = new0(DnsQuery, 1);
417 q->keys = new(DnsResourceKey, n_keys);
421 for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
422 q->keys[q->n_keys].class = keys[q->n_keys].class;
423 q->keys[q->n_keys].type = keys[q->n_keys].type;
424 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
425 if (!q->keys[q->n_keys].name)
429 name = q->keys[q->n_keys].name;
430 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
434 LIST_PREPEND(queries, m->dns_queries, q);
437 LIST_FOREACH(scopes, s, m->dns_scopes) {
440 match = dns_scope_test(s, name);
444 if (match == DNS_SCOPE_NO)
449 if (match == DNS_SCOPE_YES) {
453 assert(match == DNS_SCOPE_MAYBE);
460 if (found == DNS_SCOPE_NO)
463 r = dns_query_transaction_new(q, NULL, first);
468 LIST_FOREACH(scopes, s, first->scopes_next) {
471 match = dns_scope_test(s, name);
478 r = dns_query_transaction_new(q, NULL, s);
492 static void dns_query_set_state(DnsQuery *q, DnsQueryState state) {
495 if (q->state == state)
500 if (state == DNS_QUERY_SENT)
503 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
505 while (q->transactions)
506 dns_query_transaction_free(q->transactions);
512 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
513 DnsQuery *q = userdata;
518 dns_query_set_state(q, DNS_QUERY_TIMEOUT);
522 int dns_query_start(DnsQuery *q) {
523 DnsQueryTransaction *t;
527 assert(q->state == DNS_QUERY_NULL);
529 if (!q->transactions)
532 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);
536 dns_query_set_state(q, DNS_QUERY_SENT);
538 LIST_FOREACH(transactions_by_query, t, q->transactions) {
540 r = dns_query_transaction_start(t);
544 if (q->state != DNS_QUERY_SENT)
551 while (q->transactions)
552 dns_query_transaction_free(q->transactions);
557 void dns_query_finish(DnsQuery *q) {
558 DnsQueryTransaction *t;
559 DnsQueryState state = DNS_QUERY_NO_SERVERS;
560 DnsPacket *received = NULL;
564 if (q->state != DNS_QUERY_SENT)
567 LIST_FOREACH(transactions_by_query, t, q->transactions) {
569 /* One of the transactions is still going on, let's wait for it */
570 if (t->state == DNS_QUERY_SENT || t->state == DNS_QUERY_NULL)
573 /* One of the transactions is successful, let's use it */
574 if (t->state == DNS_QUERY_SUCCESS) {
575 q->received = dns_packet_ref(t->received);
576 dns_query_set_state(q, DNS_QUERY_SUCCESS);
580 /* One of the transactions has failed, let's see
581 * whether we find anything better, but if not, return
582 * its response packet */
583 if (t->state == DNS_QUERY_FAILURE) {
584 received = t->received;
585 state = DNS_QUERY_FAILURE;
589 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
593 if (state == DNS_QUERY_FAILURE)
594 q->received = dns_packet_ref(received);
596 dns_query_set_state(q, state);