]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-stub.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / resolve / resolved-dns-stub.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "fd-util.h"
22 #include "resolved-dns-stub.h"
23 #include "socket-util.h"
24
25 /* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
26 * IP and UDP header sizes */
27 #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
28
29 static int manager_dns_stub_udp_fd(Manager *m);
30 static int manager_dns_stub_tcp_fd(Manager *m);
31
32 static int dns_stub_make_reply_packet(
33 DnsPacket **p,
34 size_t max_size,
35 DnsQuestion *q,
36 DnsAnswer *answer,
37 bool *ret_truncated) {
38
39 bool truncated = false;
40 DnsResourceRecord *rr;
41 unsigned c = 0;
42 int r;
43
44 assert(p);
45
46 /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
47 * roundtrips aren't expensive. */
48
49 if (!*p) {
50 r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size);
51 if (r < 0)
52 return r;
53
54 r = dns_packet_append_question(*p, q);
55 if (r < 0)
56 return r;
57
58 DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q));
59 }
60
61 DNS_ANSWER_FOREACH(rr, answer) {
62
63 r = dns_question_matches_rr(q, rr, NULL);
64 if (r < 0)
65 return r;
66 if (r > 0)
67 goto add;
68
69 r = dns_question_matches_cname_or_dname(q, rr, NULL);
70 if (r < 0)
71 return r;
72 if (r > 0)
73 goto add;
74
75 continue;
76 add:
77 r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
78 if (r == -EMSGSIZE) {
79 truncated = true;
80 break;
81 }
82 if (r < 0)
83 return r;
84
85 c++;
86 }
87
88 if (ret_truncated)
89 *ret_truncated = truncated;
90 else if (truncated)
91 return -EMSGSIZE;
92
93 DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
94
95 return 0;
96 }
97
98 static int dns_stub_finish_reply_packet(
99 DnsPacket *p,
100 uint16_t id,
101 int rcode,
102 bool tc, /* set the Truncated bit? */
103 bool add_opt, /* add an OPT RR to this packet? */
104 bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */
105 bool ad) { /* set the DNSSEC authenticated data bit? */
106
107 int r;
108
109 assert(p);
110
111 if (!add_opt) {
112 /* If the client can't to EDNS0, don't do DO either */
113 edns0_do = false;
114
115 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
116 if (rcode > 0xF)
117 rcode = DNS_RCODE_SERVFAIL;
118 }
119
120 /* Don't set the AD bit unless DO is on, too */
121 if (!edns0_do)
122 ad = false;
123
124 DNS_PACKET_HEADER(p)->id = id;
125
126 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
127 1 /* qr */,
128 0 /* opcode */,
129 0 /* aa */,
130 tc /* tc */,
131 1 /* rd */,
132 1 /* ra */,
133 ad /* ad */,
134 0 /* cd */,
135 rcode));
136
137 if (add_opt) {
138 r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
139 if (r < 0)
140 return r;
141 }
142
143 return 0;
144 }
145
146 static void dns_stub_detach_stream(DnsStream *s) {
147 assert(s);
148
149 s->complete = NULL;
150 s->on_packet = NULL;
151 s->query = NULL;
152 }
153
154 static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) {
155 int r;
156
157 assert(m);
158 assert(p);
159 assert(reply);
160
161 if (s)
162 r = dns_stream_write_packet(s, reply);
163 else {
164 int fd;
165
166 fd = manager_dns_stub_udp_fd(m);
167 if (fd < 0)
168 return log_debug_errno(fd, "Failed to get reply socket: %m");
169
170 /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
171 * is because otherwise the kernel will choose it automatically based on the routing table and will
172 * thus pick 127.0.0.1 rather than 127.0.0.53. */
173
174 r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply);
175 }
176 if (r < 0)
177 return log_debug_errno(r, "Failed to send reply packet: %m");
178
179 return 0;
180 }
181
182 static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) {
183 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
184 int r;
185
186 assert(m);
187 assert(p);
188
189 r = dns_stub_make_reply_packet(&reply, DNS_PACKET_PAYLOAD_SIZE_MAX(p), p->question, NULL, NULL);
190 if (r < 0)
191 return log_debug_errno(r, "Failed to make failure packet: %m");
192
193 r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, false, !!p->opt, DNS_PACKET_DO(p), authenticated);
194 if (r < 0)
195 return log_debug_errno(r, "Failed to build failure packet: %m");
196
197 return dns_stub_send(m, s, p, reply);
198 }
199
200 static void dns_stub_query_complete(DnsQuery *q) {
201 int r;
202
203 assert(q);
204 assert(q->request_dns_packet);
205
206 switch (q->state) {
207
208 case DNS_TRANSACTION_SUCCESS: {
209 bool truncated;
210
211 r = dns_stub_make_reply_packet(&q->reply_dns_packet, DNS_PACKET_PAYLOAD_SIZE_MAX(q->request_dns_packet), q->question_idna, q->answer, &truncated);
212 if (r < 0) {
213 log_debug_errno(r, "Failed to build reply packet: %m");
214 break;
215 }
216
217 r = dns_query_process_cname(q);
218 if (r == -ELOOP) {
219 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
220 break;
221 }
222 if (r < 0) {
223 log_debug_errno(r, "Failed to process CNAME: %m");
224 break;
225 }
226 if (r == DNS_QUERY_RESTARTED)
227 return;
228
229 r = dns_stub_finish_reply_packet(
230 q->reply_dns_packet,
231 DNS_PACKET_ID(q->request_dns_packet),
232 q->answer_rcode,
233 truncated,
234 !!q->request_dns_packet->opt,
235 DNS_PACKET_DO(q->request_dns_packet),
236 dns_query_fully_authenticated(q));
237 if (r < 0) {
238 log_debug_errno(r, "Failed to finish reply packet: %m");
239 break;
240 }
241
242 (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
243 break;
244 }
245
246 case DNS_TRANSACTION_RCODE_FAILURE:
247 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
248 break;
249
250 case DNS_TRANSACTION_NOT_FOUND:
251 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
252 break;
253
254 case DNS_TRANSACTION_TIMEOUT:
255 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
256 /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
257 break;
258
259 case DNS_TRANSACTION_NO_SERVERS:
260 case DNS_TRANSACTION_INVALID_REPLY:
261 case DNS_TRANSACTION_ERRNO:
262 case DNS_TRANSACTION_ABORTED:
263 case DNS_TRANSACTION_DNSSEC_FAILED:
264 case DNS_TRANSACTION_NO_TRUST_ANCHOR:
265 case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
266 case DNS_TRANSACTION_NETWORK_DOWN:
267 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
268 break;
269
270 case DNS_TRANSACTION_NULL:
271 case DNS_TRANSACTION_PENDING:
272 case DNS_TRANSACTION_VALIDATING:
273 default:
274 assert_not_reached("Impossible state");
275 }
276
277 /* If there's a packet to write set, let's leave the stream around */
278 if (q->request_dns_stream && DNS_STREAM_QUEUED(q->request_dns_stream)) {
279
280 /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The
281 * default completion action of the stream will drop the reference. */
282
283 dns_stub_detach_stream(q->request_dns_stream);
284 q->request_dns_stream = NULL;
285 }
286
287 dns_query_free(q);
288 }
289
290 static int dns_stub_stream_complete(DnsStream *s, int error) {
291 assert(s);
292
293 log_debug_errno(error, "DNS TCP connection terminated, destroying query: %m");
294
295 assert(s->query);
296 dns_query_free(s->query);
297
298 return 0;
299 }
300
301 static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
302 DnsQuery *q = NULL;
303 int r;
304
305 assert(m);
306 assert(p);
307 assert(p->protocol == DNS_PROTOCOL_DNS);
308
309 /* Takes ownership of the *s stream object */
310
311 if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
312 in_addr_is_localhost(p->family, &p->destination) <= 0) {
313 log_error("Got packet on unexpected IP range, refusing.");
314 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
315 goto fail;
316 }
317
318 r = dns_packet_extract(p);
319 if (r < 0) {
320 log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
321 dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
322 goto fail;
323 }
324
325 if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
326 log_debug("Got EDNS OPT field with unsupported version number.");
327 dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
328 goto fail;
329 }
330
331 if (dns_type_is_obsolete(p->question->keys[0]->type)) {
332 log_debug("Got message with obsolete key type, refusing.");
333 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
334 goto fail;
335 }
336
337 if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
338 log_debug("Got request for zone transfer, refusing.");
339 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
340 goto fail;
341 }
342
343 if (!DNS_PACKET_RD(p)) {
344 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
345 log_debug("Got request with recursion disabled, refusing.");
346 dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
347 goto fail;
348 }
349
350 if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
351 log_debug("Got request with DNSSEC CD bit set, refusing.");
352 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
353 goto fail;
354 }
355
356 r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
357 if (r < 0) {
358 log_error_errno(r, "Failed to generate query object: %m");
359 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
360 goto fail;
361 }
362
363 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
364 q->clamp_ttl = true;
365
366 q->request_dns_packet = dns_packet_ref(p);
367 q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */
368 q->complete = dns_stub_query_complete;
369
370 if (s) {
371 s->on_packet = NULL;
372 s->complete = dns_stub_stream_complete;
373 s->query = q;
374 }
375
376 r = dns_query_go(q);
377 if (r < 0) {
378 log_error_errno(r, "Failed to start query: %m");
379 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
380 goto fail;
381 }
382
383 log_debug("Processing query...");
384 return;
385
386 fail:
387 if (s && DNS_STREAM_QUEUED(s))
388 dns_stub_detach_stream(s);
389
390 dns_query_free(q);
391 }
392
393 static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
394 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
395 Manager *m = userdata;
396 int r;
397
398 r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
399 if (r <= 0)
400 return r;
401
402 if (dns_packet_validate_query(p) > 0) {
403 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p));
404
405 dns_stub_process_query(m, NULL, p);
406 } else
407 log_debug("Invalid DNS stub UDP packet, ignoring.");
408
409 return 0;
410 }
411
412 static int manager_dns_stub_udp_fd(Manager *m) {
413 static const int one = 1;
414 union sockaddr_union sa = {
415 .in.sin_family = AF_INET,
416 .in.sin_port = htobe16(53),
417 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
418 };
419 _cleanup_close_ int fd = -1;
420 int r;
421
422 if (m->dns_stub_udp_fd >= 0)
423 return m->dns_stub_udp_fd;
424
425 fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
426 if (fd < 0)
427 return -errno;
428
429 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
430 return -errno;
431
432 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
433 return -errno;
434
435 if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
436 return -errno;
437
438 /* Make sure no traffic from outside the local host can leak to onto this socket */
439 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
440 return -errno;
441
442 if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
443 return -errno;
444
445 r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
446 if (r < 0)
447 return r;
448
449 (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
450 m->dns_stub_udp_fd = fd;
451 fd = -1;
452
453 return m->dns_stub_udp_fd;
454 }
455
456 static int on_dns_stub_stream_packet(DnsStream *s) {
457 assert(s);
458 assert(s->read_packet);
459
460 if (dns_packet_validate_query(s->read_packet) > 0) {
461 log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
462
463 dns_stub_process_query(s->manager, s, s->read_packet);
464 } else
465 log_debug("Invalid DNS stub TCP packet, ignoring.");
466
467 /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now,
468 * or that didn't happen in which case we want to free the stream */
469 dns_stream_unref(s);
470
471 return 0;
472 }
473
474 static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
475 DnsStream *stream;
476 Manager *m = userdata;
477 int cfd, r;
478
479 cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
480 if (cfd < 0) {
481 if (IN_SET(errno, EAGAIN, EINTR))
482 return 0;
483
484 return -errno;
485 }
486
487 r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd);
488 if (r < 0) {
489 safe_close(cfd);
490 return r;
491 }
492
493 stream->on_packet = on_dns_stub_stream_packet;
494
495 /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action
496 * of the stream, or by our packet callback, or when the manager is shut down. */
497
498 return 0;
499 }
500
501 static int manager_dns_stub_tcp_fd(Manager *m) {
502 static const int one = 1;
503 union sockaddr_union sa = {
504 .in.sin_family = AF_INET,
505 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
506 .in.sin_port = htobe16(53),
507 };
508 _cleanup_close_ int fd = -1;
509 int r;
510
511 if (m->dns_stub_tcp_fd >= 0)
512 return m->dns_stub_tcp_fd;
513
514 fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
515 if (fd < 0)
516 return -errno;
517
518 if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
519 return -errno;
520
521 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
522 return -errno;
523
524 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
525 return -errno;
526
527 if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
528 return -errno;
529
530 /* Make sure no traffic from outside the local host can leak to onto this socket */
531 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
532 return -errno;
533
534 if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
535 return -errno;
536
537 if (listen(fd, SOMAXCONN) < 0)
538 return -errno;
539
540 r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m);
541 if (r < 0)
542 return r;
543
544 (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
545 m->dns_stub_tcp_fd = fd;
546 fd = -1;
547
548 return m->dns_stub_tcp_fd;
549 }
550
551 int manager_dns_stub_start(Manager *m) {
552 const char *t = "UDP";
553 int r = 0;
554
555 assert(m);
556
557 if (m->dns_stub_listener_mode == DNS_STUB_LISTENER_NO)
558 log_debug("Not creating stub listener.");
559 else
560 log_debug("Creating stub listener using %s.",
561 m->dns_stub_listener_mode == DNS_STUB_LISTENER_UDP ? "UDP" :
562 m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" :
563 "UDP/TCP");
564
565 if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP))
566 r = manager_dns_stub_udp_fd(m);
567
568 if (r >= 0 &&
569 IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) {
570 t = "TCP";
571 r = manager_dns_stub_tcp_fd(m);
572 }
573
574 if (IN_SET(r, -EADDRINUSE, -EPERM)) {
575 if (r == -EADDRINUSE)
576 log_warning_errno(r,
577 "Another process is already listening on %s socket 127.0.0.53:53.\n"
578 "Turning off local DNS stub support.", t);
579 else
580 log_warning_errno(r,
581 "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
582 "Turning off local DNS stub support.", t);
583 manager_dns_stub_stop(m);
584 } else if (r < 0)
585 return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
586
587 return 0;
588 }
589
590 void manager_dns_stub_stop(Manager *m) {
591 assert(m);
592
593 m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source);
594 m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source);
595
596 m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
597 m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
598 }