summary refs log tree commit diff
path: root/src/dns-manager
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns-manager')
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.c15
-rw-r--r--src/dns-manager/nm-dns-manager.c224
-rw-r--r--src/dns-manager/nm-dns-plugin.c6
-rw-r--r--src/dns-manager/nm-dns-unbound.c2
-rw-r--r--src/dns-manager/nm-dns-utils.c2
5 files changed, 143 insertions, 106 deletions
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c
index 63e37bf3..8663dfd8 100644
--- a/src/dns-manager/nm-dns-dnsmasq.c
+++ b/src/dns-manager/nm-dns-dnsmasq.c
@@ -18,7 +18,7 @@
  *
  */
 
-#include "config.h"
+#include "nm-default.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -27,7 +27,6 @@
 #include <arpa/inet.h>
 #include <sys/stat.h>
 
-#include "nm-default.h"
 #include "nm-dns-dnsmasq.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
@@ -145,15 +144,15 @@ add_global_config (GString *str, const NMGlobalDnsConfig *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);
+		const char *name = nm_global_dns_domain_get_name (domain);
+
+		g_return_if_fail (name);
 
 		for (j = 0; servers && servers[j]; j++) {
-			if (!strcmp (servers[j], "*"))
+			if (!strcmp (name, "*"))
 				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]);
-			}
+			else
+				g_string_append_printf (str, "server=/%s/%s\n", name, servers[j]);
 		}
 
 	}
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index 01e8bf1d..79d345be 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -21,7 +21,7 @@
  *   and others
  */
 
-#include "config.h"
+#include "nm-default.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -35,7 +35,6 @@
 
 #include <linux/fs.h>
 
-#include "nm-default.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "nm-dns-manager.h"
@@ -82,6 +81,10 @@ G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, G_TYPE_OBJECT)
 #define NETCONFIG_PATH "/sbin/netconfig"
 #endif
 
+#define PLUGIN_RATELIMIT_INTERVAL    30
+#define PLUGIN_RATELIMIT_BURST       5
+#define PLUGIN_RATELIMIT_DELAY       300
+
 NM_DEFINE_SINGLETON_INSTANCE (NMDnsManager);
 
 /*********************************************************************************************/
