1 /* SPDX-License-Identifier: LGPL-2.1+ */
4 #include "missing_network.h"
5 #include "resolved-dns-stub.h"
6 #include "socket-util.h"
8 /* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
9 * IP and UDP header sizes */
10 #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
12 static int manager_dns_stub_udp_fd(Manager
*m
);
13 static int manager_dns_stub_tcp_fd(Manager
*m
);
15 static int dns_stub_make_reply_packet(
20 bool *ret_truncated
) {
22 bool truncated
= false;
23 DnsResourceRecord
*rr
;
29 /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
30 * roundtrips aren't expensive. */
33 r
= dns_packet_new(p
, DNS_PROTOCOL_DNS
, 0, max_size
);
37 r
= dns_packet_append_question(*p
, q
);
41 DNS_PACKET_HEADER(*p
)->qdcount
= htobe16(dns_question_size(q
));
44 DNS_ANSWER_FOREACH(rr
, answer
) {
46 r
= dns_question_matches_rr(q
, rr
, NULL
);
52 r
= dns_question_matches_cname_or_dname(q
, rr
, NULL
);
60 r
= dns_packet_append_rr(*p
, rr
, 0, NULL
, NULL
);
72 *ret_truncated
= truncated
;
76 DNS_PACKET_HEADER(*p
)->ancount
= htobe16(be16toh(DNS_PACKET_HEADER(*p
)->ancount
) + c
);
81 static int dns_stub_finish_reply_packet(
85 bool tc
, /* set the Truncated bit? */
86 bool add_opt
, /* add an OPT RR to this packet? */
87 bool edns0_do
, /* set the EDNS0 DNSSEC OK bit? */
88 bool ad
) { /* set the DNSSEC authenticated data bit? */
95 /* If the client can't to EDNS0, don't do DO either */
98 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
100 rcode
= DNS_RCODE_SERVFAIL
;
103 /* Don't set the AD bit unless DO is on, too */
107 DNS_PACKET_HEADER(p
)->id
= id
;
109 DNS_PACKET_HEADER(p
)->flags
= htobe16(DNS_PACKET_MAKE_FLAGS(
121 r
= dns_packet_append_opt(p
, ADVERTISE_DATAGRAM_SIZE_MAX
, edns0_do
, rcode
, NULL
);
129 static void dns_stub_detach_stream(DnsStream
*s
) {
137 static int dns_stub_send(Manager
*m
, DnsStream
*s
, DnsPacket
*p
, DnsPacket
*reply
) {
145 r
= dns_stream_write_packet(s
, reply
);
149 fd
= manager_dns_stub_udp_fd(m
);
151 return log_debug_errno(fd
, "Failed to get reply socket: %m");
153 /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
154 * is because otherwise the kernel will choose it automatically based on the routing table and will
155 * thus pick 127.0.0.1 rather than 127.0.0.53. */
157 r
= manager_send(m
, fd
, LOOPBACK_IFINDEX
, p
->family
, &p
->sender
, p
->sender_port
, &p
->destination
, reply
);
160 return log_debug_errno(r
, "Failed to send reply packet: %m");
165 static int dns_stub_send_failure(Manager
*m
, DnsStream
*s
, DnsPacket
*p
, int rcode
, bool authenticated
) {
166 _cleanup_(dns_packet_unrefp
) DnsPacket
*reply
= NULL
;
172 r
= dns_stub_make_reply_packet(&reply
, DNS_PACKET_PAYLOAD_SIZE_MAX(p
), p
->question
, NULL
, NULL
);
174 return log_debug_errno(r
, "Failed to make failure packet: %m");
176 r
= dns_stub_finish_reply_packet(reply
, DNS_PACKET_ID(p
), rcode
, false, !!p
->opt
, DNS_PACKET_DO(p
), authenticated
);
178 return log_debug_errno(r
, "Failed to build failure packet: %m");
180 return dns_stub_send(m
, s
, p
, reply
);
183 static void dns_stub_query_complete(DnsQuery
*q
) {
187 assert(q
->request_dns_packet
);
191 case DNS_TRANSACTION_SUCCESS
: {
194 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
);
196 log_debug_errno(r
, "Failed to build reply packet: %m");
200 r
= dns_query_process_cname(q
);
202 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
, false);
206 log_debug_errno(r
, "Failed to process CNAME: %m");
209 if (r
== DNS_QUERY_RESTARTED
)
212 r
= dns_stub_finish_reply_packet(
214 DNS_PACKET_ID(q
->request_dns_packet
),
217 !!q
->request_dns_packet
->opt
,
218 DNS_PACKET_DO(q
->request_dns_packet
),
219 dns_query_fully_authenticated(q
));
221 log_debug_errno(r
, "Failed to finish reply packet: %m");
225 (void) dns_stub_send(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, q
->reply_dns_packet
);
229 case DNS_TRANSACTION_RCODE_FAILURE
:
230 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, q
->answer_rcode
, dns_query_fully_authenticated(q
));
233 case DNS_TRANSACTION_NOT_FOUND
:
234 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_NXDOMAIN
, dns_query_fully_authenticated(q
));
237 case DNS_TRANSACTION_TIMEOUT
:
238 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED
:
239 /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
242 case DNS_TRANSACTION_NO_SERVERS
:
243 case DNS_TRANSACTION_INVALID_REPLY
:
244 case DNS_TRANSACTION_ERRNO
:
245 case DNS_TRANSACTION_ABORTED
:
246 case DNS_TRANSACTION_DNSSEC_FAILED
:
247 case DNS_TRANSACTION_NO_TRUST_ANCHOR
:
248 case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED
:
249 case DNS_TRANSACTION_NETWORK_DOWN
:
250 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
, false);
253 case DNS_TRANSACTION_NULL
:
254 case DNS_TRANSACTION_PENDING
:
255 case DNS_TRANSACTION_VALIDATING
:
257 assert_not_reached("Impossible state");
260 /* If there's a packet to write set, let's leave the stream around */
261 if (q
->request_dns_stream
&& DNS_STREAM_QUEUED(q
->request_dns_stream
)) {
263 /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The
264 * default completion action of the stream will drop the reference. */
266 dns_stub_detach_stream(q
->request_dns_stream
);
267 q
->request_dns_stream
= NULL
;
273 static int dns_stub_stream_complete(DnsStream
*s
, int error
) {
276 log_debug_errno(error
, "DNS TCP connection terminated, destroying query: %m");
279 dns_query_free(s
->query
);
284 static void dns_stub_process_query(Manager
*m
, DnsStream
*s
, DnsPacket
*p
) {
290 assert(p
->protocol
== DNS_PROTOCOL_DNS
);
292 /* Takes ownership of the *s stream object */
294 if (in_addr_is_localhost(p
->family
, &p
->sender
) <= 0 ||
295 in_addr_is_localhost(p
->family
, &p
->destination
) <= 0) {
296 log_error("Got packet on unexpected IP range, refusing.");
297 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
, false);
301 r
= dns_packet_extract(p
);
303 log_debug_errno(r
, "Failed to extract resources from incoming packet, ignoring packet: %m");
304 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_FORMERR
, false);
308 if (!DNS_PACKET_VERSION_SUPPORTED(p
)) {
309 log_debug("Got EDNS OPT field with unsupported version number.");
310 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_BADVERS
, false);
314 if (dns_type_is_obsolete(p
->question
->keys
[0]->type
)) {
315 log_debug("Got message with obsolete key type, refusing.");
316 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
, false);
320 if (dns_type_is_zone_transer(p
->question
->keys
[0]->type
)) {
321 log_debug("Got request for zone transfer, refusing.");
322 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
, false);
326 if (!DNS_PACKET_RD(p
)) {
327 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
328 log_debug("Got request with recursion disabled, refusing.");
329 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_REFUSED
, false);
333 if (DNS_PACKET_DO(p
) && DNS_PACKET_CD(p
)) {
334 log_debug("Got request with DNSSEC CD bit set, refusing.");
335 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
, false);
339 r
= dns_query_new(m
, &q
, p
->question
, p
->question
, 0, SD_RESOLVED_PROTOCOLS_ALL
|SD_RESOLVED_NO_SEARCH
);
341 log_error_errno(r
, "Failed to generate query object: %m");
342 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
, false);
346 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
349 q
->request_dns_packet
= dns_packet_ref(p
);
350 q
->request_dns_stream
= dns_stream_ref(s
); /* make sure the stream stays around until we can send a reply through it */
351 q
->complete
= dns_stub_query_complete
;
355 s
->complete
= dns_stub_stream_complete
;
361 log_error_errno(r
, "Failed to start query: %m");
362 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
, false);
366 log_debug("Processing query...");
370 if (s
&& DNS_STREAM_QUEUED(s
))
371 dns_stub_detach_stream(s
);
376 static int on_dns_stub_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
377 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
378 Manager
*m
= userdata
;
381 r
= manager_recv(m
, fd
, DNS_PROTOCOL_DNS
, &p
);
385 if (dns_packet_validate_query(p
) > 0) {
386 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p
));
388 dns_stub_process_query(m
, NULL
, p
);
390 log_debug("Invalid DNS stub UDP packet, ignoring.");
395 static int manager_dns_stub_udp_fd(Manager
*m
) {
396 union sockaddr_union sa
= {
397 .in
.sin_family
= AF_INET
,
398 .in
.sin_port
= htobe16(53),
399 .in
.sin_addr
.s_addr
= htobe32(INADDR_DNS_STUB
),
401 _cleanup_close_
int fd
= -1;
404 if (m
->dns_stub_udp_fd
>= 0)
405 return m
->dns_stub_udp_fd
;
407 fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
411 r
= setsockopt_int(fd
, SOL_SOCKET
, SO_REUSEADDR
, true);
415 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_PKTINFO
, true);
419 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_RECVTTL
, true);
423 /* Make sure no traffic from outside the local host can leak to onto this socket */
424 if (setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, "lo", 3) < 0)
427 if (bind(fd
, &sa
.sa
, sizeof(sa
.in
)) < 0)
430 r
= sd_event_add_io(m
->event
, &m
->dns_stub_udp_event_source
, fd
, EPOLLIN
, on_dns_stub_packet
, m
);
434 (void) sd_event_source_set_description(m
->dns_stub_udp_event_source
, "dns-stub-udp");
436 return m
->dns_stub_udp_fd
= TAKE_FD(fd
);
439 static int on_dns_stub_stream_packet(DnsStream
*s
) {
440 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
444 p
= dns_stream_take_read_packet(s
);
447 if (dns_packet_validate_query(p
) > 0) {
448 log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p
));
450 dns_stub_process_query(s
->manager
, s
, p
);
452 log_debug("Invalid DNS stub TCP packet, ignoring.");
454 /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now,
455 * or that didn't happen in which case we want to free the stream */
461 static int on_dns_stub_stream(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
463 Manager
*m
= userdata
;
466 cfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
468 if (IN_SET(errno
, EAGAIN
, EINTR
))
474 r
= dns_stream_new(m
, &stream
, DNS_PROTOCOL_DNS
, cfd
, NULL
);
480 stream
->on_packet
= on_dns_stub_stream_packet
;
482 /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action
483 * of the stream, or by our packet callback, or when the manager is shut down. */
488 static int manager_dns_stub_tcp_fd(Manager
*m
) {
489 union sockaddr_union sa
= {
490 .in
.sin_family
= AF_INET
,
491 .in
.sin_addr
.s_addr
= htobe32(INADDR_DNS_STUB
),
492 .in
.sin_port
= htobe16(53),
494 _cleanup_close_
int fd
= -1;
497 if (m
->dns_stub_tcp_fd
>= 0)
498 return m
->dns_stub_tcp_fd
;
500 fd
= socket(AF_INET
, SOCK_STREAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
504 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_TTL
, true);
508 r
= setsockopt_int(fd
, SOL_SOCKET
, SO_REUSEADDR
, true);
512 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_PKTINFO
, true);
516 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_RECVTTL
, true);
520 /* Make sure no traffic from outside the local host can leak to onto this socket */
521 if (setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, "lo", 3) < 0)
524 if (bind(fd
, &sa
.sa
, sizeof(sa
.in
)) < 0)
527 if (listen(fd
, SOMAXCONN
) < 0)
530 r
= sd_event_add_io(m
->event
, &m
->dns_stub_tcp_event_source
, fd
, EPOLLIN
, on_dns_stub_stream
, m
);
534 (void) sd_event_source_set_description(m
->dns_stub_tcp_event_source
, "dns-stub-tcp");
536 return m
->dns_stub_tcp_fd
= TAKE_FD(fd
);
539 int manager_dns_stub_start(Manager
*m
) {
540 const char *t
= "UDP";
545 if (m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_NO
)
546 log_debug("Not creating stub listener.");
548 log_debug("Creating stub listener using %s.",
549 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_UDP
? "UDP" :
550 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_TCP
? "TCP" :
553 if (IN_SET(m
->dns_stub_listener_mode
, DNS_STUB_LISTENER_YES
, DNS_STUB_LISTENER_UDP
))
554 r
= manager_dns_stub_udp_fd(m
);
557 IN_SET(m
->dns_stub_listener_mode
, DNS_STUB_LISTENER_YES
, DNS_STUB_LISTENER_TCP
)) {
559 r
= manager_dns_stub_tcp_fd(m
);
562 if (IN_SET(r
, -EADDRINUSE
, -EPERM
)) {
563 if (r
== -EADDRINUSE
)
565 "Another process is already listening on %s socket 127.0.0.53:53.\n"
566 "Turning off local DNS stub support.", t
);
569 "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
570 "Turning off local DNS stub support.", t
);
571 manager_dns_stub_stop(m
);
573 return log_error_errno(r
, "Failed to listen on %s socket 127.0.0.53:53: %m", t
);
578 void manager_dns_stub_stop(Manager
*m
) {
581 m
->dns_stub_udp_event_source
= sd_event_source_unref(m
->dns_stub_udp_event_source
);
582 m
->dns_stub_tcp_event_source
= sd_event_source_unref(m
->dns_stub_tcp_event_source
);
584 m
->dns_stub_udp_fd
= safe_close(m
->dns_stub_udp_fd
);
585 m
->dns_stub_tcp_fd
= safe_close(m
->dns_stub_tcp_fd
);