2 This file is part of systemd.
4 Copyright 2016 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "resolved-dns-stub.h"
22 #include "socket-util.h"
24 /* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
25 * IP and UDP header sizes */
26 #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
28 static int dns_stub_make_reply_packet(
33 bool add_opt
, /* add an OPT RR to this packet */
34 bool edns0_do
, /* set the EDNS0 DNSSEC OK bit */
35 bool ad
, /* set the DNSSEC authenticated data bit */
38 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
39 DnsResourceRecord
*rr
;
43 /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
44 * roundtrips aren't expensive. */
46 r
= dns_packet_new(&p
, DNS_PROTOCOL_DNS
, 0);
50 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
51 if (!add_opt
&& rcode
> 0xF)
52 rcode
= DNS_RCODE_SERVFAIL
;
54 DNS_PACKET_HEADER(p
)->id
= id
;
55 DNS_PACKET_HEADER(p
)->flags
= htobe16(DNS_PACKET_MAKE_FLAGS(
66 r
= dns_packet_append_question(p
, q
);
69 DNS_PACKET_HEADER(p
)->qdcount
= htobe16(dns_question_size(q
));
71 DNS_ANSWER_FOREACH(rr
, answer
) {
72 r
= dns_question_matches_rr(q
, rr
, NULL
);
78 r
= dns_question_matches_cname_or_dname(q
, rr
, NULL
);
86 r
= dns_packet_append_rr(p
, rr
, NULL
, NULL
);
92 DNS_PACKET_HEADER(p
)->ancount
= htobe16(c
);
95 r
= dns_packet_append_opt(p
, ADVERTISE_DATAGRAM_SIZE_MAX
, edns0_do
, rcode
, NULL
);
106 static void dns_stub_detach_stream(DnsStream
*s
) {
114 static int dns_stub_send(Manager
*m
, DnsStream
*s
, DnsPacket
*p
, DnsPacket
*reply
) {
122 r
= dns_stream_write_packet(s
, reply
);
126 /* Truncate the message to the right size */
127 if (reply
->size
> DNS_PACKET_PAYLOAD_SIZE_MAX(p
)) {
128 dns_packet_truncate(reply
, DNS_PACKET_UNICAST_SIZE_MAX
);
129 DNS_PACKET_HEADER(reply
)->flags
= htobe16(be16toh(DNS_PACKET_HEADER(reply
)->flags
) | DNS_PACKET_FLAG_TC
);
132 fd
= manager_dns_stub_udp_fd(m
);
134 return log_debug_errno(fd
, "Failed to get reply socket: %m");
136 /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
137 * is because otherwise the kernel will choose it automatically based on the routing table and will
138 * thus pick 127.0.0.1 rather than 127.0.0.53. */
140 r
= manager_send(m
, fd
, LOOPBACK_IFINDEX
, p
->family
, &p
->sender
, p
->sender_port
, &p
->destination
, reply
);
143 return log_debug_errno(r
, "Failed to send reply packet: %m");
148 static int dns_stub_send_failure(Manager
*m
, DnsStream
*s
, DnsPacket
*p
, int rcode
) {
149 _cleanup_(dns_packet_unrefp
) DnsPacket
*reply
= NULL
;
155 r
= dns_stub_make_reply_packet(DNS_PACKET_ID(p
), rcode
, p
->question
, NULL
, !!p
->opt
, DNS_PACKET_DO(p
), false, &reply
);
157 return log_debug_errno(r
, "Failed to build failure packet: %m");
159 return dns_stub_send(m
, s
, p
, reply
);
162 static void dns_stub_query_complete(DnsQuery
*q
) {
166 assert(q
->request_dns_packet
);
170 case DNS_TRANSACTION_SUCCESS
: {
171 _cleanup_(dns_packet_unrefp
) DnsPacket
*reply
= NULL
;
173 r
= dns_stub_make_reply_packet(
174 DNS_PACKET_ID(q
->request_dns_packet
),
178 !!q
->request_dns_packet
->opt
,
179 DNS_PACKET_DO(q
->request_dns_packet
),
180 DNS_PACKET_DO(q
->request_dns_packet
) && q
->answer_authenticated
,
183 log_debug_errno(r
, "Failed to build reply packet: %m");
187 (void) dns_stub_send(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, reply
);
191 case DNS_TRANSACTION_RCODE_FAILURE
:
192 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, q
->answer_rcode
);
195 case DNS_TRANSACTION_NOT_FOUND
:
196 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_NXDOMAIN
);
199 case DNS_TRANSACTION_TIMEOUT
:
200 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED
:
201 /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
204 case DNS_TRANSACTION_NO_SERVERS
:
205 case DNS_TRANSACTION_INVALID_REPLY
:
206 case DNS_TRANSACTION_ERRNO
:
207 case DNS_TRANSACTION_ABORTED
:
208 case DNS_TRANSACTION_DNSSEC_FAILED
:
209 case DNS_TRANSACTION_NO_TRUST_ANCHOR
:
210 case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED
:
211 case DNS_TRANSACTION_NETWORK_DOWN
:
212 (void) dns_stub_send_failure(q
->manager
, q
->request_dns_stream
, q
->request_dns_packet
, DNS_RCODE_SERVFAIL
);
215 case DNS_TRANSACTION_NULL
:
216 case DNS_TRANSACTION_PENDING
:
217 case DNS_TRANSACTION_VALIDATING
:
219 assert_not_reached("Impossible state");
222 /* If there's a packet to write set, let's leave the stream around */
223 if (q
->request_dns_stream
&& DNS_STREAM_QUEUED(q
->request_dns_stream
)) {
225 /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The
226 * default completion action of the stream will drop the reference. */
228 dns_stub_detach_stream(q
->request_dns_stream
);
229 q
->request_dns_stream
= NULL
;
235 static int dns_stub_stream_complete(DnsStream
*s
, int error
) {
238 log_debug_errno(error
, "DNS TCP connection terminated, destroying query: %m");
241 dns_query_free(s
->query
);
246 static void dns_stub_process_query(Manager
*m
, DnsStream
*s
, DnsPacket
*p
) {
252 assert(p
->protocol
== DNS_PROTOCOL_DNS
);
254 /* Takes ownership of the *s stream object */
256 if (in_addr_is_localhost(p
->family
, &p
->sender
) <= 0 ||
257 in_addr_is_localhost(p
->family
, &p
->destination
) <= 0) {
258 log_error("Got packet on unexpected IP range, refusing.");
259 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
);
263 r
= dns_packet_extract(p
);
265 log_debug_errno(r
, "Failed to extract resources from incoming packet, ignoring packet: %m");
266 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_FORMERR
);
270 if (!DNS_PACKET_VERSION_SUPPORTED(p
)) {
271 log_debug("Got EDNS OPT field with unsupported version number.");
272 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_BADVERS
);
276 if (dns_type_is_obsolete(p
->question
->keys
[0]->type
)) {
277 log_debug("Got message with obsolete key type, refusing.");
278 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
);
282 if (dns_type_is_zone_transer(p
->question
->keys
[0]->type
)) {
283 log_debug("Got request for zone transfer, refusing.");
284 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
);
288 if (!DNS_PACKET_RD(p
)) {
289 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
290 log_debug("Got request with recursion disabled, refusing.");
291 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_REFUSED
);
295 if (DNS_PACKET_DO(p
) && DNS_PACKET_CD(p
)) {
296 log_debug("Got request with DNSSEC CD bit set, refusing.");
297 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_NOTIMP
);
301 r
= dns_query_new(m
, &q
, p
->question
, p
->question
, 0, SD_RESOLVED_PROTOCOLS_ALL
|SD_RESOLVED_NO_SEARCH
|SD_RESOLVED_NO_CNAME
);
303 log_error_errno(r
, "Failed to generate query object: %m");
304 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
);
308 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
311 q
->request_dns_packet
= dns_packet_ref(p
);
312 q
->request_dns_stream
= dns_stream_ref(s
); /* make sure the stream stays around until we can send a reply through it */
313 q
->complete
= dns_stub_query_complete
;
317 s
->complete
= dns_stub_stream_complete
;
323 log_error_errno(r
, "Failed to start query: %m");
324 dns_stub_send_failure(m
, s
, p
, DNS_RCODE_SERVFAIL
);
328 log_info("Processing query...");
332 if (s
&& DNS_STREAM_QUEUED(s
))
333 dns_stub_detach_stream(s
);
338 static int on_dns_stub_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
339 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
340 Manager
*m
= userdata
;
343 r
= manager_recv(m
, fd
, DNS_PROTOCOL_DNS
, &p
);
347 if (dns_packet_validate_query(p
) > 0) {
348 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p
));
350 dns_stub_process_query(m
, NULL
, p
);
352 log_debug("Invalid DNS stub UDP packet, ignoring.");
357 int manager_dns_stub_udp_fd(Manager
*m
) {
358 static const int one
= 1;
360 union sockaddr_union sa
= {
361 .in
.sin_family
= AF_INET
,
362 .in
.sin_port
= htobe16(53),
363 .in
.sin_addr
.s_addr
= htobe32(INADDR_DNS_STUB
),
368 if (m
->dns_stub_udp_fd
>= 0)
369 return m
->dns_stub_udp_fd
;
371 m
->dns_stub_udp_fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
372 if (m
->dns_stub_udp_fd
< 0)
375 r
= setsockopt(m
->dns_stub_udp_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
381 r
= setsockopt(m
->dns_stub_udp_fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
));
387 r
= setsockopt(m
->dns_stub_udp_fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof(one
));
393 /* Make sure no traffic from outside the local host can leak to onto this socket */
394 r
= setsockopt(m
->dns_stub_udp_fd
, SOL_SOCKET
, SO_BINDTODEVICE
, "lo", 3);
400 r
= bind(m
->dns_stub_udp_fd
, &sa
.sa
, sizeof(sa
.in
));
406 r
= sd_event_add_io(m
->event
, &m
->dns_stub_udp_event_source
, m
->dns_stub_udp_fd
, EPOLLIN
, on_dns_stub_packet
, m
);
410 (void) sd_event_source_set_description(m
->dns_stub_udp_event_source
, "dns-stub-udp");
412 return m
->dns_stub_udp_fd
;
415 m
->dns_stub_udp_fd
= safe_close(m
->dns_stub_udp_fd
);
419 static int on_dns_stub_stream_packet(DnsStream
*s
) {
421 assert(s
->read_packet
);
423 if (dns_packet_validate_query(s
->read_packet
) > 0) {
424 log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s
->read_packet
));
426 dns_stub_process_query(s
->manager
, s
, s
->read_packet
);
428 log_debug("Invalid DNS stub TCP packet, ignoring.");
430 /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now,
431 * or that didn't happen in which case we want to free the stream */
437 static int on_dns_stub_stream(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
439 Manager
*m
= userdata
;
442 cfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
444 if (errno
== EAGAIN
|| errno
== EINTR
)
450 r
= dns_stream_new(m
, &stream
, DNS_PROTOCOL_DNS
, cfd
);
456 stream
->on_packet
= on_dns_stub_stream_packet
;
458 /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action
459 * of the stream, or by our packet callback, or when the manager is shut down. */
464 int manager_dns_stub_tcp_fd(Manager
*m
) {
465 static const int one
= 1;
467 union sockaddr_union sa
= {
468 .in
.sin_family
= AF_INET
,
469 .in
.sin_addr
.s_addr
= htobe32(INADDR_DNS_STUB
),
470 .in
.sin_port
= htobe16(53),
475 if (m
->dns_stub_tcp_fd
>= 0)
476 return m
->dns_stub_tcp_fd
;
478 m
->dns_stub_tcp_fd
= socket(AF_INET
, SOCK_STREAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
479 if (m
->dns_stub_tcp_fd
< 0)
482 r
= setsockopt(m
->dns_stub_tcp_fd
, IPPROTO_IP
, IP_TTL
, &one
, sizeof(one
));
488 r
= setsockopt(m
->dns_stub_tcp_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
494 r
= setsockopt(m
->dns_stub_tcp_fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
));
500 r
= setsockopt(m
->dns_stub_tcp_fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof(one
));
506 /* Make sure no traffic from outside the local host can leak to onto this socket */
507 r
= setsockopt(m
->dns_stub_tcp_fd
, SOL_SOCKET
, SO_BINDTODEVICE
, "lo", 3);
513 r
= bind(m
->dns_stub_tcp_fd
, &sa
.sa
, sizeof(sa
.in
));
519 r
= listen(m
->dns_stub_tcp_fd
, SOMAXCONN
);
525 r
= sd_event_add_io(m
->event
, &m
->dns_stub_tcp_event_source
, m
->dns_stub_tcp_fd
, EPOLLIN
, on_dns_stub_stream
, m
);
529 (void) sd_event_source_set_description(m
->dns_stub_tcp_event_source
, "dns-stub-tcp");
531 return m
->dns_stub_tcp_fd
;
534 m
->dns_stub_tcp_fd
= safe_close(m
->dns_stub_tcp_fd
);
538 int manager_dns_stub_start(Manager
*m
) {
543 r
= manager_dns_stub_udp_fd(m
);
544 if (r
== -EADDRINUSE
)
549 r
= manager_dns_stub_tcp_fd(m
);
550 if (r
== -EADDRINUSE
)
558 log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support.");
559 manager_dns_stub_stop(m
);
564 void manager_dns_stub_stop(Manager
*m
) {
567 m
->dns_stub_udp_event_source
= sd_event_source_unref(m
->dns_stub_udp_event_source
);
568 m
->dns_stub_tcp_event_source
= sd_event_source_unref(m
->dns_stub_tcp_event_source
);
570 m
->dns_stub_udp_fd
= safe_close(m
->dns_stub_udp_fd
);
571 m
->dns_stub_tcp_fd
= safe_close(m
->dns_stub_tcp_fd
);