[PATCH] dns: update dnsmasq nameservers via its DBus SetServers method



---
 src/dns-manager/nm-dns-dnsmasq.c |  337 ++++++++++++++++++++++++--------------
 src/dns-manager/nm-dns-plugin.c  |    6 +
 2 files changed, 219 insertions(+), 124 deletions(-)

diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c
index 9090e26..7ddc4f6 100644
--- a/src/dns-manager/nm-dns-dnsmasq.c
+++ b/src/dns-manager/nm-dns-dnsmasq.c
@@ -29,11 +29,16 @@
 #include <glib.h>
 #include <glib/gi18n.h>
 
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+
 #include "nm-dns-dnsmasq.h"
 #include "nm-logging.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 #include "nm-dns-utils.h"
+#include "nm-dbus-manager.h"
 
 G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
 
@@ -43,8 +48,13 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
 #define CONFFILE LOCALSTATEDIR "/run/nm-dns-dnsmasq.conf"
 #define CONFDIR SYSCONFDIR "/NetworkManager/dnsmasq.d"
 
+#define DNSMASQ_DBUS_SERVICE "uk.org.thekelleys.dnsmasq"
+#define DNSMASQ_DBUS_PATH "/uk/org/thekelleys/dnsmasq"
+#define DNSMASQ_DBUS_INTERFACE "uk.org.thekelleys.dnsmasq"
+
 typedef struct {
-	guint32 foo;
+	NMDBusManager *dbus_mgr;
+	guint name_owner_id;
 } NMDnsDnsmasqPrivate;
 
 /*******************************************/
@@ -69,12 +79,11 @@ find_dnsmasq (void)
 }
 
 static gboolean
-add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
+add_ip4_config (DBusMessage *message, NMIP4Config *ip4, gboolean split)
 {
-	char buf[INET_ADDRSTRLEN + 1];
-	struct in_addr addr;
 	int n, i;
 	gboolean added = FALSE;
+	guint32 addr;
 
 	if (split) {
 		char **domains, **iter;
@@ -83,17 +92,17 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 		 * per domain (and the manpage says this too) so only use the first
 		 * nameserver here.
 		 */
-		addr.s_addr = nm_ip4_config_get_nameserver (ip4, 0);
-		memset (&buf[0], 0, sizeof (buf));
-		if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf)))
-			return FALSE;
+		addr = g_htonl(nm_ip4_config_get_nameserver (ip4, 0));
+		dbus_message_append_args (message,
+		                          DBUS_TYPE_UINT32, &addr,
+		                          DBUS_TYPE_INVALID);
 
 		/* searches are preferred over domains */
 		n = nm_ip4_config_get_num_searches (ip4);
 		for (i = 0; i < n; i++) {
-			g_string_append_printf (str, "server=/%s/%s\n",
-				                    nm_ip4_config_get_search (ip4, i),
-				                    buf);
+			dbus_message_append_args (message,
+			                          DBUS_TYPE_STRING, nm_ip4_config_get_search (ip4, i),
+			                          DBUS_TYPE_INVALID);
 			added = TRUE;
 		}
 
@@ -101,9 +110,9 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 			/* If not searches, use any domains */
 			n = nm_ip4_config_get_num_domains (ip4);
 			for (i = 0; i < n; i++) {
-				g_string_append_printf (str, "server=/%s/%s\n",
-							            nm_ip4_config_get_domain (ip4, i),
-							            buf);
+				dbus_message_append_args (message,
+				                          DBUS_TYPE_STRING, nm_ip4_config_get_domain (ip4, i),
+				                          DBUS_TYPE_INVALID);
 				added = TRUE;
 			}
 		}
@@ -114,7 +123,9 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 		domains = nm_dns_utils_get_ip4_rdns_domains (ip4);
 		if (domains) {
 			for (iter = domains; iter && *iter; iter++)
-				g_string_append_printf (str, "server=/%s/%s\n", *iter, buf);
+				dbus_message_append_args (message,
+				                          DBUS_TYPE_STRING, *iter,
+				                          DBUS_TYPE_INVALID);
 			g_strfreev (domains);
 			added = TRUE;
 		}
@@ -124,66 +135,20 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 	if (!added) {
 		n = nm_ip4_config_get_num_nameservers (ip4);
 		for (i = 0; i < n; i++) {
-			memset (&buf[0], 0, sizeof (buf));
-			addr.s_addr = nm_ip4_config_get_nameserver (ip4, i);
-			if (inet_ntop (AF_INET, &addr, buf, sizeof (buf)))
-				g_string_append_printf (str, "server=%s\n", buf);
+			addr = g_htonl(nm_ip4_config_get_nameserver (ip4, i));
+			dbus_message_append_args (message,
+			                          DBUS_TYPE_UINT32, &addr,
+			                          DBUS_TYPE_INVALID);
 		}
 	}
 
 	return TRUE;
 }
 
