]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: add concept of delegating lookups below certain domains to specific DNS...
authorLennart Poettering <lennart@poettering.net>
Tue, 3 Sep 2024 08:56:31 +0000 (10:56 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 20 May 2025 21:00:03 +0000 (23:00 +0200)
This permits configuration of additional "delegates" which ensure that
lookups for certain DNS zones are routed to specific sets of DNS
servers, in addition to the routes we create for each network interface.

For now, this allows only static configuration, but eventually we should
open this up to IPC.

Fixes: #5573 #14159 #20485 #21260 #24532 #32022
(Fixes #32022, because now redundant)

24 files changed:
man/org.freedesktop.resolve1.xml
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/resolve/meson.build
src/resolve/resolved-bus.c
src/resolve/resolved-dns-delegate-bus.c [new file with mode: 0644]
src/resolve/resolved-dns-delegate-bus.h [new file with mode: 0644]
src/resolve/resolved-dns-delegate-gperf.gperf [new file with mode: 0644]
src/resolve/resolved-dns-delegate.c [new file with mode: 0644]
src/resolve/resolved-dns-delegate.h [new file with mode: 0644]
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-search-domain.c
src/resolve/resolved-dns-search-domain.h
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-link-bus.c
src/resolve/resolved-link.c
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/test-dns-query.c
src/resolve/test-dns-search-domain.c
src/resolve/test-dns-zone.c
src/resolve/test-resolved-link.c

index 324b6888537aa1cfecc5e5ce3a9e494d60a18ee5..7d5e997b8331437dace0ff3883f98667d613f6dc 100644 (file)
@@ -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 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="ResetServerFeatures()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="GetDelegate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListDelegates()"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="LLMNRHostname"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="LLMNR"/>
@@ -423,13 +430,13 @@ node /org/freedesktop/resolve1 {
       <para>The <function>RevertLink()</function> method may be used to revert all per-link settings
       described above to the defaults.</para>
 
-      <para>The <function>FlushCaches()</function> flushes all resource record caches maintained by the
+      <para>The <function>FlushCaches()</function> method flushes all resource record caches maintained by the
       resolver, and ensures that any subsequent lookups re-request their responses from their sources.</para>
 
-      <para>The <function>ResetServerFeatures()</function> 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.</para>
+      <para>The <function>ResetServerFeatures()</function> 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.</para>
 
       <para>The <function>RegisterService()</function> 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 {
       <function>RegisterService()</function> and deletes a DNS-SD service previously created via IPC
       again.</para>
 
+      <para>The <function>GetDelegate()</function> method returns the D-Bus object path for the specified DNS
+      delegate ID.</para>
+
+      <para>The <function>ListDelegates()</function> method returns a list of the IDs and D-Bus object paths
+      of the currently configured DNS delegates.</para>
+
       <refsect3>
         <title>The Flags Parameter</title>
 
@@ -936,6 +949,15 @@ $ gdbus introspect --system \
 
   <xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
 
+  <refsect1>
+    <title>History</title>
+    <refsect2>
+      <title>The Manager Object</title>
+
+      <para><function>GetDelegate()</function> and <function>ListDelegates()</function> were added in version 258.</para>
+    </refsect2>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para><simplelist type="inline">
index c911a64ca4f617ef1a8f96df2427fc4c4375ca5b..1075c29ed64e9f76a2eeeb7be5634e89ef3b8512 100644 (file)
@@ -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),
index e69f6194dd5404b3323dce4fc9d71633b0a21342..ad6b45ae3971c128846f4131292afde5b478eb1b 100644 (file)
@@ -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"
index a814551a9b9034230a0d24e30df7feb1b6be9074..b47dfd272498ec5896ab1facc9530ea990ab9e0d 100644 (file)
@@ -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(
index e9b47488e75e4e8c2114fbced15b09342b0a2284..be927e07060fb7bd56175001a6a88ae05b985047 100644 (file)
@@ -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 (file)
index 0000000..60134b5
--- /dev/null
@@ -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 (file)
index 0000000..7acdd5a
--- /dev/null
@@ -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 (file)
index 0000000..3040770
--- /dev/null
@@ -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 <stddef.h>
+#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 (file)
index 0000000..6a094ce
--- /dev/null
@@ -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 (file)
index 0000000..3ce3c1e
--- /dev/null
@@ -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);
index d4bfbccdc3b29b32342fe36cc82a66462e02a7ce..4525f253de79745aa6609a1b14fe77b9349fa213 100644 (file)
@@ -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)
-                return true;
+        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) {
 
-        /* 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;
+                if (scope->delegate->default_route >= 0)
+                        return scope->delegate->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);
+                /* Delegates are by default not used as default route */
+                return false;
+        } else
+                /* The global DNS scope is always suitable as default route */
+                return true;
 }
 
 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);
index 54f0b7b7a33ec234d7489cda482f699623258767..872457ffc209072dafa347553870487f7bd10496 100644 (file)
@@ -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);
index 642d52c6ea3bf7c7c5b1f6358865552f6686e0bf..63790ad1fc03503653384fba00fe5cb926176c70 100644 (file)
@@ -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();
         }
index fbd9a4490f1243f4074cf944d78375c509ec86b7..63bd357b8312345ebfe5c14ce0835f4f9d0c6e56 100644 (file)
@@ -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);
index 5996d9bcc6aaa5d0a764fe48e5faa9c489f5c1d4..7845dcfd217e1d25854a84add6c45a8bd82bfcdc 100644 (file)
@@ -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;
 }
index bb3c0e109f5eb0ec01149df55a1f2a2d04966df6..bef352aa421ba321341ea491381361d914ec254c 100644 (file)
 #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,
index 9c5f7a836ae1cbbb7dc26412cf14b38bbf17e1bf..9e5371684aa092c3dd3dd9148b397576552107f5 100644 (file)
@@ -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;
 
index 432d7cf8b0f4632733c2b373f0d09ab7b4dbd410..55c8aa36cf6aa522a21d8e0b5e00a0fcf4fcdef7 100644 (file)
@@ -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;
         }
index 856161fa47cfa0e72f4970e1ec21235b59c2138e..f44fde2882aea0bb14aa8fc8c595f91f65a0bee9 100644 (file)
@@ -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.");
 }
 
index e3f98be33db67846e6723ba95709dd79c5b890c9..bb398e83b7fa994529efb4b8d4715d0fcd6a7167 100644 (file)
@@ -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;
index ca4bd22ce381c67bf8c204296d199a084038b468..83460e1c2d4a3da65d377cc1f38cb6d74819b3cf 100644 (file)
@@ -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]);
         }
 }
index b1229dc916cf68352b7f37c466e05f2cbcefa558..8c1f8415cc7ca2a5dabbea72787897b9f2ea39ec 100644 (file)
@@ -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));
index 120080d94e8178d8774a3ec86db56bbd939a1d52..2c461e9d88dcee2b1ea93ae498f0b8f6b0f41b22 100644 (file)
@@ -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);
 
index 0336c73aa9c7db060171c8f2ee5d85af0d71b9de..96f62a362fb15d886ae9bd20b2a683737d11cf9b 100644 (file)
@@ -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);