@@ -130,6 +133,12 @@ typedef struct {
 	NMConfig *config;
 
 	gboolean dns_touched;
+
+	struct {
+		guint64 ts;
+		guint num_restarts;
+		guint timer;
+	} plugin_ratelimit;
 } NMDnsManagerPrivate;
 
 enum {
@@ -357,7 +366,6 @@ dispatch_netconfig (NMDnsManager *self,
 
 	if (searches) {
 		str = g_strjoinv (" ", searches);
-
 		write_to_netconfig (self, fd, "DNSSEARCH", str);
 		g_free (str);
 	}
@@ -405,10 +413,9 @@ write_resolv_conf (FILE *f,
                    char **options,
                    GError **error)
 {
-	char *searches_str = NULL;
-	char *nameservers_str = NULL;
-	char *options_str = NULL;
-	gboolean retval = FALSE;
+	gs_free char *searches_str = NULL;
+	gs_free char *nameservers_str = NULL;
+	gs_free char *options_str = NULL;
 	char *tmp_str;
 	GString *str;
 	int i;
@@ -425,11 +432,10 @@ write_resolv_conf (FILE *f,
 		g_free (tmp_str);
 	}
 
-	str = g_string_new ("");
-
 	if (nameservers) {
 		int num = g_strv_length (nameservers);
 
+		str = g_string_new ("");
 		for (i = 0; i < num; i++) {
 			if (i == 3) {
 				g_string_append (str, "# ");
@@ -443,28 +449,22 @@ write_resolv_conf (FILE *f,
 			g_string_append (str, nameservers[i]);
 			g_string_append_c (str, '\n');
 		}
+		nameservers_str = g_string_free (str, FALSE);
 	}
 
-	nameservers_str = g_string_free (str, FALSE);
-
 	if (fprintf (f, "# Generated by NetworkManager\n%s%s%s",
 	             searches_str ? searches_str : "",
-	             nameservers_str,
-	             options_str ? options_str : "") > 0)
-		retval = TRUE;
-	else {
+	             nameservers_str ? nameservers_str : "",
+	             options_str ? options_str : "") < 0) {
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
-		             "Could not write " _PATH_RESCONF ": %s\n",
+		             "Could not write " _PATH_RESCONF ": %s",
 		             g_strerror (errno));
+		return FALSE;
 	}
 
-	g_free (searches_str);
-	g_free (nameservers_str);
-	g_free (options_str);
-
-	return retval;
+	return TRUE;
 }
 
 static SpawnResult
@@ -474,9 +474,9 @@ dispatch_resolvconf (NMDnsManager *self,
                      char **options,
                      GError **error)
 {
-	char *cmd;
+	gs_free char *cmd = NULL;
 	FILE *f;
-	gboolean retval = FALSE;
+	gboolean success = FALSE;
 	int errnosv, err;
 
 	if (!g_file_test (RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE)) {
@@ -487,39 +487,46 @@ dispatch_resolvconf (NMDnsManager *self,
 		return SR_NOTFOUND;
 	}
 
-	if (searches || nameservers) {
-		cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
-		_LOGI ("Writing DNS information to %s", RESOLVCONF_PATH);
-		if ((f = popen (cmd, "w")) == NULL)
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_FAILED,
-			             "Could not write to %s: %s\n",
-			             RESOLVCONF_PATH,
-			             g_strerror (errno));
-		else {
-			retval = write_resolv_conf (f, searches, nameservers, options, error);
-			err = pclose (f);
-			if (err < 0) {
-				errnosv = errno;
-				g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errnosv),
-				             "Failed to close pipe to resolvconf: %d", errnosv);
-				retval = FALSE;
-			} else if (err > 0) {
-				_LOGW ("resolvconf failed with status %d", err);
-				retval = FALSE;
-			}
-		}
-	} else {
-		cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
+	if (!searches && !nameservers) {
 		_LOGI ("Removing DNS information from %s", RESOLVCONF_PATH);
-		if (nm_spawn_process (cmd, error) == 0)
-			retval = TRUE;
+
+		cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
+		if (nm_spawn_process (cmd, error) != 0)
+			return SR_ERROR;
+
+		return SR_SUCCESS;
+	}
+
+	_LOGI ("Writing DNS information to %s", RESOLVCONF_PATH);
+
+	cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
+	if ((f = popen (cmd, "w")) == NULL) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "Could not write to %s: %s",
+		             RESOLVCONF_PATH,
+		             g_strerror (errno));
+		return SR_ERROR;
 	}
 
-	g_free (cmd);
+	success = write_resolv_conf (f, searches, nameservers, options, error);
+	err = pclose (f);
+	if (err < 0) {
+		errnosv = errno;
+		g_clear_error (error);
+		g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errnosv),
+		             "Failed to close pipe to resolvconf: %d", errnosv);
+		return SR_ERROR;
+	} else if (err > 0) {
+		_LOGW ("resolvconf failed with status %d", err);
+		g_clear_error (error);
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+		             "resolvconf failed with status %d", err);
+		return SR_ERROR;
+	}
 
-	return retval ? SR_SUCCESS : SR_ERROR;
+	return success ? SR_SUCCESS : SR_ERROR;
 }
 
 #define MY_RESOLV_CONF NMRUNDIR "/resolv.conf"
