summary refs log tree commit diff
path: root/src/core/dns
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
committerMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
commite74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch)
tree3469f17ea9af91f7ff169b890633bda68b0cf76e /src/core/dns
parentbfe522304da217296e2a61040f58e35ec5d6f3f2 (diff)
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'src/core/dns')
-rw-r--r--src/core/dns/nm-dns-dnsmasq.c6
-rw-r--r--src/core/dns/nm-dns-dnsmasq.h1
-rw-r--r--src/core/dns/nm-dns-manager.c152
-rw-r--r--src/core/dns/nm-dns-manager.h3
-rw-r--r--src/core/dns/nm-dns-plugin.c2
-rw-r--r--src/core/dns/nm-dns-plugin.h3
-rw-r--r--src/core/dns/nm-dns-systemd-resolved.c465
-rw-r--r--src/core/dns/nm-dns-systemd-resolved.h31
8 files changed, 521 insertions, 142 deletions
diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c
index dcff98e9..126e0a21 100644
--- a/src/core/dns/nm-dns-dnsmasq.c
+++ b/src/core/dns/nm-dns-dnsmasq.c
@@ -15,9 +15,9 @@
 #include <sys/stat.h>
 #include <linux/if.h>
 
-#include "nm-glib-aux/nm-dbus-aux.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
diff --git a/src/core/dns/nm-dns-dnsmasq.h b/src/core/dns/nm-dns-dnsmasq.h
index bd6d4c60..575b0b26 100644
--- a/src/core/dns/nm-dns-dnsmasq.h
+++ b/src/core/dns/nm-dns-dnsmasq.h
@@ -7,6 +7,7 @@
 #define __NETWORKMANAGER_DNS_DNSMASQ_H__
 
 #include "nm-dns-plugin.h"
+#include "nm-dns-manager.h"
 
 #define NM_TYPE_DNS_DNSMASQ (nm_dns_dnsmasq_get_type())
 #define NM_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq))
diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c
index 59c8898a..4c498c82 100644
--- a/src/core/dns/nm-dns-manager.c
+++ b/src/core/dns/nm-dns-manager.c
@@ -22,8 +22,9 @@
     #include <libpsl.h>
 #endif
 
+#include "libnm-glib-aux/nm-str-buf.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dns-manager.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -83,13 +84,10 @@ typedef struct {
     GHashTable *configs_dict;
     CList       configs_lst_head;
 
-    CList     ip_configs_lst_head;
+    CList     ip_config_lst_head;
     GVariant *config_variant;
 
-    NMDnsConfigIPData *best_ip_config_4;
-    NMDnsConfigIPData *best_ip_config_6;
-
-    bool ip_configs_lst_need_sort : 1;
+    bool ip_config_lst_need_sort : 1;
 
     bool configs_lst_need_sort : 1;
 
@@ -266,9 +264,11 @@ _dns_config_ip_data_new(NMDnsConfigData * data,
         .ip_config_type = ip_config_type,
     };
     c_list_link_tail(&data->data_lst_head, &ip_data->data_lst);
-    c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_configs_lst_head,
+    c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_config_lst_head,
                      &ip_data->ip_config_lst);
 
+    /* We also need to set priv->ip_config_lst_need_sort, but the caller will do that! */
+
     g_signal_connect(ip_config,
                      NM_IS_IP4_CONFIG(ip_config) ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY
                                                  : "notify::" NM_IP6_CONFIG_DNS_PRIORITY,
@@ -325,7 +325,7 @@ _dns_config_data_free(NMDnsConfigData *data)
 }
 
 static int
-_mgr_get_ip_configs_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data)
+_mgr_get_ip_config_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data)
 {
     const NMDnsConfigIPData *a = c_list_entry(a_lst, NMDnsConfigIPData, ip_config_lst);
     const NMDnsConfigIPData *b = c_list_entry(b_lst, NMDnsConfigIPData, ip_config_lst);
@@ -341,16 +341,16 @@ _mgr_get_ip_configs_lst_cmp(const CList *a_lst, const CList *b_lst, const void *
 }
 
 static CList *
-_mgr_get_ip_configs_lst_head(NMDnsManager *self)
+_mgr_get_ip_config_lst_head(NMDnsManager *self)
 {
     NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
-    if (G_UNLIKELY(priv->ip_configs_lst_need_sort)) {
-        priv->ip_configs_lst_need_sort = FALSE;
-        c_list_sort(&priv->ip_configs_lst_head, _mgr_get_ip_configs_lst_cmp, NULL);
+    if (G_UNLIKELY(priv->ip_config_lst_need_sort)) {
+        priv->ip_config_lst_need_sort = FALSE;
+        c_list_sort(&priv->ip_config_lst_head, _mgr_get_ip_config_lst_cmp, NULL);
     }
 
-    return &priv->ip_configs_lst_head;
+    return &priv->ip_config_lst_head;
 }
 
 static int
@@ -378,11 +378,11 @@ _mgr_get_configs_lst_head(NMDnsManager *self)
 
 /*****************************************************************************/
 
-gboolean
-nm_dns_manager_has_systemd_resolved(NMDnsManager *self)
+NMDnsPlugin *
+nm_dns_manager_get_systemd_resolved(NMDnsManager *self)
 {
-    NMDnsManagerPrivate * priv;
-    NMDnsSystemdResolved *plugin = NULL;
+    NMDnsManagerPrivate *priv;
+    NMDnsPlugin *        plugin = NULL;
 
     g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
 
@@ -390,11 +390,14 @@ nm_dns_manager_has_systemd_resolved(NMDnsManager *self)
 
     if (priv->sd_resolve_plugin) {
         nm_assert(!NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin));
-        plugin = NM_DNS_SYSTEMD_RESOLVED(priv->sd_resolve_plugin);
+        plugin = priv->sd_resolve_plugin;
     } else if (NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin))
-        plugin = NM_DNS_SYSTEMD_RESOLVED(priv->plugin);
+        plugin = priv->plugin;
 
-    return plugin && nm_dns_systemd_resolved_is_running(plugin);
+    if (plugin && nm_dns_systemd_resolved_is_running(NM_DNS_SYSTEMD_RESOLVED(plugin)))
+        return plugin;
+
+    return NULL;
 }
 
 /*****************************************************************************/
