summary refs log tree commit diff
path: root/src/dns-manager
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
committerMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
commit494f296a3baab08522617b24b1f126d8f9a17502 (patch)
treec8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /src/dns-manager
parent54f6333410ffd570e62717d9e77c5c987175e397 (diff)
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'src/dns-manager')
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.c74
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.h3
-rw-r--r--src/dns-manager/nm-dns-manager.c684
-rw-r--r--src/dns-manager/nm-dns-manager.h38
-rw-r--r--src/dns-manager/nm-dns-plugin.c10
-rw-r--r--src/dns-manager/nm-dns-plugin.h10
-rw-r--r--src/dns-manager/nm-dns-unbound.c3
-rw-r--r--src/dns-manager/nm-dns-unbound.h2
8 files changed, 564 insertions, 260 deletions
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c
index f26ccfaa..63e37bf3 100644
--- a/src/dns-manager/nm-dns-dnsmasq.c
+++ b/src/dns-manager/nm-dns-dnsmasq.c
@@ -27,12 +27,9 @@
 #include <arpa/inet.h>
 #include <sys/stat.h>
 
-#include <glib.h>
-#include <glib/gi18n.h>
-
+#include "nm-default.h"
 #include "nm-dns-dnsmasq.h"
 #include "nm-utils.h"
-#include "nm-logging.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 #include "nm-dns-utils.h"
@@ -138,6 +135,30 @@ ip6_addr_to_string (const struct in6_addr *addr, const char *iface)
 	return buf;
 }
 
+static void
+add_global_config (GString *str, const NMGlobalDnsConfig *config)
+{
+	guint i, j;
+
+	g_return_if_fail (config);
+
+	for (i = 0; i < nm_global_dns_config_get_num_domains (config); i++) {
+		NMGlobalDnsDomain *domain = nm_global_dns_config_get_domain (config, i);
+		const char *const *servers = nm_global_dns_domain_get_servers (domain);
+
+		for (j = 0; servers && servers[j]; j++) {
+			if (!strcmp (servers[j], "*"))
+				g_string_append_printf (str, "server=%s\n", servers[j]);
+			else {
+				g_string_append_printf (str, "server=/%s/%s\n",
+				                        nm_global_dns_domain_get_name (domain),
+				                        servers[j]);
+			}
+		}
+
+	}
+}
+
 static gboolean
 add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
 {
@@ -204,6 +225,7 @@ update (NMDnsPlugin *plugin,
         const GSList *vpn_configs,
         const GSList *dev_configs,
         const GSList *other_configs,
+        const NMGlobalDnsConfig *global_config,
         const char *hostname)
 {
 	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
@@ -232,28 +254,32 @@ update (NMDnsPlugin *plugin,
 	/* 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);
-	}
+	if (global_config)
+		add_global_config (conf, global_config);
+	else {
+		/* 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);
+		}
 
-	/* 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);
-	}
+		/* 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);
+		}
 
-	/* 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);
+		/* 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);
+		}
 	}
 
 	/* Write out the config file */
diff --git a/src/dns-manager/nm-dns-dnsmasq.h b/src/dns-manager/nm-dns-dnsmasq.h
index fc80f079..f0393c12 100644
--- a/src/dns-manager/nm-dns-dnsmasq.h
+++ b/src/dns-manager/nm-dns-dnsmasq.h
@@ -19,9 +19,6 @@
 #ifndef __NETWORKMANAGER_DNS_DNSMASQ_H__
 #define __NETWORKMANAGER_DNS_DNSMASQ_H__
 
-#include <glib.h>
-#include <glib-object.h>
-
 #include "nm-dns-plugin.h"
 
 #define NM_TYPE_DNS_DNSMASQ            (nm_dns_dnsmasq_get_type ())
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index c4be7df2..01e8bf1d 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 #include <resolv.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -34,14 +35,12 @@
 
 #include <linux/fs.h>
 
-#include <glib.h>
-#include <glib/gi18n.h>
-
+#include "nm-default.h"
 #include "nm-utils.h"
+#include "nm-core-internal.h"
 #include "nm-dns-manager.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "nm-logging.h"
 #include "NetworkManagerUtils.h"
 #include "nm-config.h"
 
@@ -71,6 +70,47 @@ G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, G_TYPE_OBJECT)
 
 #define HASH_LEN 20
 
+#ifdef RESOLVCONF_PATH
+#define RESOLVCONF_SELECTED
+#else
+#define RESOLVCONF_PATH "/sbin/resolvconf"
+#endif
+
+#ifdef NETCONFIG_PATH
+#define NETCONFIG_SELECTED
+#else
+#define NETCONFIG_PATH "/sbin/netconfig"
+#endif
+
+NM_DEFINE_SINGLETON_INSTANCE (NMDnsManager);
+
+/*********************************************************************************************/
+
+#define _NMLOG_PREFIX_NAME                "dns-mgr"
+#define _NMLOG_DOMAIN                     LOGD_DNS
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel __level = (level); \
+        \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
+            char __prefix[20]; \
+            const NMDnsManager *const __self = (self); \
+            \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+                     "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     ((__self == singleton_instance) \
+                        ? _NMLOG_PREFIX_NAME \
+                        : ({ \
+                                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
+                                __prefix; \
+                           }) \
+                     ) \
+                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/*********************************************************************************************/
+
 typedef struct {
 	NMIP4Config *ip4_vpn_config;
 	NMIP4Config *ip4_device_config;
@@ -84,6 +124,7 @@ typedef struct {
 	guint8 prev_hash[HASH_LEN];  /* Hash when begin_updates() was called */
 
 	NMDnsManagerResolvConfMode resolv_conf_mode;
+	NMDnsManagerResolvConfManager rc_manager;
 	NMDnsPlugin *plugin;
 
 	NMConfig *config;
@@ -97,12 +138,19 @@ enum {
 	LAST_SIGNAL
 };
 
+typedef enum {
+	SR_SUCCESS,
+	SR_NOTFOUND,
+	SR_ERROR
+} SpawnResult;
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
 
 typedef struct {
 	GPtrArray *nameservers;
 	GPtrArray *searches;
+	GPtrArray *options;
 	const char *nis_domain;
 	GPtrArray *nis_servers;
 } NMResolvConfData;
@@ -128,6 +176,13 @@ add_string_item (GPtrArray *array, const char *str)
 }
 
 static void
+add_dns_option_item (GPtrArray *array, const char *str, gboolean ipv6)
+{
+	if (_nm_utils_dns_option_find_idx (array, str) < 0)
+		g_ptr_array_add (array, g_strdup (str));
+}
+
+static void
 merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
 {
 	guint32 num, num_domains, num_searches, i;
@@ -161,6 +216,14 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
 		}
 	}
 
