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/man/rules/meson.build b/man/rules/meson.build index f44643352b..048cc79430 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -1187,6 +1187,7 @@ manpages = [ ['systemd', '1', ['init'], ''], ['systemd.automount', '5', [], ''], ['systemd.device', '5', [], ''], + ['systemd.dns-delegate', '5', [], 'ENABLE_RESOLVE'], ['systemd.dnssd', '5', [], 'ENABLE_RESOLVE'], ['systemd.environment-generator', '7', [], 'ENABLE_ENVIRONMENT_D'], ['systemd.exec', '5', [], ''], diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index 3bc3a01ab7..e736f78b0f 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -509,6 +509,8 @@ search foobar.com barbar.com systemd1 resolved.conf5 + systed.dns-delegate5 + systed.dnssd5 dnssec-trust-anchors.d5 nss-resolve8 resolvectl1 diff --git a/man/systemd.dns-delegate.xml b/man/systemd.dns-delegate.xml new file mode 100644 index 0000000000..3da069d8a6 --- /dev/null +++ b/man/systemd.dns-delegate.xml @@ -0,0 +1,99 @@ + + + + + + + + systemd.dns-delegate + systemd + + + + systemd.dns-delegate + 5 + + + + systemd.dns-delegate + DNS Server Delegation Configuration + + + + + /etc/systemd/dns-delegate.d/*.dns-delegate + /run/systemd/dns-delegate.d/*.dns-delegate + /usr/local/lib/systemd/dns-delegate.d/*.dns-delegate + /usr/lib/systemd/dns-delegate.d/*.dns-delegate + + + + + Description + + *.dns-delegate files may be used to delegate DNS lookups in specific domains to + specific DNS servers. These files are read by + systemd-resolved.service8. + Each such file defines a combination of one or more DNS servers and one or more DNS domains. Each such + definition synthesizes a DNS lookup scope that ensure lookups below the specified domains are sent to the + specified DNS servers, possibly in addition to any per-interface scopes and the global scope that + systemd-resolved maintains anyway. + + + + [Delegate] Section Options + + + + DNS= + + Takes one or more DNS server specifications, in the same syntax as the option of the same name in resolved.conf5. + + + + + Domains= + + Takes one or more domain name specifications, in the same syntax as the option of the same name in resolved.conf5. + + + + + DefaultRoute= + + Takes a boolean value, defaults to off. Controls whether this DNS server is a candidate for + looking up records for which no better route exists. + + + + + + + + + Examples + + # /etc/systemd/dns-delegate.d/foobar.dns-delegate +[Delegate] +DNS=203.0.113.47 +Domain=foobar.com + + This ensures lookups of foobar.com and any domains below it are directed to + DNS server 203.0.113.47. + + + + + See Also + + systemd1 + systemd-resolved.service8 + resolved.conf5 + resolvectl1 + + + + 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/resolvectl.c b/src/resolve/resolvectl.c index 7d284ab28e..7dac8d741f 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -1364,6 +1364,26 @@ typedef enum { READ_DNS_ONLY_GLOBAL = 1 << 2, /* suppress entries with an (non-loopback) ifindex set (i.e. which are specific to some interface) */ } ReadDNSFlag; +static const char *dns_server_property_signature(ReadDNSFlag flags) { + switch (flags & (READ_DNS_WITH_IFINDEX|READ_DNS_EXTENDED)) { + + case 0: + return "iay"; + + case READ_DNS_WITH_IFINDEX: + return "iiay"; + + case READ_DNS_EXTENDED: + return "iayqs"; + + case READ_DNS_WITH_IFINDEX|READ_DNS_EXTENDED: + return "iiayqs"; + + default: + assert_not_reached(); + } +} + static int read_dns_server_one(sd_bus_message *m, ReadDNSFlag flags, char **ret) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *pretty = NULL; @@ -1379,8 +1399,7 @@ static int read_dns_server_one(sd_bus_message *m, ReadDNSFlag flags, char **ret) r = sd_bus_message_enter_container( m, 'r', - FLAGS_SET(flags, READ_DNS_WITH_IFINDEX) ? (FLAGS_SET(flags, READ_DNS_EXTENDED) ? "iiayqs" : "iiay") : - (FLAGS_SET(flags, READ_DNS_EXTENDED) ? "iayqs" : "iay")); + dns_server_property_signature(flags)); if (r <= 0) return r; @@ -1416,11 +1435,7 @@ static int read_dns_server_one(sd_bus_message *m, ReadDNSFlag flags, char **ret) } if (FLAGS_SET(flags, READ_DNS_EXTENDED)) { - r = sd_bus_message_read(m, "q", &port); - if (r < 0) - return r; - - r = sd_bus_message_read(m, "s", &name); + r = sd_bus_message_read(m, "qs", &port, &name); if (r < 0) return r; } @@ -1443,7 +1458,7 @@ static int read_dns_server_one(sd_bus_message *m, ReadDNSFlag flags, char **ret) return 1; } -static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, ReadDNSFlag flags) { +static int map_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, ReadDNSFlag flags, sd_bus_error *error, void *userdata) { char ***l = ASSERT_PTR(userdata); int r; @@ -1451,7 +1466,9 @@ static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus assert(member); assert(m); - r = sd_bus_message_enter_container(m, 'a', FLAGS_SET(flags, READ_DNS_EXTENDED) ? "(iayqs)" : "(iay)"); + const char *sig = strjoina("(", dns_server_property_signature(flags), ")"); + + r = sd_bus_message_enter_container(m, 'a', sig); if (r < 0) return r; @@ -1480,11 +1497,11 @@ static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus } static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - return map_link_dns_servers_internal(bus, member, m, error, userdata, /* flags = */ 0); + return map_dns_servers_internal(bus, member, m, /* flags = */ 0, error, userdata); } static int map_link_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - return map_link_dns_servers_internal(bus, member, m, error, userdata, READ_DNS_EXTENDED); + return map_dns_servers_internal(bus, member, m, READ_DNS_EXTENDED, error, userdata); } static int map_link_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { @@ -1530,11 +1547,17 @@ static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) { return -ENOMEM; *ret = TAKE_PTR(str); - return 1; } -static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { +static int map_domains_internal( + sd_bus *bus, + const char *member, + sd_bus_message *m, + bool with_ifindex, + sd_bus_error *error, + void *userdata) { + char ***l = ASSERT_PTR(userdata); int r; @@ -1542,14 +1565,14 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, assert(member); assert(m); - r = sd_bus_message_enter_container(m, 'a', "(sb)"); + r = sd_bus_message_enter_container(m, 'a', with_ifindex ? "(isb)" : "(sb)"); if (r < 0) return r; for (;;) { _cleanup_free_ char *pretty = NULL; - r = read_domain_one(m, false, &pretty); + r = read_domain_one(m, with_ifindex, &pretty); if (r < 0) return r; if (r == 0) @@ -1570,12 +1593,18 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, return 0; } -static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) { +static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return map_domains_internal(bus, member, m, /* with_ifindex= */ false, error, userdata); +} + +static int status_print_strv_full(int ifindex, const char *ifname, const char *delegate_id, char **p) { const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */ int pos1, pos2; if (ifname) printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, ifindex, ifname, &pos2, ansi_normal()); + else if (delegate_id) + printf("%s%nDelegate %s%n%s:", ansi_highlight(), &pos1, delegate_id, &pos2, ansi_normal()); else printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal()); @@ -1599,8 +1628,16 @@ static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) return 0; } +static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) { + return status_print_strv_full(ifindex, ifname, NULL, p); +} + +static int status_print_strv_delegate(const char *delegate_id, char **p) { + return status_print_strv_full(0, NULL, delegate_id, p); +} + static int status_print_strv_global(char **p) { - return status_print_strv_ifindex(0, NULL, p); + return status_print_strv_full(0, NULL, NULL, p); } typedef struct LinkInfo { @@ -1978,41 +2015,7 @@ static int map_global_current_dns_server_ex(sd_bus *bus, const char *member, sd_ } static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - char ***l = ASSERT_PTR(userdata); - int r; - - assert(bus); - assert(member); - assert(m); - - r = sd_bus_message_enter_container(m, 'a', "(isb)"); - if (r < 0) - return r; - - for (;;) { - _cleanup_free_ char *pretty = NULL; - - r = read_domain_one(m, true, &pretty); - if (r < 0) - return r; - if (r == 0) - break; - - if (isempty(pretty)) - continue; - - r = strv_consume(l, TAKE_PTR(pretty)); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - strv_sort(*l); - - return 0; + return map_domains_internal(bus, member, m, /* with_ifindex= */ true, error, userdata); } static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { @@ -2151,18 +2154,13 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { return 0; } -static int status_all(sd_bus *bus, StatusMode mode) { +static int status_links(sd_bus *bus, StatusMode mode, bool *empty_line) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - bool empty_line = false; int ret = 0, r; assert(bus); - r = status_global(bus, mode, &empty_line); - if (r < 0) - return r; - r = sd_netlink_open(&rtnl); if (r < 0) return log_error_errno(r, "Failed to connect to netlink: %m"); @@ -2214,11 +2212,205 @@ static int status_all(sd_bus *bus, StatusMode mode) { typesafe_qsort(infos, n_infos, interface_info_compare); FOREACH_ARRAY(info, infos, n_infos) - RET_GATHER(ret, status_ifindex(bus, info->index, info->name, mode, &empty_line)); + RET_GATHER(ret, status_ifindex(bus, info->index, info->name, mode, empty_line)); return ret; } +typedef struct DelegateInfo { + char *current_dns; + char **dns; + char **domains; + bool default_route; +} DelegateInfo; + +static void delegate_info_done(DelegateInfo *p) { + assert(p); + + free(p->current_dns); + strv_free(p->dns); + strv_free(p->domains); +} + +static int map_delegate_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return map_dns_servers_internal(bus, member, m, READ_DNS_WITH_IFINDEX|READ_DNS_EXTENDED, error, userdata); +} + +static int map_delegate_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return read_dns_server_one(m, READ_DNS_WITH_IFINDEX|READ_DNS_EXTENDED, userdata); +} + +static int map_delegate_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return map_domains_internal(bus, member, m, /* with_ifindex= */ false, error, userdata); +} + +static int status_delegate_one(sd_bus *bus, const char *id, StatusMode mode, bool *empty_line) { + + static const struct bus_properties_map property_map[] = { + { "DNS", "a(iiayqs)", map_delegate_dns_servers, offsetof(DelegateInfo, dns) }, + { "CurrentDNSServer", "(iiayqs)", map_delegate_current_dns_server, offsetof(DelegateInfo, current_dns) }, + { "Domains", "a(sb)", map_delegate_domains, offsetof(DelegateInfo, domains) }, + { "DefaultRoute", "b", NULL, offsetof(DelegateInfo, default_route) }, + {} + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(delegate_info_done) DelegateInfo delegate_info = {}; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *p = NULL; + int r; + + assert(bus); + assert(id); + + r = sd_bus_path_encode("/org/freedesktop/resolve1/dns_delegate", id, &p); + if (r < 0) + return log_oom(); + + r = bus_map_all_properties( + bus, + "org.freedesktop.resolve1", + p, + property_map, + BUS_MAP_BOOLEAN_AS_BOOL, + &error, + &m, + &delegate_info); + if (r < 0) + return log_error_errno(r, "Failed to get delegate data for %s: %s", id, bus_error_message(&error, r)); + + pager_open(arg_pager_flags); + + switch (mode) { + + case STATUS_DNS: + return status_print_strv_delegate(id, delegate_info.dns); + + case STATUS_DOMAIN: + return status_print_strv_delegate(id, delegate_info.domains); + + case STATUS_DEFAULT_ROUTE: + printf("%sDelegate %s%s: %s\n", + ansi_highlight(), id, ansi_normal(), + yes_no(delegate_info.default_route)); + + return 0; + + case STATUS_ALL: + break; + + default: + return 0; + } + + if (empty_line && *empty_line) + fputc('\n', stdout); + + printf("%sDelegate %s%s\n", + ansi_highlight(), id, ansi_normal()); + + _cleanup_(table_unrefp) Table *table = table_new_vertical(); + if (!table) + return log_oom(); + + if (delegate_info.current_dns) { + r = table_add_many(table, + TABLE_FIELD, "Current DNS Server", + TABLE_STRING, delegate_info.current_dns); + if (r < 0) + return table_log_add_error(r); + } + + r = dump_list(table, "DNS Servers", delegate_info.dns); + if (r < 0) + return r; + + r = dump_list(table, "DNS Domain", delegate_info.domains); + if (r < 0) + return r; + + r = table_add_many(table, + TABLE_FIELD, "Default Route", + TABLE_SET_MINIMUM_WIDTH, 19, + TABLE_BOOLEAN, delegate_info.default_route); + + r = table_print(table, NULL); + if (r < 0) + return table_log_print_error(r); + + if (empty_line) + *empty_line = true; + + return 0; +} + +static int status_delegates(sd_bus *bus, StatusMode mode, bool *empty_line) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r, ret = 0; + + assert(bus); + + r = bus_call_method(bus, bus_resolve_mgr, "ListDelegates", &error, &reply, NULL); + if (r < 0) { + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + log_debug("Delegates not supported, skipping."); + return 0; + } + return log_error_errno(r, "Failed to list delegates: %s", bus_error_message(&error, r)); + } + + r = sd_bus_message_enter_container(reply, 'a', "(so)"); + if (r < 0) + return bus_log_parse_error(r); + + _cleanup_strv_free_ char **l = NULL; + for (;;) { + const char *id; + + r = sd_bus_message_read(reply, "(so)", &id, NULL); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + if (strv_extend(&l, id) < 0) + return log_oom(); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + strv_sort(l); + + STRV_FOREACH(i, l) + RET_GATHER(ret, status_delegate_one(bus, *i, mode, empty_line)); + + return ret; +} + +static int status_all(sd_bus *bus, StatusMode mode) { + bool empty_line = false; + int r; + + assert(bus); + + r = status_global(bus, mode, &empty_line); + if (r < 0) + return r; + + r = status_links(bus, mode, &empty_line); + if (r < 0) + return r; + + r = status_delegates(bus, mode, &empty_line); + if (r < 0) + return r; + + return 0; +} + static int verb_status(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; 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 50f290a8d8..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" @@ -29,6 +30,7 @@ #include "resolved-mdns.h" #include "resolved-timeouts.h" #include "socket-util.h" +#include "string-table.h" #include "strv.h" #define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC) @@ -38,11 +40,24 @@ #define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC) #define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC) -int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) { +int dns_scope_new( + Manager *m, + DnsScope **ret, + DnsScopeOrigin origin, + Link *link, + DnsDelegate *delegate, + DnsProtocol protocol, + int family) { + DnsScope *s; assert(m); assert(ret); + assert(origin >= 0); + assert(origin < _DNS_SCOPE_ORIGIN_MAX); + + assert(!!link == (origin == DNS_SCOPE_LINK)); + assert(!!delegate == (origin == DNS_SCOPE_DELEGATE)); s = new(DnsScope, 1); if (!s) @@ -50,7 +65,9 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int *s = (DnsScope) { .manager = m, - .link = l, + .link = link, + .delegate = delegate, + .origin = origin, .protocol = protocol, .family = family, .resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC, @@ -66,9 +83,9 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int * not update it from the on, even if the setting * changes. */ - if (l) { - s->dnssec_mode = link_get_dnssec_mode(l); - s->dns_over_tls_mode = link_get_dns_over_tls_mode(l); + if (link) { + s->dnssec_mode = link_get_dnssec_mode(link); + s->dns_over_tls_mode = link_get_dns_over_tls_mode(link); } else { s->dnssec_mode = manager_get_dnssec_mode(m); s->dns_over_tls_mode = manager_get_dns_over_tls_mode(m); @@ -84,7 +101,12 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int dns_scope_llmnr_membership(s, true); dns_scope_mdns_membership(s, true); - log_debug("New scope on link %s, protocol %s, family %s", l ? l->ifname : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family)); + 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), + s->delegate ? s->delegate->id : "n/a"); *ret = s; return 0; @@ -112,7 +134,12 @@ DnsScope* dns_scope_free(DnsScope *s) { if (!s) return NULL; - log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->ifname : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); + 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), + s->delegate ? s->delegate->id : "n/a"); dns_scope_llmnr_membership(s, false); dns_scope_mdns_membership(s, false); @@ -143,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); } @@ -155,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; } @@ -172,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); } @@ -279,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; @@ -310,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; @@ -1380,6 +1417,14 @@ void dns_scope_dump(DnsScope *s, FILE *f) { fputs(af_to_name(s->family), 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)) { @@ -1401,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; } @@ -1686,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; @@ -1711,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) { @@ -1806,3 +1864,11 @@ int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protoco return false; } + +static const char* const dns_scope_origin_table[_DNS_SCOPE_ORIGIN_MAX] = { + [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 834bc91885..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; @@ -31,9 +32,19 @@ typedef enum DnsScopeMatch { _DNS_SCOPE_MATCH_INVALID = -EINVAL, } DnsScopeMatch; +typedef enum DnsScopeOrigin { + DNS_SCOPE_GLOBAL, + DNS_SCOPE_LINK, + DNS_SCOPE_DELEGATE, + _DNS_SCOPE_ORIGIN_MAX, + _DNS_SCOPE_ORIGIN_INVALID = -EINVAL, +} DnsScopeOrigin; + struct DnsScope { Manager *manager; + DnsScopeOrigin origin; + DnsProtocol protocol; int family; @@ -42,6 +53,7 @@ struct DnsScope { DnsOverTlsMode dns_over_tls_mode; Link *link; + DnsDelegate *delegate; DnsCache cache; DnsZone zone; @@ -74,7 +86,7 @@ struct DnsScope { bool announced; }; -int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, 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); @@ -124,3 +136,6 @@ int dns_scope_dump_cache_to_json(DnsScope *scope, sd_json_variant **ret); int dns_type_suitable_for_protocol(uint16_t type, DnsProtocol protocol); int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protocol); + +const char* dns_scope_origin_to_string(DnsScopeOrigin origin) _const_; +DnsScopeOrigin dns_scope_origin_from_string(const char *s) _pure_; 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 1e2d95b4ea..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, 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, 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, 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, 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, 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 e5c2ff8bb7..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, 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, 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 6ae6b9e09e..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, 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 0023f0bf93..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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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); diff --git a/test/knot-data/knot.conf b/test/knot-data/knot.conf index 6d177d7285..8519a6c7e5 100644 --- a/test/knot-data/knot.conf +++ b/test/knot-data/knot.conf @@ -5,6 +5,7 @@ server: user: knot:knot listen: 10.0.0.1@53 listen: fd00:dead:beef:cafe::1@53 + listen: 192.168.77.78@53 log: - target: syslog diff --git a/test/knot-data/zones/test.zone b/test/knot-data/zones/test.zone index 065ff7e2a0..75583c4ce6 100644 --- a/test/knot-data/zones/test.zone +++ b/test/knot-data/zones/test.zone @@ -22,3 +22,5 @@ unsigned NS ns1.unsigned svcb SVCB 1 . alpn=dot ipv4hint=10.0.0.1 ipv6hint=fd00:dead:beef:cafe::1 https HTTPS 1 . alpn="h2,h3" + +delegation.excercise A 1.2.3.4 diff --git a/test/units/TEST-75-RESOLVED.sh b/test/units/TEST-75-RESOLVED.sh index be3a90385d..6570febe79 100755 --- a/test/units/TEST-75-RESOLVED.sh +++ b/test/units/TEST-75-RESOLVED.sh @@ -1302,6 +1302,47 @@ testcase_15_wait_online_dns() { journalctl -b -u "$unit" --grep="dns0: link is configured by networkd and online." > /dev/null } +testcase_delegate() { + # Before we install the delegation file the DNS name should be directly resolveable via our DNS server + run resolvectl query delegation.excercise.test + grep -qF "1.2.3.4" "$RUN_OUT" + + mkdir -p /run/systemd/dns-delegate.d/ + cat >/run/systemd/dns-delegate.d/testcase.dns-delegate <