@@ -1104,7 +1107,7 @@ update_resolv_conf(NMDnsManager *                self,
 }
 
 static void
-compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN])
+compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[static HASH_LEN])
 {
     nm_auto_free_checksum GChecksum *sum = NULL;
     NMDnsConfigIPData *              ip_data;
@@ -1119,7 +1122,7 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[
 
         /* FIXME(ip-config-checksum): this relies on the fact that an IP
          * configuration without DNS parameters gives a zero checksum. */
-        head = _mgr_get_ip_configs_lst_head(self);
+        head = _mgr_get_ip_config_lst_head(self);
         c_list_for_each_entry (ip_data, head, ip_config_lst)
             nm_ip_config_hash(ip_data->ip_config, sum, TRUE);
     }
@@ -1169,27 +1172,26 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
 }
 
 static const char *
-get_nameserver_list(const NMIPConfig *config, GString **str)
+get_nameserver_list(const NMIPConfig *config, NMStrBuf *tmp_strbuf)
 {
-    guint num, i;
     char  buf[NM_UTILS_INET_ADDRSTRLEN];
     int   addr_family;
+    guint num;
+    guint i;
 
-    if (*str)
-        g_string_truncate(*str, 0);
-    else
-        *str = g_string_sized_new(64);
+    nm_str_buf_reset(tmp_strbuf);
 
     addr_family = nm_ip_config_get_addr_family(config);
     num         = nm_ip_config_get_num_nameservers(config);
     for (i = 0; i < num; i++) {
         nm_utils_inet_ntop(addr_family, nm_ip_config_get_nameserver(config, i), buf);
         if (i > 0)
-            g_string_append_c(*str, ' ');
-        g_string_append(*str, buf);
+            nm_str_buf_append_c(tmp_strbuf, ' ');
+        nm_str_buf_append(tmp_strbuf, buf);
     }
 
-    return (*str)->str;
+    nm_str_buf_maybe_expand(tmp_strbuf, 1, FALSE);
+    return nm_str_buf_get_str(tmp_strbuf);
 }
 
 static char **