+	num = nm_ip4_config_get_num_dns_options (src);
+	for (i = 0; i < num; i++) {
+		const char *option;
+
+		option = nm_ip4_config_get_dns_option (src, i);
+		add_dns_option_item (rc->options, option, FALSE);
+	}
+
 	/* NIS stuff */
 	num = nm_ip4_config_get_num_nis_servers (src);
 	for (i = 0; i < num; i++) {
@@ -225,18 +288,21 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src)
 			add_string_item (rc->searches, domain);
 		}
 	}
-}
 
+	num = nm_ip6_config_get_num_dns_options (src);
+	for (i = 0; i < num; i++) {
+		const char *option;
 
-#if defined(NETCONFIG_PATH)
-/**********************************/
-/* SUSE */
+		option = nm_ip6_config_get_dns_option (src, i);
+		add_dns_option_item (rc->options, option, TRUE);
+	}
+}
 
 static GPid
-run_netconfig (GError **error, gint *stdin_fd)
+run_netconfig (NMDnsManager *self, GError **error, gint *stdin_fd)
 {
 	char *argv[5];
-	char *tmp;
+	gs_free char *tmp = NULL;
 	GPid pid = -1;
 
 	argv[0] = NETCONFIG_PATH;
@@ -245,9 +311,8 @@ run_netconfig (GError **error, gint *stdin_fd)
 	argv[3] = "NetworkManager";
 	argv[4] = NULL;
 
-	tmp = g_strjoinv (" ", argv);
-	nm_log_dbg (LOGD_DNS, "spawning '%s'", tmp);
-	g_free (tmp);
+	_LOGD ("spawning '%s'",
+	       (tmp = g_strjoinv (" ", argv)));
 
 	if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL,
 	                               NULL, &pid, stdin_fd, NULL, NULL, error))
@@ -257,19 +322,20 @@ run_netconfig (GError **error, gint *stdin_fd)
 }
 
 static void
-write_to_netconfig (gint fd, const char *key, const char *value)
+write_to_netconfig (NMDnsManager *self, gint fd, const char *key, const char *value)
 {
 	char *str;
 	int x;
 
 	str = g_strdup_printf ("%s='%s'\n", key, value);
-	nm_log_dbg (LOGD_DNS, "writing to netconfig: %s", str);
+	_LOGD ("writing to netconfig: %s", str);
 	x = write (fd, str, strlen (str));
 	g_free (str);
 }
 
-static gboolean
-dispatch_netconfig (char **searches,
+static SpawnResult
+dispatch_netconfig (NMDnsManager *self,
+                    char **searches,
                     char **nameservers,
                     const char *nis_domain,
                     char **nis_servers,
@@ -280,34 +346,34 @@ dispatch_netconfig (char **searches,
 	gint fd;
 	int status;
 
-	pid = run_netconfig (error, &fd);
+	pid = run_netconfig (self, error, &fd);
 	if (pid <= 0)
-		return FALSE;
+		return SR_NOTFOUND;
 
 	/* NM is writing already-merged DNS information to netconfig, so it
 	 * does not apply to a specific network interface.
 	 */
-	write_to_netconfig (fd, "INTERFACE", "NetworkManager");
+	write_to_netconfig (self, fd, "INTERFACE", "NetworkManager");
 
 	if (searches) {
 		str = g_strjoinv (" ", searches);
 
-		write_to_netconfig (fd, "DNSSEARCH", str);
+		write_to_netconfig (self, fd, "DNSSEARCH", str);
 		g_free (str);
 	}
 
 	if (nameservers) {
 		str = g_strjoinv (" ", nameservers);
-		write_to_netconfig (fd, "DNSSERVERS", str);
+		write_to_netconfig (self, fd, "DNSSERVERS", str);
 		g_free (str);
 	}
 
 	if (nis_domain)
-		write_to_netconfig (fd, "NISDOMAIN", nis_domain);
+		write_to_netconfig (self, fd, "NISDOMAIN", nis_domain);
 
 	if (nis_servers) {
 		str = g_strjoinv (" ", nis_servers);
-		write_to_netconfig (fd, "NISSERVERS", str);
+		write_to_netconfig (self, fd, "NISSERVERS", str);
 		g_free (str);
 	}
 
@@ -320,28 +386,28 @@ dispatch_netconfig (char **searches,
 		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
 		             "Error waiting for netconfig to exit: %s",
 		             strerror (errsv));
-		return FALSE;
+		return SR_ERROR;
 	}
 	if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS) {
 		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
 		             "Error calling netconfig: %s %d",
 		             WIFEXITED (status) ? "exited with status" : (WIFSIGNALED (status) ? "exited with signal" : "exited with unknown reason"),
 		             WIFEXITED (status) ? WEXITSTATUS (status) : (WIFSIGNALED (status) ? WTERMSIG (status) : status));
-		return FALSE;
+		return SR_ERROR;
 	}
-	return TRUE;
+	return SR_SUCCESS;
 }
