summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2017-06-22 16:46:13 +0200
committerLubomir Rintel <lkundrak@v3.sk>2017-07-07 14:20:56 +0200
commit692da611a7a5c132f6af6f0d6baa3250e7c069b1 (patch)
treeeabfa1c7a7b3331a7560f702c204673c3aeb06c0
parentb535ecc23ace1b1bfb85536f95bc84fbab8c0276 (diff)
downloadNetworkManager-lr/connectivity-resolved.tar.gz
connectivity: use systemd-resolved for resolving the check endpointlr/connectivity-resolved
This allows us to use the correct DNS server for the particular interface independent of what the system resolver is configured to use.
-rw-r--r--src/devices/nm-device.c1
-rw-r--r--src/nm-connectivity.c211
-rw-r--r--src/nm-connectivity.h1
3 files changed, 203 insertions, 10 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 4bf34cd5b2..4548bd26e6 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -1936,6 +1936,7 @@ nm_device_check_connectivity (NMDevice *self,
/* Kick off a real connectivity check. */
nm_connectivity_check_async (nm_connectivity_get (),
+ nm_device_get_ip_ifindex (self),
nm_device_get_ip_iface (self),
concheck_cb,
data);
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 6f16b28e56..c317489362 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <curl/curl.h>
+#include "nm-bus-manager.h"
#include "nm-config.h"
#include "NetworkManagerUtils.h"
@@ -40,6 +41,8 @@ typedef struct {
guint periodic_check_id;
CURLM *curl_mhandle;
guint curl_timer;
+ GDBusProxy *resolve;
+ GCancellable *init_cancellable;
} NMConnectivityPrivate;
struct _NMConnectivity {
@@ -105,8 +108,12 @@ typedef struct {
size_t msg_size;
char *msg;
struct curl_slist *request_headers;
+ struct curl_slist *hosts;
guint timeout_id;
char *ifspec;
+ char *host;
+ char *port;
+ GCancellable *cancellable;
} ConCheckCbData;
static void
@@ -123,8 +130,12 @@ finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state)
g_simple_async_result_complete (cb_data->simple);
g_object_unref (cb_data->simple);
curl_slist_free_all (cb_data->request_headers);
+ curl_slist_free_all (cb_data->hosts);
g_free (cb_data->response);
g_source_remove (cb_data->timeout_id);
+ g_free (cb_data->host);
+ g_free (cb_data->port);
+ nm_clear_g_cancellable (&cb_data->cancellable);
g_slice_free (ConCheckCbData, cb_data);
}
@@ -326,8 +337,126 @@ timeout_cb (gpointer user_data)
return G_SOURCE_REMOVE;
}
+static void
+resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ ConCheckCbData *cb_data = user_data;
+ NMConnectivity *self;
+ NMConnectivityPrivate *priv;
+ GVariant *result;
+ GVariant *addresses;
+ gsize no_addresses;
+ int ifindex;
+ int family;
+ GVariant *address = NULL;
+ const guchar *address_buf;
+ gsize len = 0;
+ char str[INET6_ADDRSTRLEN + 1] = { 0, };
+ char *host;
+ int i;
+ gs_free_error GError *error = NULL;
+
+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
+ if (!result) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ return;
+ } else {
+ /* Never mind. Just let do curl do its own resolving. */
+ _LOGD ("can't resolve a name via systemd-resolved: %s", error->message);
+ }
+ }
+
+ self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (cb_data->simple)));
+ priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+
+ g_clear_object (&cb_data->cancellable);
+
+ if (result) {
+ addresses = g_variant_get_child_value (result, 0);
+ no_addresses = g_variant_n_children (addresses);
+ g_variant_unref (result);
+ } else {
+ addresses = NULL;
+ no_addresses = 0;
+ }
+
+ for (i = 0; i < no_addresses; i++) {
+ g_variant_get_child (addresses, i, "(ii@ay)", &ifindex, &family, &address);
+ address_buf = g_variant_get_fixed_array (address, &len, 1);
+
+ if ( (family == AF_INET && len == sizeof (struct in_addr))
+ || (family == AF_INET6 && len == sizeof (struct in6_addr))) {
+ inet_ntop (family, address_buf, str, sizeof (str));
+ host = g_strdup_printf ("%s:%s:%s", cb_data->host,
+ cb_data->port ? cb_data->port : "80", str);
+ cb_data->hosts = curl_slist_append (cb_data->hosts, host);
+ _LOGT ("adding '%s' to curl resolve list", host);
+ g_free (host);
+ }
+
+ g_variant_unref (address);
+ }
+
+ if (addresses)
+ g_variant_unref (addresses);
+
+ curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_RESOLVE, cb_data->hosts);
+ curl_multi_add_handle (priv->curl_mhandle, cb_data->curl_ehandle);
+}
+
+static gboolean
+host_and_port_from_uri (const char *uri, char **host, char **port)
+{
+ const char *p = uri;
+ const char *host_begin = NULL;
+ size_t host_len = 0;
+ const char *port_begin = NULL;
+ size_t port_len = 0;
+
+ /* scheme */
+ while (*p != ':' && *p != '/') {
+ if (!*p++)
+ return FALSE;
+ }
+
+ /* :// */
+ if (*p++ != ':')
+ return FALSE;
+ if (*p++ != '/')
+ return FALSE;
+ if (*p++ != '/')
+ return FALSE;
+ /* host */
+ if (*p == '[')
+ return FALSE;
+ host_begin = p;
+ while (*p && *p != ':' && *p != '/') {
+ host_len++;
+ p++;
+ }
+ if (host_len == 0)
+ return FALSE;
+ *host = strndup (host_begin, host_len);
+
+ /* port */
+ if (*p++ == ':') {
+ port_begin = p;
+ while (*p && *p != '/') {
+ port_len++;
+ p++;
+ }
+ if (port_len)
+ *port = strndup (port_begin, port_len);
+ }
+
+ return TRUE;
+}
+
+#define SD_RESOLVED_DNS 1
+
void
nm_connectivity_check_async (NMConnectivity *self,
+ int ifindex,
const char *iface,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -365,11 +494,29 @@ nm_connectivity_check_async (NMConnectivity *self,
curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data);
curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->request_headers);
curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec);
- curl_multi_add_handle (priv->curl_mhandle, ehandle);
- cb_data->timeout_id = g_timeout_add_seconds (30, timeout_cb, cb_data);
+ if (priv->resolve && host_and_port_from_uri (priv->uri, &cb_data->host, &cb_data->port)) {
+ cb_data->cancellable = g_cancellable_new ();
+ g_dbus_proxy_call (priv->resolve,
+ "ResolveHostname",
+ g_variant_new ("(isit)",
+ ifindex,
+ cb_data->host,
+ (gint32) AF_UNSPEC,
+ (guint64) SD_RESOLVED_DNS),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cb_data->cancellable,
+ resolve_cb,
+ cb_data);
+ _LOG2D ("resolving '%s' for '%s' using systemd-resolved", cb_data->host, priv->uri);
+ } else {
+ /* Not using systemd-resolved -- just fire the request right away. */
+ curl_multi_add_handle (priv->curl_mhandle, cb_data->curl_ehandle);
+ _LOG2D ("sending request to '%s', not using systemd-resolved", priv->uri);
+ }
- _LOG2D ("sending request to '%s'", priv->uri);
+ cb_data->timeout_id = g_timeout_add_seconds (30, timeout_cb, cb_data);
return;
} else {
_LOGD ("(%s) faking request. Connectivity check disabled", iface);
@@ -481,10 +628,43 @@ config_changed_cb (NMConfig *config,
}
static void
+resolved_proxy_created (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ NMConnectivity *self;
+ NMConnectivityPrivate *priv;
+ gs_free_error GError *error = NULL;
+ GDBusProxy *resolve;
+
+ resolve = g_dbus_proxy_new_finish (res, &error);
+ if (!resolve) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ return;
+ } else {
+ /* Log a warning, but still proceed without systemd-resolved */
+ _LOGW ("failed to connect to resolved via DBus: %s", error->message);
+ }
+ }
+
+ self = NM_CONNECTIVITY (user_data);
+ priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+ priv->resolve = resolve;
+ g_clear_object (&priv->init_cancellable);
+
+ priv->config = g_object_ref (nm_config_get ());
+ update_config (self, nm_config_get_data (priv->config));
+ g_signal_connect (G_OBJECT (priv->config),
+ NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+ G_CALLBACK (config_changed_cb),
+ self);
+}
+
+static void
nm_connectivity_init (NMConnectivity *self)
{
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
CURLcode retv;
+ NMBusManager *dbus_mgr;
+ GDBusConnection *connection;
retv = curl_global_init (CURL_GLOBAL_ALL);
if (retv == CURLE_OK)
@@ -500,14 +680,23 @@ nm_connectivity_init (NMConnectivity *self)
curl_multi_setopt (priv->curl_mhandle, CURLOPT_VERBOSE, 1);
}
- priv->config = g_object_ref (nm_config_get ());
-
- update_config (self, nm_config_get_data (priv->config));
- g_signal_connect (G_OBJECT (priv->config),
- NM_CONFIG_SIGNAL_CONFIG_CHANGED,
- G_CALLBACK (config_changed_cb),
+ dbus_mgr = nm_bus_manager_get ();
+ g_return_if_fail (dbus_mgr);
+
+ connection = nm_bus_manager_get_connection (dbus_mgr);
+ g_return_if_fail (connection);
+
+ priv->init_cancellable = g_cancellable_new ();
+ g_dbus_proxy_new (connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ NULL,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ priv->init_cancellable,
+ resolved_proxy_created,
self);
-
}
static void
@@ -527,6 +716,8 @@ dispose (GObject *object)
curl_multi_cleanup (priv->curl_mhandle);
curl_global_cleanup ();
nm_clear_g_source (&priv->periodic_check_id);
+ nm_clear_g_cancellable (&priv->init_cancellable);
+ g_clear_object (&priv->resolve);
G_OBJECT_CLASS (nm_connectivity_parent_class)->dispose (object);
}
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index d9a9f2338b..d537743439 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -42,6 +42,7 @@ NMConnectivity *nm_connectivity_get (void);
const char *nm_connectivity_state_to_string (NMConnectivityState state);
void nm_connectivity_check_async (NMConnectivity *self,
+ int ifindex,
const char *iface,
GAsyncReadyCallback callback,
gpointer user_data);