summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSusant Sahani <ssahani@vmware.com>2020-08-29 07:12:10 +0000
committerSusant Sahani <ssahani@vmware.com>2020-09-03 08:27:41 +0000
commit1f05101fb6a40fbd742f9fd7333d3b3ec2273daa (patch)
tree840763a058da9c7f673be4ebdf70ca8ad1ac343f
parent1f1f3210c91d35b9516a0a0f5471aa3e8ee024c3 (diff)
downloadsystemd-1f05101fb6a40fbd742f9fd7333d3b3ec2273daa.tar.gz
resolve: allow configurable bind address
-rw-r--r--man/resolved.conf.xml25
-rw-r--r--src/resolve/resolved-conf.c152
-rw-r--r--src/resolve/resolved-conf.h1
-rw-r--r--src/resolve/resolved-dns-stub.c208
-rw-r--r--src/resolve/resolved-dns-stub.h3
-rw-r--r--src/resolve/resolved-gperf.gperf23
-rw-r--r--src/resolve/resolved-manager.c3
-rw-r--r--src/resolve/resolved-manager.h10
-rw-r--r--test/fuzz/fuzz-unit-file/directives.service1
9 files changed, 396 insertions, 30 deletions
diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
index 535a23f500..338bee6c38 100644
--- a/man/resolved.conf.xml
+++ b/man/resolved.conf.xml
@@ -270,6 +270,31 @@
</varlistentry>
<varlistentry>
+ <term><varname>DNSStubListenerExtra=</varname></term>
+ <listitem><para>Takes an IPv4 or IPv6 address to listen on. The address may optionally be prefixed by <literal>:</literal> and
+ a protocol name (<literal>udp</literal> or <literal>tcp</literal>). When an IPv6 address is specified with a port number, then the
+ address must be in the square brackets. This option can be specified multiple times. If an empty string is assigned, then the all
+ previous assignments are cleared. It may also be optionally suffixed by a numeric port number with separator <literal>:</literal>.
+ If the protocol is not specified, the service will listen on both <literal>UDP</literal> and <literal>TCP</literal>. If the port is not
+ specified, then the service takes port as 53. This option may be used multiple times. Note that this is independent of the
+ primary DNS stub configured with <varname>DNSStubListener=</varname>, and only configures <emphasis>additional</emphasis>
+ sockets to listen on. Defaults to unset.</para>
+
+ <para>If the string in the format <literal>udp</literal>:[<replaceable>x</replaceable>]:<replaceable>y</replaceable>,
+ it is read as protocol <literal>udp</literal> and IPv6 address x on a port y.</para>
+
+ <para>If the string in the format [<replaceable>x</replaceable>]:<replaceable>y</replaceable>, it is read as both protocol
+ <literal>udp</literal> and <literal>tcp</literal>, IPv6 address x on a port y.</para>
+
+ <para>If the string in the format <replaceable>x</replaceable>, it is read as protocol both <literal>udp</literal> and
+ <literal>tcp</literal>, IPv6/IPv4 address x on a port 53.</para>
+
+ <para>If the string in the format <literal>udp</literal>:<replaceable>x</replaceable>:<replaceable>y</replaceable>,
+ it is read as protocol <literal>udp</literal> and IPv4 address x on a port y.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>ReadEtcHosts=</varname></term>
<listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default),
<command>systemd-resolved</command> will read <filename>/etc/hosts</filename>, and try to resolve
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index 6b99271245..3b2b4d2063 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -10,11 +10,13 @@
#include "resolved-dnssd.h"
#include "resolved-manager.h"
#include "resolved-dns-search-domain.h"
+#include "resolved-dns-stub.h"
#include "dns-domain.h"
#include "socket-netlink.h"
#include "specifier.h"
#include "string-table.h"
#include "string-util.h"
+#include "strv.h"
#include "utf8.h"
DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
@@ -27,6 +29,51 @@ static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MA
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
+static void dns_stub_listener_extra_hash_func(const DNSStubListenerExtra *a, struct siphash *state) {
+ unsigned port;
+
+ assert(a);
+
+ siphash24_compress(&a->mode, sizeof(a->mode), state);
+ siphash24_compress(&socket_address_family(&a->address), sizeof(a->address.type), state);
+ siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(socket_address_family(&a->address)), state);
+
+ (void) sockaddr_port(&a->address.sockaddr.sa, &port);
+ siphash24_compress(&port, sizeof(port), state);
+}
+
+static int dns_stub_listener_extra_compare_func(const DNSStubListenerExtra *a, const DNSStubListenerExtra *b) {
+ unsigned p, q;
+ int r;
+
+ assert(a);
+ assert(b);
+
+ r = CMP(a->mode, b->mode);
+ if (r != 0)
+ return r;
+
+ r = CMP(socket_address_family(&a->address), socket_address_family(&b->address));
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(socket_address_family(&a->address)));
+ if (r != 0)
+ return r;
+
+ (void) sockaddr_port(&a->address.sockaddr.sa, &p);
+ (void) sockaddr_port(&b->address.sockaddr.sa, &q);
+
+ return CMP(p, q);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ dns_stub_listener_extra_hash_ops,
+ DNSStubListenerExtra,
+ dns_stub_listener_extra_hash_func,
+ dns_stub_listener_extra_compare_func,
+ free);
+
static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
_cleanup_free_ char *server_name = NULL;
union in_addr_union address;
@@ -385,6 +432,111 @@ int config_parse_dnssd_txt(const char *unit, const char *filename, unsigned line
return 0;
}
+int config_parse_dns_stub_listener_extra(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ DNSStubListenerExtra *udp = NULL, *tcp = NULL;
+ _cleanup_free_ char *word = NULL;
+ Manager *m = userdata;
+ bool both = false;
+ const char *p;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
+ return 0;
+ }
+
+ p = rvalue;
+ r = extract_first_word(&p, &word, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r <= 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid DNSStubListenExtra='%s', ignoring assignment", rvalue);
+ return 0;
+ }
+
+ /* First look for udp/tcp. If not specified then turn both TCP and UDP */
+ if (!STR_IN_SET(word, "tcp", "udp")) {
+ both = true;
+ p = rvalue;
+ }
+
+ if (streq(word, "tcp") || both) {
+ r = dns_stub_extra_new(&tcp);
+ if (r < 0)
+ return log_oom();
+
+ tcp->mode = DNS_STUB_LISTENER_TCP;
+ }
+
+ if (streq(word, "udp") || both) {
+ r = dns_stub_extra_new(&udp);
+ if (r < 0)
+ return log_oom();
+
+ udp->mode = DNS_STUB_LISTENER_UDP;
+ }
+
+ if (tcp) {
+ r = socket_addr_port_from_string_auto(p, 53, &tcp->address);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address in DNSStubListenExtra='%s', ignoring", rvalue);
+ return 0;
+ }
+ }
+
+ if (udp) {
+ r = socket_addr_port_from_string_auto(p, 53, &udp->address);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address in DNSStubListenExtra='%s', ignoring", rvalue);
+ return 0;
+ }
+ }
+
+ if (tcp) {
+ r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, tcp);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ return log_oom();
+
+ log_warning_errno(r, "Failed to store TCP DNSStubListenExtra='%s', ignoring assignment: %m", rvalue);
+ return 0;
+ }
+ }
+
+ if (udp) {
+ r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, udp);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ return log_oom();
+
+ log_warning_errno(r, "Failed to store UDP DNSStubListenExtra='%s', ignoring assignment: %m", rvalue);
+ return 0;
+ }
+ }
+
+ TAKE_PTR(tcp);
+ TAKE_PTR(udp);
+
+ return 0;
+}
+
int manager_parse_config_file(Manager *m) {
int r;
diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h
index ac3937cfae..f8d16b5a8e 100644
--- a/src/resolve/resolved-conf.h
+++ b/src/resolve/resolved-conf.h
@@ -30,6 +30,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dns_stub_listener_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssd_service_name);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssd_service_type);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssd_txt);
+CONFIG_PARSER_PROTOTYPE(config_parse_dns_stub_listener_extra);
const char* dns_stub_listener_mode_to_string(DnsStubListenerMode p) _const_;
DnsStubListenerMode dns_stub_listener_mode_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 03edbe26dc..9cb8797458 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -4,6 +4,7 @@
#include "fd-util.h"
#include "missing_network.h"
#include "resolved-dns-stub.h"
+#include "socket-netlink.h"
#include "socket-util.h"
/* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
@@ -13,6 +14,22 @@
static int manager_dns_stub_udp_fd(Manager *m);
static int manager_dns_stub_tcp_fd(Manager *m);
+int dns_stub_extra_new(DNSStubListenerExtra **ret) {
+ DNSStubListenerExtra *l;
+
+ l = new(DNSStubListenerExtra, 1);
+ if (!l)
+ return -ENOMEM;
+
+ *l = (DNSStubListenerExtra) {
+ .fd = -1,
+ };
+
+ *ret = TAKE_PTR(l);
+
+ return 0;
+}
+
static int dns_stub_make_reply_packet(
DnsPacket **p,
size_t max_size,
@@ -386,6 +403,22 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
+static int set_dns_stub_common_socket_options(int fd) {
+ int r;
+
+ assert(fd >= 0);
+
+ r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return r;
+
+ r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return r;
+
+ return setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
+}
+
static int manager_dns_stub_udp_fd(Manager *m) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
@@ -402,15 +435,7 @@ static int manager_dns_stub_udp_fd(Manager *m) {
if (fd < 0)
return -errno;
- r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
- if (r < 0)
- return r;
-
- r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
- if (r < 0)
- return r;
-
- r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
+ r = set_dns_stub_common_socket_options(fd);
if (r < 0)
return r;
@@ -431,6 +456,64 @@ static int manager_dns_stub_udp_fd(Manager *m) {
return m->dns_stub_udp_fd = TAKE_FD(fd);
}
+static int manager_dns_stub_udp_fd_extra(Manager *m, DNSStubListenerExtra *l) {
+ _cleanup_free_ char *pretty = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ if (l->fd >= 0)
+ return 0;
+
+ fd = socket(socket_address_family(&l->address), SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
+ if (r < 0)
+ goto fail;
+
+ r = set_dns_stub_common_socket_options(fd);
+ if (r < 0)
+ goto fail;
+
+ if (bind(fd, &l->address.sockaddr.sa, l->address.size) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = sd_event_add_io(m->event, &l->dns_stub_extra_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(l->dns_stub_extra_event_source, "dns-stub-udp-extra");
+
+ l->fd = TAKE_FD(fd);
+
+ if (DEBUG_LOGGING) {
+ (void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
+ log_debug("Listening on UDP socket %s.", strnull(pretty));
+ }
+
+ return 0;
+
+ fail:
+ (void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
+ if (r == -EADDRINUSE)
+ return log_warning_errno(r,
+ "Another process is already listening on UDP socket %s.\n"
+ "Turning off local DNS stub extra support.", strnull(pretty));
+ if (r == -EPERM)
+ return log_warning_errno(r,
+ "Failed to listen on UDP socket %s: %m.\n"
+ "Turning off local DNS stub extra support.", strnull(pretty));
+
+ assert(r < 0);
+
+ return log_warning_errno(r, "Failed to listen on UDP socket %s, ignoring: %m", strnull(pretty));
+}
+
static int on_dns_stub_stream_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
@@ -492,19 +575,11 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
if (fd < 0)
return -errno;
- r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, true);
+ r = set_dns_stub_common_socket_options(fd);
if (r < 0)
return r;
- r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
- if (r < 0)
- return r;
-
- r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
- if (r < 0)
- return r;
-
- r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, true);
if (r < 0)
return r;
@@ -528,6 +603,73 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
return m->dns_stub_tcp_fd = TAKE_FD(fd);
}
+static int manager_dns_stub_tcp_fd_extra(Manager *m, DNSStubListenerExtra *l) {
+ _cleanup_free_ char *pretty = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ if (l->fd >= 0)
+ return 0;
+
+ fd = socket(socket_address_family(&l->address), SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = set_dns_stub_common_socket_options(fd);
+ if (r < 0)
+ goto fail;
+
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, true);
+ if (r < 0)
+ goto fail;
+
+ r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
+ if (r < 0)
+ goto fail;
+
+ if (bind(fd, &l->address.sockaddr.sa, l->address.size) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (listen(fd, SOMAXCONN) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = sd_event_add_io(m->event, &l->dns_stub_extra_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(l->dns_stub_extra_event_source, "dns-stub-tcp-extra");
+
+ l->fd = TAKE_FD(fd);
+
+ if (DEBUG_LOGGING) {
+ (void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
+ log_debug("Listening on TCP socket %s.", strnull(pretty));
+ }
+
+ return 0;
+
+ fail:
+ (void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
+ if (r == -EADDRINUSE)
+ return log_warning_errno(r,
+ "Another process is already listening on TCP socket %s.\n"
+ "Turning off local DNS stub extra support.", strnull(pretty));
+ if (r == -EPERM)
+ return log_warning_errno(r,
+ "Failed to listen on TCP socket %s: %m.\n"
+ "Turning off local DNS stub extra support.", strnull(pretty));
+
+ assert(r < 0);
+
+ return log_warning_errno(r, "Failed to listen on TCP socket %s, ignoring: %m", strnull(pretty));
+}
+
int manager_dns_stub_start(Manager *m) {
const char *t = "UDP";
int r = 0;
@@ -564,6 +706,22 @@ int manager_dns_stub_start(Manager *m) {
} else if (r < 0)
return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
+ if (!ordered_set_isempty(m->dns_extra_stub_listeners)) {
+ DNSStubListenerExtra *l;
+ Iterator i;
+
+ log_debug("Creating stub listener extra using %s.",
+ m->dns_stub_listener_mode == DNS_STUB_LISTENER_UDP ? "UDP" :
+ m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" :
+ "UDP/TCP");
+
+ ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners, i)
+ if (l->mode == DNS_STUB_LISTENER_UDP)
+ (void) manager_dns_stub_udp_fd_extra(m, l);
+ else
+ (void) manager_dns_stub_tcp_fd_extra(m, l);
+ }
+
return 0;
}
@@ -576,3 +734,15 @@ void manager_dns_stub_stop(Manager *m) {
m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
}
+
+void manager_dns_stub_stop_extra(Manager *m) {
+ DNSStubListenerExtra *l;
+ Iterator i;
+
+ assert(m);
+
+ ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners, i) {
+ l->dns_stub_extra_event_source = sd_event_source_unref(l->dns_stub_extra_event_source);
+ l->fd = safe_close(l->fd);
+ }
+}
diff --git a/src/resolve/resolved-dns-stub.h b/src/resolve/resolved-dns-stub.h
index f34e9db1af..e589df3831 100644
--- a/src/resolve/resolved-dns-stub.h
+++ b/src/resolve/resolved-dns-stub.h
@@ -3,5 +3,8 @@
#include "resolved-manager.h"
+int dns_stub_extra_new(DNSStubListenerExtra **ret);
+
void manager_dns_stub_stop(Manager *m);
+void manager_dns_stub_stop_extra(Manager *m);
int manager_dns_stub_start(Manager *m);
diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf
index 553da8d251..b54fa1ba99 100644
--- a/src/resolve/resolved-gperf.gperf
+++ b/src/resolve/resolved-gperf.gperf
@@ -18,14 +18,15 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
-Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
-Resolve.Domains, config_parse_search_domains, 0, 0
-Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
-Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support)
-Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
-Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode)
-Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache)
-Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
-Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts)
-Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)
+Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
+Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
+Resolve.Domains, config_parse_search_domains, 0, 0
+Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
+Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support)
+Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
+Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode)
+Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache)
+Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
+Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts)
+Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)
+Resolve.DNSStubListenerExtra, config_parse_dns_stub_listener_extra, 0, offsetof(Manager, dns_extra_stub_listeners)
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 17bdb67ed5..5c09de0c34 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -701,6 +701,7 @@ Manager *manager_free(Manager *m) {
hashmap_free(m->links);
hashmap_free(m->dns_transactions);
+ ordered_set_free(m->dns_extra_stub_listeners);
sd_event_source_unref(m->network_event_source);
sd_network_monitor_unref(m->network_monitor);
@@ -713,6 +714,8 @@ Manager *manager_free(Manager *m) {
manager_dns_stub_stop(m);
manager_varlink_done(m);
+ manager_dns_stub_stop_extra(m);
+
bus_verify_polkit_async_registry_free(m->polkit_registry);
sd_bus_flush_close_unref(m->bus);
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 390fac4a52..a3a2e08123 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -31,6 +31,14 @@ typedef struct EtcHosts {
Set *no_address;
} EtcHosts;
+typedef struct DNSStubListenerExtra {
+ int fd;
+
+ DnsStubListenerMode mode;
+ SocketAddress address;
+ sd_event_source *dns_stub_extra_event_source;
+} DNSStubListenerExtra;
+
struct Manager {
sd_event *event;
@@ -137,6 +145,8 @@ struct Manager {
int dns_stub_udp_fd;
int dns_stub_tcp_fd;
+ OrderedSet *dns_extra_stub_listeners;
+
sd_event_source *dns_stub_udp_event_source;
sd_event_source *dns_stub_tcp_event_source;
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index 224ccffb92..21a04cfba7 100644
--- a/test/fuzz/fuzz-unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -385,6 +385,7 @@ DNSLifetimeSec=
DNSSEC=
DNSSECNegativeTrustAnchors=
DNSStubListener=
+DNSStubListenerExtra=
DUIDRawData=
DUIDType=
DefaultLeaseTimeSec=