From 92f742f66bfbd0f6b40662d31dbd3b59a8c73129 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Apr 2021 16:44:34 +0200 Subject: [PATCH 1/6] dns/resolved: add ensure_resolved_running() helper method Factor out the code. We will use it next. --- src/core/dns/nm-dns-systemd-resolved.c | 31 +++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index 2a8a228900..765171c3cd 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -323,20 +323,14 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) return has_config; } -static void -send_updates(NMDnsSystemdResolved *self) +static NMTernary +ensure_resolved_running(NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - RequestItem * request_item; - - if (!priv->request_queue_to_send) { - /* nothing to do. */ - return; - } if (!priv->dbus_initied) { _LOGT("send-updates: D-Bus connection not ready"); - return; + return NM_TERNARY_DEFAULT; } if (!priv->dbus_has_owner) { @@ -344,7 +338,7 @@ send_updates(NMDnsSystemdResolved *self) /* 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..."); @@ -356,9 +350,26 @@ send_updates(NMDnsSystemdResolved *self) 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; + + if (!priv->request_queue_to_send) { + /* 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)) { From 8ee5ec00f91a5f45c3ab1469d5b145ad16961e3f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Apr 2021 16:57:16 +0200 Subject: [PATCH 2/6] dns/trivial: rename NMDnsSystemdResolvedPrivate.request_queue_to_send to "send_updates_waiting" The previous name seems confusing to me. --- src/core/dns/nm-dns-systemd-resolved.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index 765171c3cd..01fec10699 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -64,7 +64,7 @@ typedef struct { 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; @@ -362,7 +362,7 @@ send_updates(NMDnsSystemdResolved *self) NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); RequestItem * request_item; - if (!priv->request_queue_to_send) { + if (!priv->send_updates_waiting) { /* nothing to do. */ return; } @@ -374,7 +374,7 @@ send_updates(NMDnsSystemdResolved *self) if (c_list_is_empty(&priv->request_queue_lst_head)) { _LOGT("send-updates: no requests to send"); - priv->request_queue_to_send = FALSE; + priv->send_updates_waiting = FALSE; return; } @@ -382,7 +382,7 @@ send_updates(NMDnsSystemdResolved *self) priv->cancellable = g_cancellable_new(); - priv->request_queue_to_send = FALSE; + priv->send_updates_waiting = FALSE; c_list_for_each_entry (request_item, &priv->request_queue_lst_head, request_queue_lst) { if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE @@ -487,7 +487,7 @@ update(NMDnsPlugin * plugin, } } - priv->request_queue_to_send = TRUE; + priv->send_updates_waiting = TRUE; send_updates(self); return TRUE; } @@ -508,8 +508,8 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) priv->dbus_has_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; From 4285eed8ebf35bd4e751494bd49a46b88e881d6f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Apr 2021 17:56:50 +0200 Subject: [PATCH 3/6] dns/resolved: add watchdog D-Bus activating systemd-resolved Currently, it doesn't actually do anything (beside logging). Will be used next. --- src/core/dns/nm-dns-systemd-resolved.c | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index 01fec10699..fdd25aa93d 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -58,6 +58,7 @@ typedef struct { GDBusConnection *dbus_connection; GHashTable * dirty_interfaces; GCancellable * cancellable; + GSource * try_start_timeout_source; CList request_queue_lst_head; guint name_owner_changed_id; bool send_updates_warn_ratelimited : 1; @@ -323,6 +324,20 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) return has_config; } +static gboolean +_ensure_resolved_running_timeout(gpointer user_data) +{ + NMDnsSystemdResolved * self = user_data; + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->try_start_timeout_source); + + _LOGT("send-updates: timeout waiting for systemd-resolved to start. Systemd-resolved won't be " + "used until it appears on the bus"); + + return G_SOURCE_CONTINUE; +} + static NMTernary ensure_resolved_running(NMDnsSystemdResolved *self) { @@ -344,6 +359,14 @@ ensure_resolved_running(NMDnsSystemdResolved *self) _LOGT("send-updates: no name owner. Try start service..."); 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, @@ -506,6 +529,8 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) else _LOGT("D-Bus name for systemd-resolved has owner %s", owner); + nm_clear_g_source_inst(&priv->try_start_timeout_source); + priv->dbus_has_owner = !!owner; if (owner) { priv->try_start_blocked = FALSE; @@ -628,6 +653,8 @@ 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); From e7d78e90a3d6b5f5925274808fdf4ab3f6c57808 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Apr 2021 20:11:55 +0200 Subject: [PATCH 4/6] dns/resolved: track D-Bus owner of resolved D-Bus service --- src/core/dns/nm-dns-systemd-resolved.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index fdd25aa93d..2704c761b1 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -60,10 +60,10 @@ typedef struct { GCancellable * cancellable; GSource * try_start_timeout_source; CList request_queue_lst_head; + char * dbus_owner; 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 send_updates_waiting : 1; NMTernary has_link_default_route : 3; @@ -348,7 +348,7 @@ ensure_resolved_running(NMDnsSystemdResolved *self) return NM_TERNARY_DEFAULT; } - if (!priv->dbus_has_owner) { + if (!priv->dbus_owner) { if (priv->try_start_blocked) { /* we have no name owner and we already tried poking the service to * autostart. */ @@ -531,7 +531,8 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) nm_clear_g_source_inst(&priv->try_start_timeout_source); - priv->dbus_has_owner = !!owner; + nm_utils_strdup_reset(&priv->dbus_owner, owner); + if (owner) { priv->try_start_blocked = FALSE; priv->send_updates_waiting = TRUE; @@ -599,7 +600,7 @@ 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); } /*****************************************************************************/ @@ -659,6 +660,8 @@ dispose(GObject *object) 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 From 5cc6932d5a011641030ee0464bd7b5381f7d5b1d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Apr 2021 20:14:19 +0200 Subject: [PATCH 5/6] dns/resolved: use unique D-Bus name for systemd-resolved request As we already track the current name owner, it seems wrong to make requests to the well-known name. It theoretically opens a race where we would talk to the wrong systemd-resolved instance. So far, we didn't care and it didn't make much of a difference. Still, let's talk to the process that we expect. --- src/core/dns/nm-dns-systemd-resolved.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index 2704c761b1..8f71a83784 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -417,16 +417,8 @@ 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. */ 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, From e7b76f6757a871f65ab4f222e05a6e89eb3719e6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Apr 2021 17:00:22 +0200 Subject: [PATCH 6/6] dns/resolved: add API for ResolveAddress call ResolveAddress is a simple D-Bus API. One might think that it does not require much logic, and we would just make the D-Bus call directly. However, we want to ensure that we always sent our latest DNS configuration to systemd-resolved first, before making any D-Bus request to resolve a name. Also, as we D-Bus activate the service and watch the name owner, we want to handle that. If a user resolves a name while we are still D-Bus activating, then we need to queue the request. This adds some complexity in the implementation. But the benefit is that the API becomes simple from the user's point of view. The queuing and avoiding race conditions is handled internally. The implementation does not use standard glib patterns with GAsyncResult GTask and GCancellable. I find implementing them correctly is more effort and leads to a less nice API. That might be fine for public API, which should follow glib style. But this is entirely internal, and returning a NMDnsSystemdResolvedResolveHandle that allows cancellation seems more convenient to me. --- src/core/dns/nm-dns-systemd-resolved.c | 371 +++++++++++++++++++++++-- src/core/dns/nm-dns-systemd-resolved.h | 30 ++ 2 files changed, 384 insertions(+), 17 deletions(-) diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index 8f71a83784..972ef643a3 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -52,6 +52,25 @@ 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 { @@ -61,6 +80,7 @@ typedef struct { 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; @@ -85,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); /*****************************************************************************/ @@ -327,14 +377,29 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) static gboolean _ensure_resolved_running_timeout(gpointer user_data) { - NMDnsSystemdResolved * self = user_data; - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + NMDnsSystemdResolved * self = user_data; + NMDnsSystemdResolvedPrivate * priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + NMDnsSystemdResolvedResolveHandle *handle; nm_clear_g_source_inst(&priv->try_start_timeout_source); - _LOGT("send-updates: timeout waiting for systemd-resolved to start. Systemd-resolved won't be " + _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; + } + return G_SOURCE_CONTINUE; } @@ -343,20 +408,17 @@ ensure_resolved_running(NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - if (!priv->dbus_initied) { - _LOGT("send-updates: D-Bus connection not ready"); + 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 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 = @@ -382,8 +444,9 @@ ensure_resolved_running(NMDnsSystemdResolved *self) static void send_updates(NMDnsSystemdResolved *self) { - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - RequestItem * request_item; + NMDnsSystemdResolvedPrivate * priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + RequestItem * request_item; + NMDnsSystemdResolvedResolveHandle *handle; if (!priv->send_updates_waiting) { /* nothing to do. */ @@ -398,16 +461,18 @@ send_updates(NMDnsSystemdResolved *self) if (c_list_is_empty(&priv->request_queue_lst_head)) { _LOGT("send-updates: no requests to send"); priv->send_updates_waiting = FALSE; - return; + goto start_resolve; } - _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head)); - priv->cancellable = g_cancellable_new(); 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. @@ -417,6 +482,10 @@ send_updates(NMDnsSystemdResolved *self) continue; } + _LOGT("send-updates: %s ( %s )", + request_item->operation, + (ss = g_variant_print(request_item->argument, FALSE))); + g_dbus_connection_call(priv->dbus_connection, priv->dbus_owner, SYSTEMD_RESOLVED_DBUS_PATH, @@ -430,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 @@ -557,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); @@ -577,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); } @@ -597,6 +677,252 @@ nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self) /*****************************************************************************/ +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_strdup(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); +} + +/*****************************************************************************/ + static void nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self) { @@ -605,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); @@ -637,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); diff --git a/src/core/dns/nm-dns-systemd-resolved.h b/src/core/dns/nm-dns-systemd-resolved.h index 4ab04ab49c..b7e30d81f7 100644 --- a/src/core/dns/nm-dns-systemd-resolved.h +++ b/src/core/dns/nm-dns-systemd-resolved.h @@ -30,4 +30,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__ */