@@ -1224,13 +1226,14 @@ _collect_resolv_conf_data(NMDnsManager *     self,
     if (global_config)
         merge_global_dns_config(&rc, global_config);
     else {
-        nm_auto_free_gstring GString *tmp_gstring = NULL;
-        int                           prio, first_prio = 0;
-        const NMDnsConfigIPData *     ip_data;
-        const CList *                 head;
-        gboolean                      is_first = TRUE;
-
-        head = _mgr_get_ip_configs_lst_head(self);
+        nm_auto_str_buf NMStrBuf tmp_strbuf = NM_STR_BUF_INIT(0, FALSE);
+        int                      prio;
+        int                      first_prio = 0;
+        const NMDnsConfigIPData *ip_data;
+        const CList *            head;
+        gboolean                 is_first = TRUE;
+
+        head = _mgr_get_ip_config_lst_head(self);
         c_list_for_each_entry (ip_data, head, ip_config_lst) {
             gboolean skip = FALSE;
 
@@ -1244,16 +1247,13 @@ _collect_resolv_conf_data(NMDnsManager *     self,
             } else if (first_prio < 0 && first_prio != prio)
                 skip = TRUE;
 
-            if (nm_ip_config_get_num_nameservers(ip_data->ip_config)) {
-                _LOGT(
-                    "config: %8d %-7s v%c %-5d %s: %s",
-                    prio,
-                    _config_type_to_string(ip_data->ip_config_type),
-                    nm_utils_addr_family_to_char(nm_ip_config_get_addr_family(ip_data->ip_config)),
-                    ip_data->data->ifindex,
-                    skip ? "<SKIP>" : "",
-                    get_nameserver_list(ip_data->ip_config, &tmp_gstring));
-            }
+            _LOGT("config: %8d %-7s v%c %-5d %s: %s",
+                  prio,
+                  _config_type_to_string(ip_data->ip_config_type),
+                  nm_utils_addr_family_to_char(nm_ip_config_get_addr_family(ip_data->ip_config)),
+                  ip_data->data->ifindex,
+                  skip ? "<SKIP>" : "",
+                  get_nameserver_list(ip_data->ip_config, &tmp_strbuf));
 
             if (!skip)
                 merge_one_ip_config(&rc, ip_data->data->ifindex, ip_data->ip_config);
@@ -1394,7 +1394,7 @@ _mgr_configs_data_construct(NMDnsManager *self)
     CList *                        head;
     int                            prev_priority = G_MININT;
 
-    head = _mgr_get_ip_configs_lst_head(self);
+    head = _mgr_get_ip_config_lst_head(self);
 
 #if NM_MORE_ASSERTS
     /* we call _mgr_configs_data_clear() at the end of update. We
@@ -1611,7 +1611,7 @@ _mgr_configs_data_clear(NMDnsManager *self)
     NMDnsConfigIPData *ip_data;
     CList *            head;
 
-    head = _mgr_get_ip_configs_lst_head(self);
+    head = _mgr_get_ip_config_lst_head(self);
     c_list_for_each_entry (ip_data, head, ip_config_lst) {
         nm_clear_g_free(&ip_data->domains.search);
         nm_clear_pointer(&ip_data->domains.reverse, g_strfreev);
@@ -1680,7 +1680,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError
     if (priv->sd_resolve_plugin) {
         nm_dns_plugin_update(priv->sd_resolve_plugin,
                              global_config,
-                             _mgr_get_ip_configs_lst_head(self),
+                             _mgr_get_ip_config_lst_head(self),
                              priv->hostname,
                              NULL);
     }
@@ -1702,7 +1702,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError
         _LOGD("update-dns: updating plugin %s", plugin_name);
         if (!nm_dns_plugin_update(plugin,
                                   global_config,
-                                  _mgr_get_ip_configs_lst_head(self),
+                                  _mgr_get_ip_config_lst_head(self),
                                   priv->hostname,
                                   &plugin_error)) {
             _LOGW("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message);
@@ -1839,7 +1839,7 @@ _ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsConfigI
 {
     _ASSERT_dns_config_ip_data(ip_data);
 
-    NM_DNS_MANAGER_GET_PRIVATE(ip_data->data->self)->ip_configs_lst_need_sort = TRUE;
+    NM_DNS_MANAGER_GET_PRIVATE(ip_data->data->self)->ip_config_lst_need_sort = TRUE;
 }
 
 gboolean
@@ -1851,7 +1851,6 @@ nm_dns_manager_set_ip_config(NMDnsManager *    self,
     NMDnsConfigIPData *  ip_data;
     NMDnsConfigData *    data;
     int                  ifindex;
-    NMDnsConfigIPData ** p_best;
 
     g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
     g_return_val_if_fail(NM_IS_IP_CONFIG(ip_config), FALSE);
@@ -1870,10 +1869,6 @@ nm_dns_manager_set_ip_config(NMDnsManager *    self,
     if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) {
         if (!ip_data)
             return FALSE;
-        if (priv->best_ip_config_4 == ip_data)
-            priv->best_ip_config_4 = NULL;
-        if (priv->best_ip_config_6 == ip_data)
-            priv->best_ip_config_6 = NULL;
         /* deleting a config doesn't invalidate the configs' sort order. */
         _dns_config_ip_data_free(ip_data);
         if (c_list_is_empty(&data->data_lst_head))
@@ -1904,21 +1899,7 @@ nm_dns_manager_set_ip_config(NMDnsManager *    self,
     else
         ip_data->ip_config_type = ip_config_type;
 
-    priv->ip_configs_lst_need_sort = TRUE;
-
-    p_best = NM_IS_IP4_CONFIG(ip_config) ? &priv->best_ip_config_4 : &priv->best_ip_config_6;
-
-    if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) {
-        /* Only one best-device per IP version is allowed */
-        if (*p_best != ip_data) {
-            if (*p_best)
-                (*p_best)->ip_config_type = NM_DNS_IP_CONFIG_TYPE_DEFAULT;
-            *p_best = ip_data;
-        }
-    } else {
-        if (*p_best == ip_data)
-            *p_best = NULL;
-    }
+    priv->ip_config_lst_need_sort = TRUE;
 
 changed:
     if (!priv->updates_queue) {
@@ -2440,7 +2421,7 @@ _get_config_variant(NMDnsManager *self)
 
     g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
 
-    head = _mgr_get_ip_configs_lst_head(self);
+    head = _mgr_get_ip_config_lst_head(self);
     c_list_for_each_entry (ip_data, head, ip_config_lst) {
         const NMIPConfig *ip_config = ip_data->ip_config;
         GVariantBuilder   entry_builder;
@@ -2546,13 +2527,13 @@ nm_dns_manager_init(NMDnsManager *self)
     _LOGT("creating...");
 
     c_list_init(&priv->configs_lst_head);
-    c_list_init(&priv->ip_configs_lst_head);
+    c_list_init(&priv->ip_config_lst_head);
 
     priv->config = g_object_ref(nm_config_get());
 
     G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDnsConfigData, ifindex) == 0);
     priv->configs_dict = g_hash_table_new_full(nm_pint_hash,
-                                               nm_pint_equals,
+                                               nm_pint_equal,
                                                (GDestroyNotify) _dns_config_data_free,
                                                NULL);
 
@@ -2584,10 +2565,7 @@ dispose(GObject *object)
     g_clear_object(&priv->sd_resolve_plugin);
     _clear_plugin(self);
 
-    priv->best_ip_config_4 = NULL;
-    priv->best_ip_config_6 = NULL;
-
-    c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_configs_lst_head, ip_config_lst)
+    c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst)
         _dns_config_ip_data_free(ip_data);
 
     nm_clear_pointer(&priv->configs_dict, g_hash_table_destroy);
@@ -2618,13 +2596,13 @@ static const NMDBusInterfaceInfoExtended interface_info_dns_manager = {
     .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
         NM_DBUS_INTERFACE_DNS_MANAGER,
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "s", NM_DNS_MANAGER_MODE),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RcManager",
-                                                             "s",
-                                                             NM_DNS_MANAGER_RC_MANAGER),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Configuration",
-                                                             "aa{sv}",
-                                                             NM_DNS_MANAGER_CONFIGURATION), ), ),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "s", NM_DNS_MANAGER_MODE),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RcManager",
+                                                           "s",
+                                                           NM_DNS_MANAGER_RC_MANAGER),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Configuration",
+                                                           "aa{sv}",
+                                                           NM_DNS_MANAGER_CONFIGURATION), ), ),
 };
 
 static void
diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h
index 937ba62a..501085f7 100644
--- a/src/core/dns/nm-dns-manager.h
+++ b/src/core/dns/nm-dns-manager.h
@@ -11,6 +11,7 @@
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 #include "nm-setting-connection.h"
+#include "nm-dns-plugin.h"
 
 typedef enum {
     NM_DNS_IP_CONFIG_TYPE_REMOVED = -1,
@@ -148,7 +149,7 @@ typedef enum {
 
 void nm_dns_manager_stop(NMDnsManager *self);
 
-gboolean nm_dns_manager_has_systemd_resolved(NMDnsManager *self);
+NMDnsPlugin *nm_dns_manager_get_systemd_resolved(NMDnsManager *self);
 
 /*****************************************************************************/
 
diff --git a/src/core/dns/nm-dns-plugin.c b/src/core/dns/nm-dns-plugin.c
index 74d4eb2b..e5035f06 100644
--- a/src/core/dns/nm-dns-plugin.c
+++ b/src/core/dns/nm-dns-plugin.c
@@ -12,7 +12,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 
 /*****************************************************************************/
diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h
index 644d01e5..82ca7d6b 100644
--- a/src/core/dns/nm-dns-plugin.h
+++ b/src/core/dns/nm-dns-plugin.h
@@ -6,7 +6,8 @@
 #ifndef __NM_DNS_PLUGIN_H__
 #define __NM_DNS_PLUGIN_H__
 
-#include "nm-dns-manager.h"
+#include "c-list/src/c-list.h"
+
 #include "nm-config-data.h"
 
 #define NM_TYPE_DNS_PLUGIN (nm_dns_plugin_get_type())
diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c
index f6e116f3..4b8c4f94 100644
--- a/src/core/dns/nm-dns-systemd-resolved.c
+++ b/src/core/dns/nm-dns-systemd-resolved.c
@@ -16,10 +16,10 @@
 #include <sys/stat.h>
 #include <linux/if.h>
 
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -28,7 +28,7 @@
 #include "nm-setting-connection.h"
 #include "devices/nm-device.h"
 #include "NetworkManagerUtils.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 
 #define SYSTEMD_RESOLVED_DBUS_SERVICE  "org.freedesktop.resolve1"
 #define SYSTEMD_RESOLVED_MANAGER_IFACE "org.freedesktop.resolve1.Manager"
@@ -52,19 +52,40 @@ typedef struct {
     int                   ifindex;
 } RequestItem;
 
+struct _NMDnsSystemdResolvedResolveHandle {
+    CList                 handle_lst;
+    NMDnsSystemdResolved *self;
+    GSource *             timeout_source;
+    GCancellable *        handle_cancellable;
+    gpointer              callback_user_data;
+    guint                 timeout_msec;
+    bool                  is_failing_on_idle;
+    union {
+        struct {
+            NMDnsSystemdResolvedResolveAddressCallback callback;
+            guint64                                    flags;
+            int                                        ifindex;
+            int                                        addr_family;
+            NMIPAddr                                   addr;
+        } r_address;
+    };
+};
+
 /*****************************************************************************/
 
 typedef struct {
     GDBusConnection *dbus_connection;
     GHashTable *     dirty_interfaces;
     GCancellable *   cancellable;
+    GSource *        try_start_timeout_source;
     CList            request_queue_lst_head;
+    char *           dbus_owner;
+    CList            handle_lst_head;
     guint            name_owner_changed_id;
     bool             send_updates_warn_ratelimited : 1;
     bool             try_start_blocked : 1;
-    bool             dbus_has_owner : 1;
     bool             dbus_initied : 1;
-    bool             request_queue_to_send : 1;
+    bool             send_updates_waiting : 1;
     NMTernary        has_link_default_route : 3;
 } NMDnsSystemdResolvedPrivate;
 
@@ -84,9 +105,39 @@ G_DEFINE_TYPE(NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN)
 
 /*****************************************************************************/
 
-#define _NMLOG_DOMAIN LOGD_DNS
+#define _NMLOG_DOMAIN      LOGD_DNS
+#define _NMLOG_PREFIX_NAME "dns-sd-resolved"
+
 #define _NMLOG(level, ...) \
-    __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, "dns-sd-resolved", __VA_ARGS__)
+    __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, _NMLOG_PREFIX_NAME, __VA_ARGS__)
+
+#define _NMLOG2(level, handle, ...)                                                         \
+    G_STMT_START                                                                            \
+    {                                                                                       \
+        const NMLogLevel _level = (level);                                                  \
+                                                                                            \
+        if (nm_logging_enabled(_level, (_NMLOG_DOMAIN))) {                                  \
+            const NMDnsSystemdResolvedResolveHandle *const _handle = (handle);              \
+                                                                                            \
+            _nm_log(_level,                                                                 \
+                    (_NMLOG_DOMAIN),                                                        \
+                    0,                                                                      \
+                    NULL,                                                                   \
+                    NULL,                                                                   \
+                    "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]: request[" NM_HASH_OBFUSCATE_PTR_FMT \
+                    "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                               \
+                    _NMLOG_PREFIX_NAME,                                                     \
+                    NM_HASH_OBFUSCATE_PTR(self),                                            \
+                    NM_HASH_OBFUSCATE_PTR(_handle) _NM_UTILS_MACRO_REST(__VA_ARGS__));      \
+        }                                                                                   \
+    }                                                                                       \
+    G_STMT_END
+
+/*****************************************************************************/
+
+static void _resolve_complete_error(NMDnsSystemdResolvedResolveHandle *handle, GError *error);
+
+static void _resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResolveHandle *handle);
 
 /*****************************************************************************/
 
@@ -201,10 +252,7 @@ update_add_ip_config(NMDnsSystemdResolved *self,
         g_variant_builder_add(dns, "i", addr_family);
         g_variant_builder_add_value(
             dns,
-            g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
-                                      nm_ip_config_get_nameserver(data->ip_config, i),
-                                      addr_size,
-                                      1));
+            nm_g_variant_new_ay(nm_ip_config_get_nameserver(data->ip_config, i), addr_size));
         g_variant_builder_close(dns);
         has_config = TRUE;
     }
