diff options
author | Dan Williams <dcbw@redhat.com> | 2010-09-12 22:25:30 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2010-09-22 16:24:28 -0500 |
commit | 182831ca1261aee8c33c79c38a69ad333912ae2c (patch) | |
tree | 0a3f76dfd65773009082233c5f369ca6f47ca871 /src | |
parent | a7fe393d94e12ae071bbe55d242b61d0b97d469e (diff) |
dns: hook plugins into DNS updates and make dnsmasq plugin actually work
Diffstat (limited to 'src')
-rw-r--r-- | src/dns-manager/nm-dns-dnsmasq.c | 123 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-manager.c | 195 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-plugin.c | 19 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-plugin.h | 9 |
4 files changed, 213 insertions, 133 deletions
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c index 5fd6fe6570..a3bf516429 100644 --- a/src/dns-manager/nm-dns-dnsmasq.c +++ b/src/dns-manager/nm-dns-dnsmasq.c @@ -41,83 +41,11 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) #define CONFFILE LOCALSTATEDIR "/run/nm-dns-dnsmasq.conf" typedef struct { - int pid; + guint32 foo; } NMDnsDnsmasqPrivate; /*******************************************/ -#if 0 -static NMCmdLine * -create_dm_cmd_line (const char *iface, - const char *pidfile, - GError **error) -{ - const char *dm_binary; - GString *conf; - NMIP4Address *tmp; - struct in_addr addr; - char buf[INET_ADDRSTRLEN + 15]; - char localaddr[INET_ADDRSTRLEN + 1]; - int i; - - dm_binary = nm_find_dnsmasq (); - if (!dm_binary) { - nm_log_warn (LOGD_DNS, "could not find dnsmasq binary."); - return NULL; - } - - /* Create dnsmasq command line */ - cmd = nm_cmd_line_new (); - nm_cmd_line_add_string (cmd, dm_binary); - - if (getenv ("NM_DNSMASQ_DEBUG")) - nm_cmd_line_add_string (cmd, "--log-queries"); - - /* dnsmasq may read from it's default config file location, which if that - * location is a valid config file, it will combine with the options here - * and cause undesirable side-effects. Like sending bogus IP addresses - * as the gateway or whatever. So give dnsmasq a bogus config file - * location to avoid screwing up the configuration we're passing to it. - */ - memset (buf, 0, sizeof (buf)); - strcpy (buf, "/tmp/"); - for (i = 5; i < 15; i++) - buf[i] = (char) (g_random_int_range ((guint32) 'a', (guint32) 'z') & 0xFF); - strcat (buf, ".conf"); - - nm_cmd_line_add_string (cmd, "--conf-file"); - nm_cmd_line_add_string (cmd, buf); - - nm_cmd_line_add_string (cmd, "--no-hosts"); - nm_cmd_line_add_string (cmd, "--keep-in-foreground"); - nm_cmd_line_add_string (cmd, "--bind-interfaces"); - nm_cmd_line_add_string (cmd, "--except-interface=lo"); - nm_cmd_line_add_string (cmd, "--clear-on-reload"); - - /* Use strict order since in the case of VPN connections, the VPN's - * nameservers will be first in resolv.conf, and those need to be tried - * first by dnsmasq to successfully resolve names from the VPN. - */ - nm_cmd_line_add_string (cmd, "--strict-order"); - - s = g_string_new ("--listen-address="); - addr.s_addr = nm_ip4_address_get_address (tmp); - if (!inet_ntop (AF_INET, &addr, &localaddr[0], INET_ADDRSTRLEN)) { - nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X", - ntohl (addr.s_addr)); - goto error; - } - g_string_append (s, localaddr); - nm_cmd_line_add_string (cmd, s->str); - g_string_free (s, TRUE); - return cmd; - -error: - nm_cmd_line_destroy (cmd); - return NULL; -} -#endif - static inline const char * find_dnsmasq (void) { @@ -234,12 +162,19 @@ update (NMDnsPlugin *plugin, const char *hostname) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); GString *conf; GSList *iter; const char *argv[10]; GError *error = NULL; int ignored; + GPid pid = 0; + + /* 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. + */ + nm_dns_plugin_child_kill (plugin); /* Build up the new dnsmasq config file */ conf = g_string_sized_new (150); @@ -285,17 +220,18 @@ update (NMDnsPlugin *plugin, argv[0] = find_dnsmasq (); argv[1] = "--no-resolv"; /* Use only commandline */ argv[2] = "--keep-in-foreground"; - argv[3] = "--pid-file=" PIDFILE; - argv[4] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */ - argv[5] = "--conf-file=" CONFFILE; - argv[6] = NULL; + argv[3] = "--bind-interfaces"; + argv[4] = "--pid-file=" PIDFILE; + argv[5] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */ + argv[6] = "--conf-file=" CONFFILE; + argv[7] = NULL; /* And finally spawn dnsmasq */ - priv->pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq"); + pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq"); out: g_string_free (conf, TRUE); - return priv->pid ? TRUE : FALSE; + return pid ? TRUE : FALSE; } /****************************************************************/ @@ -322,7 +258,6 @@ static void child_quit (NMDnsPlugin *plugin, gint status) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); gboolean failed = TRUE; int err; @@ -341,12 +276,10 @@ child_quit (NMDnsPlugin *plugin, gint status) } else { nm_log_warn (LOGD_DNS, "dnsmasq died from an unknown cause"); } + unlink (CONFFILE); if (failed) g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); - - priv->pid = 0; - unlink (CONFFILE); } /****************************************************************/ @@ -358,11 +291,17 @@ init (NMDnsPlugin *plugin) } static gboolean -is_exclusive (NMDnsPlugin *plugin) +is_caching (NMDnsPlugin *plugin) { return TRUE; } +static const char * +get_name (NMDnsPlugin *plugin) +{ + return "dnsmasq"; +} + /****************************************************************/ NMDnsDnsmasq * @@ -377,15 +316,27 @@ nm_dns_dnsmasq_init (NMDnsDnsmasq *self) } static void +dispose (GObject *object) +{ + unlink (CONFFILE); + + G_OBJECT_CLASS (nm_dns_dnsmasq_parent_class)->dispose (object); +} + +static void nm_dns_dnsmasq_class_init (NMDnsDnsmasqClass *dns_class) { NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS (dns_class); + GObjectClass *object_class = G_OBJECT_CLASS (dns_class); g_type_class_add_private (dns_class, sizeof (NMDnsDnsmasqPrivate)); + object_class->dispose = dispose; + plugin_class->init = init; plugin_class->child_quit = child_quit; - plugin_class->is_exclusive = is_exclusive; + plugin_class->is_caching = is_caching; plugin_class->update = update; + plugin_class->get_name = get_name; } diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c index 615532133c..cd39a651e6 100644 --- a/src/dns-manager/nm-dns-manager.c +++ b/src/dns-manager/nm-dns-manager.c @@ -245,10 +245,11 @@ dispatch_netconfig (const char *domain, char **nameservers, const char *nis_domain, char **nis_servers, + gboolean caching, const char *iface, GError **error) { - char *str; + char *str, *tmp; GPid pid; gint fd; int ret; @@ -284,9 +285,14 @@ dispatch_netconfig (const char *domain, } if (nameservers) { - str = g_strjoinv (" ", nameservers); + tmp = g_strjoinv (" ", nameservers); + if (caching) + str = g_strdup_printf ("127.0.0.1 %s", tmp); + else + str = g_strdup (tmp); write_to_netconfig (fd, "DNSSERVERS", str); g_free (str); + g_free (tmp); } if (nis_domain) @@ -321,6 +327,7 @@ static gboolean write_resolv_conf (FILE *f, const char *domain, char **searches, char **nameservers, + gboolean caching, GError **error) { char *domain_str = NULL; @@ -328,6 +335,7 @@ write_resolv_conf (FILE *f, const char *domain, char *nameservers_str = NULL; int i; gboolean retval = FALSE; + GString *str; if (fprintf (f, "%s","# Generated by NetworkManager\n") < 0) { g_set_error (error, @@ -349,15 +357,16 @@ write_resolv_conf (FILE *f, const char *domain, g_free (tmp_str); } - if (nameservers) { - GString *str; - int num; + str = g_string_new (""); + + if (caching) + g_string_append_printf (str, "nameserver 127.0.0.1\n"); - str = g_string_new (""); - num = g_strv_length (nameservers); + if (nameservers) { + int num = g_strv_length (nameservers); for (i = 0; i < num; i++) { - if (i == 3) { + if (i == 3 && !caching) { g_string_append (str, "# "); g_string_append (str, _("NOTE: the libc resolver may not support more than 3 nameservers.")); g_string_append (str, "\n# "); @@ -369,14 +378,14 @@ write_resolv_conf (FILE *f, const char *domain, 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, "%s%s%s", domain_str ? domain_str : "", searches_str ? searches_str : "", - nameservers_str ? nameservers_str : "") != -1) + strlen (nameservers_str) ? nameservers_str : "") != -1) retval = TRUE; g_free (domain_str); @@ -391,6 +400,7 @@ static gboolean dispatch_resolvconf (const char *domain, char **searches, char **nameservers, + gboolean caching, const char *iface, GError **error) { @@ -412,7 +422,7 @@ dispatch_resolvconf (const char *domain, RESOLVCONF_PATH, g_strerror (errno)); else { - retval = write_resolv_conf (f, domain, searches, nameservers, error); + retval = write_resolv_conf (f, domain, searches, nameservers, caching, error); retval &= (pclose (f) == 0); } } else { @@ -432,6 +442,7 @@ static gboolean update_resolv_conf (const char *domain, char **searches, char **nameservers, + gboolean caching, const char *iface, GError **error) { @@ -479,7 +490,7 @@ update_resolv_conf (const char *domain, strcpy (tmp_resolv_conf_realpath, RESOLV_CONF); } - write_resolv_conf (f, domain, searches, nameservers, error); + write_resolv_conf (f, domain, searches, nameservers, caching, error); if (fclose (f) < 0) { if (*error == NULL) { @@ -515,23 +526,26 @@ out: } static gboolean -rewrite_resolv_conf (NMDnsManager *mgr, const char *iface, GError **error) +update_dns (NMDnsManager *self, + const char *iface, + gboolean no_caching, + GError **error) { NMDnsManagerPrivate *priv; NMResolvConfData rc; - GSList *iter; + GSList *iter, *vpn_configs = NULL, *dev_configs = NULL, *other_configs = NULL; const char *domain = NULL; const char *nis_domain = NULL; char **searches = NULL; char **nameservers = NULL; char **nis_servers = NULL; int num, i, len; - gboolean success = FALSE; + gboolean success = FALSE, caching = FALSE; g_return_val_if_fail (error != NULL, FALSE); g_return_val_if_fail (*error == NULL, FALSE); - priv = NM_DNS_MANAGER_GET_PRIVATE (mgr); + priv = NM_DNS_MANAGER_GET_PRIVATE (self); if (iface) { g_free (priv->last_iface); @@ -618,20 +632,74 @@ rewrite_resolv_conf (NMDnsManager *mgr, const char *iface, GError **error) nis_domain = rc.nis_domain; + /* Build up config lists for plugins; we use the raw configs here, not the + * merged information that we write to resolv.conf so that the plugins can + * still use the domain information in each config to provide split DNS if + * they want to. + */ + if (priv->ip4_vpn_config) + vpn_configs = g_slist_append (vpn_configs, priv->ip4_vpn_config); + if (priv->ip6_vpn_config) + vpn_configs = g_slist_append (vpn_configs, priv->ip6_vpn_config); + if (priv->ip4_device_config) + dev_configs = g_slist_append (dev_configs, priv->ip4_device_config); + if (priv->ip6_device_config) + dev_configs = g_slist_append (dev_configs, 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)) + other_configs = g_slist_append (other_configs, iter->data); + } + + /* Let any plugins do their thing first */ + for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { + NMDnsPlugin *plugin = NM_DNS_PLUGIN (iter->data); + const char *plugin_name = nm_dns_plugin_get_name (plugin); + + if (nm_dns_plugin_is_caching (plugin)) { + if (no_caching) { + nm_log_dbg (LOGD_DNS, "DNS: plugin %s ignored (caching disabled)", + plugin_name); + continue; + } + caching = TRUE; + } + + nm_log_dbg (LOGD_DNS, "DNS: updating plugin %s", plugin_name); + if (!nm_dns_plugin_update (plugin, + vpn_configs, + dev_configs, + other_configs, + priv->hostname)) { + nm_log_warn (LOGD_DNS, "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. + */ + caching = FALSE; + } + } + g_slist_free (vpn_configs); + g_slist_free (dev_configs); + g_slist_free (other_configs); + #ifdef RESOLVCONF_PATH - success = dispatch_resolvconf (domain, searches, nameservers, iface, error); + success = dispatch_resolvconf (domain, searches, nameservers, caching, iface, error); #endif #ifdef TARGET_SUSE if (success == FALSE) { success = dispatch_netconfig (domain, searches, nameservers, nis_domain, nis_servers, - iface, error); + caching, iface, error); } #endif if (success == FALSE) - success = update_resolv_conf (domain, searches, nameservers, iface, error); + success = update_resolv_conf (domain, searches, nameservers, caching, iface, error); if (success) nm_system_update_dns (); @@ -646,6 +714,26 @@ rewrite_resolv_conf (NMDnsManager *mgr, const char *iface, GError **error) return success; } +static void +plugin_failed (NMDnsPlugin *plugin, gpointer user_data) +{ + NMDnsManager *self = NM_DNS_MANAGER (user_data); + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); + GError *error = NULL; + + /* Errors with non-caching plugins aren't fatal */ + if (!nm_dns_plugin_is_caching (plugin)) + return; + + /* Disable caching until the next DNS update */ + if (!update_dns (self, priv->last_iface, TRUE, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } +} + gboolean nm_dns_manager_add_ip4_config (NMDnsManager *mgr, const char *iface, @@ -676,9 +764,11 @@ 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 (!rewrite_resolv_conf (mgr, iface, &error)) { - nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); - g_error_free (error); + if (!update_dns (mgr, iface, FALSE, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); } return TRUE; @@ -711,10 +801,11 @@ nm_dns_manager_remove_ip4_config (NMDnsManager *mgr, g_object_unref (config); - if (!rewrite_resolv_conf (mgr, iface, &error)) { - nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); - if (error) - g_error_free (error); + if (!update_dns (mgr, iface, FALSE, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); } return TRUE; @@ -752,9 +843,11 @@ 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 (!rewrite_resolv_conf (mgr, iface, &error)) { - nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); - g_error_free (error); + if (!update_dns (mgr, iface, FALSE, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); } return TRUE; @@ -787,10 +880,11 @@ nm_dns_manager_remove_ip6_config (NMDnsManager *mgr, g_object_unref (config); - if (!rewrite_resolv_conf (mgr, iface, &error)) { - nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); - if (error) - g_error_free (error); + if (!update_dns (mgr, iface, FALSE, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); } return TRUE; @@ -824,8 +918,10 @@ nm_dns_manager_set_hostname (NMDnsManager *mgr, * wants one. But hostname changes are system-wide and *not* tied to a * specific interface, so netconfig can't really handle this. Fake it. */ - if (!rewrite_resolv_conf (mgr, priv->last_iface, &error)) { - nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); + if (!update_dns (mgr, priv->last_iface, FALSE, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); g_clear_error (&error); } } @@ -848,6 +944,7 @@ load_plugins (NMDnsManager *self, const char **plugins) NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); NMDnsPlugin *plugin; const char **iter; + gboolean have_caching = FALSE; if (plugins && *plugins) { /* Create each configured plugin */ @@ -859,19 +956,39 @@ load_plugins (NMDnsManager *self, const char **plugins) else if (!strcasecmp (*iter, "chromium")) plugin = NM_DNS_PLUGIN (nm_dns_chromium_new ()); else { - nm_log_warn (LOGD_DNS, "Unknown DNS plugin '%s'", *iter); + nm_log_warn (LOGD_DNS, "Unknown DNS plugin '%s'", *iter);\ + continue; + } + g_assert (plugin); + + /* Only one caching DNS plugin is allowed */ + if (nm_dns_plugin_is_caching (plugin)) { + if (have_caching) { + nm_log_warn (LOGD_DNS, + "Ignoring plugin %s; only one caching DNS " + "plugin is allowed.", + *iter); + g_object_unref (plugin); + continue; + } + have_caching = TRUE; } - if (plugin) - priv->plugins = g_slist_append (priv->plugins, plugin); + nm_log_info (LOGD_DNS, "DNS: loaded plugin %s", nm_dns_plugin_get_name (plugin)); + priv->plugins = g_slist_append (priv->plugins, plugin); + g_signal_connect (plugin, NM_DNS_PLUGIN_FAILED, + G_CALLBACK (plugin_failed), + self); } } else { /* Create default plugins */ /* Chromium support */ +#if 0 plugin = NM_DNS_PLUGIN (nm_dns_chromium_new ()); g_assert (plugin); priv->plugins = g_slist_append (priv->plugins, plugin); +#endif } } diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c index bfa80bd87f..d47640b1c3 100644 --- a/src/dns-manager/nm-dns-plugin.c +++ b/src/dns-manager/nm-dns-plugin.c @@ -66,15 +66,22 @@ nm_dns_plugin_update (NMDnsPlugin *self, } static gboolean -is_exclusive (NMDnsPlugin *self) +is_caching (NMDnsPlugin *self) { return FALSE; } gboolean -nm_dns_plugin_is_exclusive (NMDnsPlugin *self) +nm_dns_plugin_is_caching (NMDnsPlugin *self) { - return NM_DNS_PLUGIN_GET_CLASS (self)->is_exclusive (self); + return NM_DNS_PLUGIN_GET_CLASS (self)->is_caching (self); +} + +const char * +nm_dns_plugin_get_name (NMDnsPlugin *self) +{ + g_assert (NM_DNS_PLUGIN_GET_CLASS (self)->get_name); + return NM_DNS_PLUGIN_GET_CLASS (self)->get_name (self); } /********************************************/ @@ -158,9 +165,9 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self, priv->pidfile = g_strdup (pidfile); } - nm_log_info (LOGD_DNS, "Starting %s...", priv->progname); + nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname); cmdline = g_strjoinv (" ", (char **) argv); - nm_log_dbg (LOGD_DNS, "Command line: %s", cmdline); + nm_log_dbg (LOGD_DNS, "DNS: command line: %s", cmdline); g_free (cmdline); priv->pid = 0; @@ -287,7 +294,7 @@ nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class) /* virtual methods */ object_class->dispose = dispose; object_class->finalize = finalize; - plugin_class->is_exclusive = is_exclusive; + plugin_class->is_caching = is_caching; /* signals */ signals[FAILED] = diff --git a/src/dns-manager/nm-dns-plugin.h b/src/dns-manager/nm-dns-plugin.h index cb8fabe1ff..d4298b869d 100644 --- a/src/dns-manager/nm-dns-plugin.h +++ b/src/dns-manager/nm-dns-plugin.h @@ -59,7 +59,10 @@ typedef struct { * caching nameserver that listens on localhost and would block any * other local caching nameserver from operating. */ - gboolean (*is_exclusive) (NMDnsPlugin *self); + gboolean (*is_caching) (NMDnsPlugin *self); + + /* Subclasses should override this and return their plugin name */ + const char *(*get_name) (NMDnsPlugin *self); /* Signals */ @@ -80,7 +83,9 @@ typedef struct { GType nm_dns_plugin_get_type (void); -gboolean nm_dns_plugin_is_exclusive (NMDnsPlugin *self); +gboolean nm_dns_plugin_is_caching (NMDnsPlugin *self); + +const char *nm_dns_plugin_get_name (NMDnsPlugin *self); gboolean nm_dns_plugin_update (NMDnsPlugin *self, const GSList *vpn_configs, |