-#define IP6_ADDR_BUFLEN (INET6_ADDRSTRLEN + 50)
-
-static char *
-ip6_addr_to_string (const struct in6_addr *addr, const char *iface)
-{
-	char *buf, *p;
-
-	/* allocate enough space for the address + interface name */
-	buf = g_malloc0 (IP6_ADDR_BUFLEN + 1);
-
-	/* inet_ntop is probably supposed to do this for us, but it doesn't */
-	if (IN6_IS_ADDR_V4MAPPED (addr)) {
-		if (!inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, IP6_ADDR_BUFLEN))
-			goto error;
-		return buf;
-	}
-
-	if (!inet_ntop (AF_INET6, addr, buf, IP6_ADDR_BUFLEN))
-		goto error;
-
-	/* In the case of addr being a link-local address, inet_ntop can either
-	 * return an address with scope identifier already in place (like
-	 * fe80::202:b3ff:fe8d:7aaf%wlan0) or it returns an address without
-	 * scope identifier at all (like fe80::202:b3ff:fe8d:7aaf)
-	 */
-	p = strchr (buf, '%');
-	if (p) {
-		/* If we got a scope identifier, we need to replace the '%'
-		 * with '@', since dnsmasq supports '%' in server= addresses
-		 * only since version 2.58 and up
-		 */
-		*p = '@';
-	} else if (IN6_IS_ADDR_LINKLOCAL (addr)) {
-		/* If we got no scope identifier at all append the interface name */
-		strncat (buf, "@", IP6_ADDR_BUFLEN - strlen (buf));
-		strncat (buf, iface, IP6_ADDR_BUFLEN - strlen (buf));
-	}
-
-	return buf;
-
-error:
-	g_free (buf);
-	return NULL;
-}
-
 static gboolean
-add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split, const char *iface)
+add_ip6_config (DBusMessage *message, NMIP6Config *ip6, gboolean split, const char *iface)
 {
 	const struct in6_addr *addr;
-	char *buf;
 	int n, i;
 	gboolean added = FALSE;
 
@@ -193,16 +158,31 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split, const char *ifac
 		 * the first nameserver here.
 		 */
 		addr = nm_ip6_config_get_nameserver (ip6, 0);
-		buf = ip6_addr_to_string (addr, iface);
-		if (!buf)
-			return FALSE;
+		dbus_message_append_args (message,
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[0],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[1],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[2],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[3],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[4],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[5],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[6],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[7],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[8],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[9],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[10],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[11],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[12],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[13],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[14],
+		                          DBUS_TYPE_BYTE, &addr->s6_addr[15],
+		                          DBUS_TYPE_INVALID);
 
 		/* searches are preferred over domains */
 		n = nm_ip6_config_get_num_searches (ip6);
 		for (i = 0; i < n; i++) {
-			g_string_append_printf (str, "server=/%s/%s\n",
-				                    nm_ip6_config_get_search (ip6, i),
-				                    buf);
+			dbus_message_append_args (message,
+			                          DBUS_TYPE_STRING, nm_ip6_config_get_search (ip6, i),
+			                          DBUS_TYPE_INVALID);
 			added = TRUE;
 		}
 
@@ -210,14 +190,12 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split, const char *ifac
 			/* If not searches, use any domains */
 			n = nm_ip6_config_get_num_domains (ip6);
 			for (i = 0; i < n; i++) {
-				g_string_append_printf (str, "server=/%s/%s\n",
-							            nm_ip6_config_get_domain (ip6, i),
-							            buf);
+				dbus_message_append_args (message,
+				                          DBUS_TYPE_STRING, nm_ip6_config_get_domain (ip6, i),
+				                          DBUS_TYPE_INVALID);
 				added = TRUE;
 			}
 		}
-
-		g_free (buf);
 	}
 
 	/* If no searches or domains, just add the namservers */
@@ -225,11 +203,24 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split, const char *ifac
 		n = nm_ip6_config_get_num_nameservers (ip6);
 		for (i = 0; i < n; i++) {
 			addr = nm_ip6_config_get_nameserver (ip6, i);
-			buf = ip6_addr_to_string (addr, iface);
-			if (buf) {
-				g_string_append_printf (str, "server=%s\n", buf);
-				g_free (buf);
-			}
+			dbus_message_append_args (message,
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[0],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[1],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[2],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[3],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[4],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[5],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[6],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[7],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[8],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[9],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[10],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[11],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[12],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[13],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[14],
+			                          DBUS_TYPE_BYTE, &addr->s6_addr[15],
+			                          DBUS_TYPE_INVALID);
 		}
 	}
 