-#endif
-
 
 static gboolean
 write_resolv_conf (FILE *f,
                    char **searches,
                    char **nameservers,
+                   char **options,
                    GError **error)
 {
 	char *searches_str = NULL;
 	char *nameservers_str = NULL;
+	char *options_str = NULL;
 	gboolean retval = FALSE;
 	char *tmp_str;
 	GString *str;
@@ -353,6 +419,12 @@ write_resolv_conf (FILE *f,
 		g_free (tmp_str);
 	}
 
+	if (options) {
+		tmp_str = g_strjoinv (" ", options);
+		options_str = g_strconcat ("options ", tmp_str, "\n", NULL);
+		g_free (tmp_str);
+	}
+
 	str = g_string_new ("");
 
 	if (nameservers) {
@@ -375,9 +447,10 @@ write_resolv_conf (FILE *f,
 
 	nameservers_str = g_string_free (str, FALSE);
 
-	if (fprintf (f, "# Generated by NetworkManager\n%s%s",
+	if (fprintf (f, "# Generated by NetworkManager\n%s%s%s",
 	             searches_str ? searches_str : "",
-	             nameservers_str) > 0)
+	             nameservers_str,
+	             options_str ? options_str : "") > 0)
 		retval = TRUE;
 	else {
 		g_set_error (error,
@@ -389,14 +462,16 @@ write_resolv_conf (FILE *f,
 
 	g_free (searches_str);
 	g_free (nameservers_str);
+	g_free (options_str);
 
 	return retval;
 }
 
-#ifdef RESOLVCONF_PATH
-static gboolean
-dispatch_resolvconf (char **searches,
+static SpawnResult
+dispatch_resolvconf (NMDnsManager *self,
+                     char **searches,
                      char **nameservers,
+                     char **options,
                      GError **error)
 {
 	char *cmd;
@@ -409,12 +484,12 @@ dispatch_resolvconf (char **searches,
 		                     NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_FAILED,
 		                     RESOLVCONF_PATH " is not executable");
-		return FALSE;
+		return SR_NOTFOUND;
 	}
 
 	if (searches || nameservers) {
 		cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
-		nm_log_info (LOGD_DNS, "Writing DNS information to %s", RESOLVCONF_PATH);
+		_LOGI ("Writing DNS information to %s", RESOLVCONF_PATH);
 		if ((f = popen (cmd, "w")) == NULL)
 			g_set_error (error,
 			             NM_MANAGER_ERROR,
@@ -423,7 +498,7 @@ dispatch_resolvconf (char **searches,
 			             RESOLVCONF_PATH,
 			             g_strerror (errno));
 		else {
-			retval = write_resolv_conf (f, searches, nameservers, error);
+			retval = write_resolv_conf (f, searches, nameservers, options, error);
 			err = pclose (f);
 			if (err < 0) {
 				errnosv = errno;
@@ -431,76 +506,70 @@ dispatch_resolvconf (char **searches,
 				             "Failed to close pipe to resolvconf: %d", errnosv);
 				retval = FALSE;
 			} else if (err > 0) {
-				nm_log_warn (LOGD_DNS, "resolvconf failed with status %d", err);
+				_LOGW ("resolvconf failed with status %d", err);
 				retval = FALSE;
 			}
 		}
 	} else {
 		cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
-		nm_log_info (LOGD_DNS, "Removing DNS information from %s", RESOLVCONF_PATH);
+		_LOGI ("Removing DNS information from %s", RESOLVCONF_PATH);
 		if (nm_spawn_process (cmd, error) == 0)
 			retval = TRUE;
 	}
 
 	g_free (cmd);
 
-	return retval;
+	return retval ? SR_SUCCESS : SR_ERROR;
 }
-#endif
 
-static gboolean
-update_resolv_conf (char **searches,
+#define MY_RESOLV_CONF NMRUNDIR "/resolv.conf"
+#define MY_RESOLV_CONF_TMP MY_RESOLV_CONF ".tmp"
+#define RESOLV_CONF_TMP "/etc/.resolv.conf.NetworkManager"
+
+static SpawnResult
+update_resolv_conf (NMDnsManager *self,
+                    char **searches,
                     char **nameservers,
-                    GError **error)
+                    char **options,
+                    GError **error,
+                    gboolean install_etc)
 {
-	char *tmp_resolv_conf;
-	char *tmp_resolv_conf_realpath;
-	char *resolv_conf_realpath;
 	FILE *f;
-	int do_rename = 1;
-	int old_errno = 0;
+	struct stat st;
+	gboolean ret;
 
-	g_return_val_if_fail (error != NULL, FALSE);
+	/* If we are not managing /etc/resolv.conf and it points to
+	 * MY_RESOLV_CONF, don't write the private DNS configuration to
+	 * MY_RESOLV_CONF otherwise we would overwrite the changes done by
+	 * some external application.
+	 */
+	if (!install_etc) {
+		char *path = g_file_read_link (_PATH_RESCONF, NULL);
+		gboolean ours = !g_strcmp0 (path, MY_RESOLV_CONF);
 
-	/* Find the real path of resolv.conf; it could be a symlink to something */
-	resolv_conf_realpath = realpath (_PATH_RESCONF, NULL);
-	if (!resolv_conf_realpath)
-		resolv_conf_realpath = strdup (_PATH_RESCONF);
+		g_free (path);
 
-	/* Build up the real path for the temp resolv.conf that we're about to
-	 * write out.
-	 */
-	tmp_resolv_conf = g_strdup_printf ("%s.tmp", resolv_conf_realpath);
-	tmp_resolv_conf_realpath = realpath (tmp_resolv_conf, NULL);
-	if (!tmp_resolv_conf_realpath)
-		tmp_resolv_conf_realpath = strdup (tmp_resolv_conf);
-	g_free (tmp_resolv_conf);
-	tmp_resolv_conf = NULL;
-
-	if ((f = fopen (tmp_resolv_conf_realpath, "w")) == NULL) {
-		do_rename = 0;
-		old_errno = errno;
-		if ((f = fopen (_PATH_RESCONF, "w")) == NULL) {
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_FAILED,
-			             "Could not open %s: %s\nCould not open %s: %s\n",
-			             tmp_resolv_conf_realpath,
-			             g_strerror (old_errno),
-			             _PATH_RESCONF,
-			             g_strerror (errno));
-			goto out;
+		if (ours) {
+			_LOGD ("not updating " MY_RESOLV_CONF
+			       " since it points to " _PATH_RESCONF);
+			return SR_ERROR;
 		}
-		/* Update tmp_resolv_conf_realpath so the error message on fclose()
-		 * failure will be correct.
-		 */
-		strcpy (tmp_resolv_conf_realpath, _PATH_RESCONF);
 	}
 
-	write_resolv_conf (f, searches, nameservers, error);
+	if ((f = fopen (MY_RESOLV_CONF_TMP, "w")) == NULL) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "Could not open %s: %s\n",
+		             MY_RESOLV_CONF_TMP,
+		             g_strerror (errno));
+		return SR_ERROR;
+	}
+
+	ret = write_resolv_conf (f, searches, nameservers, options, error);
 
 	if (fclose (f) < 0) {
-		if (*error == NULL) {
+		if (ret) {
 			/* only set an error here if write_resolv_conf() was successful,
 			 * since its error is more important.
 			 */
@@ -508,32 +577,97 @@ update_resolv_conf (char **searches,
 			             NM_MANAGER_ERROR,
 			             NM_MANAGER_ERROR_FAILED,
 			             "Could not close %s: %s\n",
-			             tmp_resolv_conf_realpath,
+			             MY_RESOLV_CONF_TMP,
 			             g_strerror (errno));
 		}
 	}
 
-	/* Don't rename the tempfile over top of the existing resolv.conf if there
-	 * was an error writing it out.
-	 */
-	if (*error == NULL && do_rename) {
-		if (rename (tmp_resolv_conf_realpath, resolv_conf_realpath) < 0) {
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_FAILED,
-			             "Could not replace " _PATH_RESCONF ": %s\n",
-			             g_strerror (errno));
+	if (!ret)
+		return SR_ERROR;
+
+	if (rename (MY_RESOLV_CONF_TMP, MY_RESOLV_CONF) < 0) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "Could not replace %s: %s\n",
+		             MY_RESOLV_CONF,
+		             g_strerror (errno));
+		return SR_ERROR;
+	}
+
+	if (!install_etc)
+		return SR_SUCCESS;
+
+	/* Don't overwrite a symbolic link unless it points to MY_RESOLV_CONF. */
+	if (lstat (_PATH_RESCONF, &st) != -1) {
+		/* Don't overwrite a symbolic link. */
+		if (S_ISLNK (st.st_mode)) {
+			if (stat (_PATH_RESCONF, &st) != -1) {
+				char *path = g_file_read_link (_PATH_RESCONF, NULL);
+				gboolean not_ours = g_strcmp0 (path, MY_RESOLV_CONF) != 0;
+
+				g_free (path);
+				if (not_ours)
+					return SR_SUCCESS;
+			} else {
+				if (errno != ENOENT)
+					return SR_SUCCESS;
+				g_set_error (error,
+				             NM_MANAGER_ERROR,
+				             NM_MANAGER_ERROR_FAILED,
+				             "Could not stat %s: %s\n",
+				             _PATH_RESCONF,
+				             g_strerror (errno));
+				return SR_ERROR;
+			}
 		}
+	} else if (errno != ENOENT) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "Could not lstat %s: %s\n",
+		             _PATH_RESCONF,
+		             g_strerror (errno));
+		return SR_ERROR;
 	}
 
-out:
-	free (tmp_resolv_conf_realpath);
-	free (resolv_conf_realpath);
-	return *error ? FALSE : TRUE;
+	if (unlink (RESOLV_CONF_TMP) == -1 && errno != ENOENT) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "Could not unlink %s: %s\n",
+		             RESOLV_CONF_TMP,
+		             g_strerror (errno));
+		return SR_ERROR;
+	}
+
+	if (symlink (MY_RESOLV_CONF, RESOLV_CONF_TMP) == -1) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "Could not create symlink %s pointing to %s: %s\n",
+		             RESOLV_CONF_TMP,
+		             MY_RESOLV_CONF,
+		             g_strerror (errno));
+		return SR_ERROR;
+	}
+
+	if (rename (RESOLV_CONF_TMP, _PATH_RESCONF) == -1) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "Could not rename %s to %s: %s\n",
+		             RESOLV_CONF_TMP,
+		             _PATH_RESCONF,
+		             g_strerror (errno));
+		return SR_ERROR;
+	}
+
+	return SR_SUCCESS;
 }
 
 static void