@@ -326,57 +374,105 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic)
     return has_config;
 }
 
-static void
-send_updates(NMDnsSystemdResolved *self)
+static gboolean
+_ensure_resolved_running_timeout(gpointer user_data)
 {
-    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
-    RequestItem *                request_item;
+    NMDnsSystemdResolved *             self = user_data;
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolvedResolveHandle *handle;
 
-    if (!priv->request_queue_to_send) {
-        /* nothing to do. */
-        return;
-    }
+    nm_clear_g_source_inst(&priv->try_start_timeout_source);
 
-    if (!priv->dbus_initied) {
-        _LOGT("send-updates: D-Bus connection not ready");
-        return;
+    _LOGT("timeout waiting to D-Bus activate systemd-resolved. Systemd-resolved won't be "
+          "used until it appears on the bus");
+
+again:
+    c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) {
+        gs_free_error GError *error = NULL;
+
+        if (handle->is_failing_on_idle)
+            continue;
+
+        nm_utils_error_set_literal(&error,
+                                   NM_UTILS_ERROR_NOT_READY,
+                                   "timeout waiting for systemd-resolved to start");
+        _resolve_complete_error(handle, error);
+        goto again;
     }
 
-    if (!priv->dbus_has_owner) {
+    return G_SOURCE_CONTINUE;
+}
+
+static NMTernary
+ensure_resolved_running(NMDnsSystemdResolved *self)
+{
+    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+
+    if (!priv->dbus_initied)
+        return NM_TERNARY_DEFAULT;
+
+    if (!priv->dbus_owner) {
         if (priv->try_start_blocked) {
             /* we have no name owner and we already tried poking the service to
              * autostart. */
-            _LOGT("send-updates: no name owner");
-            return;
+            return NM_TERNARY_FALSE;
         }
 
-        _LOGT("send-updates: no name owner. Try start service...");
+        _LOGT("try D-Bus activating systemd-resolved...");
         priv->try_start_blocked = TRUE;
 
+        priv->try_start_timeout_source =
+            nm_g_source_attach(nm_g_timeout_source_new(4000,
+                                                       G_PRIORITY_DEFAULT,
+                                                       _ensure_resolved_running_timeout,
+                                                       self,
+                                                       NULL),
+                               NULL);
+
         nm_dbus_connection_call_start_service_by_name(priv->dbus_connection,
                                                       SYSTEMD_RESOLVED_DBUS_SERVICE,
                                                       -1,
                                                       NULL,
                                                       NULL,
                                                       NULL);
+        return NM_TERNARY_DEFAULT;
+    }
+
+    return NM_TERNARY_TRUE;
+}
+
+static void
+send_updates(NMDnsSystemdResolved *self)
+{
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    RequestItem *                      request_item;
+    NMDnsSystemdResolvedResolveHandle *handle;
+
+    if (!priv->send_updates_waiting) {
+        /* nothing to do. */
         return;
     }
 
+    if (ensure_resolved_running(self) != NM_TERNARY_TRUE)
+        return;
+
     nm_clear_g_cancellable(&priv->cancellable);
 
     if (c_list_is_empty(&priv->request_queue_lst_head)) {
         _LOGT("send-updates: no requests to send");
-        priv->request_queue_to_send = FALSE;
-        return;
+        priv->send_updates_waiting = FALSE;
+        goto start_resolve;
     }
 
-    _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head));
-
     priv->cancellable = g_cancellable_new();
 