@@ -237,55 +228,28 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split, const char *ifac
 }
 
 static gboolean
-update (NMDnsPlugin *plugin,
-        const GSList *vpn_configs,
-        const GSList *dev_configs,
-        const GSList *other_configs,
-        const char *hostname,
-        const char *iface)
+start_dnsmasq (NMDnsDnsmasq *self)
 {
-	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
-	GString *conf;
-	GSList *iter;
-	const char *argv[12];
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	const char *argv[13];
+	char *dnsmasq_owner;
 	GError *error = NULL;
-	int ignored;
+	GString *conf;
 	GPid pid = 0;
+	int ignored;
 
-	/* Kill the old dnsmasq; there doesn't appear to be a way to get dnsmasq
-	 * to reread the config file using SIGHUP or similar.  This is a small race
-	 * here when restarting dnsmasq when DNS requests could go to the upstream
-	 * servers instead of to dnsmasq.
+	/* dnsmasq is probably already started; if it's the case, don't do
+	 * anything more.
 	 */
-	nm_dns_plugin_child_kill (plugin);
+	dnsmasq_owner = nm_dbus_manager_get_name_owner (priv->dbus_mgr, DNSMASQ_DBUS_SERVICE, NULL);
+	if (dnsmasq_owner != NULL)
+		return TRUE;
+
+	/* Start dnsmasq */
 
 	/* Build up the new dnsmasq config file */
 	conf = g_string_sized_new (150);
 
-	/* Use split DNS for VPN configs */
-	for (iter = (GSList *) vpn_configs; iter; iter = g_slist_next (iter)) {
-		if (NM_IS_IP4_CONFIG (iter->data))
-			add_ip4_config (conf, NM_IP4_CONFIG (iter->data), TRUE);
-		else if (NM_IS_IP6_CONFIG (iter->data))
-			add_ip6_config (conf, NM_IP6_CONFIG (iter->data), TRUE, iface);
-	}
-
-	/* Now add interface configs without split DNS */
-	for (iter = (GSList *) dev_configs; iter; iter = g_slist_next (iter)) {
-		if (NM_IS_IP4_CONFIG (iter->data))
-			add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
-		else if (NM_IS_IP6_CONFIG (iter->data))
-			add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE, iface);
-	}
-
-	/* And any other random configs */
-	for (iter = (GSList *) other_configs; iter; iter = g_slist_next (iter)) {
-		if (NM_IS_IP4_CONFIG (iter->data))
-			add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
-		else if (NM_IS_IP6_CONFIG (iter->data))
-			add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE, iface);
-	}
-
 	/* Write out the config file */
 	if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
 		nm_log_warn (LOGD_DNS, "Failed to write dnsmasq config file %s: (%d) %s",
@@ -310,8 +274,10 @@ update (NMDnsPlugin *plugin,
 	argv[7] = "--conf-file=" CONFFILE;
 	argv[8] = "--cache-size=400";
 	argv[9] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */
-	argv[10] = "--conf-dir=" CONFDIR;
-	argv[11] = NULL;
+	argv[10] = "--enable-dbus";
+	argv[11] = "--conf-dir=" CONFDIR;
+	argv[12] = NULL;
+
 
 	/* And finally spawn dnsmasq */
 	pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");
@@ -321,8 +287,113 @@ out:
 	return pid ? TRUE : FALSE;
 }
 
+static gboolean
+update (NMDnsPlugin *plugin,
+        const GSList *vpn_configs,
+        const GSList *dev_configs,
+        const GSList *other_configs,
+        const char *hostname,
+        const char *iface)
+{
+	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	DBusConnection *connection;
+	DBusMessage *message = NULL;
+	GSList *iter;
+	GError *error = NULL;
+	gboolean have_dnsmasq = FALSE;
+	gboolean ret = FALSE;
+	dbus_bool_t result;
+
+	have_dnsmasq = start_dnsmasq (self);
+
+	if (!have_dnsmasq)
+		goto out;
+
+	connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr);
+	if (!connection) {
+		nm_log_warn (LOGD_DNS, "Could not get the system bus to speak to dnsmasq.");
+		goto out;
+	}
+
+	message = dbus_message_new_method_call (DNSMASQ_DBUS_SERVICE, DNSMASQ_DBUS_PATH,
+	                                        DNSMASQ_DBUS_INTERFACE, "SetServers");
+
+	/* Use split DNS for VPN configs */
+	for (iter = (GSList *) vpn_configs; iter; iter = g_slist_next (iter)) {
+		if (NM_IS_IP4_CONFIG (iter->data))
+			add_ip4_config (message, NM_IP4_CONFIG (iter->data), TRUE);
+		else if (NM_IS_IP6_CONFIG (iter->data))
+			add_ip6_config (message, NM_IP6_CONFIG (iter->data), TRUE, iface);
+	}
+
+	/* Now add interface configs without split DNS */
+	for (iter = (GSList *) dev_configs; iter; iter = g_slist_next (iter)) {
+		if (NM_IS_IP4_CONFIG (iter->data))
+			add_ip4_config (message, NM_IP4_CONFIG (iter->data), FALSE);
+		else if (NM_IS_IP6_CONFIG (iter->data))
+			add_ip6_config (message, NM_IP6_CONFIG (iter->data), FALSE, iface);
+	}
+
+	/* And any other random configs */
+	for (iter = (GSList *) other_configs; iter; iter = g_slist_next (iter)) {
+		if (NM_IS_IP4_CONFIG (iter->data))
+			add_ip4_config (message, NM_IP4_CONFIG (iter->data), FALSE);
+		else if (NM_IS_IP6_CONFIG (iter->data))
+			add_ip6_config (message, NM_IP6_CONFIG (iter->data), FALSE, iface);
+	}
+
+	if (!nm_dbus_manager_get_name_owner (priv->dbus_mgr, DNSMASQ_DBUS_SERVICE, &error)) {
+		nm_log_err (LOGD_DNS, "dnsmasq not available on the bus, can't update servers.");
+		if (error)
+			nm_log_err (LOGD_DNS, "dnsmasq owner not found on bus: %s", error->message);
+		goto out;
+	}
+
+	result = dbus_connection_send (connection, message, NULL);
+	if (!result) {
+		nm_log_err (LOGD_DNS, "Could not send dnsmasq SetServers method.");
+		goto out;
+	}
+
+	ret = TRUE;
+
+out:
+	if (message)
+		dbus_message_unref (message);
+
+	return ret;
+}
+
 /****************************************************************/
 