-compute_hash (NMDnsManager *self, guint8 buffer[HASH_LEN])
+compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN])
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	GChecksum *sum;
@@ -543,6 +677,9 @@ compute_hash (NMDnsManager *self, guint8 buffer[HASH_LEN])
 	sum = g_checksum_new (G_CHECKSUM_SHA1);
 	g_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1));
 
+	if (global)
+		nm_global_dns_config_update_checksum (global, sum);
+
 	if (priv->ip4_vpn_config)
 		nm_ip4_config_hash (priv->ip4_vpn_config, sum, TRUE);
 	if (priv->ip4_device_config)
@@ -608,6 +745,38 @@ build_plugin_config_lists (NMDnsManager *self,
 }
 
 static gboolean
+merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
+{
+	NMGlobalDnsDomain *default_domain;
+	const char *const *searches;
+	const char *const *options;
+	const char *const *servers;
+	gint i;
+
+	if (!global_conf)
+		return FALSE;
+
+	searches = nm_global_dns_config_get_searches (global_conf);
+	options = nm_global_dns_config_get_options (global_conf);
+
+	for (i = 0; searches && searches[i]; i++) {
+		if (DOMAIN_IS_VALID (searches[i]))
+			add_string_item (rc->searches, searches[i]);
+	}
+
+	for (i = 0; options && options[i]; i++)
+		add_string_item (rc->options, options[i]);
+
+	default_domain = nm_global_dns_config_lookup_domain (global_conf, "*");
+	g_assert (default_domain);
+	servers = nm_global_dns_domain_get_servers (default_domain);
+	for (i = 0; servers && servers[i]; i++)
+		add_string_item (rc->nameservers, servers[i]);
+
+	return TRUE;
+}
+
+static gboolean
 update_dns (NMDnsManager *self,
             gboolean no_caching,
             GError **error)
@@ -617,57 +786,71 @@ update_dns (NMDnsManager *self,
 	GSList *iter;
 	const char *nis_domain = NULL;
 	char **searches = NULL;
+	char **options = NULL;
 	char **nameservers = NULL;
 	char **nis_servers = NULL;
 	int num, i, len;
-	gboolean success = FALSE, caching = FALSE;
+	gboolean caching = FALSE, update = TRUE;
+	gboolean resolv_conf_updated = FALSE;
+	SpawnResult result = SR_ERROR;
+	NMConfigData *data;
+	NMGlobalDnsConfig *global_config;
 
 	g_return_val_if_fail (!error || !*error, FALSE);
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
-	if (priv->resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED)
-		return TRUE;
-
-	priv->dns_touched = TRUE;
+	if (priv->resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED) {
+		update = FALSE;
+		_LOGD ("update-dns: not updating resolv.conf");
+	} else {
+		priv->dns_touched = TRUE;
+		_LOGD ("update-dns: updating resolv.conf");
+	}
 
-	nm_log_dbg (LOGD_DNS, "updating resolv.conf");
+	data = nm_config_get_data (priv->config);
+	global_config = nm_config_data_get_global_dns_config (data);
 
 	/* Update hash with config we're applying */
-	compute_hash (self, priv->hash);
+	compute_hash (self, global_config, priv->hash);
 
 	rc.nameservers = g_ptr_array_new ();
 	rc.searches = g_ptr_array_new ();
+	rc.options = g_ptr_array_new ();
 	rc.nis_domain = NULL;
 	rc.nis_servers = g_ptr_array_new ();
 
-	if (priv->ip4_vpn_config)
-		merge_one_ip4_config (&rc, priv->ip4_vpn_config);
-	if (priv->ip4_device_config)
-		merge_one_ip4_config (&rc, priv->ip4_device_config);
-
-	if (priv->ip6_vpn_config)
-		merge_one_ip6_config (&rc, priv->ip6_vpn_config);
-	if (priv->ip6_device_config)
-		merge_one_ip6_config (&rc, priv->ip6_device_config);
-
-	for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
-		if (   (iter->data == priv->ip4_vpn_config)
-		    || (iter->data == priv->ip4_device_config)
-		    || (iter->data == priv->ip6_vpn_config)
-		    || (iter->data == priv->ip6_device_config))
-			continue;
+	if (global_config)
+		merge_global_dns_config (&rc, global_config);
+	else {
+		if (priv->ip4_vpn_config)
+			merge_one_ip4_config (&rc, priv->ip4_vpn_config);
+		if (priv->ip4_device_config)
+			merge_one_ip4_config (&rc, priv->ip4_device_config);
+
+		if (priv->ip6_vpn_config)
+			merge_one_ip6_config (&rc, priv->ip6_vpn_config);
+		if (priv->ip6_device_config)
+			merge_one_ip6_config (&rc, priv->ip6_device_config);
+
+		for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
+			if (   (iter->data == priv->ip4_vpn_config)
+			    || (iter->data == priv->ip4_device_config)
+			    || (iter->data == priv->ip6_vpn_config)
+			    || (iter->data == priv->ip6_device_config))
+				continue;
 
-		if (NM_IS_IP4_CONFIG (iter->data)) {
-			NMIP4Config *config = NM_IP4_CONFIG (iter->data);
+			if (NM_IS_IP4_CONFIG (iter->data)) {
+				NMIP4Config *config = NM_IP4_CONFIG (iter->data);
 
-			merge_one_ip4_config (&rc, config);
-		} else if (NM_IS_IP6_CONFIG (iter->data)) {
-			NMIP6Config *config = NM_IP6_CONFIG (iter->data);
+				merge_one_ip4_config (&rc, config);
+			} else if (NM_IS_IP6_CONFIG (iter->data)) {
+				NMIP6Config *config = NM_IP6_CONFIG (iter->data);
 
-			merge_one_ip6_config (&rc, config);
-		} else
-			g_assert_not_reached ();
+				merge_one_ip6_config (&rc, config);
+			} else
+				g_assert_not_reached ();
+		}
 	}
 
 	/* If the hostname is a FQDN ("dcbw.example.com"), then add the domain part of it
@@ -705,6 +888,12 @@ update_dns (NMDnsManager *self,
 	} else
 		g_ptr_array_free (rc.searches, TRUE);
 
+	if (rc.options->len) {
+		g_ptr_array_add (rc.options, NULL);
+		options = (char **) g_ptr_array_free (rc.options, FALSE);
+	} else
+		g_ptr_array_free (rc.options, TRUE);
+
 	if (rc.nameservers->len) {
 		g_ptr_array_add (rc.nameservers, NULL);
 		nameservers = (char **) g_ptr_array_free (rc.nameservers, FALSE);
@@ -720,29 +909,31 @@ update_dns (NMDnsManager *self,
 	nis_domain = rc.nis_domain;
 
 	/* Let any plugins do their thing first */
