From: Nick Rosbrook Date: Fri, 11 Oct 2024 18:44:44 +0000 (-0400) Subject: resolved: add SubscribeDNSConfiguration to varlink API X-Git-Tag: v258-rc1~1462^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=54401c6fdf5385d158ea8b91949b8b5391e0da95;p=thirdparty%2Fsystemd.git resolved: add SubscribeDNSConfiguration to varlink API Add a new method to io.systemd.Resolve.Monitor that allows subscribing to changes in the systemd-resolved DNS configuration. The new method emits the full DNS configuration (one entry for global configuration, and one entry for each interface), any time the configuration is updated. --- diff --git a/src/resolve/org.freedesktop.resolve1.policy b/src/resolve/org.freedesktop.resolve1.policy index b96c8c0d6ad..097e78e73ca 100644 --- a/src/resolve/org.freedesktop.resolve1.policy +++ b/src/resolve/org.freedesktop.resolve1.policy @@ -150,6 +150,17 @@ unix-user:systemd-resolve + + Subscribe to DNS configuration + Authentication is required to subscribe to DNS configuration. + + auth_admin + auth_admin + auth_admin_keep + + unix-user:systemd-resolve + + Dump cache Authentication is required to dump cache. diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c index a11b21350a5..642d52c6ea3 100644 --- a/src/resolve/resolved-dns-search-domain.c +++ b/src/resolve/resolved-dns-search-domain.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-json.h" + #include "alloc-util.h" #include "dns-domain.h" #include "resolved-dns-search-domain.h" @@ -197,3 +199,21 @@ int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDo *ret = NULL; return 0; } + +int dns_search_domain_dump_to_json(DnsSearchDomain *domain, sd_json_variant **ret) { + int ifindex = 0; + + assert(domain); + assert(ret); + + if (domain->type == DNS_SEARCH_DOMAIN_LINK) { + assert(domain->link); + ifindex = domain->link->ifindex; + } + + return sd_json_buildo( + ret, + SD_JSON_BUILD_PAIR_STRING("name", domain->name), + SD_JSON_BUILD_PAIR_BOOLEAN("routeOnly", domain->route_only), + SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex))); +} diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h index f0d96aca37d..3e5229c12f5 100644 --- a/src/resolve/resolved-dns-search-domain.h +++ b/src/resolve/resolved-dns-search-domain.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "sd-json.h" + #include "list.h" #include "macro.h" @@ -54,3 +56,5 @@ static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) { } DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref); + +int dns_search_domain_dump_to_json(DnsSearchDomain *domain, sd_json_variant **ret); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 1702866a143..54d0d32ec5d 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -7,6 +7,7 @@ #include "alloc-util.h" #include "errno-util.h" #include "fd-util.h" +#include "json-util.h" #include "resolved-bus.h" #include "resolved-dns-server.h" #include "resolved-dns-stub.h" @@ -881,6 +882,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { dns_cache_flush(&m->unicast_scope->cache); (void) manager_send_changed(m, "CurrentDNSServer"); + (void) manager_send_dns_configuration_changed(m, NULL, /* reset= */ false); return s; } @@ -1180,3 +1182,28 @@ void dns_server_reset_accessible_all(DnsServer *first) { LIST_FOREACH(servers, s, first) dns_server_reset_accessible(s); } + +int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret) { + bool accessible = false; + int ifindex, r; + + assert(server); + assert(ret); + + ifindex = dns_server_ifindex(server); + + r = dns_server_is_accessible(server); + if (r < 0) + log_debug_errno(r, "Failed to check if %s is accessible, assume not: %m", dns_server_string_full(server)); + else + accessible = r; + + return sd_json_buildo( + ret, + JSON_BUILD_PAIR_IN_ADDR("address", &server->address, server->family), + SD_JSON_BUILD_PAIR_INTEGER("family", server->family), + SD_JSON_BUILD_PAIR_UNSIGNED("port", dns_server_port(server)), + SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)), + JSON_BUILD_PAIR_STRING_NON_EMPTY("name", server->server_name), + SD_JSON_BUILD_PAIR_BOOLEAN("accessible", accessible)); +} diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 8915b87e957..0f066b15b9f 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -190,6 +190,7 @@ static inline bool dns_server_is_fallback(DnsServer *s) { } int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret); +int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret); int dns_server_is_accessible(DnsServer *s); static inline void dns_server_reset_accessible(DnsServer *s) { diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c index b0c4b833b5d..77d2663e4f3 100644 --- a/src/resolve/resolved-link-bus.c +++ b/src/resolve/resolved-link-bus.c @@ -272,6 +272,7 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi (void) link_save_user(l); (void) manager_write_resolv_conf(l->manager); (void) manager_send_changed(l->manager, "DNS"); + (void) manager_send_dns_configuration_changed(l->manager, l, /* reset= */ true); if (j) log_link_info(l, "Bus client set DNS server list to: %s", j); @@ -751,6 +752,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error (void) link_save_user(l); (void) manager_write_resolv_conf(l->manager); (void) manager_send_changed(l->manager, "DNS"); + (void) manager_send_dns_configuration_changed(l->manager, l, /* reset= */ true); manager_llmnr_maybe_stop(l->manager); manager_mdns_maybe_stop(l->manager); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 883bc097015..e3749786918 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -22,6 +22,7 @@ #include "idn-util.h" #include "io-util.h" #include "iovec-util.h" +#include "json-util.h" #include "memstream-util.h" #include "missing_network.h" #include "missing_socket.h" @@ -108,6 +109,11 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void * /* Now check all the links, and if mDNS/llmr are disabled everywhere, stop them globally too. */ manager_llmnr_maybe_stop(m); manager_mdns_maybe_stop(m); + + /* The accessible flag on link DNS servers will have been reset by + * link_update(). Just reset the global DNS servers. */ + (void) manager_send_dns_configuration_changed(m, NULL, /* reset= */ true); + return 0; fail: @@ -192,6 +198,8 @@ static int manager_process_address(sd_netlink *rtnl, sd_netlink_message *mm, voi break; } + (void) manager_send_dns_configuration_changed(m, l, /* reset= */ true); + return 0; fail: @@ -199,6 +207,38 @@ fail: return 0; } +static int manager_process_route(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { + Manager *m = ASSERT_PTR(userdata); + Link *l = NULL; + uint16_t type; + uint32_t ifindex = 0; + int r; + + assert(rtnl); + assert(mm); + + r = sd_netlink_message_get_type(mm, &type); + if (r < 0) { + log_warning_errno(r, "Failed not get message type, ignoring: %m"); + return 0; + } + + if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) { + log_warning("Unexpected message type %u when processing route, ignoring.", type); + return 0; + } + + r = sd_netlink_message_read_u32(mm, RTA_OIF, &ifindex); + if (r < 0) + log_full_errno(r == -ENODATA ? LOG_DEBUG : LOG_WARNING, r, "Failed to get route ifindex, ignoring: %m"); + else + l = hashmap_get(m->links, INT_TO_PTR(ifindex)); + + (void) manager_send_dns_configuration_changed(m, l, /* reset= */ true); + + return 0; +} + static int manager_rtnl_listen(Manager *m) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; int r; @@ -289,6 +329,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void * (void) manager_write_resolv_conf(m); (void) manager_send_changed(m, "DNS"); + (void) manager_send_dns_configuration_changed(m, NULL, /* reset= */ true); /* Now check all the links, and if mDNS/llmr are disabled everywhere, stop them globally too. */ manager_llmnr_maybe_stop(m); @@ -808,10 +849,14 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->network_event_source); sd_network_monitor_unref(m->network_monitor); + sd_netlink_slot_unref(m->netlink_new_route_slot); + sd_netlink_slot_unref(m->netlink_del_route_slot); sd_netlink_unref(m->rtnl); sd_event_source_unref(m->rtnl_event_source); sd_event_source_unref(m->clock_change_event_source); + sd_json_variant_unref(m->dns_configuration_json); + manager_llmnr_stop(m); manager_mdns_stop(m); manager_dns_stub_stop(m); @@ -1938,3 +1983,181 @@ void dns_manager_reset_statistics(Manager *m) { m->n_failure_responses_served_stale_total = 0; zero(m->n_dnssec_verdict); } + +static int dns_configuration_json_append( + const char *ifname, + int ifindex, + int default_route, + DnsServer *current_dns_server, + DnsServer *dns_servers, + DnsSearchDomain *search_domains, + sd_json_variant **configuration) { + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *dns_servers_json = NULL, + *search_domains_json = NULL, + *current_dns_server_json = NULL; + int r; + + assert(configuration); + + if (dns_servers) { + r = sd_json_variant_new_array(&dns_servers_json, NULL, 0); + if (r < 0) + return r; + } + + if (search_domains) { + r = sd_json_variant_new_array(&search_domains_json, NULL, 0); + if (r < 0) + return r; + } + + if (current_dns_server) { + r = dns_server_dump_configuration_to_json(current_dns_server, ¤t_dns_server_json); + if (r < 0) + return r; + } + + LIST_FOREACH(servers, s, dns_servers) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + + assert(dns_servers_json); + + r = dns_server_dump_configuration_to_json(s, &v); + if (r < 0) + return r; + + r = sd_json_variant_append_array(&dns_servers_json, v); + if (r < 0) + return r; + } + + LIST_FOREACH(domains, d, search_domains) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + + assert(search_domains_json); + + r = dns_search_domain_dump_to_json(d, &v); + if (r < 0) + return r; + + r = sd_json_variant_append_array(&search_domains_json, v); + if (r < 0) + return r; + } + + return sd_json_variant_append_arraybo( + configuration, + JSON_BUILD_PAIR_STRING_NON_EMPTY("ifname", ifname), + SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)), + SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "defaultRoute", SD_JSON_BUILD_BOOLEAN(default_route > 0)), + JSON_BUILD_PAIR_VARIANT_NON_NULL("currentServer", current_dns_server_json), + JSON_BUILD_PAIR_VARIANT_NON_NULL("servers", dns_servers_json), + JSON_BUILD_PAIR_VARIANT_NON_NULL("searchDomains", search_domains_json)); +} + +int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL; + Link *l; + int r; + + assert(m); + assert(ret); + + /* Global DNS configuration */ + r = dns_configuration_json_append( + /* ifname = */ NULL, + /* ifindex = */ 0, + /* default_route = */ 0, + manager_get_dns_server(m), + m->dns_servers, + m->search_domains, + &configuration); + if (r < 0) + return r; + + /* Append configuration for each link */ + HASHMAP_FOREACH(l, m->links) { + r = dns_configuration_json_append( + l->ifname, + l->ifindex, + link_get_default_route(l), + link_get_dns_server(l), + l->dns_servers, + l->search_domains, + &configuration); + if (r < 0) + return r; + } + + return sd_json_buildo(ret, SD_JSON_BUILD_PAIR_VARIANT("configuration", configuration)); +} + +int manager_send_dns_configuration_changed(Manager *m, Link *l, bool reset) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL; + int r; + + assert(m); + + if (set_isempty(m->varlink_dns_configuration_subscription)) + return 0; + + if (reset) { + dns_server_reset_accessible_all(m->dns_servers); + + if (l) + dns_server_reset_accessible_all(l->dns_servers); + } + + r = manager_dump_dns_configuration_json(m, &configuration); + if (r < 0) + return log_warning_errno(r, "Failed to dump DNS configuration json: %m"); + + if (sd_json_variant_equal(configuration, m->dns_configuration_json)) + return 0; + + JSON_VARIANT_REPLACE(m->dns_configuration_json, TAKE_PTR(configuration)); + + r = varlink_many_notify(m->varlink_dns_configuration_subscription, m->dns_configuration_json); + if (r < 0) + return log_warning_errno(r, "Failed to send DNS configuration event: %m"); + + return 0; +} + +int manager_start_dns_configuration_monitor(Manager *m) { + Link *l; + int r; + + assert(m); + assert(!m->dns_configuration_json); + assert(!m->netlink_new_route_slot); + assert(!m->netlink_del_route_slot); + + dns_server_reset_accessible_all(m->dns_servers); + + HASHMAP_FOREACH(l, m->links) + dns_server_reset_accessible_all(l->dns_servers); + + r = manager_dump_dns_configuration_json(m, &m->dns_configuration_json); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, &m->netlink_new_route_slot, RTM_NEWROUTE, manager_process_route, NULL, m, "resolve-NEWROUTE"); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, &m->netlink_del_route_slot, RTM_DELROUTE, manager_process_route, NULL, m, "resolve-DELROUTE"); + if (r < 0) + return r; + + return 0; +} + +void manager_stop_dns_configuration_monitor(Manager *m) { + assert(m); + + m->dns_configuration_json = sd_json_variant_unref(m->dns_configuration_json); + m->netlink_new_route_slot = sd_netlink_slot_unref(m->netlink_new_route_slot); + m->netlink_del_route_slot = sd_netlink_slot_unref(m->netlink_del_route_slot); +} diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 5d870111d1b..3481a8836bf 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -153,6 +153,12 @@ struct Manager { sd_varlink_server *varlink_monitor_server; Set *varlink_query_results_subscription; + Set *varlink_dns_configuration_subscription; + + sd_json_variant *dns_configuration_json; + + sd_netlink_slot *netlink_new_route_slot; + sd_netlink_slot *netlink_del_route_slot; sd_event_source *clock_change_event_source; @@ -225,3 +231,9 @@ int socket_disable_pmtud(int fd, int af); int dns_manager_dump_statistics_json(Manager *m, sd_json_variant **ret); void dns_manager_reset_statistics(Manager *m); + +int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret); +int manager_send_dns_configuration_changed(Manager *m, Link *l, bool reset); + +int manager_start_dns_configuration_monitor(Manager *m); +void manager_stop_dns_configuration_monitor(Manager *m); diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index fd2fc588532..02e00eef7da 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -127,15 +127,25 @@ static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userd static void vl_on_notification_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) { Manager *m = ASSERT_PTR(userdata); + sd_varlink *removed_link = NULL; assert(s); assert(link); - sd_varlink *removed_link = set_remove(m->varlink_query_results_subscription, link); + removed_link = set_remove(m->varlink_query_results_subscription, link); if (removed_link) { sd_varlink_unref(removed_link); log_debug("%u query result monitor clients remain active", set_size(m->varlink_query_results_subscription)); } + + removed_link = set_remove(m->varlink_dns_configuration_subscription, link); + if (removed_link) { + sd_varlink_unref(removed_link); + log_debug("%u DNS monitor clients remain active", set_size(m->varlink_dns_configuration_subscription)); + + if (set_isempty(m->varlink_dns_configuration_subscription)) + manager_stop_dns_configuration_monitor(m); + } } static bool validate_and_mangle_flags( @@ -1354,6 +1364,45 @@ static int vl_method_reset_statistics(sd_varlink *link, sd_json_variant *paramet return sd_varlink_replyb(link, SD_JSON_BUILD_EMPTY_OBJECT); } +static int vl_method_subscribe_dns_configuration(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + Manager *m = ASSERT_PTR(sd_varlink_get_userdata(ASSERT_PTR(link))); + int r; + + /* if the client didn't set the more flag, it is using us incorrectly */ + if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)) + return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL); + + r = verify_polkit(link, parameters, "org.freedesktop.resolve1.subscribe-dns-configuration"); + if (r <= 0) + return r; + + if (set_isempty(m->varlink_dns_configuration_subscription)) { + r = manager_start_dns_configuration_monitor(m); + if (r < 0) + return log_error_errno(r, "Failed to start DNS configuration monitor: %m"); + } + + r = sd_varlink_notify(link, m->dns_configuration_json); + if (r < 0) + goto fail; + + r = set_ensure_put(&m->varlink_dns_configuration_subscription, NULL, link); + if (r < 0) + goto fail; + sd_varlink_ref(link); + + log_debug("%u clients now attached for link configuration varlink notifications", + set_size(m->varlink_dns_configuration_subscription)); + + return 1; +fail: + if (set_isempty(m->varlink_dns_configuration_subscription)) + manager_stop_dns_configuration_monitor(m); + + + return log_debug_errno(r, "Failed to subscribe client to DNS configuration monitor: %m"); +} + static int varlink_monitor_server_init(Manager *m) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL; int r; @@ -1377,7 +1426,8 @@ static int varlink_monitor_server_init(Manager *m) { "io.systemd.Resolve.Monitor.DumpCache", vl_method_dump_cache, "io.systemd.Resolve.Monitor.DumpServerState", vl_method_dump_server_state, "io.systemd.Resolve.Monitor.DumpStatistics", vl_method_dump_statistics, - "io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics); + "io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics, + "io.systemd.Resolve.Monitor.SubscribeDNSConfiguration", vl_method_subscribe_dns_configuration); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/shared/varlink-io.systemd.Resolve.Monitor.c b/src/shared/varlink-io.systemd.Resolve.Monitor.c index bc8907ddbe0..18d4eafefa0 100644 --- a/src/shared/varlink-io.systemd.Resolve.Monitor.c +++ b/src/shared/varlink-io.systemd.Resolve.Monitor.c @@ -112,6 +112,52 @@ static SD_VARLINK_DEFINE_METHOD( ResetStatistics, VARLINK_DEFINE_POLKIT_INPUT); +static SD_VARLINK_DEFINE_STRUCT_TYPE( + DNSServer, + SD_VARLINK_FIELD_COMMENT("IPv4 or IPv6 address of the server."), + SD_VARLINK_DEFINE_FIELD(address, SD_VARLINK_INT, SD_VARLINK_ARRAY), + SD_VARLINK_FIELD_COMMENT("Address family of the server, one of AF_INET or AF_INET6."), + SD_VARLINK_DEFINE_FIELD(family, SD_VARLINK_INT, 0), + SD_VARLINK_FIELD_COMMENT("Port number of the server."), + SD_VARLINK_DEFINE_FIELD(port, SD_VARLINK_INT, 0), + SD_VARLINK_FIELD_COMMENT("Interface index for which this server is configured."), + SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Server Name Indication (SNI) of the server."), + SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Indicates if the DNS server is accessible or not."), + SD_VARLINK_DEFINE_FIELD(accessible, SD_VARLINK_BOOL, 0)); + +static SD_VARLINK_DEFINE_STRUCT_TYPE( + SearchDomain, + SD_VARLINK_FIELD_COMMENT("Domain name."), + SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("Indicates whether or not this is a routing-only domain."), + SD_VARLINK_DEFINE_FIELD(routeOnly, SD_VARLINK_BOOL, 0), + SD_VARLINK_FIELD_COMMENT("Interface index for which this search domain is configured."), + SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); + +static SD_VARLINK_DEFINE_STRUCT_TYPE( + DNSConfiguration, + SD_VARLINK_FIELD_COMMENT("Interface name, if any, associated with this configuration. Empty for global configuration."), + SD_VARLINK_DEFINE_FIELD(ifname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Interface index, if any, associated with this configuration. Empty for global configuration."), + SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Indicates whether or not this link's DNS servers will be used for resolving domain names that do not match any link's configured domains."), + SD_VARLINK_DEFINE_FIELD(defaultRoute, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("DNS server currently selected to use for lookups."), + SD_VARLINK_DEFINE_FIELD_BY_TYPE(currentServer, DNSServer, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Array of configured DNS servers."), + SD_VARLINK_DEFINE_FIELD_BY_TYPE(servers, DNSServer, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Array of configured search domains."), + SD_VARLINK_DEFINE_FIELD_BY_TYPE(searchDomains, SearchDomain, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE)); + +static SD_VARLINK_DEFINE_METHOD_FULL( + SubscribeDNSConfiguration, + SD_VARLINK_REQUIRES_MORE, + SD_VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The current global and per-interface DNS configurations"), + SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(configuration, DNSConfiguration, SD_VARLINK_ARRAY)); + SD_VARLINK_DEFINE_INTERFACE( io_systemd_Resolve_Monitor, "io.systemd.Resolve.Monitor", @@ -129,4 +175,12 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_type_TransactionStatistics, &vl_type_CacheStatistics, &vl_type_DnssecStatistics, - &vl_type_ServerState); + &vl_type_ServerState, + SD_VARLINK_SYMBOL_COMMENT("Encapsulates a DNS server address specification."), + &vl_type_DNSServer, + SD_VARLINK_SYMBOL_COMMENT("Encapsulates a search domain specification."), + &vl_type_SearchDomain, + SD_VARLINK_SYMBOL_COMMENT("Encapsulates a global or per-link DNS configuration, including configured DNS servers, search domains, and more."), + &vl_type_DNSConfiguration, + SD_VARLINK_SYMBOL_COMMENT("Sends the complete global and per-link DNS configurations when any changes are made to them. The current configurations are given immediately when this method is invoked."), + &vl_method_SubscribeDNSConfiguration);