diff options
-rw-r--r-- | src/devices/nm-device.c | 1 | ||||
-rw-r--r-- | src/nm-connectivity.c | 237 | ||||
-rw-r--r-- | src/nm-connectivity.h | 1 |
3 files changed, 214 insertions, 25 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index b47b8914dd..035165dd34 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2967,6 +2967,7 @@ concheck_start (NMDevice *self, is_periodic ? ", periodic-check" : ""); handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self), + nm_device_get_ip_ifindex (self), nm_device_get_ip_iface (self), concheck_cb, handle); diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 7ef99e54f9..2ce5481b49 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -17,12 +17,13 @@ * * Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de> * Copyright (C) 2011 Dan Williams <dcbw@redhat.com> - * Copyright (C) 2016,2017 Red Hat, Inc. + * Copyright (C) 2016 - 2018 Red Hat, Inc. */ #include "nm-default.h" #include "nm-connectivity.h" +#include "nm-dbus-manager.h" #include <string.h> @@ -72,8 +73,11 @@ struct _NMConnectivityCheckHandle { struct { char *response; + int ifindex; + GCancellable *resolve_cancellable; CURL *curl_ehandle; struct curl_slist *request_headers; + struct curl_slist *hosts; GString *recv_msg; } concheck; @@ -98,6 +102,8 @@ typedef struct { CList handles_lst_head; CList completed_handles_lst_head; char *uri; + char *host; + char *port; char *response; gboolean enabled; guint interval; @@ -193,7 +199,9 @@ cb_data_complete (NMConnectivityCheckHandle *cb_data, curl_easy_cleanup (cb_data->concheck.curl_ehandle); curl_slist_free_all (cb_data->concheck.request_headers); + curl_slist_free_all (cb_data->concheck.hosts); } + nm_clear_g_cancellable (&cb_data->concheck.resolve_cancellable); #endif nm_clear_g_source (&cb_data->timeout_id); @@ -559,8 +567,139 @@ _idle_cb (gpointer user_data) return G_SOURCE_REMOVE; } +static void +do_curl_request (NMConnectivityCheckHandle *cb_data) +{ + NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (cb_data->self); + CURL *ehandle; + + ehandle = curl_easy_init (); + if (!ehandle) { + cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, NULL, "curl error"); + return; + } + + cb_data->concheck.response = g_strdup (priv->response); + cb_data->concheck.curl_ehandle = ehandle; + cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); + cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data); + + curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); + curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); + curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); + curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); + curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); + curl_easy_setopt (ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts); + + curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle); +} + +static void +resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + NMConnectivityCheckHandle *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; + gsize i; + gs_free_error GError *error = NULL; + + result = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = cb_data->self; + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + if (!result) { + /* Never mind. Just let do curl do its own resolving. */ + _LOG2D ("can't resolve a name via systemd-resolved: %s", error->message); + do_curl_request (cb_data); + return; + } + + addresses = g_variant_get_child_value (result, 0); + no_addresses = g_variant_n_children (addresses); + g_variant_unref (result); + + 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", priv->host, + priv->port ? priv->port : "80", str); + cb_data->concheck.hosts = curl_slist_append (cb_data->concheck.hosts, host); + _LOG2T ("adding '%s' to curl resolve list", host); + g_free (host); + } + + g_variant_unref (address); + } + + g_variant_unref (addresses); + do_curl_request (cb_data); +} + +#define SD_RESOLVED_DNS 1 + +static void +resolved_proxy_created (GObject *object, GAsyncResult *res, gpointer user_data) +{ + NMConnectivityCheckHandle *cb_data = user_data; + NMConnectivity *self; + NMConnectivityPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = cb_data->self; + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + if (!proxy) { + /* Log a warning, but still proceed without systemd-resolved */ + _LOG2D ("failed to connect to resolved via DBus: %s", error->message); + do_curl_request (cb_data); + return; + } + + g_dbus_proxy_call (proxy, + "ResolveHostname", + g_variant_new ("(isit)", + cb_data->concheck.ifindex, + priv->host, + (gint32) AF_UNSPEC, + (guint64) SD_RESOLVED_DNS), + G_DBUS_CALL_FLAGS_NONE, + -1, + cb_data->concheck.resolve_cancellable, + resolve_cb, + cb_data); + g_object_unref (proxy); + + _LOG2D ("resolving '%s' for '%s' using systemd-resolved", priv->host, priv->uri); +} + NMConnectivityCheckHandle * nm_connectivity_check_start (NMConnectivity *self, + int ifindex, const char *iface, NMConnectivityCheckCallback callback, gpointer user_data) @@ -585,35 +724,29 @@ nm_connectivity_check_start (NMConnectivity *self, cb_data->ifspec = g_strdup_printf ("if!%s", iface); #if WITH_CONCHECK - if (iface) { - CURL *ehandle; - - if ( priv->enabled - && (ehandle = curl_easy_init ())) { - - cb_data->concheck.response = g_strdup (priv->response); - cb_data->concheck.curl_ehandle = ehandle; - cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); - curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); - curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); - curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); - curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); - curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); - curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle); - - cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data); - - _LOG2D ("start request to '%s'", priv->uri); - return cb_data; - } + if (iface && ifindex > 0 && priv->enabled && priv->host) { + cb_data->concheck.ifindex = ifindex; + cb_data->concheck.resolve_cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + 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", + cb_data->concheck.resolve_cancellable, + resolved_proxy_created, + cb_data); + + _LOG2D ("start request to '%s'", priv->uri); + return cb_data; } #endif _LOG2D ("start fake request"); cb_data->timeout_id = g_idle_add (_idle_cb, cb_data); + return cb_data; } @@ -649,6 +782,54 @@ nm_connectivity_get_interval (NMConnectivity *self) : 0; } +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; +} + static void update_config (NMConnectivity *self, NMConfigData *config_data) { @@ -682,6 +863,10 @@ update_config (NMConnectivity *self, NMConfigData *config_data) if (changed) { g_free (priv->uri); priv->uri = g_strdup (uri); + + g_clear_pointer (&priv->host, g_free); + g_clear_pointer (&priv->port, g_free); + host_and_port_from_uri (uri, &priv->host, &priv->port); } /* Set the interval. */ @@ -785,6 +970,8 @@ dispose (GObject *object) cb_data_complete (cb_data, NM_CONNECTIVITY_DISPOSING, "shutting down"); g_clear_pointer (&priv->uri, g_free); + g_clear_pointer (&priv->host, g_free); + g_clear_pointer (&priv->port, g_free); g_clear_pointer (&priv->response, g_free); #if WITH_CONCHECK diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 178f27ad9a..2bf07bc080 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -58,6 +58,7 @@ typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self, gpointer user_data); NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self, + int ifindex, const char *iface, NMConnectivityCheckCallback callback, gpointer user_data); |