-    priv->request_queue_to_send = FALSE;
+    priv->send_updates_waiting = FALSE;
+
+    _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head));
 
     c_list_for_each_entry (request_item, &priv->request_queue_lst_head, request_queue_lst) {
+        gs_free char *ss = NULL;
+
         if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE
             && priv->has_link_default_route == NM_TERNARY_FALSE) {
             /* The "SetLinkDefaultRoute" API is only supported since v240.
@@ -386,16 +482,12 @@ send_updates(NMDnsSystemdResolved *self)
             continue;
         }
 
-        /* Above we explicitly call "StartServiceByName" trying to avoid D-Bus activating systmd-resolved
-         * multiple times. There is still a race, were we might hit this line although actually
-         * the service just quit this very moment. In that case, we would try to D-Bus activate the
-         * service multiple times during each call (something we wanted to avoid).
-         *
-         * But this is hard to avoid, because we'd have to check the error failure to detect the reason
-         * and retry. The race is not critical, because at worst it results in logging a warning
-         * about failure to start systemd.resolved. */
+        _LOGT("send-updates: %s ( %s )",
+              request_item->operation,
+              (ss = g_variant_print(request_item->argument, FALSE)));
+
         g_dbus_connection_call(priv->dbus_connection,
-                               SYSTEMD_RESOLVED_DBUS_SERVICE,
+                               priv->dbus_owner,
                                SYSTEMD_RESOLVED_DBUS_PATH,
                                SYSTEMD_RESOLVED_MANAGER_IFACE,
                                request_item->operation,
@@ -407,6 +499,15 @@ send_updates(NMDnsSystemdResolved *self)
                                call_done,
                                request_item);
     }
+
+start_resolve:
+    c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) {
+        if (handle->handle_cancellable)
+            continue;
+        if (handle->is_failing_on_idle)
+            continue;
+        _resolve_start(self, handle);
+    }
 }
 
 static gboolean
@@ -479,7 +580,7 @@ update(NMDnsPlugin *            plugin,
         }
     }
 
-    priv->request_queue_to_send = TRUE;
+    priv->send_updates_waiting = TRUE;
     send_updates(self);
     return TRUE;
 }
@@ -498,10 +599,13 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner)
     else
         _LOGT("D-Bus name for systemd-resolved has owner %s", owner);
 
-    priv->dbus_has_owner = !!owner;
+    nm_clear_g_source_inst(&priv->try_start_timeout_source);
+
+    nm_utils_strdup_reset(&priv->dbus_owner, owner);
+
     if (owner) {
-        priv->try_start_blocked     = FALSE;
-        priv->request_queue_to_send = TRUE;
+        priv->try_start_blocked    = FALSE;
+        priv->send_updates_waiting = TRUE;
     } else
         priv->has_link_default_route = NM_TERNARY_DEFAULT;
 
@@ -531,6 +635,7 @@ name_owner_changed_cb(GDBusConnection *connection,
          * returns. */
         priv->dbus_initied = TRUE;
         nm_clear_g_cancellable(&priv->cancellable);
+        _LOGT("D-Bus connection is ready");
     }
 
     name_owner_changed(user_data, new_owner);
@@ -551,6 +656,7 @@ get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data)
     g_clear_object(&priv->cancellable);
 
     priv->dbus_initied = TRUE;
+    _LOGT("D-Bus connection is ready");
 
     name_owner_changed(self, name_owner);
 }
@@ -566,7 +672,253 @@ nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self)
 
     priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
 