@@ -536,7 +543,7 @@ update_resolv_conf (NMDnsManager *self,
 {
 	FILE *f;
 	struct stat st;
-	gboolean ret;
+	gboolean success;
 
 	/* If we are not managing /etc/resolv.conf and it points to
 	 * MY_RESOLV_CONF, don't write the private DNS configuration to
@@ -544,15 +551,12 @@ update_resolv_conf (NMDnsManager *self,
 	 * some external application.
 	 */
 	if (!install_etc) {
-		char *path = g_file_read_link (_PATH_RESCONF, NULL);
-		gboolean ours = !g_strcmp0 (path, MY_RESOLV_CONF);
+		gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
 
-		g_free (path);
-
-		if (ours) {
+		if (g_strcmp0 (path, MY_RESOLV_CONF) == 0) {
 			_LOGD ("not updating " MY_RESOLV_CONF
 			       " since it points to " _PATH_RESCONF);
-			return SR_ERROR;
+			return SR_SUCCESS;
 		}
 	}
 
@@ -560,36 +564,35 @@ update_resolv_conf (NMDnsManager *self,
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
-		             "Could not open %s: %s\n",
+		             "Could not open %s: %s",
 		             MY_RESOLV_CONF_TMP,
 		             g_strerror (errno));
 		return SR_ERROR;
 	}
 
-	ret = write_resolv_conf (f, searches, nameservers, options, error);
+	success = write_resolv_conf (f, searches, nameservers, options, error);
 
 	if (fclose (f) < 0) {
-		if (ret) {
+		if (success) {
 			/* only set an error here if write_resolv_conf() was successful,
 			 * since its error is more important.
 			 */
 			g_set_error (error,
 			             NM_MANAGER_ERROR,
 			             NM_MANAGER_ERROR_FAILED,
-			             "Could not close %s: %s\n",
+			             "Could not close %s: %s",
 			             MY_RESOLV_CONF_TMP,
 			             g_strerror (errno));
 		}
-	}
-
-	if (!ret)
+		return SR_ERROR;
+	} else if (!success)
 		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",
+		             "Could not replace %s: %s",
 		             MY_RESOLV_CONF,
 		             g_strerror (errno));
 		return SR_ERROR;
@@ -598,44 +601,50 @@ update_resolv_conf (NMDnsManager *self,
 	if (!install_etc)
 		return SR_SUCCESS;
 
-	/* Don't overwrite a symbolic link unless it points to MY_RESOLV_CONF. */
+	/* A symlink pointing to NM's own resolv.conf (MY_RESOLV_CONF) is always
+	 * overwritten to ensure that changes are indicated with inotify.  Symlinks
+	 * pointing to any other file are never overwritten.
+	 */
 	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;
+				gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
 
-				g_free (path);
-				if (not_ours)
+				if (g_strcmp0 (path, MY_RESOLV_CONF) != 0) {
+					/* It's not NM's symlink; do nothing */
 					return SR_SUCCESS;
+				}
+
+				/* resolv.conf is a symlink owned by NM and the target is accessible
+				 */
 			} 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;
+				/* resolv.conf is a symlink but the target is not accessible;
+				 * some other program is probably managing resolv.conf and
+				 * NM should not touch it.
+				 */
+				return SR_SUCCESS;
 			}
 		}
 	} else if (errno != ENOENT) {
+		/* NM cannot read /etc/resolv.conf */
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
-		             "Could not lstat %s: %s\n",
+		             "Could not lstat %s: %s",
 		             _PATH_RESCONF,
 		             g_strerror (errno));
 		return SR_ERROR;
 	}
 
+	/* By this point, either /etc/resolv.conf does not exist, is a regular
+	 * file, or is a symlink already owned by NM.  In all cases /etc/resolv.conf
+	 * is replaced with a symlink pointing to NM's resolv.conf in /var/run/.
+	 */
 	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",
+		             "Could not unlink %s: %s",
 		             RESOLV_CONF_TMP,
 		             g_strerror (errno));
 		return SR_ERROR;
@@ -645,7 +654,7 @@ update_resolv_conf (NMDnsManager *self,
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
-		             "Could not create symlink %s pointing to %s: %s\n",
+		             "Could not create symlink %s pointing to %s: %s",
 		             RESOLV_CONF_TMP,
 		             MY_RESOLV_CONF,
 		             g_strerror (errno));
@@ -656,7 +665,7 @@ update_resolv_conf (NMDnsManager *self,
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
-		             "Could not rename %s to %s: %s\n",
+		             "Could not rename %s to %s: %s",
 		             RESOLV_CONF_TMP,
 		             _PATH_RESCONF,
 		             g_strerror (errno));
