]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-transaction.c
resolved: read the system /etc/resolv.conf unless we wrote it ourselves
[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
135 t->block_gc++;
136 SET_FOREACH(z, t->zone_items, i)
137 dns_zone_item_conflict(z);
138 t->block_gc--;
139
140 dns_transaction_gc(t);
141}
142
143void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
144 DnsQuery *q;
145 DnsZoneItem *z;
146 Iterator i;
147
148 assert(t);
149 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
150 assert(IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
151
152 /* Note that this call might invalidate the query. Callers
153 * should hence not attempt to access the query or transaction
154 * after calling this function. */
155
156 log_debug("Transaction on scope %s on %s/%s now complete with <%s>",
157 dns_protocol_to_string(t->scope->protocol),
158 t->scope->link ? t->scope->link->name : "*",
159 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
160 dns_transaction_state_to_string(state));
161
162 t->state = state;
163
164 dns_transaction_stop(t);
165
166 /* Notify all queries that are interested, but make sure the
167 * transaction isn't freed while we are still looking at it */
168 t->block_gc++;
169 SET_FOREACH(q, t->queries, i)
170 dns_query_ready(q);
171 SET_FOREACH(z, t->zone_items, i)
172 dns_zone_item_ready(z);
173 t->block_gc--;
174
175 dns_transaction_gc(t);
176}
177
178static int on_stream_complete(DnsStream *s, int error) {
179 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
180 DnsTransaction *t;
181
182 assert(s);
183 assert(s->transaction);
184
185 /* Copy the data we care about out of the stream before we
186 * destroy it. */
187 t = s->transaction;
188 p = dns_packet_ref(s->read_packet);
189
190 t->stream = dns_stream_free(t->stream);
191
192 if (error != 0) {
193 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
194 return 0;
195 }
196
197 t->block_gc++;
198 dns_transaction_process_reply(t, p);
199 t->block_gc--;
200
201 /* If the response wasn't useful, then complete the transition now */
202 if (t->state == DNS_TRANSACTION_PENDING)
203 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
204
205 return 0;
206}
207
208static int dns_transaction_open_tcp(DnsTransaction *t) {
209 _cleanup_close_ int fd = -1;
210 int r;
211
212 assert(t);
213
214 if (t->stream)
215 return 0;
216
217 if (t->scope->protocol == DNS_PROTOCOL_DNS)
218 fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53);
219 else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
220
221 /* When we already received a query to this (but it was truncated), send to its sender address */
222 if (t->received)
223 fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
224 else {
225 union in_addr_union address;
226 int family;
227
228 /* Otherwise, try to talk to the owner of a
229 * the IP address, in case this is a reverse
230 * PTR lookup */
231 r = dns_question_extract_reverse_address(t->question, &family, &address);
232 if (r < 0)
233 return r;
234 if (r == 0)
235 return -EINVAL;
236
237 fd = dns_scope_tcp_socket(t->scope, family, &address, 5355);
238 }
239 } else
240 return -EAFNOSUPPORT;
241
242 if (fd < 0)
243 return fd;
244
245 r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
246 if (r < 0)
247 return r;
248
249 fd = -1;
250
251 r = dns_stream_write_packet(t->stream, t->sent);
252 if (r < 0) {
253 t->stream = dns_stream_free(t->stream);
254 return r;
255 }
256
257 t->received = dns_packet_unref(t->received);
258 t->stream->complete = on_stream_complete;
259 t->stream->transaction = t;
260
261 /* The interface index is difficult to determine if we are
262 * connecting to the local host, hence fill this in right away
263 * instead of determining it from the socket */
264 if (t->scope->link)
265 t->stream->ifindex = t->scope->link->ifindex;
266
267 return 0;
268}
269
270void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
271 int r;
272
273 assert(t);
274 assert(p);
275 assert(t->state == DNS_TRANSACTION_PENDING);
276
277 /* Note that this call might invalidate the query. Callers
278 * should hence not attempt to access the query or transaction
279 * after calling this function. */
280
281 if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
282 assert(t->scope->link);
283
284 /* For LLMNR we will not accept any packets from other
285 * interfaces */
286
287 if (p->ifindex != t->scope->link->ifindex)
288 return;
289
290 if (p->family != t->scope->family)
291 return;
292
293 /* Tentative packets are not full responses but still
294 * useful for identifying uniqueness conflicts during
295 * probing. */
296 if (DNS_PACKET_T(p)) {
297 dns_transaction_tentative(t, p);
298 return;
299 }
300 }
301
302 if (t->scope->protocol == DNS_PROTOCOL_DNS) {
303
304 /* For DNS we are fine with accepting packets on any
305 * interface, but the source IP address must be one of
306 * a valid DNS server */
307
308 if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender))
309 return;
310
311 if (p->sender_port != 53)
312 return;
313 }
314
315 if (t->received != p) {
316 dns_packet_unref(t->received);
317 t->received = dns_packet_ref(p);
318 }
319
320 if (p->ipproto == IPPROTO_TCP) {
321 if (DNS_PACKET_TC(p)) {
322 /* Truncated via TCP? Somebody must be fucking with us */
323 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
324 return;
325 }
326
327 if (DNS_PACKET_ID(p) != t->id) {
328 /* Not the reply to our query? Somebody must be fucking with us */
329 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
330 return;
331 }
332 }
333
334 if (DNS_PACKET_TC(p)) {
335 /* Response was truncated, let's try again with good old TCP */
336 r = dns_transaction_open_tcp(t);
337 if (r == -ESRCH) {
338 /* No servers found? Damn! */
339 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
340 return;
341 }
342 if (r < 0) {
343 /* On LLMNR, if we cannot connect to the host,
344 * we immediately give up */
345 if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
346 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
347 return;
348 }
349
350 /* On DNS, couldn't send? Try immediately again, with a new server */
351 dns_scope_next_dns_server(t->scope);
352
353 r = dns_transaction_go(t);
354 if (r < 0) {
355 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
356 return;
357 }
358
359 return;
360 }
361 }
362
363 /* Parse and update the cache */
364 r = dns_packet_extract(p);
365 if (r < 0) {
366 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
367 return;
368 }
369
370 /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
371 dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0);
372
373 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
374 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
375 else
376 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
377}
378
379static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
380 DnsTransaction *t = userdata;
381 int r;
382
383 assert(s);
384 assert(t);
385
386 /* Timeout reached? Try again, with a new server */
387 dns_scope_next_dns_server(t->scope);
388
389 r = dns_transaction_go(t);
390 if (r < 0)
391 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
392
393 return 0;
394}
395
396static int dns_transaction_make_packet(DnsTransaction *t) {
397 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
398 unsigned n, added = 0;
399 int r;
400
401 assert(t);
402
403 if (t->sent)
404 return 0;
405
406 r = dns_packet_new_query(&p, t->scope->protocol, 0);
407 if (r < 0)
408 return r;
409
410 for (n = 0; n < t->question->n_keys; n++) {
411 r = dns_scope_good_key(t->scope, t->question->keys[n]);
412 if (r < 0)
413 return r;
414 if (r == 0)
415 continue;
416
417 r = dns_packet_append_key(p, t->question->keys[n], NULL);
418 if (r < 0)
419 return r;
420
421 added++;
422 }
423
424 if (added <= 0)
425 return -EDOM;
426
427 DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
428 DNS_PACKET_HEADER(p)->id = t->id;
429
430 t->sent = p;
431 p = NULL;
432
433 return 0;
434}
435
436int dns_transaction_go(DnsTransaction *t) {
437 bool had_stream;
438 int r;
439
440 assert(t);
441
442 had_stream = !!t->stream;
443
444 dns_transaction_stop(t);
445
446 log_debug("Beginning transaction on scope %s on %s/%s",
447 dns_protocol_to_string(t->scope->protocol),
448 t->scope->link ? t->scope->link->name : "*",
449 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
450
451 if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
452 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
453 return 0;
454 }
455
456 if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
457 /* If we already tried via a stream, then we don't
458 * retry on LLMNR. See RFC 4795, Section 2.7. */
459 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
460 return 0;
461 }
462
463 t->n_attempts++;
464 t->received = dns_packet_unref(t->received);
465 t->cached = dns_answer_unref(t->cached);
466 t->cached_rcode = 0;
467
468 /* First, let's try the cache */
469 dns_cache_prune(&t->scope->cache);
470 r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
471 if (r < 0)
472 return r;
473 if (r > 0) {
474 if (t->cached_rcode == DNS_RCODE_SUCCESS)
475 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
476 else
477 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
478 return 0;
479 }
480
481 /* Otherwise, we need to ask the network */
482 r = dns_transaction_make_packet(t);
483 if (r == -EDOM) {
484 /* Not the right request to make on this network?
485 * (i.e. an A request made on IPv6 or an AAAA request
486 * made on IPv4, on LLMNR or mDNS.) */
487 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
488 return 0;
489 }
490 if (r < 0)
491 return r;
492
493 if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
494 (dns_question_endswith(t->question, "in-addr.arpa") > 0 ||
495 dns_question_endswith(t->question, "ip6.arpa") > 0)) {
496
497 /* RFC 4795, Section 2.4. says reverse lookups shall
498 * always be made via TCP on LLMNR */
499 r = dns_transaction_open_tcp(t);
500 } else {
501 /* Try via UDP, and if that fails due to large size try via TCP */
502 r = dns_scope_send(t->scope, t->sent);
503 if (r == -EMSGSIZE)
504 r = dns_transaction_open_tcp(t);
505 }
506 if (r == -ESRCH) {
507 /* No servers to send this to? */
508 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
509 return 0;
510 }
511 if (r < 0) {
512 /* Couldn't send? Try immediately again, with a new server */
513 dns_scope_next_dns_server(t->scope);
514
515 return dns_transaction_go(t);
516 }
517
9a015429
LP
518 r = sd_event_add_time(
519 t->scope->manager->event,
520 &t->timeout_event_source,
521 clock_boottime_or_monotonic(),
522 now(clock_boottime_or_monotonic()) + TRANSACTION_TIMEOUT_USEC(t->scope->protocol), 0,
523 on_transaction_timeout, t);
ec2c5e43
LP
524 if (r < 0)
525 return r;
526
527 t->state = DNS_TRANSACTION_PENDING;
528 return 1;
529}
530
531static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
532 [DNS_TRANSACTION_NULL] = "null",
533 [DNS_TRANSACTION_PENDING] = "pending",
534 [DNS_TRANSACTION_FAILURE] = "failure",
535 [DNS_TRANSACTION_SUCCESS] = "success",
536 [DNS_TRANSACTION_NO_SERVERS] = "no-servers",
537 [DNS_TRANSACTION_TIMEOUT] = "timeout",
538 [DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
539 [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
540 [DNS_TRANSACTION_RESOURCES] = "resources",
541 [DNS_TRANSACTION_ABORTED] = "aborted",
542};
543DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);