]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-transaction.c
update TODO
[thirdparty/systemd.git] / src / resolve / resolved-dns-transaction.c
CommitLineData
ec2c5e43
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 "af-list.h"
23
24#include "resolved-dns-transaction.h"
25
26DnsTransaction* dns_transaction_free(DnsTransaction *t) {
27 DnsQuery *q;
28 DnsZoneItem *i;
29
30 if (!t)
31 return NULL;
32
33 sd_event_source_unref(t->timeout_event_source);
34
35 dns_question_unref(t->question);
36 dns_packet_unref(t->sent);
37 dns_packet_unref(t->received);
38 dns_answer_unref(t->cached);
39
40 dns_stream_free(t->stream);
41
42 if (t->scope) {
43 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
44
45 if (t->id != 0)
46 hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
47 }
48
49 while ((q = set_steal_first(t->queries)))
50 set_remove(q->transactions, t);
51 set_free(t->queries);
52
53 while ((i = set_steal_first(t->zone_items)))
54 i->probe_transaction = NULL;
55 set_free(t->zone_items);
56
57 free(t);
58 return NULL;
59}
60
61DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
62
63void dns_transaction_gc(DnsTransaction *t) {
64 assert(t);
65
66 if (t->block_gc > 0)
67 return;
68
69 if (set_isempty(t->queries) && set_isempty(t->zone_items))
70 dns_transaction_free(t);
71}
72
73int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
74 _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
75 int r;
76
77 assert(ret);
78 assert(s);
79 assert(q);
80
81 r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL, NULL);
82 if (r < 0)
83 return r;
84
85 t = new0(DnsTransaction, 1);
86 if (!t)
87 return -ENOMEM;
88
89 t->question = dns_question_ref(q);
90
91 do
92 random_bytes(&t->id, sizeof(t->id));
93 while (t->id == 0 ||
94 hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
95
96 r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
97 if (r < 0) {
98 t->id = 0;
99 return r;
100 }
101
102 LIST_PREPEND(transactions_by_scope, s->transactions, t);
103 t->scope = s;
104
105 if (ret)
106 *ret = t;
107
108 t = NULL;
109
110 return 0;
111}
112
113static void dns_transaction_stop(DnsTransaction *t) {
114 assert(t);
115
116 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
117 t->stream = dns_stream_free(t->stream);
118}
119
120static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
121 DnsZoneItem *z;
122 Iterator i;
123
124 assert(t);
125 assert(p);
126
127 if (manager_our_packet(t->scope->manager, p) != 0)
128 return;
129
130 log_debug("Transaction on scope %s on %s/%s got tentative packet",
131 dns_protocol_to_string(t->scope->protocol),
132 t->scope->link ? t->scope->link->name : "*",
133 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
134
a4076574
LP
135 /* RFC 4795, Section 4.1 says that the peer with the
136 * lexicographically smaller IP address loses */
137 if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) < 0) {
138 log_debug("Peer has lexicographically smaller IP address and thus lost in the conflict.");
139 return;
140 }
141
142 log_debug("We have the lexicographically smaller IP address and thus lost in the conflict.");
143
ec2c5e43
LP
144 t->block_gc++;
145 SET_FOREACH(z, t->zone_items, i)
146 dns_zone_item_conflict(z);
147 t->block_gc--;
148
149 dns_transaction_gc(t);
150}
151
152void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
153 DnsQuery *q;
154 DnsZoneItem *z;
155 Iterator i;
156
157 assert(t);
158 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
e56187ca
LP
159
160 if (!IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING))
161 return;
ec2c5e43
LP
162
163 /* Note that this call might invalidate the query. Callers
164 * should hence not attempt to access the query or transaction
165 * after calling this function. */
166
167 log_debug("Transaction on scope %s on %s/%s now complete with <%s>",
168 dns_protocol_to_string(t->scope->protocol),
169 t->scope->link ? t->scope->link->name : "*",
170 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
171 dns_transaction_state_to_string(state));
172
173 t->state = state;
174
175 dns_transaction_stop(t);
176
177 /* Notify all queries that are interested, but make sure the
178 * transaction isn't freed while we are still looking at it */
179 t->block_gc++;
180 SET_FOREACH(q, t->queries, i)
181 dns_query_ready(q);
182 SET_FOREACH(z, t->zone_items, i)
183 dns_zone_item_ready(z);
184 t->block_gc--;
185
186 dns_transaction_gc(t);
187}
188
189static int on_stream_complete(DnsStream *s, int error) {
190 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
191 DnsTransaction *t;
192
193 assert(s);
194 assert(s->transaction);
195
196 /* Copy the data we care about out of the stream before we
197 * destroy it. */
198 t = s->transaction;
199 p = dns_packet_ref(s->read_packet);
200
201 t->stream = dns_stream_free(t->stream);
202
203 if (error != 0) {
204 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
205 return 0;
206 }
207
a4076574
LP
208 if (dns_packet_validate_reply(p) <= 0) {
209 log_debug("Invalid LLMNR TCP packet.");
210 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
211 return 0;
212 }
213
214 dns_scope_check_conflicts(t->scope, p);
215
ec2c5e43
LP
216 t->block_gc++;
217 dns_transaction_process_reply(t, p);
218 t->block_gc--;
219
220 /* If the response wasn't useful, then complete the transition now */
221 if (t->state == DNS_TRANSACTION_PENDING)
222 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
223
224 return 0;
225}
226
227static int dns_transaction_open_tcp(DnsTransaction *t) {
228 _cleanup_close_ int fd = -1;
229 int r;
230
231 assert(t);
232
233 if (t->stream)
234 return 0;
235
236 if (t->scope->protocol == DNS_PROTOCOL_DNS)
237 fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53);
238 else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
239
240 /* When we already received a query to this (but it was truncated), send to its sender address */
241 if (t->received)
242 fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
243 else {
244 union in_addr_union address;
245 int family;
246
247 /* Otherwise, try to talk to the owner of a
248 * the IP address, in case this is a reverse
249 * PTR lookup */
250 r = dns_question_extract_reverse_address(t->question, &family, &address);
251 if (r < 0)
252 return r;
253 if (r == 0)
254 return -EINVAL;
255
256 fd = dns_scope_tcp_socket(t->scope, family, &address, 5355);
257 }
258 } else
259 return -EAFNOSUPPORT;
260
261 if (fd < 0)
262 return fd;
263
264 r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
265 if (r < 0)
266 return r;
267
268 fd = -1;
269
270 r = dns_stream_write_packet(t->stream, t->sent);
271 if (r < 0) {
272 t->stream = dns_stream_free(t->stream);
273 return r;
274 }
275
276 t->received = dns_packet_unref(t->received);
277 t->stream->complete = on_stream_complete;
278 t->stream->transaction = t;
279
280 /* The interface index is difficult to determine if we are
281 * connecting to the local host, hence fill this in right away
282 * instead of determining it from the socket */
283 if (t->scope->link)
284 t->stream->ifindex = t->scope->link->ifindex;
285
286 return 0;
287}
288
289void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
290 int r;
291
292 assert(t);
293 assert(p);
294 assert(t->state == DNS_TRANSACTION_PENDING);
295
296 /* Note that this call might invalidate the query. Callers
297 * should hence not attempt to access the query or transaction
298 * after calling this function. */
299
300 if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
301 assert(t->scope->link);
302
303 /* For LLMNR we will not accept any packets from other
304 * interfaces */
305
306 if (p->ifindex != t->scope->link->ifindex)
307 return;
308
309 if (p->family != t->scope->family)
310 return;
311
312 /* Tentative packets are not full responses but still
313 * useful for identifying uniqueness conflicts during
314 * probing. */
315 if (DNS_PACKET_T(p)) {
316 dns_transaction_tentative(t, p);
317 return;
318 }
319 }
320
321 if (t->scope->protocol == DNS_PROTOCOL_DNS) {
322
323 /* For DNS we are fine with accepting packets on any
324 * interface, but the source IP address must be one of
325 * a valid DNS server */
326
327 if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender))
328 return;
329
330 if (p->sender_port != 53)
331 return;
332 }
333
334 if (t->received != p) {
335 dns_packet_unref(t->received);
336 t->received = dns_packet_ref(p);
337 }
338
339 if (p->ipproto == IPPROTO_TCP) {
340 if (DNS_PACKET_TC(p)) {
341 /* Truncated via TCP? Somebody must be fucking with us */
342 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
343 return;
344 }
345
346 if (DNS_PACKET_ID(p) != t->id) {
347 /* Not the reply to our query? Somebody must be fucking with us */
348 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
349 return;
350 }
351 }
352
353 if (DNS_PACKET_TC(p)) {
354 /* Response was truncated, let's try again with good old TCP */
355 r = dns_transaction_open_tcp(t);
356 if (r == -ESRCH) {
357 /* No servers found? Damn! */
358 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
359 return;
360 }
361 if (r < 0) {
362 /* On LLMNR, if we cannot connect to the host,
363 * we immediately give up */
364 if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
365 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
366 return;
367 }
368
369 /* On DNS, couldn't send? Try immediately again, with a new server */
370 dns_scope_next_dns_server(t->scope);
371
372 r = dns_transaction_go(t);
373 if (r < 0) {
374 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
375 return;
376 }
377
378 return;
379 }
380 }
381
382 /* Parse and update the cache */
383 r = dns_packet_extract(p);
384 if (r < 0) {
385 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
386 return;
387 }
388
389 /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
a4076574 390 dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
ec2c5e43
LP
391
392 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
393 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
394 else
395 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
396}
397
398static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
399 DnsTransaction *t = userdata;
400 int r;
401
402 assert(s);
403 assert(t);
404
405 /* Timeout reached? Try again, with a new server */
406 dns_scope_next_dns_server(t->scope);
407
408 r = dns_transaction_go(t);
409 if (r < 0)
410 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
411
412 return 0;
413}
414
415static int dns_transaction_make_packet(DnsTransaction *t) {
416 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
417 unsigned n, added = 0;
418 int r;
419
420 assert(t);
421
422 if (t->sent)
423 return 0;
424
425 r = dns_packet_new_query(&p, t->scope->protocol, 0);
426 if (r < 0)
427 return r;
428
429 for (n = 0; n < t->question->n_keys; n++) {
430 r = dns_scope_good_key(t->scope, t->question->keys[n]);
431 if (r < 0)
432 return r;
433 if (r == 0)
434 continue;
435
436 r = dns_packet_append_key(p, t->question->keys[n], NULL);
437 if (r < 0)
438 return r;
439
440 added++;
441 }
442
443 if (added <= 0)
444 return -EDOM;
445
446 DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
447 DNS_PACKET_HEADER(p)->id = t->id;
448
449 t->sent = p;
450 p = NULL;
451
452 return 0;
453}
454
455int dns_transaction_go(DnsTransaction *t) {
456 bool had_stream;
457 int r;
458
459 assert(t);
460
461 had_stream = !!t->stream;
462
463 dns_transaction_stop(t);
464
e56187ca 465 log_debug("Excercising transaction on scope %s on %s/%s",
ec2c5e43
LP
466 dns_protocol_to_string(t->scope->protocol),
467 t->scope->link ? t->scope->link->name : "*",
468 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
469
470 if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
471 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
472 return 0;
473 }
474
475 if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
476 /* If we already tried via a stream, then we don't
477 * retry on LLMNR. See RFC 4795, Section 2.7. */
478 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
479 return 0;
480 }
481
482 t->n_attempts++;
483 t->received = dns_packet_unref(t->received);
484 t->cached = dns_answer_unref(t->cached);
485 t->cached_rcode = 0;
486
4d926a69
LP
487 /* Check the cache, but only if this transaction is not used
488 * for probing or verifying a zone item. */
489 if (set_isempty(t->zone_items)) {
2c27fbca 490
4d926a69
LP
491 /* Before trying the cache, let's make sure we figured out a
492 * server to use. Should this cause a change of server this
493 * might flush the cache. */
494 dns_scope_get_dns_server(t->scope);
2c27fbca 495
4d926a69
LP
496 /* Let's then prune all outdated entries */
497 dns_cache_prune(&t->scope->cache);
498
499 r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
500 if (r < 0)
501 return r;
502 if (r > 0) {
503 log_debug("Cache hit!");
504 if (t->cached_rcode == DNS_RCODE_SUCCESS)
505 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
506 else
507 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
508 return 0;
509 }
ec2c5e43
LP
510 }
511
6e068472
LP
512 if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) {
513 usec_t jitter;
514
515 /* RFC 4795 Section 2.7 suggests all queries should be
516 * delayed by a random time from 0 to JITTER_INTERVAL. */
517
518 t->initial_jitter = true;
519
520 random_bytes(&jitter, sizeof(jitter));
521 jitter %= LLMNR_JITTER_INTERVAL_USEC;
522
523 r = sd_event_add_time(
524 t->scope->manager->event,
525 &t->timeout_event_source,
526 clock_boottime_or_monotonic(),
a4076574
LP
527 now(clock_boottime_or_monotonic()) + jitter,
528 LLMNR_JITTER_INTERVAL_USEC,
6e068472
LP
529 on_transaction_timeout, t);
530 if (r < 0)
531 return r;
532
533 t->n_attempts = 0;
534 t->state = DNS_TRANSACTION_PENDING;
535
536 log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter);
537 return 0;
538 }
539
2c27fbca
LP
540 log_debug("Cache miss!");
541
ec2c5e43
LP
542 /* Otherwise, we need to ask the network */
543 r = dns_transaction_make_packet(t);
544 if (r == -EDOM) {
545 /* Not the right request to make on this network?
546 * (i.e. an A request made on IPv6 or an AAAA request
547 * made on IPv4, on LLMNR or mDNS.) */
548 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
549 return 0;
550 }
551 if (r < 0)
552 return r;
553
554 if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
555 (dns_question_endswith(t->question, "in-addr.arpa") > 0 ||
556 dns_question_endswith(t->question, "ip6.arpa") > 0)) {
557
558 /* RFC 4795, Section 2.4. says reverse lookups shall
559 * always be made via TCP on LLMNR */
560 r = dns_transaction_open_tcp(t);
561 } else {
562 /* Try via UDP, and if that fails due to large size try via TCP */
a4076574 563 r = dns_scope_emit(t->scope, t->sent);
ec2c5e43
LP
564 if (r == -EMSGSIZE)
565 r = dns_transaction_open_tcp(t);
566 }
567 if (r == -ESRCH) {
568 /* No servers to send this to? */
569 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
570 return 0;
571 }
572 if (r < 0) {
13b551ac
LP
573 if (t->scope->protocol != DNS_PROTOCOL_DNS) {
574 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
575 return 0;
576 }
577
ec2c5e43
LP
578 /* Couldn't send? Try immediately again, with a new server */
579 dns_scope_next_dns_server(t->scope);
580
581 return dns_transaction_go(t);
582 }
583
9a015429
LP
584 r = sd_event_add_time(
585 t->scope->manager->event,
586 &t->timeout_event_source,
587 clock_boottime_or_monotonic(),
588 now(clock_boottime_or_monotonic()) + TRANSACTION_TIMEOUT_USEC(t->scope->protocol), 0,
589 on_transaction_timeout, t);
ec2c5e43
LP
590 if (r < 0)
591 return r;
592
593 t->state = DNS_TRANSACTION_PENDING;
594 return 1;
595}
596
597static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
598 [DNS_TRANSACTION_NULL] = "null",
599 [DNS_TRANSACTION_PENDING] = "pending",
600 [DNS_TRANSACTION_FAILURE] = "failure",
601 [DNS_TRANSACTION_SUCCESS] = "success",
602 [DNS_TRANSACTION_NO_SERVERS] = "no-servers",
603 [DNS_TRANSACTION_TIMEOUT] = "timeout",
604 [DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
605 [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
606 [DNS_TRANSACTION_RESOURCES] = "resources",
607 [DNS_TRANSACTION_ABORTED] = "aborted",
608};
609DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);