summary refs log tree commit diff
path: root/src/nm-pacrunner-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-pacrunner-manager.c')
-rw-r--r--src/nm-pacrunner-manager.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c
new file mode 100644
index 00000000..450cf0e3
--- /dev/null
+++ b/src/nm-pacrunner-manager.c
@@ -0,0 +1,468 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2016 Atul Anand <atulhjp@gmail.com>.
+ */
+
+#include "nm-default.h"
+
+#include "nm-pacrunner-manager.h"
+
+#include "nm-utils.h"
+#include "platform/nm-platform.h"
+#include "nm-proxy-config.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+
+#define PACRUNNER_DBUS_SERVICE "org.pacrunner"
+#define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager"
+#define PACRUNNER_DBUS_PATH "/org/pacrunner/manager"
+
+/*****************************************************************************/
+
+struct remove_data {
+	char *iface;
+	char *path;
+};
+
+typedef struct {
+	char *iface;
+	GPtrArray *domains;
+	GDBusProxy *pacrunner;
+	GCancellable *pacrunner_cancellable;
+	GList *args;
+	GList *remove;
+} NMPacrunnerManagerPrivate;
+
+struct _NMPacrunnerManager {
+	GObject parent;
+	NMPacrunnerManagerPrivate _priv;
+};
+
+struct _NMPacrunnerManagerClass {
+	GObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMPacrunnerManager, nm_pacrunner_manager, G_TYPE_OBJECT)
+
+#define NM_PACRUNNER_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMPacrunnerManager, NM_IS_PACRUNNER_MANAGER)
+
+/*****************************************************************************/
+
+NM_DEFINE_SINGLETON_GETTER (NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYPE_PACRUNNER_MANAGER);
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN      LOGD_PROXY
+#define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG_DOMAIN, "pacrunner", __VA_ARGS__)
+
+/*****************************************************************************/
+
+static void
+remove_data_destroy (struct remove_data *data)
+{
+	g_return_if_fail (data != NULL);
+
+	g_free (data->iface);
+	g_free (data->path);
+	memset (data, 0, sizeof (struct remove_data));
+	g_free (data);
+}
+
+static void
+add_proxy_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config)
+{
+	const char *pac_url, *pac_script;
+	NMProxyConfigMethod method;
+
+	method = nm_proxy_config_get_method (proxy_config);
+
+	if (method == NM_PROXY_CONFIG_METHOD_AUTO) {
+		pac_url = nm_proxy_config_get_pac_url (proxy_config);
+		if (pac_url) {
+			g_variant_builder_add (proxy_data, "{sv}",
+			                       "URL",
+			                       g_variant_new_string (pac_url));
+		}
+
+		pac_script = nm_proxy_config_get_pac_script (proxy_config);
+		if (pac_script) {
+			g_variant_builder_add (proxy_data, "{sv}",
+			                       "Script",
+			                       g_variant_new_string (pac_script));
+		}
+	}
+
+	g_variant_builder_add (proxy_data, "{sv}",
+	                       "BrowserOnly",
+	                       g_variant_new_boolean (nm_proxy_config_get_browser_only (proxy_config)));
+}
+
+static void
+add_ip4_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP4Config *ip4)
+{
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	int i;
+	char *cidr = NULL;
+
+	/* Extract searches */
+	for (i = 0; i < nm_ip4_config_get_num_searches (ip4); i++)
+		g_ptr_array_add (priv->domains, g_strdup (nm_ip4_config_get_search (ip4, i)));
+
+	/* Extract domains */
+	for (i = 0; i < nm_ip4_config_get_num_domains (ip4); i++)
+		g_ptr_array_add (priv->domains, g_strdup (nm_ip4_config_get_domain (ip4, i)));
+
+	/* Add addresses and routes in CIDR form */
+	for (i = 0; i < nm_ip4_config_get_num_addresses (ip4); i++) {
+		const NMPlatformIP4Address *address = nm_ip4_config_get_address (ip4, i);
+
+		cidr = g_strdup_printf ("%s/%u",
+		                        nm_utils_inet4_ntop (address->address, NULL),
+		                        address->plen);
+		g_ptr_array_add (priv->domains, g_strdup (cidr));
+		g_free (cidr);
+	}
+
+	for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) {
+		const NMPlatformIP4Route *routes = nm_ip4_config_get_route (ip4, i);
+
+		cidr = g_strdup_printf ("%s/%u",
+		                        nm_utils_inet4_ntop (routes->network, NULL),
+		                        routes->plen);
+		g_ptr_array_add (priv->domains, g_strdup (cidr));
+		g_free (cidr);
+	}
+}
+
+static void
+add_ip6_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP6Config *ip6)
+{
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	int i;
+	char *cidr = NULL;
+
+	/* Extract searches */
+	for (i = 0; i < nm_ip6_config_get_num_searches (ip6); i++)
+		g_ptr_array_add (priv->domains, g_strdup (nm_ip6_config_get_search (ip6, i)));
+
+	/* Extract domains */
+	for (i = 0; i < nm_ip6_config_get_num_domains (ip6); i++)
+		g_ptr_array_add (priv->domains, g_strdup (nm_ip6_config_get_domain (ip6, i)));
+
+	/* Add addresses and routes in CIDR form */
+	for (i = 0; i < nm_ip6_config_get_num_addresses (ip6); i++) {
+		const NMPlatformIP6Address *address = nm_ip6_config_get_address (ip6, i);
+
+		cidr = g_strdup_printf ("%s/%u",
+		                        nm_utils_inet6_ntop (&address->address, NULL),
+		                        address->plen);
+		g_ptr_array_add (priv->domains, g_strdup (cidr));
+		g_free (cidr);
+	}
+
+	for (i = 0; i < nm_ip6_config_get_num_routes (ip6); i++) {
+		const NMPlatformIP6Route *routes = nm_ip6_config_get_route (ip6, i);
+
+		cidr = g_strdup_printf ("%s/%u",
+		                        nm_utils_inet6_ntop (&routes->network, NULL),
+		                        routes->plen);
+		g_ptr_array_add (priv->domains, g_strdup (cidr));
+		g_free (cidr);
+	}
+}
+
+static void
+pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+	NMPacrunnerManager *self = NM_PACRUNNER_MANAGER (user_data);
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *variant = NULL;
+	const char *path = NULL;
+	GList *iter = NULL;
+	gboolean found = FALSE;
+
+	variant = g_dbus_proxy_call_finish (priv->pacrunner, res, &error);
+	if (!variant) {
+		_LOGD ("sending proxy config to pacrunner failed: %s", error->message);
+	} else {
+		struct remove_data *data;
+		g_variant_get (variant, "(&o)", &path);
+
+		/* Replace the old path (if any) of proxy config with the new one returned
+		 * from CreateProxyConfiguration() DBus method on pacrunner.
+		 */
+		for (iter = g_list_first (priv->remove); iter; iter = g_list_next (iter)) {
+			struct remove_data *r = iter->data;
+			if (g_strcmp0 (priv->iface, r->iface) == 0) {
+				g_free (r->path);
+				r->path = g_strdup (path);
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found) {
+			data = g_malloc0 (sizeof (struct remove_data));
+			data->iface = g_strdup (priv->iface);
+			data->path = g_strdup (path);
+			priv->remove = g_list_append (priv->remove, data);
+			_LOGD ("proxy config sent to pacrunner");
+		}
+	}
+}
+
+static void
+send_pacrunner_proxy_data (NMPacrunnerManager *self, GVariant *pacrunner_manager_args)
+{
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+
+	if (!pacrunner_manager_args)
+		return;
+
+	if (priv->pacrunner)
+		g_dbus_proxy_call (priv->pacrunner,
+		                   "CreateProxyConfiguration",
+		                   pacrunner_manager_args,
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   NULL,
+		                  (GAsyncReadyCallback) pacrunner_send_done,
+		                   self);
+}
+
+static void
+name_owner_changed (GObject *object,
+                    GParamSpec *pspec,
+                    gpointer user_data)
+{
+	NMPacrunnerManager *self = NM_PACRUNNER_MANAGER (user_data);
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	gs_free char *owner = NULL;
+	GList *iter = NULL;
+
+	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
+	if (owner) {
+		_LOGD ("pacrunner appeared as %s", owner);
+		for (iter = g_list_first(priv->args); iter; iter = g_list_next(iter)) {
+			send_pacrunner_proxy_data (self, iter->data);
+		}
+	} else {
+		_LOGD ("pacrunner disappeared");
+	}
+}
+
+static void
+pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+	NMPacrunnerManager *self = user_data;
+	NMPacrunnerManagerPrivate *priv;
+	GError *error = NULL;
+	GDBusProxy *proxy;
+
+	proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+	if (!proxy) {
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			_LOGW ("failed to connect to pacrunner via DBus: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+
+	priv->pacrunner = proxy;
+	nm_clear_g_cancellable (&priv->pacrunner_cancellable);
+
+	g_signal_connect (priv->pacrunner, "notify::g-name-owner",
+	                  G_CALLBACK (name_owner_changed), self);
+}
+
+/**
+ * nm_pacrunner_manager_send:
+ * @self: the #NMPacrunnerManager
+ * @iface: the iface for the connection or %NULL
+ * @proxy_config: proxy config of the connection
+ * @ip4_config: IP4 config of the connection
+ * @ip6_config: IP6 config of the connection
+ */
+void
+nm_pacrunner_manager_send (NMPacrunnerManager *self,
+                           const char *iface,
+                           NMProxyConfig *proxy_config,
+                           NMIP4Config *ip4_config,
+                           NMIP6Config *ip6_config)
+{
+	char **strv = NULL;
+	NMProxyConfigMethod method;
+	NMPacrunnerManagerPrivate *priv;
+	GVariantBuilder proxy_data;
+	GVariant *pacrunner_manager_args;
+
+	g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self));
+	g_return_if_fail (proxy_config);
+
+	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+
+	g_free (priv->iface);
+	priv->iface = g_strdup (iface);
+
+	g_variant_builder_init (&proxy_data, G_VARIANT_TYPE_VARDICT);
+
+	if (iface) {
+		g_variant_builder_add (&proxy_data, "{sv}",
+		                       "Interface",
+		                       g_variant_new_string (iface));
+	}
+
+	method = nm_proxy_config_get_method (proxy_config);
+	switch (method) {
+	case NM_PROXY_CONFIG_METHOD_AUTO:
+		g_variant_builder_add (&proxy_data, "{sv}",
+		                       "Method",
+		                       g_variant_new_string ("auto"));
+
+		break;
+	case NM_PROXY_CONFIG_METHOD_NONE:
+		g_variant_builder_add (&proxy_data, "{sv}",
+		                       "Method",
+		                       g_variant_new_string ("direct"));
+	}
+
+	priv->domains = g_ptr_array_new_with_free_func (g_free);
+
+	/* Extract stuff from configs */
+	add_proxy_config (self, &proxy_data, proxy_config);
+
+	if (ip4_config)
+		add_ip4_config (self, &proxy_data, ip4_config);
+	if (ip6_config)
+		add_ip6_config (self, &proxy_data, ip6_config);
+
+	g_ptr_array_add (priv->domains, NULL);
+	strv = (char **) g_ptr_array_free (priv->domains, (priv->domains->len == 1));
+
+	if (strv) {
+		g_variant_builder_add (&proxy_data, "{sv}",
+		                       "Domains",
+		                       g_variant_new_strv ((const char *const *) strv, -1));
+		g_strfreev (strv);
+	}
+
+	pacrunner_manager_args = g_variant_ref_sink (g_variant_new ("(a{sv})", &proxy_data));
+	priv->args = g_list_append (priv->args, pacrunner_manager_args);
+
+	/* Send if pacrunner is available on Bus, otherwise
+	 * argument has already been appended above to be
+	 * sent when pacrunner appears.
+	 */
+	send_pacrunner_proxy_data (self, pacrunner_manager_args);
+}
+
+static void
+pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+	/* @self may be a dangling pointer. However, we don't use it as the
+	 * logging macro below does not dereference @self. */
+	NMPacrunnerManager *self = user_data;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *ret = NULL;
+
+	ret = g_dbus_proxy_call_finish ((GDBusProxy *) source, res, &error);
+
+	if (!ret)
+		_LOGD ("Couldn't remove proxy config from pacrunner: %s", error->message);
+	else
+		_LOGD ("Successfully removed proxy config from pacrunner");
+}
+
+/**
+ * nm_pacrunner_manager_remove:
+ * @self: the #NMPacrunnerManager
+ * @iface: the iface for the connection to be removed
+ * from pacrunner
+ */
+void
+nm_pacrunner_manager_remove (NMPacrunnerManager *self, const char *iface)
+{
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	GList *list;
+
+	for (list = g_list_first(priv->remove); list; list = g_list_next(list)) {
+		struct remove_data *data = list->data;
+		if (g_strcmp0 (data->iface, iface) == 0) {
+			if (priv->pacrunner && data->path)
+				g_dbus_proxy_call (priv->pacrunner,
+				                   "DestroyProxyConfiguration",
+				                   g_variant_new ("(o)", data->path),
+				                   G_DBUS_CALL_FLAGS_NONE,
+				                   -1,
+				                   NULL,
+				                  (GAsyncReadyCallback) pacrunner_remove_done,
+				                   self);
+			break;
+		}
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_pacrunner_manager_init (NMPacrunnerManager *self)
+{
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+
+	priv->pacrunner_cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_NONE,
+	                          NULL,
+	                          PACRUNNER_DBUS_SERVICE,
+	                          PACRUNNER_DBUS_PATH,
+	                          PACRUNNER_DBUS_INTERFACE,
+	                          priv->pacrunner_cancellable,
+	                          (GAsyncReadyCallback) pacrunner_proxy_cb,
+	                          self);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE ((NMPacrunnerManager *) object);
+
+	g_clear_pointer (&priv->iface, g_free);
+
+	nm_clear_g_cancellable (&priv->pacrunner_cancellable);
+
+	g_clear_object (&priv->pacrunner);
+
+	g_list_free_full (priv->args, (GDestroyNotify) g_variant_unref);
+	priv->args = NULL;
+
+	g_list_free_full (priv->remove, (GDestroyNotify) remove_data_destroy);
+	priv->remove = NULL;
+
+	G_OBJECT_CLASS (nm_pacrunner_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_pacrunner_manager_class_init (NMPacrunnerManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = dispose;
+}