+static void
+name_owner_changed_cb (NMDBusManager *dbus_mgr,
+                       const char *name,
+                       const char *old_owner,
+                       const char *new_owner,
+                       gpointer user_data)
+{
+	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (user_data);
+	gboolean old_owner_good = (old_owner && strlen (old_owner));
+	gboolean new_owner_good = (new_owner && strlen (new_owner));
+
+	/* Can't handle the signal if its not from dnsmasq */
+	if (strcmp (DNSMASQ_DBUS_SERVICE, name))
+		return;
+
+	if (!old_owner_good && new_owner_good) {
+		nm_log_warn (LOGD_DNS, "dnsmasq appeared on DBus: %s",
+		             new_owner);
+	} else if (old_owner_good && new_owner_good) {
+		nm_log_dbg (LOGD_DNS, "DBus name owner for dnsmasq changed: %s -> %s",
+		             old_owner, new_owner);
+	} else {
+		nm_log_warn (LOGD_DNS, "dnsmasq disappeared from the bus.");
+		g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
+	}
+}
+
 static const char *
 dm_exit_code_to_msg (int status)
 {
@@ -400,13 +471,31 @@ nm_dns_dnsmasq_new (void)
 static void
 nm_dns_dnsmasq_init (NMDnsDnsmasq *self)
 {
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+
+	g_assert (priv->dbus_mgr);
+
+	priv->name_owner_id = g_signal_connect (priv->dbus_mgr,
+	                                        NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+	                                        G_CALLBACK (name_owner_changed_cb),
+	                                        self);
 }
 
 static void
 dispose (GObject *object)
 {
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (object);
+
 	unlink (CONFFILE);
 
+	if (priv->dbus_mgr) {
+		if (priv->name_owner_id)
+			g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
+		g_object_unref (priv->dbus_mgr);
+	}
+
 	G_OBJECT_CLASS (nm_dns_dnsmasq_parent_class)->dispose (object);
 }
 
diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c
index b26f2b9..8f86d98 100644
--- a/src/dns-manager/nm-dns-plugin.c
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -133,6 +133,12 @@ watch_cb (GPid pid, gint status, gpointer user_data)
 	g_free (priv->progname);
 	priv->progname = NULL;
 
+	if (priv->pidfile) {
+		unlink (priv->pidfile);
+		g_free (priv->pidfile);
+		priv->pidfile = NULL;
+	}
+
 	g_signal_emit (self, signals[CHILD_QUIT], 0, status);
 }
 
-- 
1.7.10.4



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]