-	if (priv->plugin) {
+	if (update && priv->plugin) {
 		NMDnsPlugin *plugin = priv->plugin;
 		const char *plugin_name = nm_dns_plugin_get_name (plugin);
 		GSList *vpn_configs = NULL, *dev_configs = NULL, *other_configs = NULL;
 
 		if (nm_dns_plugin_is_caching (plugin)) {
 			if (no_caching) {
-				nm_log_dbg (LOGD_DNS, "DNS: plugin %s ignored (caching disabled)",
-				            plugin_name);
+				_LOGD ("update-dns: plugin %s ignored (caching disabled)",
+				       plugin_name);
 				goto skip;
 			}
 			caching = TRUE;
 		}
 
-		build_plugin_config_lists (self, &vpn_configs, &dev_configs, &other_configs);
+		if (!global_config)
+			build_plugin_config_lists (self, &vpn_configs, &dev_configs, &other_configs);
 
-		nm_log_dbg (LOGD_DNS, "DNS: updating plugin %s", plugin_name);
+		_LOGD ("update-dns: updating plugin %s", plugin_name);
 		if (!nm_dns_plugin_update (plugin,
 		                           vpn_configs,
 		                           dev_configs,
 		                           other_configs,
+		                           global_config,
 		                           priv->hostname)) {
-			nm_log_warn (LOGD_DNS, "DNS: plugin %s update failed", plugin_name);
+			_LOGW ("update-dns: plugin %s update failed", plugin_name);
 
 			/* If the plugin failed to update, we shouldn't write out a local
 			 * caching DNS configuration to resolv.conf.
@@ -768,35 +959,50 @@ update_dns (NMDnsManager *self,
 		nameservers[0] = g_strdup ("127.0.0.1");
 	}
 
-#ifdef RESOLVCONF_PATH
-	success = dispatch_resolvconf (searches, nameservers, error);
-#endif
+	if (update) {
+		switch (priv->rc_manager) {
+		case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE:
+			result = update_resolv_conf (self, searches, nameservers, options, error, TRUE);
+			resolv_conf_updated = TRUE;
+			break;
+		case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF:
+			result = dispatch_resolvconf (self, searches, nameservers, options, error);
+			break;
+		case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG:
+			result = dispatch_netconfig (self, searches, nameservers, nis_domain,
+			                             nis_servers, error);
+			break;
+		default:
+			g_assert_not_reached ();
+		}
 
-#ifdef NETCONFIG_PATH
-	if (success == FALSE) {
-		g_clear_error (error);
-		success = dispatch_netconfig (searches, nameservers,
-		                              nis_domain, nis_servers, error);
+		if (result == SR_NOTFOUND) {
+			_LOGD ("update-dns: program not available, writing to resolv.conf");
+			g_clear_error (error);
+			result = update_resolv_conf (self, searches, nameservers, options, error, TRUE);
+			resolv_conf_updated = TRUE;
+		}
 	}
-#endif
 
-	if (success == FALSE) {
-		g_clear_error (error);
-		success = update_resolv_conf (searches, nameservers, error);
-	}
+	/* Unless we've already done it, update private resolv.conf in NMRUNDIR
+	   ignoring any errors */
+	if (!resolv_conf_updated)
+		update_resolv_conf (self, searches, nameservers, options, NULL, FALSE);
 
 	/* signal that resolv.conf was changed */
-	if (success)
+	if (update && result == SR_SUCCESS)
 		g_signal_emit (self, signals[CONFIG_CHANGED], 0);
 
 	if (searches)
 		g_strfreev (searches);
+	if (options)
+		g_strfreev (options);
 	if (nameservers)
 		g_strfreev (nameservers);
 	if (nis_servers)
 		g_strfreev (nis_servers);
 
-	return success;
+	return !update || result == SR_SUCCESS;
 }
 
 static void
@@ -811,7 +1017,7 @@ plugin_failed (NMDnsPlugin *plugin, gpointer user_data)
 
 	/* Disable caching until the next DNS update */
 	if (!update_dns (self, TRUE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 }
@@ -822,18 +1028,18 @@ plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data)
 	NMDnsManager *self = NM_DNS_MANAGER (user_data);
 	GError *error = NULL;
 
-	nm_log_warn (LOGD_DNS, "DNS: plugin %s child quit unexpectedly; refreshing DNS",
+	_LOGW ("plugin %s child quit unexpectedly; refreshing DNS",
 	             nm_dns_plugin_get_name (plugin));
 
 	/* Let the plugin try to spawn the child again */
 	if (!update_dns (self, FALSE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 }
 
 gboolean
-nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
+nm_dns_manager_add_ip4_config (NMDnsManager *self,
                                const char *iface,
                                NMIP4Config *config,
                                NMDnsIPConfigType cfg_type)
@@ -841,10 +1047,10 @@ nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
 	NMDnsManagerPrivate *priv;
 	GError *error = NULL;
 
-	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (self != NULL, FALSE);
 	g_return_val_if_fail (config != NULL, FALSE);
 
-	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
 	g_object_set_data_full (G_OBJECT (config), IP_CONFIG_IFACE_TAG, g_strdup (iface), g_free);
 
@@ -863,8 +1069,8 @@ nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
 	if (!g_slist_find (priv->configs, config))
 		priv->configs = g_slist_append (priv->configs, g_object_ref (config));
 
-	if (!priv->updates_queue && !update_dns (mgr, FALSE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+	if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 
@@ -872,15 +1078,15 @@ nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
 }
 
 gboolean
-nm_dns_manager_remove_ip4_config (NMDnsManager *mgr, NMIP4Config *config)
+nm_dns_manager_remove_ip4_config (NMDnsManager *self, NMIP4Config *config)
 {
 	NMDnsManagerPrivate *priv;
 	GError *error = NULL;
 
-	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (self != NULL, FALSE);
 	g_return_val_if_fail (config != NULL, FALSE);
 
-	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
 	/* Can't remove it if it wasn't in the list to begin with */
 	if (!g_slist_find (priv->configs, config))
@@ -895,8 +1101,8 @@ nm_dns_manager_remove_ip4_config (NMDnsManager *mgr, NMIP4Config *config)
 
 	g_object_unref (config);
 
-	if (!priv->updates_queue && !update_dns (mgr, FALSE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+	if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 
@@ -906,7 +1112,7 @@ nm_dns_manager_remove_ip4_config (NMDnsManager *mgr, NMIP4Config *config)
 }
 
 gboolean
-nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
+nm_dns_manager_add_ip6_config (NMDnsManager *self,
                                const char *iface,
                                NMIP6Config *config,
                                NMDnsIPConfigType cfg_type)
@@ -914,10 +1120,10 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
 	NMDnsManagerPrivate *priv;
 	GError *error = NULL;
 
-	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (self != NULL, FALSE);
 	g_return_val_if_fail (config != NULL, FALSE);
 
-	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
 	g_object_set_data_full (G_OBJECT (config), IP_CONFIG_IFACE_TAG, g_strdup (iface), g_free);
 
@@ -936,8 +1142,8 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
 	if (!g_slist_find (priv->configs, config))
 		priv->configs = g_slist_append (priv->configs, g_object_ref (config));
 
-	if (!priv->updates_queue && !update_dns (mgr, FALSE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+	if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 
@@ -945,15 +1151,15 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
 }
 
 gboolean
-nm_dns_manager_remove_ip6_config (NMDnsManager *mgr, NMIP6Config *config)
+nm_dns_manager_remove_ip6_config (NMDnsManager *self, NMIP6Config *config)
 {
 	NMDnsManagerPrivate *priv;
 	GError *error = NULL;
 
-	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (self != NULL, FALSE);
 	g_return_val_if_fail (config != NULL, FALSE);
 
-	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
 	/* Can't remove it if it wasn't in the list to begin with */
 	if (!g_slist_find (priv->configs, config))
@@ -966,10 +1172,10 @@ nm_dns_manager_remove_ip6_config (NMDnsManager *mgr, NMIP6Config *config)
 	if (config == priv->ip6_device_config)
 		priv->ip6_device_config = NULL;
 
-	g_object_unref (config);	
+	g_object_unref (config);
 
-	if (!priv->updates_queue && !update_dns (mgr, FALSE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+	if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 
@@ -979,19 +1185,19 @@ nm_dns_manager_remove_ip6_config (NMDnsManager *mgr, NMIP6Config *config)
 }
 
 void
-nm_dns_manager_set_initial_hostname (NMDnsManager *mgr,
+nm_dns_manager_set_initial_hostname (NMDnsManager *self,
                                      const char *hostname)
 {
-	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
 	priv->hostname = g_strdup (hostname);
 }
 
 void
-nm_dns_manager_set_hostname (NMDnsManager *mgr,
+nm_dns_manager_set_hostname (NMDnsManager *self,
                              const char *hostname)
 {
-	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	const char *filtered = NULL;
 
@@ -1010,25 +1216,25 @@ nm_dns_manager_set_hostname (NMDnsManager *mgr,
 	g_free (priv->hostname);
 	priv->hostname = g_strdup (filtered);
 
-	if (!priv->updates_queue && !update_dns (mgr, FALSE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+	if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 }
 
 NMDnsManagerResolvConfMode
-nm_dns_manager_get_resolv_conf_mode (NMDnsManager *mgr)
+nm_dns_manager_get_resolv_conf_mode (NMDnsManager *self)
 {
-	return NM_DNS_MANAGER_GET_PRIVATE (mgr)->resolv_conf_mode;
+	return NM_DNS_MANAGER_GET_PRIVATE (self)->resolv_conf_mode;
 }
 
 void
-nm_dns_manager_begin_updates (NMDnsManager *mgr, const char *func)
+nm_dns_manager_begin_updates (NMDnsManager *self, const char *func)
 {
 	NMDnsManagerPrivate *priv;
 
-	g_return_if_fail (mgr != NULL);
-	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	g_return_if_fail (self != NULL);
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
 	/* Save current hash when starting a new batch */
 	if (priv->updates_queue == 0)
@@ -1036,36 +1242,36 @@ nm_dns_manager_begin_updates (NMDnsManager *mgr, const char *func)
 
 	priv->updates_queue++;
 
-	nm_log_dbg (LOGD_DNS, "(%s): queueing DNS updates (%d)", func, priv->updates_queue);
+	_LOGD ("(%s): queueing DNS updates (%d)", func, priv->updates_queue);
 }
 
 void
-nm_dns_manager_end_updates (NMDnsManager *mgr, const char *func)
+nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
 {
 	NMDnsManagerPrivate *priv;
 	GError *error = NULL;
 	gboolean changed;
 	guint8 new[HASH_LEN];
 
-	g_return_if_fail (mgr != NULL);
+	g_return_if_fail (self != NULL);
 
-	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	g_return_if_fail (priv->updates_queue > 0);
 
-	compute_hash (mgr, new);
+	compute_hash (self, nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)), new);
 	changed = (memcmp (new, priv->prev_hash, sizeof (new)) != 0) ? TRUE : FALSE;
-	nm_log_dbg (LOGD_DNS, "(%s): DNS configuration %s", __func__, changed ? "changed" : "did not change");
+	_LOGD ("(%s): DNS configuration %s", func, changed ? "changed" : "did not change");
 
 	priv->updates_queue--;
 	if ((priv->updates_queue > 0) || (changed == FALSE)) {
-		nm_log_dbg (LOGD_DNS, "(%s): no DNS changes to commit (%d)", func, priv->updates_queue);
+		_LOGD ("(%s): no DNS changes to commit (%d)", func, priv->updates_queue);
 		return;
 	}
 
 	/* Commit all the outstanding changes */
-	nm_log_dbg (LOGD_DNS, "(%s): committing DNS changes (%d)", func, priv->updates_queue);
-	if (!update_dns (mgr, FALSE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+	_LOGD ("(%s): committing DNS changes (%d)", func, priv->updates_queue);
+	if (!update_dns (self, FALSE, &error)) {
+		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
 
@@ -1085,6 +1291,12 @@ init_resolv_conf_mode (NMDnsManager *self)
 
 	g_clear_object (&priv->plugin);
 
+	mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config));
+	if (!g_strcmp0 (mode, "none")) {
+		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
+		goto out;
+	}
+
 	fd = open (_PATH_RESCONF, O_RDONLY);
 	if (fd != -1) {
 		if (ioctl (fd, FS_IOC_GETFLAGS, &flags) == -1)
@@ -1092,17 +1304,13 @@ init_resolv_conf_mode (NMDnsManager *self)
 		close (fd);
 
 		if (flags & FS_IMMUTABLE_FL) {
-			nm_log_info (LOGD_DNS, "DNS: " _PATH_RESCONF " is immutable; not managing");
+			_LOGI ("set resolv-conf-mode: none -- " _PATH_RESCONF " is immutable");
 			priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
 			return;
 		}
 	}
 
-	mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config));
-	if (!g_strcmp0 (mode, "none")) {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
-		nm_log_info (LOGD_DNS, "DNS: not managing " _PATH_RESCONF);
-	} else if (!g_strcmp0 (mode, "dnsmasq")) {
+	if (!g_strcmp0 (mode, "dnsmasq")) {
 		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY;
 		priv->plugin = nm_dns_dnsmasq_new ();
 	} else if (!g_strcmp0 (mode, "unbound")) {
@@ -1110,15 +1318,61 @@ init_resolv_conf_mode (NMDnsManager *self)
 		priv->plugin = nm_dns_unbound_new ();
 	} else {
 		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT;
-		if (mode && g_strcmp0 (mode, "default") != 0)
-			nm_log_warn (LOGD_DNS, "Unknown DNS mode '%s'", mode);
+		if (mode && g_strcmp0 (mode, "default") != 0) {
+			_LOGW ("set resolve-conf-mode: default -- unknown configuration '%s'", mode);
+			return;
+		}
+		mode = "default";
 	}
 
 	if (priv->plugin) {
-		nm_log_info (LOGD_DNS, "DNS: loaded plugin %s", nm_dns_plugin_get_name (priv->plugin));
 		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self);
 		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self);
 	}
+
+out:
+	_LOGI ("set resolv-conf-mode: %s%s%s%s", mode,
+	       NM_PRINT_FMT_QUOTED (priv->plugin, ", plugin=\"", nm_dns_plugin_get_name (priv->plugin), "\"", ""));
+}
+
+static void
+init_resolv_conf_manager (NMDnsManager *self)
+{
+	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+	const char *man, *desc = "";
+
+	man = nm_config_data_get_rc_manager (nm_config_get_data (priv->config));
+	if (!g_strcmp0 (man, "none"))
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE;
+	else if (!g_strcmp0 (man, "resolvconf"))
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF;
+	else if (!g_strcmp0 (man, "netconfig"))
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG;
+	else {
+#if defined(RESOLVCONF_SELECTED)
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF;
+#elif defined(NETCONFIG_SELECTED)
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG;
+#else
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE;
+#endif
+		if (man)
+			_LOGW ("unknown resolv.conf manager '%s'", man);
+	}
+
+	switch (priv->rc_manager) {
+	case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF:
+		desc = "resolvconf";
+		break;
+	case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG:
+		desc = "netconfig";
+		break;
+	case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE:
+		desc = "none";
+		break;
+	}
+
+	_LOGI ("using resolv.conf manager '%s'", desc);
 }
 
 static void
@@ -1132,12 +1386,16 @@ config_changed_cb (NMConfig *config,
 
 	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_DNS_MODE))
 		init_resolv_conf_mode (self);
+	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_RC_MANAGER))
+		init_resolv_conf_manager (self);
 
 	if (NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_SIGHUP |
 	                           NM_CONFIG_CHANGE_SIGUSR1 |
-	                           NM_CONFIG_CHANGE_DNS_MODE)) {
+	                           NM_CONFIG_CHANGE_DNS_MODE |
+	                           NM_CONFIG_CHANGE_RC_MANAGER |
+	                           NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) {
 		if (!update_dns (self, TRUE, &error)) {
-			nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message);
+			_LOGW ("could not commit DNS changes: %s", error->message);
 			g_clear_error (&error);
 		}
 	}
@@ -1148,15 +1406,19 @@ nm_dns_manager_init (NMDnsManager *self)
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
-	/* Set the initial hash */
-	compute_hash (self, NM_DNS_MANAGER_GET_PRIVATE (self)->hash);
+	_LOGT ("creating...");
 
 	priv->config = g_object_ref (nm_config_get ());
+	/* Set the initial hash */
+	compute_hash (self, nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)),
+	              NM_DNS_MANAGER_GET_PRIVATE (self)->hash);
+
 	g_signal_connect (G_OBJECT (priv->config),
 	                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
 	                  G_CALLBACK (config_changed_cb),
 	                  self);
 	init_resolv_conf_mode (self);
+	init_resolv_conf_manager (self);
 }
 
 static void
@@ -1166,6 +1428,8 @@ dispose (GObject *object)
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 
+	_LOGT ("disposing");
+
 	if (priv->plugin) {
 		g_signal_handlers_disconnect_by_func (priv->plugin, plugin_failed, self);
 		g_signal_handlers_disconnect_by_func (priv->plugin, plugin_child_quit, self);
@@ -1178,7 +1442,7 @@ dispose (GObject *object)
 	 * DNS updates yet, there's no reason to touch resolv.conf on shutdown.
 	 */
 	if (priv->dns_touched && !update_dns (self, TRUE, &error)) {
-		nm_log_warn (LOGD_DNS, "could not commit DNS changes on shutdown: %s", error->message);
+		_LOGW ("could not commit DNS changes on shutdown: %s", error->message);
 		g_clear_error (&error);
 		priv->dns_touched = FALSE;
 	}
diff --git a/src/dns-manager/nm-dns-manager.h b/src/dns-manager/nm-dns-manager.h
index bc374fcf..7a55f1a2 100644
--- a/src/dns-manager/nm-dns-manager.h
+++ b/src/dns-manager/nm-dns-manager.h
@@ -24,7 +24,7 @@
 #ifndef __NETWORKMANAGER_DNS_MANAGER_H__
 #define __NETWORKMANAGER_DNS_MANAGER_H__
 
-#include <glib-object.h>
+#include "nm-default.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 
@@ -41,7 +41,7 @@ G_BEGIN_DECLS
 #define NM_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass))
 #define NM_IS_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_DNS_MANAGER))
 #define NM_IS_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_DNS_MANAGER))
-#define NM_DNS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) 
+#define NM_DNS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DNS_MANAGER, NMDnsManagerClass))
 
 typedef struct {
 	GObject parent;
@@ -59,26 +59,26 @@ GType nm_dns_manager_get_type (void);
 NMDnsManager * nm_dns_manager_get (void);
 
 /* Allow changes to be batched together */
-void nm_dns_manager_begin_updates (NMDnsManager *mgr, const char *func);
-void nm_dns_manager_end_updates (NMDnsManager *mgr, const char *func);
+void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func);
+void nm_dns_manager_end_updates (NMDnsManager *self, const char *func);
 
-gboolean nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
+gboolean nm_dns_manager_add_ip4_config (NMDnsManager *self,
                                         const char *iface,
                                         NMIP4Config *config,
                                         NMDnsIPConfigType cfg_type);
 
-gboolean nm_dns_manager_remove_ip4_config (NMDnsManager *mgr, NMIP4Config *config);
+gboolean nm_dns_manager_remove_ip4_config (NMDnsManager *self, NMIP4Config *config);
 
-gboolean nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
+gboolean nm_dns_manager_add_ip6_config (NMDnsManager *self,
                                         const char *iface,
                                         NMIP6Config *config,
                                         NMDnsIPConfigType cfg_type);
 
-gboolean nm_dns_manager_remove_ip6_config (NMDnsManager *mgr, NMIP6Config *config);
+gboolean nm_dns_manager_remove_ip6_config (NMDnsManager *self, NMIP6Config *config);
 
-void nm_dns_manager_set_initial_hostname (NMDnsManager *mgr,
+void nm_dns_manager_set_initial_hostname (NMDnsManager *self,
                                           const char *hostname);
-void nm_dns_manager_set_hostname         (NMDnsManager *mgr,
+void nm_dns_manager_set_hostname         (NMDnsManager *self,
                                           const char *hostname);
 
 /**
@@ -99,7 +99,23 @@ typedef enum {
 	NM_DNS_MANAGER_RESOLV_CONF_PROXY
 } NMDnsManagerResolvConfMode;
 
-NMDnsManagerResolvConfMode nm_dns_manager_get_resolv_conf_mode (NMDnsManager *mgr);
+/**
+ * NMDnsManagerResolvConfManager
+ * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: NM directly writes resolv.conf
+ * @NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: NM is managing resolv.conf
+     through resolvconf
+ * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: NM is managing resolv.conf
+     through netconfig
+ *
+ * NMDnsManager's management of resolv.conf
+ */
+typedef enum {
+	NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE,
+	NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF,
+	NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG,
+} NMDnsManagerResolvConfManager;
+
+NMDnsManagerResolvConfMode nm_dns_manager_get_resolv_conf_mode (NMDnsManager *self);
 
 G_END_DECLS
 
diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c
index 5b95d1c5..a8236696 100644
--- a/src/dns-manager/nm-dns-plugin.c
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -24,10 +24,9 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <glib.h>
 
+#include "nm-default.h"
 #include "nm-dns-plugin.h"
-#include "nm-logging.h"
 #include "NetworkManagerUtils.h"
 
 typedef struct {
@@ -57,6 +56,7 @@ nm_dns_plugin_update (NMDnsPlugin *self,
                       const GSList *vpn_configs,
                       const GSList *dev_configs,
                       const GSList *other_configs,
+                      const NMGlobalDnsConfig *global_config,
                       const char *hostname)
 {
 	g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE);
@@ -65,6 +65,7 @@ nm_dns_plugin_update (NMDnsPlugin *self,
 	                                               vpn_configs,
 	                                               dev_configs,
 	                                               other_configs,
+	                                               global_config,
 	                                               hostname);
 }
 
@@ -188,10 +189,7 @@ nm_dns_plugin_child_kill (NMDnsPlugin *self)
 {
 	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
 
-	if (priv->watch_id) {
-		g_source_remove (priv->watch_id);
-		priv->watch_id = 0;
-	}
+	nm_clear_g_source (&priv->watch_id);
 
 	if (priv->pid) {
 		nm_utils_kill_child_sync (priv->pid, SIGTERM, LOGD_DNS, priv->progname, NULL, 1000, 0);
diff --git a/src/dns-manager/nm-dns-plugin.h b/src/dns-manager/nm-dns-plugin.h
index ee735a5a..7ecaa424 100644
--- a/src/dns-manager/nm-dns-plugin.h
+++ b/src/dns-manager/nm-dns-plugin.h
@@ -19,8 +19,9 @@
 #ifndef __NETWORKMANAGER_DNS_PLUGIN_H__
 #define __NETWORKMANAGER_DNS_PLUGIN_H__
 
-#include <glib.h>
-#include <glib-object.h>
+#include "nm-default.h"
+
+#include "nm-config-data.h"
 
 #define NM_TYPE_DNS_PLUGIN            (nm_dns_plugin_get_type ())
 #define NM_DNS_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNS_PLUGIN, NMDnsPlugin))
@@ -47,13 +48,15 @@ typedef struct {
 	 * NMIP4Config or NMIP6Config objects from VPN connections, while
 	 * 'dev_configs' is a list of NMPI4Config or NMIP6Config objects from
 	 * active devices.  'other_configs' represent other IP configuration that
-	 * may be in-use.  Configs of the same IP version are sorted in priority
+	 * may be in-use.  'global_config' is the optional global DNS
+	 * configuration.  Configs of the same IP version are sorted in priority
 	 * order.
 	 */
 	gboolean (*update) (NMDnsPlugin *self,
 	                    const GSList *vpn_configs,
 	                    const GSList *dev_configs,
 	                    const GSList *other_configs,
+	                    const NMGlobalDnsConfig *global_config,
 	                    const char *hostname);
 
 	/* Subclasses should override and return TRUE if they start a local
@@ -92,6 +95,7 @@ gboolean nm_dns_plugin_update (NMDnsPlugin *self,
                                const GSList *vpn_configs,
                                const GSList *dev_configs,
                                const GSList *other_configs,
+                               const NMGlobalDnsConfig *global_config,
                                const char *hostname);
 
 /* For subclasses/plugins */
diff --git a/src/dns-manager/nm-dns-unbound.c b/src/dns-manager/nm-dns-unbound.c
index 5723bf82..d36e3f85 100644
--- a/src/dns-manager/nm-dns-unbound.c
+++ b/src/dns-manager/nm-dns-unbound.c
@@ -31,6 +31,7 @@ update (NMDnsPlugin *plugin,
         const GSList *vpn_configs,
         const GSList *dev_configs,
         const GSList *other_configs,
+        const NMGlobalDnsConfig *global_config,
         const char *hostname)
 {
 	/* TODO: We currently call a script installed with the dnssec-trigger
@@ -42,7 +43,7 @@ update (NMDnsPlugin *plugin,
 	 * without calling custom scripts. The dnssec-trigger functionality
 	 * may be eventually merged into NetworkManager.
 	 */
-	return nm_spawn_process ("/usr/libexec/dnssec-trigger-script --async --update", NULL) == 0;
+	return nm_spawn_process (DNSSEC_TRIGGER_SCRIPT " --async --update", NULL) == 0;
 }
 
 static gboolean
diff --git a/src/dns-manager/nm-dns-unbound.h b/src/dns-manager/nm-dns-unbound.h
index 261326b2..abc056b9 100644
--- a/src/dns-manager/nm-dns-unbound.h
+++ b/src/dns-manager/nm-dns-unbound.h
@@ -18,8 +18,6 @@
 #ifndef __NETWORKMANAGER_DNS_UNBOUND_H__
 #define __NETWORKMANAGER_DNS_UNBOUND_H__
 
-#include <glib-object.h>
-
 #include "nm-dns-plugin.h"
 
 #define NM_TYPE_DNS_UNBOUND            (nm_dns_unbound_get_type ())