-    return priv->dbus_initied && (priv->dbus_has_owner || !priv->try_start_blocked);
+    return priv->dbus_initied && (priv->dbus_owner || !priv->try_start_blocked);
+}
+
+/*****************************************************************************/
+
+static void
+_resolve_complete(NMDnsSystemdResolvedResolveHandle *      handle,
+                  const NMDnsSystemdResolvedAddressResult *names,
+                  guint                                    names_len,
+                  guint64                                  flags,
+                  GError *                                 error)
+{
+    NMDnsSystemdResolved *       self;
+    NMDnsSystemdResolvedPrivate *priv;
+
+    g_return_if_fail(handle && NM_IS_DNS_SYSTEMD_RESOLVED(handle->self));
+
+    self = handle->self;
+    priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+
+    nm_assert(c_list_contains(&priv->handle_lst_head, &handle->handle_lst));
+
+    c_list_unlink(&handle->handle_lst);
+
+    handle->self = NULL;
+
+    nm_clear_g_source_inst(&handle->timeout_source);
+    nm_clear_g_cancellable(&handle->handle_cancellable);
+
+    handle->r_address
+        .callback(self, handle, names, names_len, flags, error, handle->callback_user_data);
+
+    nm_g_slice_free(handle);
+}
+
+static void
+_resolve_complete_error(NMDnsSystemdResolvedResolveHandle *handle, GError *error)
+{
+    NMDnsSystemdResolved *self = handle->self;
+
+    nm_assert(error);
+    _LOG2T(handle, "request failed: %s", error->message);
+    _resolve_complete(handle, NULL, 0, 0, error);
+}
+
+static void
+_resolve_handle_call_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    gs_unref_variant GVariant *v             = NULL;
+    gs_free_error GError *             error = NULL;
+    NMDnsSystemdResolvedResolveHandle *handle;
+    NMDnsSystemdResolved *             self;
+    GVariantIter *                     v_names_iter;
+    guint64                            v_flags;
+    int                                v_ifindex;
+    char *                             v_name;
+    gs_unref_array GArray *v_names = NULL;
+    gs_free char *         ss      = NULL;
+
+    v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    handle = user_data;
+    self   = handle->self;
+
+    if (error) {
+        gs_free char *remote_error = NULL;
+
+        remote_error = g_dbus_error_get_remote_error(error);
+        if (nm_streq0(remote_error, "org.freedesktop.DBus.Error.ServiceUnknown")) {
+            _LOG2T(handle, "request failed due to service stop. Retry");
+            g_clear_object(&handle->handle_cancellable);
+            _resolve_start(self, handle);
+            return;
+        }
+
+        _resolve_complete_error(handle, error);
+        return;
+    }
+
+    _LOG2T(handle, "request completed: %s", (ss = g_variant_print(v, FALSE)));
+
+    v_names = g_array_new(FALSE, FALSE, sizeof(NMDnsSystemdResolvedAddressResult));
+
+    G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDnsSystemdResolvedAddressResult, name) == 0);
+    g_array_set_clear_func(v_names, nm_indirect_g_free);
+
+    g_variant_get(v, "(a(is)t)", &v_names_iter, &v_flags);
+
+    while (g_variant_iter_next(v_names_iter, "(is)", &v_ifindex, &v_name)) {
+        NMDnsSystemdResolvedAddressResult *n;
+
+        n  = nm_g_array_append_new(v_names, NMDnsSystemdResolvedAddressResult);
+        *n = (NMDnsSystemdResolvedAddressResult){
+            .name    = g_steal_pointer(&v_name),
+            .ifindex = v_ifindex,
+        };
+    }
+    g_variant_iter_free(v_names_iter);
+
+    _resolve_complete(handle,
+                      &g_array_index(v_names, NMDnsSystemdResolvedAddressResult, 0),
+                      v_names->len,
+                      v_flags,
+                      NULL);
+}
+
+static gboolean
+_resolve_failing_on_idle(gpointer user_data)
+{
+    NMDnsSystemdResolvedResolveHandle *handle = user_data;
+    gs_free_error GError *error               = NULL;
+
+    nm_utils_error_set_literal(&error,
+                               NM_UTILS_ERROR_NOT_READY,
+                               "systemd-resolved is not available");
+    _resolve_complete_error(handle, error);
+    return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+_resolve_handle_timeout(gpointer user_data)
+{
+    NMDnsSystemdResolvedResolveHandle *handle = user_data;
+    gs_free_error GError *error               = NULL;
+
+    nm_utils_error_set_literal(&error, NM_UTILS_ERROR_UNKNOWN, "timeout for request");
+    _resolve_complete_error(handle, error);
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+_resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResolveHandle *handle)
+{
+    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMTernary                    is_running;
+
+    nm_assert(!handle->handle_cancellable);
+    nm_assert(!handle->is_failing_on_idle);
+
+    is_running = ensure_resolved_running(self);
+
+    if (is_running == NM_TERNARY_FALSE) {
+        /* Systemd-resolved is not is_running and shall not be used. We fail
+         * on an idle handler. */
+        _LOG2T(handle, "systemd-resolved not running. Failing on idle...");
+        nm_assert(!handle->timeout_source);
+        handle->is_failing_on_idle = TRUE;
+        handle->timeout_source     = nm_g_source_attach(
+            nm_g_idle_source_new(G_PRIORITY_DEFAULT, _resolve_failing_on_idle, handle, NULL),
+            NULL);
+        return;
+    }
+
+    if (!handle->timeout_source) {
+        handle->timeout_source = nm_g_source_attach(nm_g_timeout_source_new(handle->timeout_msec,
+                                                                            G_PRIORITY_DEFAULT,
+                                                                            _resolve_handle_timeout,
+                                                                            handle,
+                                                                            NULL),
+                                                    NULL);
+    }
+
+    if (is_running == NM_TERNARY_DEFAULT) {
+        /* we are D-Bus activating systemd-resolved. Wait for it... */
+        _LOG2T(handle, "waiting for systemd-resolved to start...");
+        return;
+    }
+
+    nm_assert(!priv->send_updates_waiting);
+
+    handle->handle_cancellable = g_cancellable_new();
+
+    _LOG2T(handle, "start D-Bus request...");
+    g_dbus_connection_call(priv->dbus_connection,
+                           priv->dbus_owner,
+                           SYSTEMD_RESOLVED_DBUS_PATH,
+                           SYSTEMD_RESOLVED_MANAGER_IFACE,
+                           "ResolveAddress",
+                           g_variant_new("(ii@ayt)",
+                                         handle->r_address.ifindex,
+                                         handle->r_address.addr_family,
+                                         nm_g_variant_new_ay_inaddr(handle->r_address.addr_family,
+                                                                    &handle->r_address.addr),
+                                         handle->r_address.flags),
+                           G_VARIANT_TYPE("(a(is)t)"),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           handle->timeout_msec + 1000u,
+                           handle->handle_cancellable,
+                           _resolve_handle_call_cb,
+                           handle);
+}
+
+NMDnsSystemdResolvedResolveHandle *
+nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved *                     self,
+                                        int                                        ifindex,
+                                        int                                        addr_family,
+                                        const NMIPAddr *                           addr,
+                                        guint64                                    flags,
+                                        guint                                      timeout_msec,
+                                        NMDnsSystemdResolvedResolveAddressCallback callback,
+                                        gpointer                                   user_data)
+{
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolvedResolveHandle *handle;
+    char                               addr_str[NM_UTILS_INET_ADDRSTRLEN];
+
+    g_return_val_if_fail(NM_IS_DNS_SYSTEMD_RESOLVED(self), NULL);
+    nm_assert_addr_family(addr_family);
+    nm_assert(addr);
+    nm_assert(callback);
+
+    handle  = g_slice_new(NMDnsSystemdResolvedResolveHandle);
+    *handle = (NMDnsSystemdResolvedResolveHandle){
+        .self               = self,
+        .timeout_msec       = timeout_msec,
+        .callback_user_data = user_data,
+        .r_address =
+            {
+                .ifindex     = ifindex,
+                .addr_family = addr_family,
+                .addr        = *addr,
+                .flags       = flags,
+                .callback    = callback,
+            },
+    };
+    c_list_link_tail(&priv->handle_lst_head, &handle->handle_lst);
+
+    _LOG2T(handle,
+           "resolve-address(ifindex=%d, %s, flags=%" G_GINT64_MODIFIER "x): new request",
+           handle->r_address.ifindex,
+           nm_utils_inet_ntop(handle->r_address.addr_family, &handle->r_address.addr, addr_str),
+           handle->r_address.flags);
+
+    _resolve_start(self, handle);
+
+    return handle;
+}
+
+void
+nm_dns_systemd_resolved_resolve_cancel(NMDnsSystemdResolvedResolveHandle *handle)
+{
+    gs_free_error GError *error = NULL;
+
+    nm_utils_error_set_cancelled(&error, FALSE, "NMDnsSystemdResolved");
+    _resolve_complete_error(handle, error);
 }
 
 /*****************************************************************************/
