[PATCH] dns: update dnsmasq nameservers via its DBus SetServers method
- From: Mathieu Trudel-Lapierre <mathieu trudel-lapierre canonical com>
- To: networkmanager-list gnome org
- Cc: Mathieu Trudel-Lapierre <mathieu trudel-lapierre canonical com>, mathieu-tl gmail com
- Subject: [PATCH] dns: update dnsmasq nameservers via its DBus SetServers method
- Date: Thu, 26 Jul 2012 15:05:01 -0400
---
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]