/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
+#include "errno-util.h"
#include "fd-util.h"
+#include "missing_network.h"
#include "resolved-dns-stub.h"
#include "socket-util.h"
assert(p);
- if (!add_opt) {
+ if (add_opt) {
+ r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
+ if (r == -EMSGSIZE) /* Hit the size limit? then indicate truncation */
+ tc = true;
+ else if (r < 0)
+ return r;
+
+ } else {
/* If the client can't to EDNS0, don't do DO either */
edns0_do = false;
0 /* cd */,
rcode));
- if (add_opt) {
- r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
- if (r < 0)
- return r;
- }
-
return 0;
}
-static void dns_stub_detach_stream(DnsStream *s) {
- assert(s);
-
- s->complete = NULL;
- s->on_packet = NULL;
- s->query = NULL;
-}
-
static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) {
int r;
break;
}
- r = dns_query_process_cname(q);
- if (r == -ELOOP) {
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
- break;
+ if (!truncated) {
+ r = dns_query_process_cname(q);
+ if (r == -ELOOP) {
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
+ break;
+ }
+ if (r < 0) {
+ log_debug_errno(r, "Failed to process CNAME: %m");
+ break;
+ }
+ if (r == DNS_QUERY_RESTARTED)
+ return;
}
- if (r < 0) {
- log_debug_errno(r, "Failed to process CNAME: %m");
- break;
- }
- if (r == DNS_QUERY_RESTARTED)
- return;
r = dns_stub_finish_reply_packet(
q->reply_dns_packet,
assert_not_reached("Impossible state");
}
- /* If there's a packet to write set, let's leave the stream around */
- if (q->request_dns_stream && DNS_STREAM_QUEUED(q->request_dns_stream)) {
-
- /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The
- * default completion action of the stream will drop the reference. */
-
- dns_stub_detach_stream(q->request_dns_stream);
- q->request_dns_stream = NULL;
- }
-
dns_query_free(q);
}
static int dns_stub_stream_complete(DnsStream *s, int error) {
assert(s);
- log_debug_errno(error, "DNS TCP connection terminated, destroying query: %m");
+ log_debug_errno(error, "DNS TCP connection terminated, destroying queries: %m");
+
+ for (;;) {
+ DnsQuery *q;
- assert(s->query);
- dns_query_free(s->query);
+ q = set_first(s->queries);
+ if (!q)
+ break;
+
+ dns_query_free(q);
+ }
+ /* This drops the implicit ref we keep around since it was allocated, as incoming stub connections
+ * should be kept as long as the client wants to. */
+ dns_stream_unref(s);
return 0;
}
assert(p);
assert(p->protocol == DNS_PROTOCOL_DNS);
- /* Takes ownership of the *s stream object */
-
if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
in_addr_is_localhost(p->family, &p->destination) <= 0) {
log_error("Got packet on unexpected IP range, refusing.");
q->complete = dns_stub_query_complete;
if (s) {
- s->on_packet = NULL;
- s->complete = dns_stub_stream_complete;
- s->query = q;
+ /* Remember which queries belong to this stream, so that we can cancel them when the stream
+ * is disconnected early */
+
+ r = set_ensure_allocated(&s->queries, &trivial_hash_ops);
+ if (r < 0) {
+ log_oom();
+ goto fail;
+ }
+
+ if (set_put(s->queries, q) < 0) {
+ log_oom();
+ goto fail;
+ }
}
r = dns_query_go(q);
return;
fail:
- if (s && DNS_STREAM_QUEUED(s))
- dns_stub_detach_stream(s);
-
dns_query_free(q);
}
}
static int manager_dns_stub_udp_fd(Manager *m) {
- static const int one = 1;
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(53),
if (fd < 0)
return -errno;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
+ if (r < 0)
+ return r;
/* Make sure no traffic from outside the local host can leak to onto this socket */
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
- return -errno;
+ r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
+ if (r < 0)
+ return r;
if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
return -errno;
return r;
(void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
- m->dns_stub_udp_fd = fd;
- fd = -1;
- return m->dns_stub_udp_fd;
+ return m->dns_stub_udp_fd = TAKE_FD(fd);
}
static int on_dns_stub_stream_packet(DnsStream *s) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+
assert(s);
- assert(s->read_packet);
- if (dns_packet_validate_query(s->read_packet) > 0) {
- log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
+ p = dns_stream_take_read_packet(s);
+ assert(p);
+
+ if (dns_packet_validate_query(p) > 0) {
+ log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p));
- dns_stub_process_query(s->manager, s, s->read_packet);
+ dns_stub_process_query(s->manager, s, p);
} else
log_debug("Invalid DNS stub TCP packet, ignoring.");
- /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now,
- * or that didn't happen in which case we want to free the stream */
- dns_stream_unref(s);
-
return 0;
}
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (cfd < 0) {
- if (IN_SET(errno, EAGAIN, EINTR))
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
return 0;
return -errno;
}
- r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd);
+ r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL);
if (r < 0) {
safe_close(cfd);
return r;
}
stream->on_packet = on_dns_stub_stream_packet;
+ stream->complete = dns_stub_stream_complete;
- /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action
- * of the stream, or by our packet callback, or when the manager is shut down. */
+ /* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */
return 0;
}
static int manager_dns_stub_tcp_fd(Manager *m) {
- static const int one = 1;
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
if (fd < 0)
return -errno;
- if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
+ if (r < 0)
+ return r;
/* Make sure no traffic from outside the local host can leak to onto this socket */
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
- return -errno;
+ r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
+ if (r < 0)
+ return r;
if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
return -errno;
return r;
(void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
- m->dns_stub_tcp_fd = fd;
- fd = -1;
- return m->dns_stub_tcp_fd;
+ return m->dns_stub_tcp_fd = TAKE_FD(fd);
}
int manager_dns_stub_start(Manager *m) {