dns: hook plugins into DNS updates and make dnsmasq plugin actually work

This commit is contained in:
Dan Williams 2010-09-12 22:25:30 -05:00
parent 4da443dc69
commit a2982b5f7b
4 changed files with 213 additions and 133 deletions

View file

@ -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 *
@ -376,16 +315,28 @@ 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;
}

View file

@ -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 ("");
str = g_string_new ("");
num = g_strv_length (nameservers);
if (caching)
g_string_append_printf (str, "nameserver 127.0.0.1\n");
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
}
}

View file

@ -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] =

View file

@ -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,