]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-query.c
resolved: don't trip up when an rtlink message does not include the MTU
[thirdparty/systemd.git] / src / resolve / resolved-dns-query.c
CommitLineData
74b2466e
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include "resolved-dns-query.h"
23#include "resolved-dns-domain.h"
24
25#define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
26#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
27#define ATTEMPTS_MAX 8
8ba9fd9c 28#define CNAME_MAX 8
39762fdf 29#define QUERIES_MAX 2048
8ba9fd9c
LP
30
31static int dns_query_transaction_start(DnsQueryTransaction *t);
74b2466e
LP
32
33DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
34 if (!t)
35 return NULL;
36
37 sd_event_source_unref(t->timeout_event_source);
ad867662
LP
38
39 dns_packet_unref(t->sent);
40 dns_packet_unref(t->received);
41
42 sd_event_source_unref(t->tcp_event_source);
43 safe_close(t->tcp_fd);
74b2466e
LP
44
45 if (t->query) {
46 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
47 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
48 }
49
50 if (t->scope)
51 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
52
53 free(t);
54 return NULL;
55}
56
57DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
58
59static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
60 _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
61 int r;
62
63 assert(q);
64 assert(s);
65
66 r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
67 if (r < 0)
68 return r;
69
70 t = new0(DnsQueryTransaction, 1);
71 if (!t)
72 return -ENOMEM;
73
ad867662
LP
74 t->tcp_fd = -1;
75
74b2466e
LP
76 do
77 random_bytes(&t->id, sizeof(t->id));
78 while (t->id == 0 ||
79 hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
80
81 r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
82 if (r < 0) {
83 t->id = 0;
84 return r;
85 }
86
87 LIST_PREPEND(transactions_by_query, q->transactions, t);
88 t->query = q;
89
90 LIST_PREPEND(transactions_by_scope, s->transactions, t);
91 t->scope = s;
92
93 if (ret)
94 *ret = t;
95
96 t = NULL;
97
98 return 0;
99}
100
ad867662
LP
101static void dns_query_transaction_stop(DnsQueryTransaction *t) {
102 assert(t);
103
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);
107}
108
74b2466e
LP
109static void dns_query_transaction_set_state(DnsQueryTransaction *t, DnsQueryState state) {
110 assert(t);
111
112 if (t->state == state)
113 return;
114
115 t->state = state;
116
8ba9fd9c 117 if (state != DNS_QUERY_PENDING) {
ad867662
LP
118 dns_query_transaction_stop(t);
119 dns_query_finish(t->query);
120 }
121}
122
123static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
124 DnsQueryTransaction *t = userdata;
125 int r;
126
127 assert(t);
128
129 if (revents & EPOLLOUT) {
130 struct iovec iov[2];
131 be16_t sz;
132 ssize_t ss;
133
134 sz = htobe16(t->sent->size);
135
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;
140
141 IOVEC_INCREMENT(iov, 2, t->tcp_written);
142
143 ss = writev(fd, iov, 2);
144 if (ss < 0) {
145 if (errno != EINTR && errno != EAGAIN) {
146 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
147 return -errno;
148 }
149 } else
150 t->tcp_written += ss;
151
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);
155 if (r < 0) {
156 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
157 return r;
158 }
159 }
160 }
161
162 if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
163
164 if (t->tcp_read < sizeof(t->tcp_read_size)) {
165 ssize_t ss;
166
167 ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
168 if (ss < 0) {
169 if (errno != EINTR && errno != EAGAIN) {
170 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
171 return -errno;
172 }
173 } else if (ss == 0) {
174 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
175 return -EIO;
176 } else
177 t->tcp_read += ss;
178 }
179
180 if (t->tcp_read >= sizeof(t->tcp_read_size)) {
181
182 if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
183 dns_query_transaction_set_state(t, DNS_QUERY_INVALID_REPLY);
184 return -EBADMSG;
185 }
186
187 if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
188 ssize_t ss;
189
190 if (!t->received) {
191 r = dns_packet_new(&t->received, be16toh(t->tcp_read_size));
192 if (r < 0) {
193 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
194 return r;
195 }
196 }
197
198 ss = read(fd,
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);
201 if (ss < 0) {
202 if (errno != EINTR && errno != EAGAIN) {
203 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
204 return -errno;
205 }
206 } else if (ss == 0) {
207 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
208 return -EIO;
209 } else
210 t->tcp_read += ss;
211 }
212
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);
216 return 0;
217 }
218 }
219 }
220
221 return 0;
222}
223
224static int dns_query_transaction_start_tcp(DnsQueryTransaction *t) {
225 int r;
226
227 assert(t);
228
229 if (t->tcp_fd >= 0)
230 return 0;
231
232 t->tcp_written = 0;
233 t->tcp_read = 0;
234 t->received = dns_packet_unref(t->received);
235
236 t->tcp_fd = dns_scope_tcp_socket(t->scope);
237 if (t->tcp_fd < 0)
238 return t->tcp_fd;
239
240 r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
241 if (r < 0) {
242 t->tcp_fd = safe_close(t->tcp_fd);
243 return r;
244 }
74b2466e 245
ad867662 246 return 0;
74b2466e
LP
247}
248
ad867662
LP
249void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
250 int r;
251
74b2466e
LP
252 assert(t);
253 assert(p);
254
8ba9fd9c 255 if (t->state != DNS_QUERY_PENDING)
ad867662
LP
256 return;
257
258 if (t->received != p) {
259 dns_packet_unref(t->received);
260 t->received = dns_packet_ref(p);
261 }
262
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);
267 return;
268 }
269
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);
273 return;
274 }
275 }
276
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);
8ba9fd9c
LP
280 if (r == -ESRCH) {
281 /* No servers found? Damn! */
282 dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS);
283 return;
284 }
ad867662 285 if (r < 0) {
8ba9fd9c
LP
286 /* Couldn't send? Try immediately again, with a new server */
287 dns_scope_next_dns_server(t->scope);
288
289 r = dns_query_transaction_start(t);
290 if (r < 0) {
291 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
292 return;
293 }
294
ad867662
LP
295 return;
296 }
297 }
74b2466e 298
b9d394ea
LP
299 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
300 dns_query_transaction_set_state(t, DNS_QUERY_SUCCESS);
301 else
74b2466e 302 dns_query_transaction_set_state(t, DNS_QUERY_FAILURE);
74b2466e
LP
303}
304
305static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
306 DnsQueryTransaction *t = userdata;
307 int r;
308
309 assert(s);
310 assert(t);
311
312 /* Timeout reached? Try again, with a new server */
313 dns_scope_next_dns_server(t->scope);
314
315 r = dns_query_transaction_start(t);
316 if (r < 0)
ad867662 317 dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES);
74b2466e
LP
318
319 return 0;
320}
321
ad867662 322static int dns_query_make_packet(DnsQueryTransaction *t) {
74b2466e
LP
323 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
324 unsigned n;
325 int r;
326
327 assert(t);
328
ad867662 329 if (t->sent)
74b2466e 330 return 0;
74b2466e
LP
331
332 r = dns_packet_new_query(&p, 0);
333 if (r < 0)
334 return r;
335
336 for (n = 0; n < t->query->n_keys; n++) {
337 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
338 if (r < 0)
339 return r;
340 }
341
342 DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
343 DNS_PACKET_HEADER(p)->id = t->id;
344
ad867662
LP
345 t->sent = p;
346 p = NULL;
347
348 return 0;
349}
350
8ba9fd9c 351static int dns_query_transaction_start(DnsQueryTransaction *t) {
ad867662
LP
352 int r;
353
354 assert(t);
355
356 dns_query_transaction_stop(t);
357
358 if (t->n_attempts >= ATTEMPTS_MAX) {
359 dns_query_transaction_set_state(t, DNS_QUERY_ATTEMPTS_MAX);
360 return 0;
361 }
ad867662
LP
362
363 r = dns_query_make_packet(t);
364 if (r < 0)
365 return r;
366
8ba9fd9c
LP
367 t->n_attempts++;
368 t->received = dns_packet_unref(t->received);
369
ad867662
LP
370 /* Try via UDP, and if that fails due to large size try via TCP */
371 r = dns_scope_send(t->scope, t->sent);
372 if (r == -EMSGSIZE)
373 r = dns_query_transaction_start_tcp(t);
374
375 if (r == -ESRCH) {
376 dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS);
377 return 0;
378 }
74b2466e
LP
379 if (r < 0) {
380 /* Couldn't send? Try immediately again, with a new server */
381 dns_scope_next_dns_server(t->scope);
382
383 return dns_query_transaction_start(t);
384 }
385
ad867662
LP
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);
387 if (r < 0)
388 return r;
74b2466e 389
8ba9fd9c 390 dns_query_transaction_set_state(t, DNS_QUERY_PENDING);
ad867662 391 return 1;
74b2466e
LP
392}
393
394DnsQuery *dns_query_free(DnsQuery *q) {
395 unsigned n;
396
397 if (!q)
398 return NULL;
399
400 sd_bus_message_unref(q->request);
ad867662 401 dns_packet_unref(q->received);
74b2466e
LP
402 sd_event_source_unref(q->timeout_event_source);
403
404 while (q->transactions)
405 dns_query_transaction_free(q->transactions);
406
39762fdf 407 if (q->manager) {
74b2466e 408 LIST_REMOVE(queries, q->manager->dns_queries, q);
39762fdf
LP
409 q->manager->n_dns_queries--;
410 }
74b2466e
LP
411
412 for (n = 0; n < q->n_keys; n++)
413 free(q->keys[n].name);
414 free(q->keys);
415 free(q);
416
417 return NULL;
418}
419
420int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
421 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
74b2466e 422 const char *name = NULL;
74b2466e
LP
423
424 assert(m);
425
426 if (n_keys <= 0 || n_keys >= 65535)
427 return -EINVAL;
428
39762fdf
LP
429 if (m->n_dns_queries >= QUERIES_MAX)
430 return -EBUSY;
431
74b2466e
LP
432 assert(keys);
433
434 q = new0(DnsQuery, 1);
435 if (!q)
436 return -ENOMEM;
437
438 q->keys = new(DnsResourceKey, n_keys);
439 if (!q->keys)
440 return -ENOMEM;
441
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)
447 return -ENOMEM;
448
449 if (!name)
450 name = q->keys[q->n_keys].name;
451 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
452 return -EINVAL;
453 }
454
455 LIST_PREPEND(queries, m->dns_queries, q);
39762fdf 456 m->n_dns_queries++;
74b2466e
LP
457 q->manager = m;
458
8ba9fd9c
LP
459 if (ret)
460 *ret = q;
461 q = NULL;
462
463 return 0;
464}
465
466static void dns_query_stop(DnsQuery *q) {
467 assert(q);
468
469 q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
470
471 while (q->transactions)
472 dns_query_transaction_free(q->transactions);
473}
474
475static void dns_query_set_state(DnsQuery *q, DnsQueryState state) {
476 DnsQueryState old_state;
477 assert(q);
478
479 if (q->state == state)
480 return;
481
482 old_state = q->state;
483 q->state = state;
484
485 if (!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING)) {
486 dns_query_stop(q);
487
488 if (old_state == DNS_QUERY_PENDING && q->complete)
489 q->complete(q);
490 }
491}
492
493static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
494 DnsQuery *q = userdata;
495
496 assert(s);
497 assert(q);
498
499 dns_query_set_state(q, DNS_QUERY_TIMEOUT);
500 return 0;
501}
502
503int dns_query_start(DnsQuery *q) {
504 DnsScopeMatch found = DNS_SCOPE_NO;
505 DnsScope *s, *first = NULL;
506 DnsQueryTransaction *t;
507 int r;
508
509 assert(q);
510
511 if (q->state != DNS_QUERY_NULL)
512 return 0;
513
514 assert(q->n_keys > 0);
515
516 LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
74b2466e
LP
517 DnsScopeMatch match;
518
8ba9fd9c 519 match = dns_scope_test(s, q->keys[0].name);
74b2466e
LP
520 if (match < 0)
521 return match;
522
523 if (match == DNS_SCOPE_NO)
524 continue;
525
526 found = match;
527
528 if (match == DNS_SCOPE_YES) {
529 first = s;
530 break;
531 } else {
532 assert(match == DNS_SCOPE_MAYBE);
533
534 if (!first)
535 first = s;
536 }
537 }
538
539 if (found == DNS_SCOPE_NO)
8ba9fd9c 540 return -ESRCH;
74b2466e
LP
541
542 r = dns_query_transaction_new(q, NULL, first);
543 if (r < 0)
544 return r;
545
74b2466e
LP
546 LIST_FOREACH(scopes, s, first->scopes_next) {
547 DnsScopeMatch match;
548
8ba9fd9c 549 match = dns_scope_test(s, q->keys[0].name);
74b2466e
LP
550 if (match < 0)
551 return match;
552
553 if (match != found)
554 continue;
555
556 r = dns_query_transaction_new(q, NULL, s);
557 if (r < 0)
558 return r;
74b2466e
LP
559 }
560
8ba9fd9c 561 q->received = dns_packet_unref(q->received);
74b2466e
LP
562
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);
564 if (r < 0)
565 goto fail;
566
8ba9fd9c 567 dns_query_set_state(q, DNS_QUERY_PENDING);
74b2466e
LP
568
569 LIST_FOREACH(transactions_by_query, t, q->transactions) {
570
571 r = dns_query_transaction_start(t);
572 if (r < 0)
573 goto fail;
574
8ba9fd9c 575 if (q->state != DNS_QUERY_PENDING)
74b2466e
LP
576 break;
577 }
578
8ba9fd9c 579 return 1;
74b2466e
LP
580
581fail:
8ba9fd9c 582 dns_query_stop(q);
74b2466e
LP
583 return r;
584}
585
586void dns_query_finish(DnsQuery *q) {
587 DnsQueryTransaction *t;
ad867662
LP
588 DnsQueryState state = DNS_QUERY_NO_SERVERS;
589 DnsPacket *received = NULL;
74b2466e
LP
590
591 assert(q);
592
8ba9fd9c 593 if (q->state != DNS_QUERY_PENDING)
74b2466e
LP
594 return;
595
596 LIST_FOREACH(transactions_by_query, t, q->transactions) {
597
598 /* One of the transactions is still going on, let's wait for it */
8ba9fd9c 599 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
74b2466e
LP
600 return;
601
ad867662 602 /* One of the transactions is successful, let's use it */
74b2466e 603 if (t->state == DNS_QUERY_SUCCESS) {
ad867662 604 q->received = dns_packet_ref(t->received);
74b2466e
LP
605 dns_query_set_state(q, DNS_QUERY_SUCCESS);
606 return;
607 }
608
ad867662
LP
609 /* One of the transactions has failed, let's see
610 * whether we find anything better, but if not, return
611 * its response packet */
74b2466e 612 if (t->state == DNS_QUERY_FAILURE) {
ad867662 613 received = t->received;
74b2466e 614 state = DNS_QUERY_FAILURE;
ad867662
LP
615 continue;
616 }
74b2466e 617
ad867662 618 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
74b2466e
LP
619 state = t->state;
620 }
621
622 if (state == DNS_QUERY_FAILURE)
ad867662 623 q->received = dns_packet_ref(received);
74b2466e
LP
624
625 dns_query_set_state(q, state);
626}
8ba9fd9c
LP
627
628int dns_query_follow_cname(DnsQuery *q, const char *name) {
629 DnsResourceKey *keys;
630 unsigned i;
631
632 assert(q);
633
634 if (q->n_cname > CNAME_MAX)
635 return -ELOOP;
636
637 keys = new(DnsResourceKey, q->n_keys);
638 if (!keys)
639 return -ENOMEM;
640
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);
645 if (!keys[i].name) {
646
647 for (; i > 0; i--)
648 free(keys[i-1].name);
649 free(keys);
650 return -ENOMEM;
651 }
652 }
653
654 for (i = 0; i < q->n_keys; i++)
655 free(q->keys[i].name);
656 free(q->keys);
657
658 q->keys = keys;
659
660 q->n_cname++;
661
662 dns_query_set_state(q, DNS_QUERY_NULL);
663 return 0;
664}
665
666int dns_query_matches_rr(DnsQuery *q, DnsResourceRecord *rr) {
667 unsigned i;
668 int r;
669
670 assert(q);
671 assert(rr);
672
673 for (i = 0; i < q->n_keys; i++) {
674
675 if (rr->key.class != q->keys[i].class)
676 continue;
677
678 if (rr->key.type != q->keys[i].type &&
679 q->keys[i].type != DNS_TYPE_ANY)
680 continue;
681
682 r = dns_name_equal(rr->key.name, q->keys[i].name);
683 if (r != 0)
684 return r;
685 }
686
687 return 0;
688}
689
690int dns_query_matches_cname(DnsQuery *q, DnsResourceRecord *rr) {
691 unsigned i;
692 int r;
693
694 assert(q);
695 assert(rr);
696
697 for (i = 0; i < q->n_keys; i++) {
698
699 if (rr->key.class != q->keys[i].class)
700 continue;
701
702 if (rr->key.type != DNS_TYPE_CNAME)
703 continue;
704
705 r = dns_name_equal(rr->key.name, q->keys[i].name);
706 if (r != 0)
707 return r;
708 }
709
710 return 0;
711}