@@ -799,6 +808,7 @@ update_dns (NMDnsManager *self,
 	g_return_val_if_fail (!error || !*error, FALSE);
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+	nm_clear_g_source (&priv->plugin_ratelimit.timer);
 
 	if (priv->resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED) {
 		update = FALSE;
@@ -863,7 +873,8 @@ update_dns (NMDnsManager *self,
 	if (priv->hostname) {
 		const char *hostdomain = strchr (priv->hostname, '.');
 
-		if (hostdomain) {
+		if (   hostdomain
+		    && !nm_utils_ipaddr_valid (AF_UNSPEC, priv->hostname)) {
 			hostdomain++;
 			if (DOMAIN_IS_VALID (hostdomain))
 				add_string_item (rc.searches, hostdomain);
@@ -1022,20 +1033,47 @@ plugin_failed (NMDnsPlugin *plugin, gpointer user_data)
 	}
 }
 
-static void
-plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data)
+static gboolean
+plugin_child_quit_update_dns (gpointer user_data)
 {
-	NMDnsManager *self = NM_DNS_MANAGER (user_data);
 	GError *error = NULL;
-
-	_LOGW ("plugin %s child quit unexpectedly; refreshing DNS",
-	             nm_dns_plugin_get_name (plugin));
+	NMDnsManager *self = NM_DNS_MANAGER (user_data);
 
 	/* Let the plugin try to spawn the child again */
 	if (!update_dns (self, FALSE, &error)) {
 		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data)
+{
+	NMDnsManager *self = NM_DNS_MANAGER (user_data);
+	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+	gint64 ts = nm_utils_get_monotonic_timestamp_ms ();
+
+	_LOGW ("plugin %s child quit unexpectedly", nm_dns_plugin_get_name (plugin));
+
+	if (   !priv->plugin_ratelimit.ts
+	    || (ts - priv->plugin_ratelimit.ts) / 1000 > PLUGIN_RATELIMIT_INTERVAL) {
+		priv->plugin_ratelimit.ts = ts;
+		priv->plugin_ratelimit.num_restarts = 0;
+	} else {
+		priv->plugin_ratelimit.num_restarts++;
+		if (priv->plugin_ratelimit.num_restarts > PLUGIN_RATELIMIT_BURST) {
+			_LOGW ("plugin %s child respawning too fast, delaying update for %u seconds",
+			        nm_dns_plugin_get_name (plugin), PLUGIN_RATELIMIT_DELAY);
+			priv->plugin_ratelimit.timer = g_timeout_add_seconds (PLUGIN_RATELIMIT_DELAY,
+			                                                      plugin_child_quit_update_dns,
+			                                                      self);
+			return;
+		}
+	}
+
+	plugin_child_quit_update_dns (self);
 }
 
 gboolean
diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c
index a8236696..8313ed13 100644
--- a/src/dns-manager/nm-dns-plugin.c
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -17,7 +17,7 @@
  *
  */
 
-#include "config.h"
+#include "nm-default.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -25,7 +25,6 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
-#include "nm-default.h"
 #include "nm-dns-plugin.h"
 #include "NetworkManagerUtils.h"
 
@@ -33,7 +32,7 @@ typedef struct {
 	gboolean disposed;
 
 	GPid pid;
-	guint32 watch_id;
+	guint watch_id;
 	char *progname;
 	char *pidfile;
 } NMDnsPluginPrivate;
@@ -130,6 +129,7 @@ watch_cb (GPid pid, gint status, gpointer user_data)
 	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
 
 	priv->pid = 0;
+	priv->watch_id = 0;
 	g_free (priv->progname);
 	priv->progname = NULL;
 
diff --git a/src/dns-manager/nm-dns-unbound.c b/src/dns-manager/nm-dns-unbound.c
index d36e3f85..4c1af103 100644
--- a/src/dns-manager/nm-dns-unbound.c
+++ b/src/dns-manager/nm-dns-unbound.c
@@ -17,7 +17,7 @@
  * Copyright (C) 2014 Red Hat, Inc.
  * Author: Pavel Šimerda <psimerda@redhat.com>
  */
-#include "config.h"
+#include "nm-default.h"
 
 #include "nm-dns-unbound.h"
 #include "NetworkManagerUtils.h"
diff --git a/src/dns-manager/nm-dns-utils.c b/src/dns-manager/nm-dns-utils.c
index 4a5255a4..e920611a 100644
--- a/src/dns-manager/nm-dns-utils.c
+++ b/src/dns-manager/nm-dns-utils.c
@@ -17,7 +17,7 @@
  *
  */
 
-#include "config.h"
+#include "nm-default.h"
 
 #include <arpa/inet.h>
 #include <string.h>