1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Lennart Poettering
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.
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.
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/>.
22 #include "resolved-dns-stub.h"
23 #include "socket-util.h"
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)
29 static int manager_dns_stub_udp_fd(Manager
*m
);
30 static int manager_dns_stub_tcp_fd(Manager
*m
);
32 static int dns_stub_make_reply_packet(
37 bool *ret_truncated
) {
39 bool truncated
= false;
40 DnsResourceRecord
*rr
;
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. */
50 r
= dns_packet_new(p
, DNS_PROTOCOL_DNS
, 0, max_size
);
54 r
= dns_packet_append_question(*p
, q
);
58 DNS_PACKET_HEADER(*p
)->qdcount
= htobe16(dns_question_size(q
));
61 DNS_ANSWER_FOREACH(rr
, answer
) {
63 r
= dns_question_matches_rr(q
, rr
, NULL
);
69 r
= dns_question_matches_cname_or_dname(q
, rr
, NULL
);
77 r
= dns_packet_append_rr(*p
, rr
, 0, NULL
, NULL
);
89 *ret_truncated
= truncated
;
93 DNS_PACKET_HEADER(*p
)->ancount
= htobe16(be16toh(DNS_PACKET_HEADER(*p
)->ancount
) + c
);
98 static int dns_stub_finish_reply_packet(
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? */
112 /* If the client can't to EDNS0, don't do DO either */
115 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
117 rcode
= DNS_RCODE_SERVFAIL
;
120 /* Don't set the AD bit unless DO is on, too */
124 DNS_PACKET_HEADER(p
)->id
= id
;
126 DNS_PACKET_HEADER(p
)->flags
= htobe16(DNS_PACKET_MAKE_FLAGS(
138 r
= dns_packet_append_opt(p
, ADVERTISE_DATAGRAM_SIZE_MAX
, edns0_do
, rcode
, NULL
);
146 static void dns_stub_detach_stream(DnsStream
*s
) {
154 static int dns_stub_send(Manager
*m
, DnsStream
*s
, DnsPacket
*p
, DnsPacket
*reply
) {
162 r
= dns_stream_write_packet(s
, reply
);
166 fd
= manager_dns_stub_udp_fd(m
);
168 return log_debug_errno(fd
, "Failed to get reply socket: %m");
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. */
174 r
= manager_send(m
, fd
, LOOPBACK_IFINDEX
, p
->family
, &p
->sender
, p
->sender_port
, &p
->destination
, reply
);
177 return log_debug_errno(r
, "Failed to send reply packet: %m");
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
;
189 r
= dns_stub_make_reply_packet(&reply
, DNS_PACKET_PAYLOAD_SIZE_MAX(p
), p
->question
, NULL
, NULL
);
191 return log_debug_errno(r
, "Failed to make failure packet: %m");
193 r
= dns_stub_finish_reply_packet(reply
, DNS_PACKET_ID(p
), rcode
, false, !!p
->opt
, DNS_PACKET_DO(p
), authenticated
);
195 return log_debug_errno(r
, "Failed to build failure packet: %m");
197 return dns_stub_send(m
, s
, p
, reply
);
200 static void dns_stub_query_complete(DnsQuery
*q
) {
204 assert(q
->request_dns_packet
);
208 case DNS_TRANSACTION_SUCCESS
: {
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
);
213 log_debug_errno(r
, "Failed to build reply packet: %m");
217 r
= dns_query_process_cname(q
);
219 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
, false);
223 log_debug_errno(r
, "Failed to process CNAME: %m");
226 if (r
== DNS_QUERY_RESTARTED
)
229 r
= dns_stub_finish_reply_packet(
231 DNS_PACKET_ID(q
->request_dns_packet
),
234 !!q
->request_dns_packet
->opt
,
235 DNS_PACKET_DO(q
->request_dns_packet
),
236 dns_query_fully_authenticated(q
));
238 log_debug_errno(r
, "Failed to finish reply packet: %m");
242 (void) dns_stub_send(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, q
->reply_dns_packet
);
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
));
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
));
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 */
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);
270 case DNS_TRANSACTION_NULL
:
271 case DNS_TRANSACTION_PENDING
:
272 case DNS_TRANSACTION_VALIDATING
:
274 assert_not_reached("Impossible state");
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
)) {
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. */
283 dns_stub_detach_stream(q
->request_dns_stream
);
284 q
->request_dns_stream
= NULL
;
290 static int dns_stub_stream_complete(DnsStream
*s
, int error
) {
293 log_debug_errno(error
, "DNS TCP connection terminated, destroying query: %m");
296 dns_query_free(s
->query
);
301 static void dns_stub_process_query(Manager
*m
, DnsStream
*s
, DnsPacket
*p
) {
307 assert(p
->protocol
== DNS_PROTOCOL_DNS
);
309 /* Takes ownership of the *s stream object */
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);
318 r
= dns_packet_extract(p
);
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);
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);
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);
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);
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);
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);
356 r
= dns_query_new(m
, &q
, p
->question
, p
->question
, 0, SD_RESOLVED_PROTOCOLS_ALL
|SD_RESOLVED_NO_SEARCH
);
358 log_error_errno(r
, "Failed to generate query object: %m");
359 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
, false);
363 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
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
;
372 s
->complete
= dns_stub_stream_complete
;
378 log_error_errno(r
, "Failed to start query: %m");
379 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
, false);
383 log_debug("Processing query...");
387 if (s
&& DNS_STREAM_QUEUED(s
))
388 dns_stub_detach_stream(s
);
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
;
398 r
= manager_recv(m
, fd
, DNS_PROTOCOL_DNS
, &p
);
402 if (dns_packet_validate_query(p
) > 0) {
403 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p
));
405 dns_stub_process_query(m
, NULL
, p
);
407 log_debug("Invalid DNS stub UDP packet, ignoring.");
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
),
419 _cleanup_close_
int fd
= -1;
422 if (m
->dns_stub_udp_fd
>= 0)
423 return m
->dns_stub_udp_fd
;
425 fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
429 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof one
) < 0)
432 if (setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof one
) < 0)
435 if (setsockopt(fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof one
) < 0)
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)
442 if (bind(fd
, &sa
.sa
, sizeof(sa
.in
)) < 0)
445 r
= sd_event_add_io(m
->event
, &m
->dns_stub_udp_event_source
, fd
, EPOLLIN
, on_dns_stub_packet
, m
);
449 (void) sd_event_source_set_description(m
->dns_stub_udp_event_source
, "dns-stub-udp");
450 m
->dns_stub_udp_fd
= fd
;
453 return m
->dns_stub_udp_fd
;
456 static int on_dns_stub_stream_packet(DnsStream
*s
) {
458 assert(s
->read_packet
);
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
));
463 dns_stub_process_query(s
->manager
, s
, s
->read_packet
);
465 log_debug("Invalid DNS stub TCP packet, ignoring.");
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 */
474 static int on_dns_stub_stream(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
476 Manager
*m
= userdata
;
479 cfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
481 if (IN_SET(errno
, EAGAIN
, EINTR
))
487 r
= dns_stream_new(m
, &stream
, DNS_PROTOCOL_DNS
, cfd
);
493 stream
->on_packet
= on_dns_stub_stream_packet
;
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. */
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),
508 _cleanup_close_
int fd
= -1;
511 if (m
->dns_stub_tcp_fd
>= 0)
512 return m
->dns_stub_tcp_fd
;
514 fd
= socket(AF_INET
, SOCK_STREAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
518 if (setsockopt(fd
, IPPROTO_IP
, IP_TTL
, &one
, sizeof one
) < 0)
521 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof one
) < 0)
524 if (setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof one
) < 0)
527 if (setsockopt(fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof one
) < 0)
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)
534 if (bind(fd
, &sa
.sa
, sizeof(sa
.in
)) < 0)
537 if (listen(fd
, SOMAXCONN
) < 0)
540 r
= sd_event_add_io(m
->event
, &m
->dns_stub_tcp_event_source
, fd
, EPOLLIN
, on_dns_stub_stream
, m
);
544 (void) sd_event_source_set_description(m
->dns_stub_tcp_event_source
, "dns-stub-tcp");
545 m
->dns_stub_tcp_fd
= fd
;
548 return m
->dns_stub_tcp_fd
;
551 int manager_dns_stub_start(Manager
*m
) {
552 const char *t
= "UDP";
557 if (m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_NO
)
558 log_debug("Not creating stub listener.");
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" :
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
);
569 IN_SET(m
->dns_stub_listener_mode
, DNS_STUB_LISTENER_YES
, DNS_STUB_LISTENER_TCP
)) {
571 r
= manager_dns_stub_tcp_fd(m
);
574 if (IN_SET(r
, -EADDRINUSE
, -EPERM
)) {
575 if (r
== -EADDRINUSE
)
577 "Another process is already listening on %s socket 127.0.0.53:53.\n"
578 "Turning off local DNS stub support.", t
);
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
);
585 return log_error_errno(r
, "Failed to listen on %s socket 127.0.0.53:53: %m", t
);
590 void manager_dns_stub_stop(Manager
*m
) {
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
);
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
);