diff --git a/man/org.freedesktop.resolve1.xml b/man/org.freedesktop.resolve1.xml
index 324b688853..7d5e997b83 100644
--- a/man/org.freedesktop.resolve1.xml
+++ b/man/org.freedesktop.resolve1.xml
@@ -115,6 +115,9 @@ node /org/freedesktop/resolve1 {
ResetStatistics();
FlushCaches();
ResetServerFeatures();
+ GetDelegate(in s id,
+ out o path);
+ ListDelegates(out a(so) delegates);
properties:
readonly s LLMNRHostname = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
@@ -202,6 +205,10 @@ node /org/freedesktop/resolve1 {
+
+
+
+
@@ -423,13 +430,13 @@ node /org/freedesktop/resolve1 {
The RevertLink() method may be used to revert all per-link settings
described above to the defaults.
- The FlushCaches() flushes all resource record caches maintained by the
+ The FlushCaches() method flushes all resource record caches maintained by the
resolver, and ensures that any subsequent lookups re-request their responses from their sources.
- The ResetServerFeatures() flushes any feature information learned about
- remote DNS servers. This ensures that subsequent lookups will be initially attempted at the highest DNS
- protocol feature level again, possibly requiring a (potentially slow) downgrade cycle to recognize the
- supported feature level again.
+ The ResetServerFeatures() method flushes any feature information learned
+ about remote DNS servers. This ensures that subsequent lookups will be initially attempted at the
+ highest DNS protocol feature level again, possibly requiring a (potentially slow) downgrade cycle to
+ recognize the supported feature level again.
The RegisterService() method may be used to register a DNS-SD service on the
host. This functionality is closely related to the functionality provided by
@@ -447,6 +454,12 @@ node /org/freedesktop/resolve1 {
RegisterService() and deletes a DNS-SD service previously created via IPC
again.
+ The GetDelegate() method returns the D-Bus object path for the specified DNS
+ delegate ID.
+
+ The ListDelegates() method returns a list of the IDs and D-Bus object paths
+ of the currently configured DNS delegates.
+
The Flags Parameter
@@ -936,6 +949,15 @@ $ gdbus introspect --system \
+
+ History
+
+ The Manager Object
+
+ GetDelegate() and ListDelegates() were added in version 258.
+
+
+
See Also
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index c911a64ca4..1075c29ed6 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -86,6 +86,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_STUB_LOOP, ELOOP),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DNSSD_SERVICE, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_DNSSD_SERVICE_EXISTS, EEXIST),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DELEGATE, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_DNS_FORMERR, EBADMSG),
SD_BUS_ERROR_MAP(BUS_ERROR_DNS_SERVFAIL, EHOSTDOWN),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index e69f6194dd..ad6b45ae39 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -84,6 +84,7 @@
#define BUS_ERROR_STUB_LOOP "org.freedesktop.resolve1.StubLoop"
#define BUS_ERROR_NO_SUCH_DNSSD_SERVICE "org.freedesktop.resolve1.NoSuchDnssdService"
#define BUS_ERROR_DNSSD_SERVICE_EXISTS "org.freedesktop.resolve1.DnssdServiceExists"
+#define BUS_ERROR_NO_SUCH_DELEGATE "org.freedesktop.resolve1.NoSuchDelegate"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
#define BUS_ERROR_DNS_FORMERR _BUS_ERROR_DNS "FORMERR"
diff --git a/src/resolve/meson.build b/src/resolve/meson.build
index a814551a9b..b47dfd2724 100644
--- a/src/resolve/meson.build
+++ b/src/resolve/meson.build
@@ -13,6 +13,8 @@ systemd_resolved_extract_sources = files(
'resolved-conf.c',
'resolved-dns-answer.c',
'resolved-dns-cache.c',
+ 'resolved-dns-delegate.c',
+ 'resolved-dns-delegate-bus.c',
'resolved-dns-dnssec.c',
'resolved-dns-packet.c',
'resolved-dns-query.c',
@@ -90,8 +92,14 @@ resolved_dnssd_gperf_c = custom_target(
output : 'resolved-dnssd-gperf.c',
command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
-generated_sources += [dns_type_from_name_h, dns_type_to_name_h, resolved_gperf_c, resolved_dnssd_gperf_c]
-systemd_resolved_extract_sources += [dns_type_from_name_h, dns_type_to_name_h, resolved_gperf_c, resolved_dnssd_gperf_c]
+resolved_dns_delegate_gperf_c = custom_target(
+ 'resolved-dns-delegate-gperf.c',
+ input : 'resolved-dns-delegate-gperf.gperf',
+ output : 'resolved-dns-delegate-gperf.c',
+ command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
+
+generated_sources += [dns_type_from_name_h, dns_type_to_name_h, resolved_gperf_c, resolved_dnssd_gperf_c, resolved_dns_delegate_gperf_c]
+systemd_resolved_extract_sources += [dns_type_from_name_h, dns_type_to_name_h, resolved_gperf_c, resolved_dnssd_gperf_c, resolved_dns_delegate_gperf_c]
if conf.get('ENABLE_DNS_OVER_TLS') == 1
systemd_resolved_extract_sources += files(
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index e9b47488e7..be927e0706 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -16,6 +16,8 @@
#include "resolved-bus.h"
#include "resolved-def.h"
#include "resolved-dns-answer.h"
+#include "resolved-dns-delegate.h"
+#include "resolved-dns-delegate-bus.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-query.h"
@@ -2068,6 +2070,64 @@ static int bus_method_unregister_service(sd_bus_message *message, void *userdata
return call_dnssd_method(m, message, bus_dnssd_method_unregister, error);
}
+static int bus_method_get_delegate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *p = NULL;
+ Manager *m = ASSERT_PTR(userdata);
+ int r;
+
+ assert(message);
+
+ const char *id;
+ r = sd_bus_message_read(message, "s", &id);
+ if (r < 0)
+ return r;
+
+ DnsDelegate *d = hashmap_get(m->delegates, id);
+ if (!d)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DELEGATE, "Delegate '%s' not known", id);
+
+ p = dns_delegate_bus_path(d);
+ if (!p)
+ return -ENOMEM;
+
+ return sd_bus_reply_method_return(message, "o", p);
+}
+
+static int bus_method_list_delegates(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *m = ASSERT_PTR(userdata);
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(so)");
+ if (r < 0)
+ return r;
+
+ DnsDelegate *d;
+ HASHMAP_FOREACH(d, m->delegates) {
+ _cleanup_free_ char *p = NULL;
+
+ p = dns_delegate_bus_path(d);
+ if (!p)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(reply, "(so)", d->id, p);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_send(reply);
+}
+
static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -2206,6 +2266,16 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_NO_RESULT,
bus_method_reset_server_features,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetDelegate",
+ SD_BUS_ARGS("s", id),
+ SD_BUS_RESULT("o", path),
+ bus_method_get_delegate,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListDelegates",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(so)", delegates),
+ bus_method_list_delegates,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
@@ -2215,7 +2285,8 @@ const BusObjectImplementation manager_object = {
"org.freedesktop.resolve1.Manager",
.vtables = BUS_VTABLES(resolve_vtable),
.children = BUS_IMPLEMENTATIONS(&link_object,
- &dnssd_object),
+ &dnssd_object,
+ &dns_delegate_object),
};
static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
diff --git a/src/resolve/resolved-dns-delegate-bus.c b/src/resolve/resolved-dns-delegate-bus.c
new file mode 100644
index 0000000000..60134b504c
--- /dev/null
+++ b/src/resolve/resolved-dns-delegate-bus.c
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bus-get-properties.h"
+#include "resolved-bus.h"
+#include "resolved-dns-delegate.h"
+#include "resolved-dns-delegate-bus.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
+#include "resolved-manager.h"
+#include "strv.h"
+
+static int property_get_dns(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ DnsDelegate *d = ASSERT_PTR(userdata);
+ int r;
+
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "(iiayqs)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(servers, s, d->dns_servers) {
+ r = bus_dns_server_append(reply, s, /* with_ifindex= */ true, /* extended= */ true);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_current_dns_server(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ DnsDelegate *d = ASSERT_PTR(userdata);
+
+ assert(reply);
+
+ return bus_dns_server_append(reply, d->current_dns_server, /* with_ifindex= */ true, /* extended= */ true);
+}
+
+static int property_get_domains(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ DnsDelegate *delegate = ASSERT_PTR(userdata);
+ int r;
+
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "(sb)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(domains, d, delegate->search_domains) {
+ r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int dns_delegate_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ assert(bus);
+ assert(path);
+ assert(interface);
+ assert(found);
+
+ _cleanup_free_ char *e = NULL;
+ if (sd_bus_path_decode(path, "/org/freedesktop/resolve1/dns_delegate", &e) <= 0)
+ return 0;
+
+ DnsDelegate *d = hashmap_get(m->delegates, e);
+ if (!d)
+ return 0;
+
+ *found = d;
+ return 1;
+}
+
+char* dns_delegate_bus_path(const DnsDelegate *d) {
+ char *p;
+
+ assert(d);
+
+ if (sd_bus_path_encode("/org/freedesktop/resolve1/dns_delegate", d->id, &p) < 0)
+ return NULL;
+
+ return p;
+}
+
+static int dns_delegate_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ Manager *m = ASSERT_PTR(userdata);
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(nodes);
+
+ _cleanup_strv_free_ char **l = NULL;
+ DnsDelegate *d;
+ HASHMAP_FOREACH(d, m->delegates) {
+ _cleanup_free_ char *p = NULL;
+
+ p = dns_delegate_bus_path(d);
+ if (!p)
+ return -ENOMEM;
+
+ r = strv_consume(&l, TAKE_PTR(p));
+ if (r < 0)
+ return r;
+ }
+
+ *nodes = TAKE_PTR(l);
+
+ return 1;
+}
+
+static const sd_bus_vtable dns_delegate_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ SD_BUS_PROPERTY("DNS", "a(iiayqs)", property_get_dns, 0, 0),
+ SD_BUS_PROPERTY("CurrentDNSServer", "(iiayqs)", property_get_current_dns_server, 0, 0),
+ SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
+ SD_BUS_PROPERTY("DefaultRoute", "b", bus_property_get_tristate, offsetof(DnsDelegate, default_route), 0),
+
+ SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation dns_delegate_object = {
+ "/org/freedesktop/resolve1/dns_delegate",
+ "org.freedesktop.resolve1.DnsDelegate",
+ .fallback_vtables = BUS_FALLBACK_VTABLES({dns_delegate_vtable, dns_delegate_object_find}),
+ .node_enumerator = dns_delegate_node_enumerator,
+};
diff --git a/src/resolve/resolved-dns-delegate-bus.h b/src/resolve/resolved-dns-delegate-bus.h
new file mode 100644
index 0000000000..7acdd5a983
--- /dev/null
+++ b/src/resolve/resolved-dns-delegate-bus.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+typedef struct BusObjectImplementation BusObjectImplementation;
+typedef struct DnsDelegate DnsDelegate;
+
+extern const BusObjectImplementation dns_delegate_object;
+
+char* dns_delegate_bus_path(const DnsDelegate *d);
diff --git a/src/resolve/resolved-dns-delegate-gperf.gperf b/src/resolve/resolved-dns-delegate-gperf.gperf
new file mode 100644
index 0000000000..3040770f3a
--- /dev/null
+++ b/src/resolve/resolved-dns-delegate-gperf.gperf
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+%{
+#if __GNUC__ >= 15
+_Pragma("GCC diagnostic ignored \"-Wzero-as-null-pointer-constant\"")
+#endif
+#include
+#include "conf-parser.h"
+#include "resolved-dns-delegate.h"
+%}
+struct ConfigPerfItem;
+%null_strings
+%language=ANSI-C
+%define slot-name section_and_lvalue
+%define hash-function-name resolved_dns_delegate_gperf_hash
+%define lookup-function-name resolved_dns_delegate_gperf_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+Delegate.DNS, config_parse_delegate_dns_servers, 0, 0
+Delegate.Domains, config_parse_delegate_domains, 0, 0
+Delegate.DefaultRoute, config_parse_tristate, 0, offsetof(DnsDelegate, default_route)
diff --git a/src/resolve/resolved-dns-delegate.c b/src/resolve/resolved-dns-delegate.c
new file mode 100644
index 0000000000..6a094ce68e
--- /dev/null
+++ b/src/resolve/resolved-dns-delegate.c
@@ -0,0 +1,366 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "conf-files.h"
+#include "dns-domain.h"
+#include "in-addr-util.h"
+#include "path-util.h"
+#include "resolved-dns-delegate.h"
+#include "resolved-dns-scope.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
+#include "resolved-manager.h"
+#include "socket-netlink.h"
+#include "strv.h"
+
+#define DNS_DELEGATES_MAX 4096U
+#define DNS_DELEGATE_SEARCH_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dns-delegate.d"))
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dns_delegate_hash_ops,
+ char,
+ string_hash_func,
+ string_compare_func,
+ DnsDelegate,
+ dns_delegate_free);
+
+int dns_delegate_new(Manager *m, const char *id, DnsDelegate **ret) {
+ int r;
+
+ assert(m);
+ assert(id);
+
+ if (hashmap_size(m->delegates) >= DNS_DELEGATES_MAX)
+ return -E2BIG;
+
+ _cleanup_free_ char *id_copy = strdup(id);
+ if (!id_copy)
+ return -ENOMEM;
+
+ _cleanup_(dns_delegate_freep) DnsDelegate *d = new(DnsDelegate, 1);
+ if (!d)
+ return -ENOMEM;
+
+ *d = (DnsDelegate) {
+ .id = TAKE_PTR(id_copy),
+ .default_route = -1,
+ };
+
+ r = dns_scope_new(
+ m,
+ &d->scope,
+ DNS_SCOPE_DELEGATE,
+ /* link= */ NULL,
+ d,
+ DNS_PROTOCOL_DNS,
+ AF_UNSPEC);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_put(&m->delegates, &dns_delegate_hash_ops, d->id, d);
+ if (r < 0)
+ return r;
+
+ d->manager = m;
+
+ log_debug("New delegate '%s'.", id);
+
+ if (ret)
+ *ret = d;
+
+ TAKE_PTR(d);
+ return 0;
+}
+
+DnsDelegate *dns_delegate_free(DnsDelegate *d) {
+ if (!d)
+ return NULL;
+
+ Manager *m = d->manager;
+
+ log_debug("Removing delegate '%s'.", d->id);
+
+ dns_server_unlink_all(d->dns_servers);
+ dns_search_domain_unlink_all(d->search_domains);
+
+ dns_scope_free(d->scope);
+
+ if (m)
+ hashmap_remove(m->delegates, d->id);
+
+ free(d->id);
+
+ return mfree(d);
+}
+
+DnsServer* dns_delegate_set_dns_server(DnsDelegate *d, DnsServer *s) {
+ assert(d);
+
+ if (d->current_dns_server == s)
+ return s;
+
+ if (s)
+ log_debug("Switching delegate '%s' to DNS server %s.", d->id, strna(dns_server_string_full(s)));
+
+ dns_server_unref(d->current_dns_server);
+ d->current_dns_server = dns_server_ref(s);
+
+ /* Skip flushing the cache if server stale feature is enabled. */
+ if (d->manager->stale_retention_usec == 0)
+ dns_cache_flush(&d->scope->cache);
+
+ return s;
+}
+
+DnsServer *dns_delegate_get_dns_server(DnsDelegate *d) {
+ assert(d);
+
+ if (!d->current_dns_server)
+ dns_delegate_set_dns_server(d, d->dns_servers);
+
+ return d->current_dns_server;
+}
+
+void dns_delegate_next_dns_server(DnsDelegate *d, DnsServer *if_current) {
+ assert(d);
+
+ /* If we have issues with a DNS server, let's switch to the next one (in a round robin scheme). If
+ * non-NULL if_current points to the DNS server that was selected at the beginning of whatever bigger
+ * operation we are currently executing, and hence if we already switched away from it we suppress
+ * switching again, so that each operation only results in a single switch, not multiple. */
+
+ /* If the current server of the transaction is specified, and we already are at a different one,
+ * don't do anything */
+ if (if_current && d->current_dns_server != if_current)
+ return;
+
+ /* If currently have no DNS server, then don't do anything, we'll pick it lazily the next time a DNS
+ * server is needed. */
+ if (!d->current_dns_server)
+ return;
+
+ /* Change to the next one, but make sure to follow the linked list only if this server is actually
+ * still linked. */
+ if (d->current_dns_server->linked && d->current_dns_server->servers_next) {
+ dns_delegate_set_dns_server(d, d->current_dns_server->servers_next);
+ return;
+ }
+
+ /* Pick the first one again, after we reached the end */
+ dns_delegate_set_dns_server(d, d->dns_servers);
+}
+
+static int dns_delegate_load(Manager *m, const char *path) {
+ int r;
+
+ assert(m);
+ assert(path);
+
+ _cleanup_free_ char *fn = NULL;
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
+
+ const char *e = endswith(fn, ".dns-delegate");
+ if (!e)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DNS delegate file name does not end in .dns-delegate, refusing: %s", fn);
+
+ _cleanup_free_ char *id = strndup(fn, e - fn);
+ if (!string_is_safe(id))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DNS delegate file name contains weird characters, refusing: %s", fn);
+
+ _cleanup_free_ char *dropin_dirname = strjoin(id, ".dns-delegate.d");
+ if (!dropin_dirname)
+ return log_oom();
+
+ _cleanup_(dns_delegate_freep) DnsDelegate *d = NULL;
+ r = dns_delegate_new(m, id, &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate delegate '%s': %m", id);
+
+ r = config_parse_many(
+ STRV_MAKE_CONST(path),
+ DNS_DELEGATE_SEARCH_DIRS,
+ dropin_dirname,
+ /* root= */ NULL,
+ "Delegate\0",
+ config_item_perf_lookup,
+ resolved_dns_delegate_gperf_lookup,
+ /* flags= */ 0,
+ d,
+ /* ret_stats_by_path= */ NULL,
+ /* ret_drop_in_files= */ NULL);
+ if (r < 0)
+ return r;
+
+ log_info("Successfully loaded delegate '%s'.", d->id);
+
+ TAKE_PTR(d);
+ return 0;
+}
+
+int manager_load_delegates(Manager *m) {
+ _cleanup_strv_free_ char **files = NULL;
+ int r;
+
+ assert(m);
+
+ r = conf_files_list_strv(&files, ".dns-delegate", /* root= */ NULL, /* flags= */ 0, DNS_DELEGATE_SEARCH_DIRS);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate .dns-delegate files: %m");
+
+ STRV_FOREACH(f, files)
+ (void) dns_delegate_load(m, *f);
+
+ return 0;
+}
+
+int config_parse_delegate_dns_servers(
+ 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) {
+
+ DnsDelegate *d = ASSERT_PTR(userdata);
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ /* Empty assignment means clear the list */
+ if (isempty(rvalue)) {
+ dns_server_unlink_all(d->dns_servers);
+ return 0;
+ }
+
+ /* Otherwise, add to the list */
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ r = extract_first_word(&rvalue, &word, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse DNS server string '%s', ignoring.", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ _cleanup_free_ char *server_name = NULL;
+ union in_addr_union address;
+ int family, ifindex = 0;
+ uint16_t port;
+ r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse DNS server string '%s', ignoring.", word);
+ continue;
+ }
+
+ /* Silently filter out 0.0.0.0, 127.0.0.53, 127.0.0.54 (our own stub DNS listener) */
+ if (!dns_server_address_valid(family, &address))
+ continue;
+
+ /* By default, the port number is determined with the transaction feature level.
+ * See dns_transaction_port() and dns_server_port(). */
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
+ /* Filter out duplicates */
+ DnsServer *s = dns_server_find(d->dns_servers, family, &address, port, ifindex, server_name);
+ if (s) {
+ /* Drop the marker. This is used to find the servers that ceased to exist, see
+ * manager_mark_dns_servers() and manager_flush_marked_dns_servers(). */
+ dns_server_move_back_and_unmark(s);
+ return 0;
+ }
+
+ r = dns_server_new(
+ d->manager,
+ /* ret= */ NULL,
+ DNS_SERVER_DELEGATE,
+ /* link= */ NULL,
+ d,
+ family,
+ &address,
+ port,
+ ifindex,
+ server_name,
+ RESOLVE_CONFIG_SOURCE_FILE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add DNS server: %m");
+ }
+
+ return 0;
+}
+
+int config_parse_delegate_domains(
+ 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) {
+
+ DnsDelegate *d = ASSERT_PTR(userdata);
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ /* Empty assignment means clear the list */
+ if (isempty(rvalue)) {
+ dns_search_domain_unlink_all(d->search_domains);
+ return 0;
+ }
+
+ /* Otherwise, add to the list */
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ r = extract_first_word(&rvalue, &word, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse search domains string '%s', ignoring.", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ const char *name = word;
+
+ bool route_only = name[0] == '~';
+ if (route_only)
+ name++;
+
+ if (dns_name_is_root(name) || streq(name, "*")) {
+ route_only = true;
+ name = ".";
+ }
+
+ DnsSearchDomain *domain;
+ r = dns_search_domain_find(d->search_domains, name, &domain);
+ if (r < 0)
+ return log_error_errno(r, "Failed to find search domain: %m");
+ if (r > 0)
+ dns_search_domain_move_back_and_unmark(domain);
+ else {
+ r = dns_search_domain_new(d->manager, &domain, DNS_SEARCH_DOMAIN_DELEGATE, /* link= */ NULL, d, name);
+ if (r < 0)
+ return r;
+ }
+
+ domain->route_only = route_only;
+ }
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-delegate.h b/src/resolve/resolved-dns-delegate.h
new file mode 100644
index 0000000000..3ce3c1e172
--- /dev/null
+++ b/src/resolve/resolved-dns-delegate.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "conf-parser.h"
+#include "list.h"
+
+typedef struct DnsDelegate DnsDelegate;
+typedef struct DnsScope DnsScope;
+typedef struct DnsSearchDomain DnsSearchDomain;
+typedef struct DnsServer DnsServer;
+typedef struct Manager Manager;
+
+#define DELEGATE_SEARCH_DOMAINS_MAX 256
+#define DELEGATE_DNS_SERVERS_MAX 256
+
+/* A DnsDelegate object is used to manage additional, explicitly configured unicast DNS lookup scopes,
+ * independent from any network link and from the global scope. */
+
+struct DnsDelegate {
+ Manager *manager;
+ char *id;
+
+ LIST_HEAD(DnsServer, dns_servers);
+ unsigned n_dns_servers;
+ DnsServer *current_dns_server;
+
+ LIST_HEAD(DnsSearchDomain, search_domains);
+ unsigned n_search_domains;
+
+ int default_route;
+
+ DnsScope *scope;
+
+ LIST_FIELDS(DnsDelegate, delegates);
+};
+
+int dns_delegate_new(Manager *m, const char *id, DnsDelegate **ret);
+DnsDelegate *dns_delegate_free(DnsDelegate *d);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsDelegate*, dns_delegate_free);
+
+DnsServer* dns_delegate_set_dns_server(DnsDelegate *d, DnsServer *s);
+DnsServer *dns_delegate_get_dns_server(DnsDelegate *d);
+void dns_delegate_next_dns_server(DnsDelegate *d, DnsServer *if_current);
+
+int manager_load_delegates(Manager *m);
+
+const struct ConfigPerfItem* resolved_dns_delegate_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_delegate_dns_servers);
+CONFIG_PARSER_PROTOTYPE(config_parse_delegate_domains);
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index d4bfbccdc3..4525f253de 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -12,6 +12,7 @@
#include "missing_network.h"
#include "random-util.h"
#include "resolved-dns-answer.h"
+#include "resolved-dns-delegate.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-query.h"
#include "resolved-dns-question.h"
@@ -44,6 +45,7 @@ int dns_scope_new(
DnsScope **ret,
DnsScopeOrigin origin,
Link *link,
+ DnsDelegate *delegate,
DnsProtocol protocol,
int family) {
@@ -55,6 +57,7 @@ int dns_scope_new(
assert(origin < _DNS_SCOPE_ORIGIN_MAX);
assert(!!link == (origin == DNS_SCOPE_LINK));
+ assert(!!delegate == (origin == DNS_SCOPE_DELEGATE));
s = new(DnsScope, 1);
if (!s)
@@ -63,6 +66,7 @@ int dns_scope_new(
*s = (DnsScope) {
.manager = m,
.link = link,
+ .delegate = delegate,
.origin = origin,
.protocol = protocol,
.family = family,
@@ -97,11 +101,12 @@ int dns_scope_new(
dns_scope_llmnr_membership(s, true);
dns_scope_mdns_membership(s, true);
- log_debug("New scope on link %s, protocol %s, family %s, origin %s",
+ log_debug("New scope on link %s, protocol %s, family %s, origin %s, delegate %s",
link ? link->ifname : "*",
dns_protocol_to_string(protocol),
family == AF_UNSPEC ? "*" : af_to_name(family),
- dns_scope_origin_to_string(origin));
+ dns_scope_origin_to_string(origin),
+ s->delegate ? s->delegate->id : "n/a");
*ret = s;
return 0;
@@ -129,11 +134,12 @@ DnsScope* dns_scope_free(DnsScope *s) {
if (!s)
return NULL;
- log_debug("Removing scope on link %s, protocol %s, family %s, origin %s",
+ log_debug("Removing scope on link %s, protocol %s, family %s, origin %s, delegate %s",
s->link ? s->link->ifname : "*",
dns_protocol_to_string(s->protocol),
s->family == AF_UNSPEC ? "*" : af_to_name(s->family),
- dns_scope_origin_to_string(s->origin));
+ dns_scope_origin_to_string(s->origin),
+ s->delegate ? s->delegate->id : "n/a");
dns_scope_llmnr_membership(s, false);
dns_scope_mdns_membership(s, false);
@@ -164,8 +170,11 @@ DnsServer *dns_scope_get_dns_server(DnsScope *s) {
if (s->protocol != DNS_PROTOCOL_DNS)
return NULL;
- if (s->link)
+ if (s->link) {
+ assert(!s->delegate);
return link_get_dns_server(s->link);
+ } else if (s->delegate)
+ return dns_delegate_get_dns_server(s->delegate);
else
return manager_get_dns_server(s->manager);
}
@@ -176,8 +185,11 @@ unsigned dns_scope_get_n_dns_servers(DnsScope *s) {
if (s->protocol != DNS_PROTOCOL_DNS)
return 0;
- if (s->link)
+ if (s->link) {
+ assert(!s->delegate);
return s->link->n_dns_servers;
+ } else if (s->delegate)
+ return s->delegate->n_dns_servers;
else
return s->manager->n_dns_servers;
}
@@ -193,6 +205,8 @@ void dns_scope_next_dns_server(DnsScope *s, DnsServer *if_current) {
if (s->link)
link_next_dns_server(s->link, if_current);
+ else if (s->delegate)
+ dns_delegate_next_dns_server(s->delegate, if_current);
else
manager_next_dns_server(s->manager, if_current);
}
@@ -300,6 +314,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, int family, DnsPacket *p) {
if (fd < 0)
return fd;
+ assert(s->link);
r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, LLMNR_PORT, NULL, p);
if (r < 0)
return r;
@@ -331,6 +346,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, int family, DnsPacket *p) {
if (fd < 0)
return fd;
+ assert(s->link);
r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, p->destination_port ?: MDNS_PORT, NULL, p);
if (r < 0)
return r;
@@ -1403,6 +1419,12 @@ void dns_scope_dump(DnsScope *s, FILE *f) {
fputs(" origin=", f);
fputs(dns_scope_origin_to_string(s->origin), f);
+
+ if (s->delegate) {
+ fputs(" id=", f);
+ fputs(s->delegate->id, f);
+ }
+
fputs("]\n", f);
if (!dns_zone_is_empty(&s->zone)) {
@@ -1424,6 +1446,8 @@ DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
if (s->link)
return s->link->search_domains;
+ if (s->delegate)
+ return s->delegate->search_domains;
return s->manager->search_domains;
}
@@ -1709,6 +1733,8 @@ static bool dns_scope_has_route_only_domains(DnsScope *scope) {
if (scope->link)
first = scope->link->search_domains;
+ else if (scope->delegate)
+ first = scope->delegate->search_domains;
else
first = scope->manager->search_domains;
@@ -1734,18 +1760,27 @@ bool dns_scope_is_default_route(DnsScope *scope) {
if (scope->protocol != DNS_PROTOCOL_DNS)
return false;
- /* The global DNS scope is always suitable as default route */
- if (!scope->link)
+ if (scope->link) {
+
+ /* Honour whatever is explicitly configured. This is really the best approach, and trumps any
+ * automatic logic. */
+ if (scope->link->default_route >= 0)
+ return scope->link->default_route;
+
+ /* Otherwise check if we have any route-only domains, as a sensible heuristic: if so, let's not
+ * volunteer as default route. */
+ return !dns_scope_has_route_only_domains(scope);
+
+ } else if (scope->delegate) {
+
+ if (scope->delegate->default_route >= 0)
+ return scope->delegate->default_route;
+
+ /* Delegates are by default not used as default route */
+ return false;
+ } else
+ /* The global DNS scope is always suitable as default route */
return true;
-
- /* Honour whatever is explicitly configured. This is really the best approach, and trumps any
- * automatic logic. */
- if (scope->link->default_route >= 0)
- return scope->link->default_route;
-
- /* Otherwise check if we have any route-only domains, as a sensible heuristic: if so, let's not
- * volunteer as default route. */
- return !dns_scope_has_route_only_domains(scope);
}
int dns_scope_dump_cache_to_json(DnsScope *scope, sd_json_variant **ret) {
@@ -1831,8 +1866,9 @@ int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protoco
}
static const char* const dns_scope_origin_table[_DNS_SCOPE_ORIGIN_MAX] = {
- [DNS_SCOPE_GLOBAL] = "global",
- [DNS_SCOPE_LINK] = "link",
+ [DNS_SCOPE_GLOBAL] = "global",
+ [DNS_SCOPE_LINK] = "link",
+ [DNS_SCOPE_DELEGATE] = "delegate",
};
DEFINE_STRING_TABLE_LOOKUP(dns_scope_origin, DnsScopeOrigin);
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 54f0b7b7a3..872457ffc2 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -12,6 +12,7 @@
#include "resolved-dns-zone.h"
#include "socket-util.h"
+typedef struct DnsDelegate DnsDelegate;
typedef struct DnsQuery DnsQuery;
typedef struct DnsQueryCandidate DnsQueryCandidate;
typedef struct DnsQuestion DnsQuestion;
@@ -34,6 +35,7 @@ typedef enum DnsScopeMatch {
typedef enum DnsScopeOrigin {
DNS_SCOPE_GLOBAL,
DNS_SCOPE_LINK,
+ DNS_SCOPE_DELEGATE,
_DNS_SCOPE_ORIGIN_MAX,
_DNS_SCOPE_ORIGIN_INVALID = -EINVAL,
} DnsScopeOrigin;
@@ -51,6 +53,7 @@ struct DnsScope {
DnsOverTlsMode dns_over_tls_mode;
Link *link;
+ DnsDelegate *delegate;
DnsCache cache;
DnsZone zone;
@@ -83,7 +86,7 @@ struct DnsScope {
bool announced;
};
-int dns_scope_new(Manager *m, DnsScope **ret, DnsScopeOrigin origin, Link *link, DnsProtocol protocol, int family);
+int dns_scope_new(Manager *m, DnsScope **ret, DnsScopeOrigin origin, Link *link, DnsDelegate *delegate, DnsProtocol protocol, int family);
DnsScope* dns_scope_free(DnsScope *s);
void dns_scope_packet_received(DnsScope *s, usec_t rtt);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
index 642d52c6ea..63790ad1fc 100644
--- a/src/resolve/resolved-dns-search-domain.c
+++ b/src/resolve/resolved-dns-search-domain.c
@@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
+#include "resolved-dns-delegate.h"
#include "resolved-dns-search-domain.h"
#include "resolved-link.h"
#include "resolved-manager.h"
@@ -12,7 +13,8 @@ int dns_search_domain_new(
Manager *m,
DnsSearchDomain **ret,
DnsSearchDomainType type,
- Link *l,
+ Link *link,
+ DnsDelegate *delegate,
const char *name) {
_cleanup_free_ char *normalized = NULL;
@@ -20,15 +22,19 @@ int dns_search_domain_new(
int r;
assert(m);
- assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
+ assert((type == DNS_SEARCH_DOMAIN_LINK) == !!link);
+ assert((type == DNS_SEARCH_DOMAIN_DELEGATE) == !!delegate);
assert(name);
r = dns_name_normalize(name, 0, &normalized);
if (r < 0)
return r;
- if (l) {
- if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
+ if (link) {
+ if (link->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
+ return -E2BIG;
+ } else if (delegate) {
+ if (delegate->n_search_domains >= DELEGATE_SEARCH_DOMAINS_MAX)
return -E2BIG;
} else {
if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX)
@@ -49,9 +55,9 @@ int dns_search_domain_new(
switch (type) {
case DNS_SEARCH_DOMAIN_LINK:
- d->link = l;
- LIST_APPEND(domains, l->search_domains, d);
- l->n_search_domains++;
+ d->link = link;
+ LIST_APPEND(domains, link->search_domains, d);
+ link->n_search_domains++;
break;
case DNS_SEARCH_DOMAIN_SYSTEM:
@@ -59,6 +65,12 @@ int dns_search_domain_new(
m->n_search_domains++;
break;
+ case DNS_SEARCH_DOMAIN_DELEGATE:
+ d->delegate = delegate;
+ LIST_APPEND(domains, delegate->search_domains, d);
+ delegate->n_search_domains++;
+ break;
+
default:
assert_not_reached();
}
@@ -101,6 +113,13 @@ void dns_search_domain_unlink(DnsSearchDomain *d) {
LIST_REMOVE(domains, d->manager->search_domains, d);
d->manager->n_search_domains--;
break;
+
+ case DNS_SEARCH_DOMAIN_DELEGATE:
+ assert(d->delegate);
+ assert(d->delegate->n_search_domains > 0);
+ LIST_REMOVE(domains, d->delegate->search_domains, d);
+ d->delegate->n_search_domains--;
+ break;
}
d->linked = false;
@@ -136,6 +155,13 @@ void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
break;
+ case DNS_SEARCH_DOMAIN_DELEGATE:
+ assert(d->delegate);
+ tail = LIST_FIND_TAIL(domains, d);
+ LIST_REMOVE(domains, d->delegate->search_domains, d);
+ LIST_INSERT_AFTER(domains, d->delegate->search_domains, tail, d);
+ break;
+
default:
assert_not_reached();
}
diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h
index fbd9a4490f..63bd357b83 100644
--- a/src/resolve/resolved-dns-search-domain.h
+++ b/src/resolve/resolved-dns-search-domain.h
@@ -7,6 +7,7 @@
#include "macro.h"
#include "memory-util.h"
+typedef struct DnsDelegate DnsDelegate;
typedef struct DnsSearchDomain DnsSearchDomain;
typedef struct Link Link;
typedef struct Manager Manager;
@@ -14,6 +15,7 @@ typedef struct Manager Manager;
typedef enum DnsSearchDomainType {
DNS_SEARCH_DOMAIN_SYSTEM,
DNS_SEARCH_DOMAIN_LINK,
+ DNS_SEARCH_DOMAIN_DELEGATE,
} DnsSearchDomainType;
struct DnsSearchDomain {
@@ -23,6 +25,7 @@ struct DnsSearchDomain {
DnsSearchDomainType type;
Link *link;
+ DnsDelegate *delegate;
char *name;
@@ -38,6 +41,7 @@ int dns_search_domain_new(
DnsSearchDomain **ret,
DnsSearchDomainType type,
Link *link,
+ DnsDelegate *delegate,
const char *name);
DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 5996d9bcc6..7845dcfd21 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -11,6 +11,7 @@
#include "json-util.h"
#include "resolved-bus.h"
#include "resolved-dns-cache.h"
+#include "resolved-dns-delegate.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-scope.h"
#include "resolved-dns-search-domain.h"
@@ -36,7 +37,8 @@ int dns_server_new(
Manager *m,
DnsServer **ret,
DnsServerType type,
- Link *l,
+ Link *link,
+ DnsDelegate *delegate,
int family,
const union in_addr_union *in_addr,
uint16_t port,
@@ -48,14 +50,18 @@ int dns_server_new(
DnsServer *s;
assert(m);
- assert((type == DNS_SERVER_LINK) == !!l);
+ assert((type == DNS_SERVER_LINK) == !!link);
+ assert((type == DNS_SERVER_DELEGATE) == !!delegate);
assert(in_addr);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
- if (l) {
- if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
+ if (link) {
+ if (link->n_dns_servers >= LINK_DNS_SERVERS_MAX)
+ return -E2BIG;
+ } else if (delegate) {
+ if (delegate->n_dns_servers >= DELEGATE_DNS_SERVERS_MAX)
return -E2BIG;
} else {
if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
@@ -90,9 +96,9 @@ int dns_server_new(
switch (type) {
case DNS_SERVER_LINK:
- s->link = l;
- LIST_APPEND(servers, l->dns_servers, s);
- l->n_dns_servers++;
+ s->link = link;
+ LIST_APPEND(servers, link->dns_servers, s);
+ link->n_dns_servers++;
break;
case DNS_SERVER_SYSTEM:
@@ -105,16 +111,20 @@ int dns_server_new(
m->n_dns_servers++;
break;
+ case DNS_SERVER_DELEGATE:
+ s->delegate = delegate;
+ LIST_APPEND(servers, delegate->dns_servers, s);
+ delegate->n_dns_servers++;
+ break;
default:
assert_not_reached();
}
s->linked = true;
- /* A new DNS server that isn't fallback is added and the one
- * we used so far was a fallback one? Then let's try to pick
- * the new one */
- if (type != DNS_SERVER_FALLBACK && dns_server_is_fallback(m->current_dns_server))
+ /* A new non-fallback DNS server is added and the one we used so far was a fallback one? Then
+ * let's try to pick the new one */
+ if (type == DNS_SERVER_SYSTEM && dns_server_is_fallback(m->current_dns_server))
manager_set_dns_server(m, NULL);
if (ret)
@@ -171,6 +181,14 @@ void dns_server_unlink(DnsServer *s) {
LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
s->manager->n_dns_servers--;
break;
+
+ case DNS_SERVER_DELEGATE:
+ assert(s->delegate);
+ assert(s->delegate->n_dns_servers > 0);
+ LIST_REMOVE(servers, s->delegate->dns_servers, s);
+ s->delegate->n_dns_servers--;
+ break;
+
default:
assert_not_reached();
}
@@ -183,6 +201,9 @@ void dns_server_unlink(DnsServer *s) {
if (s->manager->current_dns_server == s)
manager_set_dns_server(s->manager, NULL);
+ if (s->delegate && s->delegate->current_dns_server == s)
+ dns_delegate_set_dns_server(s->delegate, NULL);
+
/* No need to keep a default stream around anymore */
dns_server_unref_stream(s);
@@ -202,8 +223,8 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
if (!s->linked || !s->servers_next)
return;
- /* Move us to the end of the list, so that the order is
- * strictly kept, if we are not at the end anyway. */
+ /* Move us to the end of the list, so that the order is strictly kept, if we are not at the end
+ * anyway. */
switch (s->type) {
@@ -226,6 +247,13 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
break;
+ case DNS_SERVER_DELEGATE:
+ assert(s->delegate);
+ tail = LIST_FIND_TAIL(servers, s);
+ LIST_REMOVE(servers, s->delegate->dns_servers, s);
+ LIST_INSERT_AFTER(servers, s->delegate->dns_servers, tail, s);
+ break;
+
default:
assert_not_reached();
}
@@ -886,7 +914,18 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
return 0;
}
- return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_FILE);
+ return dns_server_new(
+ m,
+ /* ret= */ NULL,
+ type,
+ /* link= */ NULL,
+ /* delegate= */ NULL,
+ family,
+ &address,
+ port,
+ ifindex,
+ server_name,
+ RESOLVE_CONFIG_SOURCE_FILE);
}
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
@@ -931,7 +970,13 @@ static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
- r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+ r = dns_search_domain_new(
+ m,
+ &d,
+ DNS_SEARCH_DOMAIN_SYSTEM,
+ /* link= */ NULL,
+ /* delegate= */ NULL,
+ domain);
if (r < 0)
return r;
}
@@ -999,8 +1044,27 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
return s;
}
+static bool manager_search_default_route_dns_server(Manager *m) {
+ assert(m);
+
+ LIST_FOREACH(scopes, scope, m->dns_scopes) {
+ /* Ignore the global scope, it's handled separately */
+ if (scope->origin == DNS_SCOPE_GLOBAL)
+ continue;
+
+ /* Scope has no DNS server? */
+ if (dns_scope_get_n_dns_servers(scope) == 0)
+ continue;
+
+ /* If this is suitable as default route, we found what we are looking for */
+ if (dns_scope_is_default_route(scope))
+ return true;
+ }
+
+ return false;
+}
+
DnsServer *manager_get_dns_server(Manager *m) {
- Link *l;
assert(m);
/* Try to read updates resolv.conf */
@@ -1019,22 +1083,10 @@ DnsServer *manager_get_dns_server(Manager *m) {
manager_set_dns_server(m, NULL);
}
- if (!m->current_dns_server) {
- bool found = false;
-
- /* No DNS servers configured, let's see if there are
- * any on any links. If not, we use the fallback
- * servers */
-
- HASHMAP_FOREACH(l, m->links)
- if (l->dns_servers && l->default_route) {
- found = true;
- break;
- }
-
- if (!found)
- manager_set_dns_server(m, m->fallback_dns_servers);
- }
+ /* If no DNS servers are configured, let's see if there are any on any links or delegates. If not, we
+ * use the fallback servers */
+ if (!m->current_dns_server && !manager_search_default_route_dns_server(m))
+ manager_set_dns_server(m, m->fallback_dns_servers);
return m->current_dns_server;
}
@@ -1090,11 +1142,15 @@ void dns_server_flush_cache(DnsServer *s) {
/* Flush the cache of the scope this server belongs to */
- current = s->link ? s->link->current_dns_server : s->manager->current_dns_server;
+ current = s->link ? s->link->current_dns_server :
+ s->delegate ? s->delegate->current_dns_server :
+ s->manager->current_dns_server;
if (current != s)
return;
- scope = s->link ? s->link->unicast_scope : s->manager->unicast_scope;
+ scope = s->link ? s->link->unicast_scope :
+ s->delegate ? s->delegate->scope :
+ s->manager->unicast_scope;
if (!scope)
return;
@@ -1199,10 +1255,14 @@ void dns_server_unref_stream(DnsServer *s) {
DnsScope *dns_server_scope(DnsServer *s) {
assert(s);
+ assert(s->linked);
assert((s->type == DNS_SERVER_LINK) == !!s->link);
+ assert((s->type == DNS_SERVER_DELEGATE) == !!s->delegate);
if (s->link)
return s->link->unicast_scope;
+ if (s->delegate)
+ return s->delegate->scope;
return s->manager->unicast_scope;
}
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index bb3c0e109f..bef352aa42 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -10,10 +10,11 @@
#include "resolved-dnstls.h"
#include "time-util.h"
+typedef struct DnsDelegate DnsDelegate;
+typedef struct DnsPacket DnsPacket;
typedef struct DnsScope DnsScope;
typedef struct DnsServer DnsServer;
typedef struct DnsStream DnsStream;
-typedef struct DnsPacket DnsPacket;
typedef struct Link Link;
typedef struct Manager Manager;
@@ -21,6 +22,7 @@ typedef enum DnsServerType {
DNS_SERVER_SYSTEM,
DNS_SERVER_FALLBACK,
DNS_SERVER_LINK,
+ DNS_SERVER_DELEGATE,
_DNS_SERVER_TYPE_MAX,
_DNS_SERVER_TYPE_INVALID = -EINVAL,
} DnsServerType;
@@ -56,6 +58,7 @@ struct DnsServer {
DnsServerType type;
Link *link;
+ DnsDelegate *delegate;
int family;
union in_addr_union address;
@@ -114,6 +117,7 @@ int dns_server_new(
DnsServer **ret,
DnsServerType type,
Link *link,
+ DnsDelegate *delegate,
int family,
const union in_addr_union *address,
uint16_t port,
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index 9c5f7a836a..9e5371684a 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -258,7 +258,7 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
if (s)
dns_server_move_back_and_unmark(s);
else {
- r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name, RESOLVE_CONFIG_SOURCE_DBUS);
+ r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, /* delegate= */ NULL, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name, RESOLVE_CONFIG_SOURCE_DBUS);
if (r < 0) {
dns_server_unlink_all(l->dns_servers);
goto finalize;
@@ -266,7 +266,6 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
changed = true;
}
-
}
changed = dns_server_unlink_marked(l->dns_servers) || changed;
@@ -387,7 +386,7 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
- r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
+ r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, /* delegate= */ NULL, name);
if (r < 0)
goto clear;
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index 432d7cf8b0..55c8aa36cf 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -139,7 +139,7 @@ void link_allocate_scopes(Link *l) {
if (!l->unicast_scope) {
dns_server_reset_features_all(l->dns_servers);
- r = dns_scope_new(l->manager, &l->unicast_scope, DNS_SCOPE_LINK, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
+ r = dns_scope_new(l->manager, &l->unicast_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate DNS scope, ignoring: %m");
}
@@ -149,7 +149,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET, true) &&
link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv4_scope) {
- r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, DNS_SCOPE_LINK, l, DNS_PROTOCOL_LLMNR, AF_INET);
+ r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_LLMNR, AF_INET);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv4 scope, ignoring: %m");
}
@@ -159,7 +159,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET6, true) &&
link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv6_scope) {
- r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, DNS_SCOPE_LINK, l, DNS_PROTOCOL_LLMNR, AF_INET6);
+ r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_LLMNR, AF_INET6);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv6 scope, ignoring: %m");
}
@@ -169,7 +169,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET, true) &&
link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv4_scope) {
- r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, DNS_SCOPE_LINK, l, DNS_PROTOCOL_MDNS, AF_INET);
+ r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_MDNS, AF_INET);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv4 scope, ignoring: %m");
}
@@ -179,7 +179,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET6, true) &&
link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv6_scope) {
- r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, DNS_SCOPE_LINK, l, DNS_PROTOCOL_MDNS, AF_INET6);
+ r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_MDNS, AF_INET6);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv6 scope, ignoring: %m");
}
@@ -279,7 +279,7 @@ static int link_update_dns_server_one(Link *l, const char *str) {
return 0;
}
- return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name, RESOLVE_CONFIG_SOURCE_NETWORKD);
+ return dns_server_new(l->manager, /* ret= */ NULL, DNS_SERVER_LINK, l, /* delegate= */ NULL, family, &a, port, 0, name, RESOLVE_CONFIG_SOURCE_NETWORKD);
}
static int link_update_dns_servers(Link *l) {
@@ -498,7 +498,7 @@ static int link_update_search_domain_one(Link *l, const char *name, bool route_o
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
- r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
+ r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, /* delegate= */ NULL, name);
if (r < 0)
return r;
}
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 856161fa47..f44fde2882 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -34,6 +34,7 @@
#include "resolved-bus.h"
#include "resolved-conf.h"
#include "resolved-dns-answer.h"
+#include "resolved-dns-delegate.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-query.h"
#include "resolved-dns-question.h"
@@ -577,6 +578,10 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si
HASHMAP_FOREACH(l, m->links)
LIST_FOREACH(servers, server, l->dns_servers)
dns_server_dump(server, f);
+ DnsDelegate *delegate;
+ HASHMAP_FOREACH(delegate, m->delegates)
+ LIST_FOREACH(servers, server, delegate->dns_servers)
+ dns_server_dump(server, f);
return memstream_dump(LOG_INFO, &ms);
}
@@ -655,6 +660,7 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa
m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
dnssd_service_clear_on_reload(m->dnssd_services);
m->unicast_scope = dns_scope_free(m->unicast_scope);
+ m->delegates = hashmap_free(m->delegates);
dns_trust_anchor_flush(&m->trust_anchor);
@@ -672,9 +678,11 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa
if (r < 0)
log_warning_errno(r, "Failed to load DNS-SD configuration files: %m");
+ manager_load_delegates(m);
+
/* The default scope configuration is influenced by the manager's configuration (modes, etc.), so
* recreate it on reload. */
- r = dns_scope_new(m, &m->unicast_scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
+ r = dns_scope_new(m, &m->unicast_scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
return r;
@@ -755,7 +763,9 @@ int manager_new(Manager **ret) {
if (r < 0)
log_warning_errno(r, "Failed to load DNS-SD configuration files: %m");
- r = dns_scope_new(m, &m->unicast_scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
+ manager_load_delegates(m);
+
+ r = dns_scope_new(m, &m->unicast_scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
return r;
@@ -824,7 +834,6 @@ int manager_start(Manager *m) {
Manager *manager_free(Manager *m) {
Link *l;
- DnssdService *s;
if (!m)
return NULL;
@@ -836,12 +845,13 @@ Manager *manager_free(Manager *m) {
while ((l = hashmap_first(m->links)))
link_free(l);
+ m->delegates = hashmap_free(m->delegates);
+
while (m->dns_queries)
dns_query_free(m->dns_queries);
m->stub_queries_by_packet = hashmap_free(m->stub_queries_by_packet);
-
- dns_scope_free(m->unicast_scope);
+ m->unicast_scope = dns_scope_free(m->unicast_scope);
/* At this point only orphaned streams should remain. All others should have been freed already by their
* owners */
@@ -894,6 +904,7 @@ Manager *manager_free(Manager *m) {
free(m->llmnr_hostname);
free(m->mdns_hostname);
+ DnssdService *s;
while ((s = hashmap_first(m->dnssd_services)))
dnssd_service_free(s);
hashmap_free(m->dnssd_services);
@@ -1621,7 +1632,7 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
}
/* Then, add the per-link servers */
- HASHMAP_FOREACH(l, m->links) {
+ HASHMAP_FOREACH(l, m->links)
LIST_FOREACH(servers, s, l->dns_servers) {
r = ordered_set_put(*dns, s);
if (r == -EEXIST)
@@ -1629,7 +1640,17 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
if (r < 0)
return r;
}
- }
+
+ /* Third, add the delegate servers and domains */
+ DnsDelegate *d;
+ HASHMAP_FOREACH(d, m->delegates)
+ LIST_FOREACH(servers, s, d->dns_servers) {
+ r = ordered_set_put(*dns, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
/* If we found nothing, add the fallback servers */
if (ordered_set_isempty(*dns)) {
@@ -1651,7 +1672,6 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
* > 0 or true: return only domains which are for routing only
*/
int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) {
- Link *l;
int r;
assert(m);
@@ -1674,8 +1694,23 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_
return r;
}
- HASHMAP_FOREACH(l, m->links) {
+ DnsDelegate *delegate;
+ HASHMAP_FOREACH(delegate, m->delegates)
+ LIST_FOREACH(domains, d, delegate->search_domains) {
+ if (filter_route >= 0 &&
+ d->route_only != !!filter_route)
+ continue;
+
+ r = ordered_set_put(*domains, d->name);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+
+ Link *l;
+ HASHMAP_FOREACH(l, m->links)
LIST_FOREACH(domains, d, l->search_domains) {
if (filter_route >= 0 &&
@@ -1688,7 +1723,6 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_
if (r < 0)
return r;
}
- }
return 0;
}
@@ -1771,14 +1805,18 @@ void manager_flush_caches(Manager *m, int log_level) {
}
void manager_reset_server_features(Manager *m) {
- Link *l;
dns_server_reset_features_all(m->dns_servers);
dns_server_reset_features_all(m->fallback_dns_servers);
+ Link *l;
HASHMAP_FOREACH(l, m->links)
dns_server_reset_features_all(l->dns_servers);
+ DnsDelegate *d;
+ HASHMAP_FOREACH(d, m->delegates)
+ dns_server_reset_features_all(d->dns_servers);
+
log_info("Resetting learnt feature levels on all servers.");
}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index e3f98be33d..bb398e83b7 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -89,6 +89,8 @@ typedef struct Manager {
LIST_HEAD(DnsScope, dns_scopes);
DnsScope *unicast_scope;
+ Hashmap *delegates; /* id string → DnsDelegate objects */
+
/* LLMNR */
int llmnr_ipv4_udp_fd;
int llmnr_ipv6_udp_fd;
diff --git a/src/resolve/test-dns-query.c b/src/resolve/test-dns-query.c
index ca4bd22ce3..83460e1c2d 100644
--- a/src/resolve/test-dns-query.c
+++ b/src/resolve/test-dns-query.c
@@ -786,7 +786,7 @@ static void go_env_setup(GoEnvironment *env, GoConfig *cfg) {
}
if (cfg->has_scope) {
- ASSERT_OK(dns_scope_new(&env->manager, &env->scope, env->link ? DNS_SCOPE_LINK : DNS_SCOPE_GLOBAL, env->link, env->protocol, env->family));
+ ASSERT_OK(dns_scope_new(&env->manager, &env->scope, env->link ? DNS_SCOPE_LINK : DNS_SCOPE_GLOBAL, env->link, /* delegate= */ NULL, env->protocol, env->family));
ASSERT_NOT_NULL(env->scope);
env->server_addr.in.s_addr = htobe32(0x7f000001);
@@ -794,7 +794,7 @@ static void go_env_setup(GoEnvironment *env, GoConfig *cfg) {
env->server_port = 53;
ASSERT_OK(dns_server_new(&env->manager, &env->server, env->server_type,
- env->link, env->family, &env->server_addr, env->server_port,
+ env->link, /* delegate= */ NULL, env->family, &env->server_addr, env->server_port,
env->ifindex, env->server_name, RESOLVE_CONFIG_SOURCE_DBUS));
ASSERT_NOT_NULL(env->server);
@@ -808,7 +808,7 @@ static void go_env_setup(GoEnvironment *env, GoConfig *cfg) {
for (size_t i = 0 ; i < env->n_search_domains; i++) {
DnsSearchDomainType type = (env->link == NULL) ? DNS_SEARCH_DOMAIN_SYSTEM : DNS_SEARCH_DOMAIN_LINK;
- ASSERT_OK(dns_search_domain_new(&env->manager, &env->search_domains[i], type, env->link, SEARCH_DOMAINS[i]));
+ ASSERT_OK(dns_search_domain_new(&env->manager, &env->search_domains[i], type, env->link, /* delegate= */ NULL, SEARCH_DOMAINS[i]));
ASSERT_NOT_NULL(env->search_domains[i]);
}
}
diff --git a/src/resolve/test-dns-search-domain.c b/src/resolve/test-dns-search-domain.c
index b1229dc916..8c1f8415cc 100644
--- a/src/resolve/test-dns-search-domain.c
+++ b/src/resolve/test-dns-search-domain.c
@@ -28,7 +28,7 @@ TEST(dns_search_domain_new_system) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd = NULL;
- ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"));
+ ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local"));
ASSERT_NOT_NULL(sd);
ASSERT_TRUE(sd->linked);
@@ -40,12 +40,12 @@ TEST(dns_search_domain_new_system_limit) {
DnsSearchDomain *sd = NULL;
for (size_t i = 0; i < MANAGER_SEARCH_DOMAINS_MAX; i++) {
- ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"));
+ ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local"));
ASSERT_NOT_NULL(sd);
ASSERT_EQ(manager.n_search_domains, i + 1);
}
- ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"), E2BIG);
+ ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local"), E2BIG);
ASSERT_NOT_NULL(sd);
dns_search_domain_unlink_all(manager.search_domains);
@@ -59,7 +59,7 @@ TEST(dns_search_domain_new_link) {
ASSERT_OK(link_new(&manager, &link, 1));
ASSERT_NOT_NULL(link);
- ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local."));
+ ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local."));
ASSERT_NOT_NULL(sd);
ASSERT_TRUE(sd->linked);
@@ -75,12 +75,12 @@ TEST(dns_search_domain_new_link_limit) {
ASSERT_NOT_NULL(link);
for (size_t i = 0; i < LINK_SEARCH_DOMAINS_MAX; i++) {
- ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local"));
+ ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local"));
ASSERT_NOT_NULL(sd);
ASSERT_EQ(link->n_search_domains, i + 1);
}
- ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local"), E2BIG);
+ ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local"), E2BIG);
ASSERT_NOT_NULL(sd);
}
@@ -93,13 +93,13 @@ TEST(dns_search_domain_unlink_system) {
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd3 = NULL;
DnsSearchDomain *sd2 = NULL;
- dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
+ dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
- dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
+ dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
- dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
+ dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_TRUE(sd2->linked);
@@ -122,13 +122,13 @@ TEST(dns_search_domain_unlink_link) {
ASSERT_OK(link_new(&manager, &link, 1));
ASSERT_NOT_NULL(link);
- dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_LINK, link, "local");
+ dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
- dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_LINK, link, "vpn.example.com");
+ dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
- dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_LINK, link, "org");
+ dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_TRUE(sd2->linked);
@@ -150,13 +150,13 @@ TEST(dns_search_domain_mark_all) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL;
- dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
+ dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
- dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
+ dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
- dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
+ dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_FALSE(sd1->marked);
@@ -178,13 +178,13 @@ TEST(dns_search_domain_move_back_and_unmark) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL;
- dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
+ dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
- dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
+ dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
- dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
+ dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
dns_search_domain_move_back_and_unmark(sd1);
@@ -210,13 +210,13 @@ TEST(dns_search_domain_unlink_marked) {
DnsSearchDomain *sd1 = NULL, *sd2 = NULL;
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd3 = NULL;
- dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
+ dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
- dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
+ dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
- dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
+ dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
dns_search_domain_unlink_marked(sd1);
@@ -244,13 +244,13 @@ TEST(dns_search_domain_unlink_all) {
Manager manager = {};
DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL;
- dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
+ dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
- dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
+ dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
- dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
+ dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
dns_search_domain_unlink_all(sd1);
@@ -266,13 +266,13 @@ TEST(dns_search_domain_find) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL, *ret = NULL;
- dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
+ dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
- dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
+ dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
- dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
+ dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_TRUE(dns_search_domain_find(sd1, "local", &ret));
diff --git a/src/resolve/test-dns-zone.c b/src/resolve/test-dns-zone.c
index 120080d94e..2c461e9d88 100644
--- a/src/resolve/test-dns-zone.c
+++ b/src/resolve/test-dns-zone.c
@@ -27,7 +27,7 @@ TEST(dns_zone_put_simple) {
DnsZoneItem *item = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- ASSERT_OK(dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET));
+ ASSERT_OK(dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET));
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@@ -51,7 +51,7 @@ TEST(dns_zone_put_any_class_is_invalid) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@@ -69,7 +69,7 @@ TEST(dns_zone_put_any_type_is_invalid) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@@ -91,7 +91,7 @@ TEST(dns_zone_remove_rr_match) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_in = NULL, *rr_out = NULL;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@@ -116,7 +116,7 @@ TEST(dns_zone_remove_rr_match_one) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_in = NULL, *rr_out = NULL;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@@ -149,7 +149,7 @@ TEST(dns_zone_remove_rr_different_payload) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_in = NULL, *rr_out = NULL;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@@ -179,7 +179,7 @@ TEST(dns_zone_remove_rrs_by_key) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr1 = NULL, *rr2 = NULL, *rr3 = NULL;
DnsResourceKey *key = NULL;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@@ -249,7 +249,7 @@ TEST(dns_zone_lookup_match_a) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@@ -271,7 +271,7 @@ TEST(dns_zone_lookup_match_cname) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@@ -294,7 +294,7 @@ TEST(dns_zone_lookup_match_any) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@@ -325,7 +325,7 @@ TEST(dns_zone_lookup_match_any_apex) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@@ -350,7 +350,7 @@ TEST(dns_zone_lookup_match_nothing) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@@ -371,7 +371,7 @@ TEST(dns_zone_lookup_match_nothing_with_soa) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
- dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
+ dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
diff --git a/src/resolve/test-resolved-link.c b/src/resolve/test-resolved-link.c
index 0336c73aa9..96f62a362f 100644
--- a/src/resolve/test-resolved-link.c
+++ b/src/resolve/test-resolved-link.c
@@ -184,7 +184,7 @@ static void link_alloc_env_setup(LinkAllocEnv *env, int family, DnsServerType se
link = env->link;
ASSERT_OK(dns_server_new(&env->manager, &env->server, env->server_type,
- link, family, &env->server_addr, env->server_port,
+ link, /* delegate= */ NULL, family, &env->server_addr, env->server_port,
env->ifindex, env->server_name, RESOLVE_CONFIG_SOURCE_DBUS));
ASSERT_NOT_NULL(env->server);