@@ -579,6 +931,7 @@ nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self)
     priv->has_link_default_route = NM_TERNARY_DEFAULT;
 
     c_list_init(&priv->request_queue_lst_head);
+    c_list_init(&priv->handle_lst_head);
     priv->dirty_interfaces = g_hash_table_new(nm_direct_hash, NULL);
 
     priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET);
@@ -611,8 +964,18 @@ nm_dns_systemd_resolved_new(void)
 static void
 dispose(GObject *object)
 {
-    NMDnsSystemdResolved *       self = NM_DNS_SYSTEMD_RESOLVED(object);
-    NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolved *             self = NM_DNS_SYSTEMD_RESOLVED(object);
+    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolvedResolveHandle *handle;
+
+    while ((handle = c_list_first_entry(&priv->handle_lst_head,
+                                        NMDnsSystemdResolvedResolveHandle,
+                                        handle_lst))) {
+        gs_free_error GError *error = NULL;
+
+        nm_utils_error_set_cancelled(&error, TRUE, "NMDnsSystemdResolved");
+        _resolve_complete_error(handle, error);
+    }
 
     free_pending_updates(self);
 
@@ -620,10 +983,14 @@ dispose(GObject *object)
 
     nm_clear_g_cancellable(&priv->cancellable);
 
+    nm_clear_g_source_inst(&priv->try_start_timeout_source);
+
     g_clear_object(&priv->dbus_connection);
     nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_unref);
 
     G_OBJECT_CLASS(nm_dns_systemd_resolved_parent_class)->dispose(object);
+
+    nm_clear_g_free(&priv->dbus_owner);
 }
 
 static void
diff --git a/src/core/dns/nm-dns-systemd-resolved.h b/src/core/dns/nm-dns-systemd-resolved.h
index 4ab04ab4..9f973318 100644
--- a/src/core/dns/nm-dns-systemd-resolved.h
+++ b/src/core/dns/nm-dns-systemd-resolved.h
@@ -8,6 +8,7 @@
 #define __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__
 
 #include "nm-dns-plugin.h"
+#include "nm-dns-manager.h"
 
 #define NM_TYPE_DNS_SYSTEMD_RESOLVED (nm_dns_systemd_resolved_get_type())
 #define NM_DNS_SYSTEMD_RESOLVED(obj) \
@@ -30,4 +31,34 @@ NMDnsPlugin *nm_dns_systemd_resolved_new(void);
 
 gboolean nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self);
 
+/*****************************************************************************/
+
+typedef struct _NMDnsSystemdResolvedResolveHandle NMDnsSystemdResolvedResolveHandle;
+
+typedef struct {
+    const char *name;
+    int         ifindex;
+} NMDnsSystemdResolvedAddressResult;
+
+typedef void (*NMDnsSystemdResolvedResolveAddressCallback)(
+    NMDnsSystemdResolved *                   self,
+    NMDnsSystemdResolvedResolveHandle *      handle,
+    const NMDnsSystemdResolvedAddressResult *names,
+    guint                                    names_len,
+    guint64                                  flags,
+    GError *                                 error,
+    gpointer                                 user_data);
+
+NMDnsSystemdResolvedResolveHandle *
+nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved *                     self,
+                                        int                                        ifindex,
+                                        int                                        addr_family,
+                                        const NMIPAddr *                           addr,
+                                        guint64                                    flags,
+                                        guint                                      timeout_msec,
+                                        NMDnsSystemdResolvedResolveAddressCallback callback,
+                                        gpointer                                   user_data);
+
+void nm_dns_systemd_resolved_resolve_cancel(NMDnsSystemdResolvedResolveHandle *handle);
+
 #endif /* __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__ */