1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "errno-util.h"
5 #include "missing_network.h"
6 #include "resolved-dns-stub.h"
7 #include "socket-netlink.h"
8 #include "socket-util.h"
10 /* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
11 * IP and UDP header sizes */
12 #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
14 static int manager_dns_stub_udp_fd(Manager
*m
);
15 static int manager_dns_stub_tcp_fd(Manager
*m
);
17 int dns_stub_extra_new(DNSStubListenerExtra
**ret
) {
18 DNSStubListenerExtra
*l
;
20 l
= new(DNSStubListenerExtra
, 1);
24 *l
= (DNSStubListenerExtra
) {
33 static int dns_stub_make_reply_packet(
38 bool *ret_truncated
) {
40 bool truncated
= false;
41 DnsResourceRecord
*rr
;
47 /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
48 * roundtrips aren't expensive. */
51 r
= dns_packet_new(p
, DNS_PROTOCOL_DNS
, 0, max_size
);
55 r
= dns_packet_append_question(*p
, q
);
59 DNS_PACKET_HEADER(*p
)->qdcount
= htobe16(dns_question_size(q
));
62 DNS_ANSWER_FOREACH(rr
, answer
) {
64 r
= dns_question_matches_rr(q
, rr
, NULL
);
70 r
= dns_question_matches_cname_or_dname(q
, rr
, NULL
);
78 r
= dns_packet_append_rr(*p
, rr
, 0, NULL
, NULL
);
90 *ret_truncated
= truncated
;
94 DNS_PACKET_HEADER(*p
)->ancount
= htobe16(be16toh(DNS_PACKET_HEADER(*p
)->ancount
) + c
);
99 static int dns_stub_finish_reply_packet(
103 bool tc
, /* set the Truncated bit? */
104 bool add_opt
, /* add an OPT RR to this packet? */
105 bool edns0_do
, /* set the EDNS0 DNSSEC OK bit? */
106 bool ad
) { /* set the DNSSEC authenticated data bit? */
113 r
= dns_packet_append_opt(p
, ADVERTISE_DATAGRAM_SIZE_MAX
, edns0_do
, rcode
, NULL
);
114 if (r
== -EMSGSIZE
) /* Hit the size limit? then indicate truncation */
120 /* If the client can't to EDNS0, don't do DO either */
123 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
125 rcode
= DNS_RCODE_SERVFAIL
;
128 /* Don't set the AD bit unless DO is on, too */
132 DNS_PACKET_HEADER(p
)->id
= id
;
134 DNS_PACKET_HEADER(p
)->flags
= htobe16(DNS_PACKET_MAKE_FLAGS(
148 static int dns_stub_send(Manager
*m
, DnsStream
*s
, DnsPacket
*p
, DnsPacket
*reply
) {
156 r
= dns_stream_write_packet(s
, reply
);
160 fd
= manager_dns_stub_udp_fd(m
);
162 return log_debug_errno(fd
, "Failed to get reply socket: %m");
164 /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
165 * is because otherwise the kernel will choose it automatically based on the routing table and will
166 * thus pick 127.0.0.1 rather than 127.0.0.53. */
168 r
= manager_send(m
, fd
, LOOPBACK_IFINDEX
, p
->family
, &p
->sender
, p
->sender_port
, &p
->destination
, reply
);
171 return log_debug_errno(r
, "Failed to send reply packet: %m");
176 static int dns_stub_send_failure(Manager
*m
, DnsStream
*s
, DnsPacket
*p
, int rcode
, bool authenticated
) {
177 _cleanup_(dns_packet_unrefp
) DnsPacket
*reply
= NULL
;
183 r
= dns_stub_make_reply_packet(&reply
, DNS_PACKET_PAYLOAD_SIZE_MAX(p
), p
->question
, NULL
, NULL
);
185 return log_debug_errno(r
, "Failed to make failure packet: %m");
187 r
= dns_stub_finish_reply_packet(reply
, DNS_PACKET_ID(p
), rcode
, false, !!p
->opt
, DNS_PACKET_DO(p
), authenticated
);
189 return log_debug_errno(r
, "Failed to build failure packet: %m");
191 return dns_stub_send(m
, s
, p
, reply
);
194 static void dns_stub_query_complete(DnsQuery
*q
) {
198 assert(q
->request_dns_packet
);
202 case DNS_TRANSACTION_SUCCESS
: {
205 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
);
207 log_debug_errno(r
, "Failed to build reply packet: %m");
212 r
= dns_query_process_cname(q
);
214 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
, false);
218 log_debug_errno(r
, "Failed to process CNAME: %m");
221 if (r
== DNS_QUERY_RESTARTED
)
225 r
= dns_stub_finish_reply_packet(
227 DNS_PACKET_ID(q
->request_dns_packet
),
230 !!q
->request_dns_packet
->opt
,
231 DNS_PACKET_DO(q
->request_dns_packet
),
232 dns_query_fully_authenticated(q
));
234 log_debug_errno(r
, "Failed to finish reply packet: %m");
238 (void) dns_stub_send(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, q
->reply_dns_packet
);
242 case DNS_TRANSACTION_RCODE_FAILURE
:
243 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, q
->answer_rcode
, dns_query_fully_authenticated(q
));
246 case DNS_TRANSACTION_NOT_FOUND
:
247 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_NXDOMAIN
, dns_query_fully_authenticated(q
));
250 case DNS_TRANSACTION_TIMEOUT
:
251 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED
:
252 /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
255 case DNS_TRANSACTION_NO_SERVERS
:
256 case DNS_TRANSACTION_INVALID_REPLY
:
257 case DNS_TRANSACTION_ERRNO
:
258 case DNS_TRANSACTION_ABORTED
:
259 case DNS_TRANSACTION_DNSSEC_FAILED
:
260 case DNS_TRANSACTION_NO_TRUST_ANCHOR
:
261 case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED
:
262 case DNS_TRANSACTION_NETWORK_DOWN
:
263 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
, false);
266 case DNS_TRANSACTION_NULL
:
267 case DNS_TRANSACTION_PENDING
:
268 case DNS_TRANSACTION_VALIDATING
:
270 assert_not_reached("Impossible state");
276 static int dns_stub_stream_complete(DnsStream
*s
, int error
) {
279 log_debug_errno(error
, "DNS TCP connection terminated, destroying queries: %m");
284 q
= set_first(s
->queries
);
291 /* This drops the implicit ref we keep around since it was allocated, as incoming stub connections
292 * should be kept as long as the client wants to. */
297 static void dns_stub_process_query(Manager
*m
, DnsStream
*s
, DnsPacket
*p
) {
298 _cleanup_(dns_query_freep
) DnsQuery
*q
= NULL
;
303 assert(p
->protocol
== DNS_PROTOCOL_DNS
);
305 if (in_addr_is_localhost(p
->family
, &p
->sender
) <= 0 ||
306 in_addr_is_localhost(p
->family
, &p
->destination
) <= 0) {
307 log_error("Got packet on unexpected IP range, refusing.");
308 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
, false);
312 r
= dns_packet_extract(p
);
314 log_debug_errno(r
, "Failed to extract resources from incoming packet, ignoring packet: %m");
315 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_FORMERR
, false);
319 if (!DNS_PACKET_VERSION_SUPPORTED(p
)) {
320 log_debug("Got EDNS OPT field with unsupported version number.");
321 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_BADVERS
, false);
325 if (dns_type_is_obsolete(p
->question
->keys
[0]->type
)) {
326 log_debug("Got message with obsolete key type, refusing.");
327 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
, false);
331 if (dns_type_is_zone_transer(p
->question
->keys
[0]->type
)) {
332 log_debug("Got request for zone transfer, refusing.");
333 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
, false);
337 if (!DNS_PACKET_RD(p
)) {
338 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
339 log_debug("Got request with recursion disabled, refusing.");
340 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_REFUSED
, false);
344 if (DNS_PACKET_DO(p
) && DNS_PACKET_CD(p
)) {
345 log_debug("Got request with DNSSEC CD bit set, refusing.");
346 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
, false);
350 r
= dns_query_new(m
, &q
, p
->question
, p
->question
, 0, SD_RESOLVED_PROTOCOLS_ALL
|SD_RESOLVED_NO_SEARCH
);
352 log_error_errno(r
, "Failed to generate query object: %m");
353 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
, false);
357 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
360 q
->request_dns_packet
= dns_packet_ref(p
);
361 q
->request_dns_stream
= dns_stream_ref(s
); /* make sure the stream stays around until we can send a reply through it */
362 q
->complete
= dns_stub_query_complete
;
365 /* Remember which queries belong to this stream, so that we can cancel them when the stream
366 * is disconnected early */
368 r
= set_ensure_put(&s
->queries
, NULL
, q
);
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 static int on_dns_stub_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
388 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
389 Manager
*m
= userdata
;
392 r
= manager_recv(m
, fd
, DNS_PROTOCOL_DNS
, &p
);
396 if (dns_packet_validate_query(p
) > 0) {
397 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p
));
399 dns_stub_process_query(m
, NULL
, p
);
401 log_debug("Invalid DNS stub UDP packet, ignoring.");
406 static int set_dns_stub_common_socket_options(int fd
) {
411 r
= setsockopt_int(fd
, SOL_SOCKET
, SO_REUSEADDR
, true);
415 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_PKTINFO
, true);
419 return setsockopt_int(fd
, IPPROTO_IP
, IP_RECVTTL
, true);
422 static int manager_dns_stub_udp_fd(Manager
*m
) {
423 union sockaddr_union sa
= {
424 .in
.sin_family
= AF_INET
,
425 .in
.sin_port
= htobe16(53),
426 .in
.sin_addr
.s_addr
= htobe32(INADDR_DNS_STUB
),
428 _cleanup_close_
int fd
= -1;
431 if (m
->dns_stub_udp_fd
>= 0)
432 return m
->dns_stub_udp_fd
;
434 fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
438 r
= set_dns_stub_common_socket_options(fd
);
442 /* Make sure no traffic from outside the local host can leak to onto this socket */
443 r
= socket_bind_to_ifindex(fd
, LOOPBACK_IFINDEX
);
447 if (bind(fd
, &sa
.sa
, sizeof(sa
.in
)) < 0)
450 r
= sd_event_add_io(m
->event
, &m
->dns_stub_udp_event_source
, fd
, EPOLLIN
, on_dns_stub_packet
, m
);
454 (void) sd_event_source_set_description(m
->dns_stub_udp_event_source
, "dns-stub-udp");
456 return m
->dns_stub_udp_fd
= TAKE_FD(fd
);
459 static int manager_dns_stub_udp_fd_extra(Manager
*m
, DNSStubListenerExtra
*l
) {
460 _cleanup_free_
char *pretty
= NULL
;
461 _cleanup_close_
int fd
= -1;
467 fd
= socket(socket_address_family(&l
->address
), SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
473 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_FREEBIND
, true);
477 r
= set_dns_stub_common_socket_options(fd
);
481 if (bind(fd
, &l
->address
.sockaddr
.sa
, l
->address
.size
) < 0) {
486 r
= sd_event_add_io(m
->event
, &l
->event_source
, fd
, EPOLLIN
, on_dns_stub_packet
, m
);
490 (void) sd_event_source_set_description(l
->event_source
, "dns-stub-udp-extra");
495 (void) sockaddr_pretty(&l
->address
.sockaddr
.sa
, FAMILY_ADDRESS_SIZE(l
->address
.sockaddr
.sa
.sa_family
), true, true, &pretty
);
496 log_debug("Listening on UDP socket %s.", strnull(pretty
));
502 (void) sockaddr_pretty(&l
->address
.sockaddr
.sa
, FAMILY_ADDRESS_SIZE(l
->address
.sockaddr
.sa
.sa_family
), true, true, &pretty
);
503 if (r
== -EADDRINUSE
)
504 return log_warning_errno(r
,
505 "Another process is already listening on UDP socket %s.\n"
506 "Turning off local DNS stub extra support.", strnull(pretty
));
508 return log_warning_errno(r
,
509 "Failed to listen on UDP socket %s: %m.\n"
510 "Turning off local DNS stub extra support.", strnull(pretty
));
514 return log_warning_errno(r
, "Failed to listen on UDP socket %s, ignoring: %m", strnull(pretty
));
517 static int on_dns_stub_stream_packet(DnsStream
*s
) {
518 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
522 p
= dns_stream_take_read_packet(s
);
525 if (dns_packet_validate_query(p
) > 0) {
526 log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p
));
528 dns_stub_process_query(s
->manager
, s
, p
);
530 log_debug("Invalid DNS stub TCP packet, ignoring.");
535 static int on_dns_stub_stream(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
537 Manager
*m
= userdata
;
540 cfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
542 if (ERRNO_IS_ACCEPT_AGAIN(errno
))
548 r
= dns_stream_new(m
, &stream
, DNS_STREAM_STUB
, DNS_PROTOCOL_DNS
, cfd
, NULL
);
554 stream
->on_packet
= on_dns_stub_stream_packet
;
555 stream
->complete
= dns_stub_stream_complete
;
557 /* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */
562 static int manager_dns_stub_tcp_fd(Manager
*m
) {
563 union sockaddr_union sa
= {
564 .in
.sin_family
= AF_INET
,
565 .in
.sin_addr
.s_addr
= htobe32(INADDR_DNS_STUB
),
566 .in
.sin_port
= htobe16(53),
568 _cleanup_close_
int fd
= -1;
571 if (m
->dns_stub_tcp_fd
>= 0)
572 return m
->dns_stub_tcp_fd
;
574 fd
= socket(AF_INET
, SOCK_STREAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
578 r
= set_dns_stub_common_socket_options(fd
);
582 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_TTL
, 1);
586 /* Make sure no traffic from outside the local host can leak to onto this socket */
587 r
= socket_bind_to_ifindex(fd
, LOOPBACK_IFINDEX
);
591 if (bind(fd
, &sa
.sa
, sizeof(sa
.in
)) < 0)
594 if (listen(fd
, SOMAXCONN
) < 0)
597 r
= sd_event_add_io(m
->event
, &m
->dns_stub_tcp_event_source
, fd
, EPOLLIN
, on_dns_stub_stream
, m
);
601 (void) sd_event_source_set_description(m
->dns_stub_tcp_event_source
, "dns-stub-tcp");
603 return m
->dns_stub_tcp_fd
= TAKE_FD(fd
);
606 static int manager_dns_stub_tcp_fd_extra(Manager
*m
, DNSStubListenerExtra
*l
) {
607 _cleanup_free_
char *pretty
= NULL
;
608 _cleanup_close_
int fd
= -1;
614 fd
= socket(socket_address_family(&l
->address
), SOCK_STREAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
620 r
= set_dns_stub_common_socket_options(fd
);
624 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_TTL
, 1);
628 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_FREEBIND
, true);
632 if (bind(fd
, &l
->address
.sockaddr
.sa
, l
->address
.size
) < 0) {
637 if (listen(fd
, SOMAXCONN
) < 0) {
642 r
= sd_event_add_io(m
->event
, &l
->event_source
, fd
, EPOLLIN
, on_dns_stub_packet
, m
);
646 (void) sd_event_source_set_description(l
->event_source
, "dns-stub-tcp-extra");
651 (void) sockaddr_pretty(&l
->address
.sockaddr
.sa
, FAMILY_ADDRESS_SIZE(l
->address
.sockaddr
.sa
.sa_family
), true, true, &pretty
);
652 log_debug("Listening on TCP socket %s.", strnull(pretty
));
658 (void) sockaddr_pretty(&l
->address
.sockaddr
.sa
, FAMILY_ADDRESS_SIZE(l
->address
.sockaddr
.sa
.sa_family
), true, true, &pretty
);
659 if (r
== -EADDRINUSE
)
660 return log_warning_errno(r
,
661 "Another process is already listening on TCP socket %s.\n"
662 "Turning off local DNS stub extra support.", strnull(pretty
));
664 return log_warning_errno(r
,
665 "Failed to listen on TCP socket %s: %m.\n"
666 "Turning off local DNS stub extra support.", strnull(pretty
));
670 return log_warning_errno(r
, "Failed to listen on TCP socket %s, ignoring: %m", strnull(pretty
));
673 int manager_dns_stub_start(Manager
*m
) {
674 const char *t
= "UDP";
679 if (m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_NO
)
680 log_debug("Not creating stub listener.");
682 log_debug("Creating stub listener using %s.",
683 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_UDP
? "UDP" :
684 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_TCP
? "TCP" :
687 if (FLAGS_SET(m
->dns_stub_listener_mode
, DNS_STUB_LISTENER_UDP
))
688 r
= manager_dns_stub_udp_fd(m
);
691 FLAGS_SET(m
->dns_stub_listener_mode
, DNS_STUB_LISTENER_TCP
)) {
693 r
= manager_dns_stub_tcp_fd(m
);
696 if (IN_SET(r
, -EADDRINUSE
, -EPERM
)) {
697 if (r
== -EADDRINUSE
)
699 "Another process is already listening on %s socket 127.0.0.53:53.\n"
700 "Turning off local DNS stub support.", t
);
703 "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
704 "Turning off local DNS stub support.", t
);
705 manager_dns_stub_stop(m
);
707 return log_error_errno(r
, "Failed to listen on %s socket 127.0.0.53:53: %m", t
);
709 if (!ordered_set_isempty(m
->dns_extra_stub_listeners
)) {
710 DNSStubListenerExtra
*l
;
713 log_debug("Creating stub listener extra using %s.",
714 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_UDP
? "UDP" :
715 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_TCP
? "TCP" :
718 ORDERED_SET_FOREACH(l
, m
->dns_extra_stub_listeners
, i
)
719 if (l
->mode
== DNS_STUB_LISTENER_UDP
)
720 (void) manager_dns_stub_udp_fd_extra(m
, l
);
722 (void) manager_dns_stub_tcp_fd_extra(m
, l
);
728 void manager_dns_stub_stop(Manager
*m
) {
731 m
->dns_stub_udp_event_source
= sd_event_source_unref(m
->dns_stub_udp_event_source
);
732 m
->dns_stub_tcp_event_source
= sd_event_source_unref(m
->dns_stub_tcp_event_source
);
734 m
->dns_stub_udp_fd
= safe_close(m
->dns_stub_udp_fd
);
735 m
->dns_stub_tcp_fd
= safe_close(m
->dns_stub_tcp_fd
);
738 void manager_dns_stub_stop_extra(Manager
*m
) {
739 DNSStubListenerExtra
*l
;
744 ORDERED_SET_FOREACH(l
, m
->dns_extra_stub_listeners
, i
) {
745 l
->event_source
= sd_event_source_unref(l
->event_source
);
746 l
->fd
= safe_close(l
->fd
);