1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <net/if_arp.h>
5 #include "errno-util.h"
7 #include "missing_network.h"
8 #include "missing_socket.h"
9 #include "resolved-dns-stub.h"
10 #include "socket-netlink.h"
11 #include "socket-util.h"
12 #include "string-table.h"
14 /* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
15 * IP and UDP header sizes */
16 #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
18 static int manager_dns_stub_fd_extra(Manager
*m
, DnsStubListenerExtra
*l
, int type
);
20 static void dns_stub_listener_extra_hash_func(const DnsStubListenerExtra
*a
, struct siphash
*state
) {
23 siphash24_compress(&a
->mode
, sizeof(a
->mode
), state
);
24 siphash24_compress(&a
->family
, sizeof(a
->family
), state
);
25 siphash24_compress(&a
->address
, FAMILY_ADDRESS_SIZE(a
->family
), state
);
26 siphash24_compress(&a
->port
, sizeof(a
->port
), state
);
29 static int dns_stub_listener_extra_compare_func(const DnsStubListenerExtra
*a
, const DnsStubListenerExtra
*b
) {
35 r
= CMP(a
->mode
, b
->mode
);
39 r
= CMP(a
->family
, b
->family
);
43 r
= memcmp(&a
->address
, &b
->address
, FAMILY_ADDRESS_SIZE(a
->family
));
47 return CMP(a
->port
, b
->port
);
50 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
51 dns_stub_listener_extra_hash_ops
,
53 dns_stub_listener_extra_hash_func
,
54 dns_stub_listener_extra_compare_func
,
55 dns_stub_listener_extra_free
);
57 int dns_stub_listener_extra_new(
59 DnsStubListenerExtra
**ret
) {
61 DnsStubListenerExtra
*l
;
63 l
= new(DnsStubListenerExtra
, 1);
67 *l
= (DnsStubListenerExtra
) {
75 DnsStubListenerExtra
*dns_stub_listener_extra_free(DnsStubListenerExtra
*p
) {
79 p
->udp_event_source
= sd_event_source_unref(p
->udp_event_source
);
80 p
->tcp_event_source
= sd_event_source_unref(p
->tcp_event_source
);
85 static int dns_stub_make_reply_packet(
90 bool *ret_truncated
) {
92 bool truncated
= false;
93 DnsResourceRecord
*rr
;
99 /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
100 * roundtrips aren't expensive. */
103 r
= dns_packet_new(p
, DNS_PROTOCOL_DNS
, 0, max_size
);
107 r
= dns_packet_append_question(*p
, q
);
111 DNS_PACKET_HEADER(*p
)->qdcount
= htobe16(dns_question_size(q
));
114 DNS_ANSWER_FOREACH(rr
, answer
) {
116 r
= dns_question_matches_rr(q
, rr
, NULL
);
122 r
= dns_question_matches_cname_or_dname(q
, rr
, NULL
);
130 r
= dns_packet_append_rr(*p
, rr
, 0, NULL
, NULL
);
131 if (r
== -EMSGSIZE
) {
142 *ret_truncated
= truncated
;
146 DNS_PACKET_HEADER(*p
)->ancount
= htobe16(be16toh(DNS_PACKET_HEADER(*p
)->ancount
) + c
);
151 static int dns_stub_finish_reply_packet(
155 bool tc
, /* set the Truncated bit? */
156 bool add_opt
, /* add an OPT RR to this packet? */
157 bool edns0_do
, /* set the EDNS0 DNSSEC OK bit? */
158 bool ad
) { /* set the DNSSEC authenticated data bit? */
165 r
= dns_packet_append_opt(p
, ADVERTISE_DATAGRAM_SIZE_MAX
, edns0_do
, /* include_rfc6975 = */ false, rcode
, NULL
);
166 if (r
== -EMSGSIZE
) /* Hit the size limit? then indicate truncation */
172 /* If the client can't to EDNS0, don't do DO either */
175 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
177 rcode
= DNS_RCODE_SERVFAIL
;
180 /* Don't set the AD bit unless DO is on, too */
184 DNS_PACKET_HEADER(p
)->id
= id
;
186 DNS_PACKET_HEADER(p
)->flags
= htobe16(DNS_PACKET_MAKE_FLAGS(
200 static int dns_stub_send(
202 DnsStubListenerExtra
*l
,
214 r
= dns_stream_write_packet(s
, reply
);
216 /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
217 * is because otherwise the kernel will choose it automatically based on the routing table and will
218 * thus pick 127.0.0.1 rather than 127.0.0.53. */
220 manager_dns_stub_fd_extra(m
, l
, SOCK_DGRAM
),
221 l
? p
->ifindex
: LOOPBACK_IFINDEX
, /* force loopback iface if this is the main listener stub */
222 p
->family
, &p
->sender
, p
->sender_port
, &p
->destination
,
225 return log_debug_errno(r
, "Failed to send reply packet: %m");
230 static int dns_stub_send_failure(
232 DnsStubListenerExtra
*l
,
236 bool authenticated
) {
238 _cleanup_(dns_packet_unrefp
) DnsPacket
*reply
= NULL
;
244 r
= dns_stub_make_reply_packet(&reply
, DNS_PACKET_PAYLOAD_SIZE_MAX(p
), p
->question
, NULL
, NULL
);
246 return log_debug_errno(r
, "Failed to make failure packet: %m");
248 r
= dns_stub_finish_reply_packet(reply
, DNS_PACKET_ID(p
), rcode
, false, !!p
->opt
, DNS_PACKET_DO(p
), authenticated
);
250 return log_debug_errno(r
, "Failed to build failure packet: %m");
252 return dns_stub_send(m
, l
, s
, p
, reply
);
255 static void dns_stub_query_complete(DnsQuery
*q
) {
259 assert(q
->request_dns_packet
);
263 case DNS_TRANSACTION_SUCCESS
: {
266 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
);
268 log_debug_errno(r
, "Failed to build reply packet: %m");
273 r
= dns_query_process_cname(q
);
275 (void) dns_stub_send_failure(q
->manager
, q
->stub_listener_extra
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
, false);
279 log_debug_errno(r
, "Failed to process CNAME: %m");
282 if (r
== DNS_QUERY_RESTARTED
)
286 r
= dns_stub_finish_reply_packet(
288 DNS_PACKET_ID(q
->request_dns_packet
),
291 !!q
->request_dns_packet
->opt
,
292 DNS_PACKET_DO(q
->request_dns_packet
),
293 dns_query_fully_authenticated(q
));
295 log_debug_errno(r
, "Failed to finish reply packet: %m");
299 (void) dns_stub_send(q
->manager
, q
->stub_listener_extra
, q
->request_dns_stream
, q
->request_dns_packet
, q
->reply_dns_packet
);
303 case DNS_TRANSACTION_RCODE_FAILURE
:
304 (void) dns_stub_send_failure(q
->manager
, q
->stub_listener_extra
, q
->request_dns_stream
, q
->request_dns_packet
, q
->answer_rcode
, dns_query_fully_authenticated(q
));
307 case DNS_TRANSACTION_NOT_FOUND
:
308 (void) dns_stub_send_failure(q
->manager
, q
->stub_listener_extra
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_NXDOMAIN
, dns_query_fully_authenticated(q
));
311 case DNS_TRANSACTION_TIMEOUT
:
312 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED
:
313 /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
316 case DNS_TRANSACTION_NO_SERVERS
:
317 case DNS_TRANSACTION_INVALID_REPLY
:
318 case DNS_TRANSACTION_ERRNO
:
319 case DNS_TRANSACTION_ABORTED
:
320 case DNS_TRANSACTION_DNSSEC_FAILED
:
321 case DNS_TRANSACTION_NO_TRUST_ANCHOR
:
322 case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED
:
323 case DNS_TRANSACTION_NETWORK_DOWN
:
324 (void) dns_stub_send_failure(q
->manager
, q
->stub_listener_extra
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
, false);
327 case DNS_TRANSACTION_NULL
:
328 case DNS_TRANSACTION_PENDING
:
329 case DNS_TRANSACTION_VALIDATING
:
331 assert_not_reached("Impossible state");
337 static int dns_stub_stream_complete(DnsStream
*s
, int error
) {
340 log_debug_errno(error
, "DNS TCP connection terminated, destroying queries: %m");
345 q
= set_first(s
->queries
);
352 /* This drops the implicit ref we keep around since it was allocated, as incoming stub connections
353 * should be kept as long as the client wants to. */
358 static void dns_stub_process_query(Manager
*m
, DnsStubListenerExtra
*l
, DnsStream
*s
, DnsPacket
*p
) {
359 _cleanup_(dns_query_freep
) DnsQuery
*q
= NULL
;
364 assert(p
->protocol
== DNS_PROTOCOL_DNS
);
366 if (!l
&& /* l == NULL if this is the main stub */
367 (in_addr_is_localhost(p
->family
, &p
->sender
) <= 0 ||
368 in_addr_is_localhost(p
->family
, &p
->destination
) <= 0)) {
369 log_error("Got packet on unexpected IP range, refusing.");
370 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_SERVFAIL
, false);
374 r
= dns_packet_extract(p
);
376 log_debug_errno(r
, "Failed to extract resources from incoming packet, ignoring packet: %m");
377 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_FORMERR
, false);
381 if (!DNS_PACKET_VERSION_SUPPORTED(p
)) {
382 log_debug("Got EDNS OPT field with unsupported version number.");
383 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_BADVERS
, false);
387 if (dns_type_is_obsolete(p
->question
->keys
[0]->type
)) {
388 log_debug("Got message with obsolete key type, refusing.");
389 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_NOTIMP
, false);
393 if (dns_type_is_zone_transer(p
->question
->keys
[0]->type
)) {
394 log_debug("Got request for zone transfer, refusing.");
395 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_NOTIMP
, false);
399 if (!DNS_PACKET_RD(p
)) {
400 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
401 log_debug("Got request with recursion disabled, refusing.");
402 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_REFUSED
, false);
406 if (DNS_PACKET_DO(p
) && DNS_PACKET_CD(p
)) {
407 log_debug("Got request with DNSSEC CD bit set, refusing.");
408 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_NOTIMP
, false);
412 r
= dns_query_new(m
, &q
, p
->question
, p
->question
, 0, SD_RESOLVED_PROTOCOLS_ALL
|SD_RESOLVED_NO_SEARCH
);
414 log_error_errno(r
, "Failed to generate query object: %m");
415 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_SERVFAIL
, false);
419 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
422 q
->request_dns_packet
= dns_packet_ref(p
);
423 q
->request_dns_stream
= dns_stream_ref(s
); /* make sure the stream stays around until we can send a reply through it */
424 q
->stub_listener_extra
= l
;
425 q
->complete
= dns_stub_query_complete
;
428 /* Remember which queries belong to this stream, so that we can cancel them when the stream
429 * is disconnected early */
431 r
= set_ensure_put(&s
->queries
, NULL
, q
);
441 log_error_errno(r
, "Failed to start query: %m");
442 dns_stub_send_failure(m
, l
, s
, p
, DNS_RCODE_SERVFAIL
, false);
446 log_debug("Processing query...");
450 static int on_dns_stub_packet_internal(sd_event_source
*s
, int fd
, uint32_t revents
, Manager
*m
, DnsStubListenerExtra
*l
) {
451 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
454 r
= manager_recv(m
, fd
, DNS_PROTOCOL_DNS
, &p
);
458 if (dns_packet_validate_query(p
) > 0) {
459 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p
));
461 dns_stub_process_query(m
, l
, NULL
, p
);
463 log_debug("Invalid DNS stub UDP packet, ignoring.");
468 static int on_dns_stub_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
469 return on_dns_stub_packet_internal(s
, fd
, revents
, userdata
, NULL
);
472 static int on_dns_stub_packet_extra(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
473 DnsStubListenerExtra
*l
= userdata
;
477 return on_dns_stub_packet_internal(s
, fd
, revents
, l
->manager
, l
);
480 static int on_dns_stub_stream_packet(DnsStream
*s
) {
481 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
485 p
= dns_stream_take_read_packet(s
);
488 if (dns_packet_validate_query(p
) > 0) {
489 log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p
));
491 dns_stub_process_query(s
->manager
, s
->stub_listener_extra
, s
, p
);
493 log_debug("Invalid DNS stub TCP packet, ignoring.");
498 static int on_dns_stub_stream_internal(sd_event_source
*s
, int fd
, uint32_t revents
, Manager
*m
, DnsStubListenerExtra
*l
) {
502 cfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
504 if (ERRNO_IS_ACCEPT_AGAIN(errno
))
510 r
= dns_stream_new(m
, &stream
, DNS_STREAM_STUB
, DNS_PROTOCOL_DNS
, cfd
, NULL
);
516 stream
->stub_listener_extra
= l
;
517 stream
->on_packet
= on_dns_stub_stream_packet
;
518 stream
->complete
= dns_stub_stream_complete
;
520 /* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */
525 static int on_dns_stub_stream(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
526 return on_dns_stub_stream_internal(s
, fd
, revents
, userdata
, NULL
);
529 static int on_dns_stub_stream_extra(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
530 DnsStubListenerExtra
*l
= userdata
;
533 return on_dns_stub_stream_internal(s
, fd
, revents
, l
->manager
, l
);
536 static int set_dns_stub_common_socket_options(int fd
, int family
) {
540 assert(IN_SET(family
, AF_INET
, AF_INET6
));
542 r
= setsockopt_int(fd
, SOL_SOCKET
, SO_REUSEADDR
, true);
546 r
= socket_set_recvpktinfo(fd
, family
, true);
550 r
= socket_set_recvttl(fd
, family
, true);
557 static int manager_dns_stub_fd(Manager
*m
, int type
) {
558 union sockaddr_union sa
= {
559 .in
.sin_family
= AF_INET
,
560 .in
.sin_addr
.s_addr
= htobe32(INADDR_DNS_STUB
),
561 .in
.sin_port
= htobe16(53),
563 _cleanup_close_
int fd
= -1;
566 assert(IN_SET(type
, SOCK_DGRAM
, SOCK_STREAM
));
568 sd_event_source
**event_source
= type
== SOCK_DGRAM
? &m
->dns_stub_udp_event_source
: &m
->dns_stub_tcp_event_source
;
570 return sd_event_source_get_io_fd(*event_source
);
572 fd
= socket(AF_INET
, type
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
576 r
= set_dns_stub_common_socket_options(fd
, AF_INET
);
580 /* Make sure no traffic from outside the local host can leak to onto this socket */
581 r
= socket_bind_to_ifindex(fd
, LOOPBACK_IFINDEX
);
585 r
= setsockopt_int(fd
, IPPROTO_IP
, IP_TTL
, 1);
589 if (bind(fd
, &sa
.sa
, sizeof(sa
.in
)) < 0)
592 if (type
== SOCK_STREAM
&&
593 listen(fd
, SOMAXCONN
) < 0)
596 r
= sd_event_add_io(m
->event
, event_source
, fd
, EPOLLIN
,
597 type
== SOCK_DGRAM
? on_dns_stub_packet
: on_dns_stub_stream
,
602 r
= sd_event_source_set_io_fd_own(*event_source
, true);
606 (void) sd_event_source_set_description(*event_source
,
607 type
== SOCK_DGRAM
? "dns-stub-udp" : "dns-stub-tcp");
612 static int manager_dns_stub_fd_extra(Manager
*m
, DnsStubListenerExtra
*l
, int type
) {
613 _cleanup_free_
char *pretty
= NULL
;
614 _cleanup_close_
int fd
= -1;
615 union sockaddr_union sa
;
619 assert(IN_SET(type
, SOCK_DGRAM
, SOCK_STREAM
));
622 return manager_dns_stub_fd(m
, type
);
624 sd_event_source
**event_source
= type
== SOCK_DGRAM
? &l
->udp_event_source
: &l
->tcp_event_source
;
626 return sd_event_source_get_io_fd(*event_source
);
628 if (l
->family
== AF_INET
)
629 sa
= (union sockaddr_union
) {
630 .in
.sin_family
= l
->family
,
631 .in
.sin_port
= htobe16(l
->port
!= 0 ? l
->port
: 53U),
632 .in
.sin_addr
= l
->address
.in
,
635 sa
= (union sockaddr_union
) {
636 .in6
.sin6_family
= l
->family
,
637 .in6
.sin6_port
= htobe16(l
->port
!= 0 ? l
->port
: 53U),
638 .in6
.sin6_addr
= l
->address
.in6
,
641 fd
= socket(l
->family
, type
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
647 r
= set_dns_stub_common_socket_options(fd
, l
->family
);
651 /* Do not set IP_TTL for extra DNS stub listeners, as the address may not be local and in that case
652 * people may want ttl > 1. */
654 r
= socket_set_freebind(fd
, l
->family
, true);
658 if (bind(fd
, &sa
.sa
, SOCKADDR_LEN(sa
)) < 0) {
663 if (type
== SOCK_STREAM
&&
664 listen(fd
, SOMAXCONN
) < 0) {
669 r
= sd_event_add_io(m
->event
, event_source
, fd
, EPOLLIN
,
670 type
== SOCK_DGRAM
? on_dns_stub_packet_extra
: on_dns_stub_stream_extra
,
675 r
= sd_event_source_set_io_fd_own(*event_source
, true);
679 (void) sd_event_source_set_description(*event_source
,
680 type
== SOCK_DGRAM
? "dns-stub-udp-extra" : "dns-stub-tcp-extra");
683 (void) in_addr_port_to_string(l
->family
, &l
->address
, l
->port
, &pretty
);
684 log_debug("Listening on %s socket %s.",
685 type
== SOCK_DGRAM
? "UDP" : "TCP",
693 (void) in_addr_port_to_string(l
->family
, &l
->address
, l
->port
, &pretty
);
694 return log_warning_errno(r
,
695 r
== -EADDRINUSE
? "Another process is already listening on %s socket %s: %m" :
696 "Failed to listen on %s socket %s: %m",
697 type
== SOCK_DGRAM
? "UDP" : "TCP",
701 int manager_dns_stub_start(Manager
*m
) {
702 const char *t
= "UDP";
707 if (m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_NO
)
708 log_debug("Not creating stub listener.");
710 log_debug("Creating stub listener using %s.",
711 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_UDP
? "UDP" :
712 m
->dns_stub_listener_mode
== DNS_STUB_LISTENER_TCP
? "TCP" :
715 if (FLAGS_SET(m
->dns_stub_listener_mode
, DNS_STUB_LISTENER_UDP
))
716 r
= manager_dns_stub_fd(m
, SOCK_DGRAM
);
719 FLAGS_SET(m
->dns_stub_listener_mode
, DNS_STUB_LISTENER_TCP
)) {
721 r
= manager_dns_stub_fd(m
, SOCK_STREAM
);
724 if (IN_SET(r
, -EADDRINUSE
, -EPERM
)) {
726 r
== -EADDRINUSE
? "Another process is already listening on %s socket 127.0.0.53:53.\n"
727 "Turning off local DNS stub support." :
728 "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
729 "Turning off local DNS stub support.",
731 manager_dns_stub_stop(m
);
733 return log_error_errno(r
, "Failed to listen on %s socket 127.0.0.53:53: %m", t
);
735 if (!ordered_set_isempty(m
->dns_extra_stub_listeners
)) {
736 DnsStubListenerExtra
*l
;
738 log_debug("Creating extra stub listeners.");
740 ORDERED_SET_FOREACH(l
, m
->dns_extra_stub_listeners
) {
741 if (FLAGS_SET(l
->mode
, DNS_STUB_LISTENER_UDP
))
742 (void) manager_dns_stub_fd_extra(m
, l
, SOCK_DGRAM
);
743 if (FLAGS_SET(l
->mode
, DNS_STUB_LISTENER_TCP
))
744 (void) manager_dns_stub_fd_extra(m
, l
, SOCK_STREAM
);
751 void manager_dns_stub_stop(Manager
*m
) {
754 m
->dns_stub_udp_event_source
= sd_event_source_unref(m
->dns_stub_udp_event_source
);
755 m
->dns_stub_tcp_event_source
= sd_event_source_unref(m
->dns_stub_tcp_event_source
);
758 static const char* const dns_stub_listener_mode_table
[_DNS_STUB_LISTENER_MODE_MAX
] = {
759 [DNS_STUB_LISTENER_NO
] = "no",
760 [DNS_STUB_LISTENER_UDP
] = "udp",
761 [DNS_STUB_LISTENER_TCP
] = "tcp",
762 [DNS_STUB_LISTENER_YES
] = "yes",
764 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode
, DnsStubListenerMode
, DNS_STUB_LISTENER_YES
);