diff options
author | Dan Williams <dcbw@redhat.com> | 2010-09-22 16:19:28 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2010-09-22 16:19:28 -0500 |
commit | a211fadce06e7a78e693b85192b50f9e09df8972 (patch) | |
tree | 3421272e1605f701516ea0346faf76c1be4bbad0 | |
parent | a1731c60644432cdec94841a033340bfc88568ec (diff) | |
parent | 06bd99f61779417453a049934ab136c8be9df955 (diff) |
Merge remote branch 'origin/cachingdns'
Caching DNS with dnsmasq works well enough to merge for now. THere
are still some issues with the BIND plugin because BIND is god-awful
unecessarily complex so we'll disable that in a further commit.
-rw-r--r-- | man/NetworkManager.conf.5.in | 30 | ||||
-rw-r--r-- | src/dns-manager/Makefile.am | 13 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-bind.c | 527 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-bind.h | 47 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-dnsmasq.c | 370 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-dnsmasq.h | 47 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-manager.c | 334 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-manager.h | 2 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-plugin.c | 318 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-plugin.h | 112 | ||||
-rw-r--r-- | src/main.c | 12 | ||||
-rw-r--r-- | src/nm-device.c | 4 | ||||
-rw-r--r-- | src/nm-policy.c | 6 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-connection.c | 4 |
14 files changed, 1746 insertions, 80 deletions
diff --git a/man/NetworkManager.conf.5.in b/man/NetworkManager.conf.5.in index 44d6da1be0..4e2bae9eb6 100644 --- a/man/NetworkManager.conf.5.in +++ b/man/NetworkManager.conf.5.in @@ -44,12 +44,13 @@ Description of sections and available keys follows: This section is the only mandatory section of the configuration file. .TP .B plugins=\fIplugin1\fP,\fIplugin2\fP, ... -List plugin names separated by ','. Plugins are used to read/write system-wide -connection. When more plugins are specified, the connections are read from all -listed plugins. When writing connections, the plugins will be asked to save the -connection in the order listed here. If the first plugin cannot write out that -connection type, or can't write out any connections, the next plugin is tried. -If none of the plugins can save the connection, the error is returned to the user. +List system settings plugin names separated by ','. These plugins are used to +read/write system-wide connection. When more plugins are specified, the +connections are read from all listed plugins. When writing connections, the +plugins will be asked to save the connection in the order listed here. If the +first plugin cannot write out that connection type, or can't write out any +connections, the next plugin is tried. If none of the plugins can save the +connection, the error is returned to the user. .P .RS .B "Available plugins:" @@ -104,6 +105,23 @@ Example: .nf no-auto-default=00:22:68:5c:5d:c4,00:1e:65:ff:aa:ee .fi +.TP +.B dns=\fIplugin1\fP,\fIplugin2\fP, ... +List DNS plugin names separated by ','. DNS plugins are used to provide local +caching nameserver functionality (which speeds up DNS queries) and to push +DNS data to applications that use it. +.P +.RS +.B "Available plugins:" +.br +.TP +.I dnsmasq +this plugin uses dnsmasq to provide local caching nameserver functionality. +.TP +.I bind +this plugin uses the ISC BIND program to provide local caching nameserver +functionality. +.RE .SS [keyfile] This section contains keyfile-specific options and thus only has effect when using \fIkeyfile\fP plugin. .TP diff --git a/src/dns-manager/Makefile.am b/src/dns-manager/Makefile.am index d1ea5f87e9..1ffe62dcf0 100644 --- a/src/dns-manager/Makefile.am +++ b/src/dns-manager/Makefile.am @@ -6,13 +6,20 @@ INCLUDES = \ noinst_LTLIBRARIES = libdns-manager.la -libdns_manager_la_SOURCES = nm-dns-manager.h nm-dns-manager.c +libdns_manager_la_SOURCES = \ + nm-dns-manager.h \ + nm-dns-manager.c \ + nm-dns-plugin.h \ + nm-dns-plugin.c \ + nm-dns-dnsmasq.h \ + nm-dns-dnsmasq.c \ + nm-dns-bind.h \ + nm-dns-bind.c libdns_manager_la_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ - -DNM_PKGDATADIR=\"$(pkgdatadir)\" \ - -DNM_LOCALSTATEDIR=\"$(localstatedir)\" + -DLOCALSTATEDIR=\"$(localstatedir)\" libdns_manager_la_LIBADD = \ $(top_builddir)/src/logging/libnm-logging.la \ diff --git a/src/dns-manager/nm-dns-bind.c b/src/dns-manager/nm-dns-bind.c new file mode 100644 index 0000000000..2e1ec67dec --- /dev/null +++ b/src/dns-manager/nm-dns-bind.c @@ -0,0 +1,527 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2010 Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <sys/stat.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "nm-dns-bind.h" +#include "nm-logging.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" + +G_DEFINE_TYPE (NMDnsBind, nm_dns_bind, NM_TYPE_DNS_PLUGIN) + +#define NM_DNS_BIND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_BIND, NMDnsBindPrivate)) + +#define PIDFILE LOCALSTATEDIR "/run/nm-dns-named.pid" +#define CONFFILE LOCALSTATEDIR "/run/nm-dns-named.conf" + +typedef struct { + GPid pid; +} NMDnsBindPrivate; + +/*******************************************/ + +static inline const char * +find_bind (void) +{ + static const char *paths[] = { + "/usr/local/sbin/named", + "/usr/sbin/named", + "/sbin/named", + NULL + }; + const char **binary = paths; + + while (*binary != NULL) { + if (g_file_test (*binary, G_FILE_TEST_EXISTS)) + return *binary; + binary++; + } + return NULL; +} + +static gboolean +start_bind (NMDnsBind *self) +{ + const char *argv[10]; + + argv[0] = find_bind (); + argv[1] = "-f"; /* don't daemonize; stay in foreground */ + argv[2] = "-c"; + argv[3] = CONFFILE; + argv[4] = NULL; + + /* And finally spawn bind */ + return nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/named"); +} + +/*******************************************/ + +static gboolean +find_address (GPtrArray *array, const char *addr) +{ + int n; + + for (n = 0; n < array->len; n++) { + if (g_strcmp0 ((const char*) g_ptr_array_index (array, n), addr) == 0) + return TRUE; + } + return FALSE; +} + +static void +add_ip4_nameservers (NMIP4Config *ip4, GPtrArray *array) +{ + int i; + + for (i = 0; i < nm_ip4_config_get_num_nameservers (ip4); i++) { + char buf[INET_ADDRSTRLEN + 1]; + struct in_addr addr; + + memset (&buf[0], 0, sizeof (buf)); + addr.s_addr = nm_ip4_config_get_nameserver (ip4, i); + if (inet_ntop (AF_INET, &addr, buf, sizeof (buf))) { + if (!find_address (array, buf)) + g_ptr_array_add (array, g_strdup (buf)); + } + } +} + +static gboolean +ip6_addr_to_string (const struct in6_addr *addr, char *buf, size_t buflen) +{ + /* inet_ntop is probably supposed to do this for us, but it doesn't */ + if (IN6_IS_ADDR_V4MAPPED (addr)) + return !!inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, buflen); + + return !!inet_ntop (AF_INET6, addr, buf, buflen); +} + +static void +add_ip6_nameservers (NMIP6Config *ip6, GPtrArray *array) +{ + char buf[INET6_ADDRSTRLEN + 1]; + int i; + + for (i = 0; i < nm_ip6_config_get_num_nameservers (ip6); i++) { + memset (buf, 0, sizeof (buf)); + if (ip6_addr_to_string (nm_ip6_config_get_nameserver (ip6, i), buf, sizeof (buf))) { + if (!find_address (array, buf)) + g_ptr_array_add (array, g_strdup (buf)); + } + } +} + +typedef struct { + guint32 dhash; + char *domain; + GPtrArray *servers; +} ZoneInfo; + +static ZoneInfo * +zone_new (const char *domain) +{ + ZoneInfo *info; + + g_return_val_if_fail (domain != NULL, NULL); + + info = g_malloc0 (sizeof (ZoneInfo)); + info->domain = g_strdup (domain); + info->dhash = g_str_hash (domain); + info->servers = g_ptr_array_sized_new (4); + return info; +} + +static void +zone_add_nameserver (ZoneInfo *info, const char *server) +{ + guint32 i; + + g_return_if_fail (info != NULL); + g_return_if_fail (server != NULL); + + for (i = 0; i < info->servers->len; i++) { + if (g_strcmp0 ((char *) g_ptr_array_index (info->servers, i), server) == 0) + return; + } + g_ptr_array_add (info->servers, g_strdup (server)); +} + +static void +zone_free (ZoneInfo *info) +{ + g_return_if_fail (info != NULL); + + g_free (info->domain); + g_ptr_array_foreach (info->servers, (GFunc) g_free, NULL); + g_ptr_array_free (info->servers, TRUE); + memset (info, 0, sizeof (ZoneInfo)); + g_free (info); +} + +static ZoneInfo * +find_zone (GPtrArray *zones, const char *domain) +{ + guint32 dhash, i; + + g_return_val_if_fail (domain != NULL, FALSE); + + dhash = g_str_hash (domain); + for (i = 0; i < zones->len; i++) { + ZoneInfo *zone = g_ptr_array_index (zones, i); + + if (zone->dhash == dhash) + return zone; + } + return NULL; +} + +static void +add_zone (GObject *ip, GPtrArray *zones) +{ + guint32 i, j, ns, nd, nn; + GPtrArray *to_add; + ZoneInfo *z; + + if (NM_IS_IP4_CONFIG (ip)) { + ns = nm_ip4_config_get_num_searches (NM_IP4_CONFIG (ip)); + nd = nm_ip4_config_get_num_domains (NM_IP4_CONFIG (ip)); + nn = nm_ip4_config_get_num_nameservers (NM_IP4_CONFIG (ip)); + } else if (NM_IS_IP6_CONFIG (ip)) { + ns = nm_ip6_config_get_num_searches (NM_IP6_CONFIG (ip)); + nd = nm_ip6_config_get_num_domains (NM_IP6_CONFIG (ip)); + nn = nm_ip6_config_get_num_nameservers (NM_IP6_CONFIG (ip)); + } else + g_assert_not_reached (); + + /* If we don't have any domains or searches, or we don't have any + * nameservers, we can't do split DNS for this config. + */ + if ((!nd && !ns) || !nn) + return; + + to_add = g_ptr_array_sized_new (MAX (ns, nd)); + + /* searches are preferred over domains */ + for (i = 0; i < ns; i++) { + const char *domain = NULL; + + if (NM_IS_IP4_CONFIG (ip)) + domain = nm_ip4_config_get_search (NM_IP4_CONFIG (ip), i); + else if (NM_IS_IP6_CONFIG (ip)) + domain = nm_ip6_config_get_search (NM_IP6_CONFIG (ip), i); + + z = find_zone (zones, domain); + if (!z) { + z = zone_new (domain); + g_ptr_array_add (zones, z); + } + g_ptr_array_add (to_add, z); + } + + if (ns == 0) { + /* If no searches, add any domains */ + for (i = 0; i < nd; i++) { + const char *domain = NULL; + + if (NM_IS_IP4_CONFIG (ip)) + domain = nm_ip4_config_get_domain (NM_IP4_CONFIG (ip), i); + else if (NM_IS_IP6_CONFIG (ip)) + domain = nm_ip6_config_get_domain (NM_IP6_CONFIG (ip), i); + + z = find_zone (zones, domain); + if (!z) { + z = zone_new (domain); + g_ptr_array_add (zones, z); + } + g_ptr_array_add (to_add, z); + } + } + + /* Now add the nameservers to every zone for this config */ + for (i = 0; i < nn; i++) { + char buf[INET6_ADDRSTRLEN + 1]; + struct in_addr addr4; + const struct in6_addr *addr6; + + memset (&buf[0], 0, sizeof (buf)); + + if (NM_IS_IP4_CONFIG (ip)) { + addr4.s_addr = nm_ip4_config_get_nameserver (NM_IP4_CONFIG (ip), i); + if (!inet_ntop (AF_INET, &addr4, buf, sizeof (buf))) + continue; + } else if (NM_IS_IP6_CONFIG (ip)) { + addr6 = nm_ip6_config_get_nameserver (NM_IP6_CONFIG (ip), i); + if (!ip6_addr_to_string (addr6, buf, sizeof (buf))) + continue; + } + + /* Add this nameserver to every zone from this IP config */ + for (j = 0; j < to_add->len; j++) { + z = g_ptr_array_index (to_add, j); + zone_add_nameserver (z, buf); + } + } + + g_ptr_array_free (to_add, TRUE); +} + +static gboolean +update (NMDnsPlugin *plugin, + const GSList *vpn_configs, + const GSList *dev_configs, + const GSList *other_configs, + const char *hostname) +{ + NMDnsBind *self = NM_DNS_BIND (plugin); + NMDnsBindPrivate *priv = NM_DNS_BIND_GET_PRIVATE (self); + GString *conf; + GPtrArray *globals, *zones; + GSList *iter; + GError *error = NULL; + int ignored, i, j; + gboolean success = FALSE; + + /* Build up the new bind config file */ + conf = g_string_sized_new (200); + globals = g_ptr_array_sized_new (6); + + /* If any of the VPN configs *don't* have domains or searches, then we + * dont' have any split DNS configuration for them, and we add them + * first in the global nameserver lists. Otherwise we add them later as + * split DNS zones. + */ + for (iter = (GSList *) vpn_configs; iter;iter = g_slist_next (iter)) { + if (NM_IS_IP4_CONFIG (iter->data)) { + NMIP4Config *ip4 = NM_IP4_CONFIG (iter->data); + + if (!nm_ip4_config_get_num_domains (ip4) && !nm_ip4_config_get_num_searches (ip4)) + add_ip4_nameservers (ip4, globals); + } else if (NM_IS_IP6_CONFIG (iter->data)) { + NMIP6Config *ip6 = NM_IP6_CONFIG (iter->data); + + if (!nm_ip6_config_get_num_domains (ip6) && !nm_ip6_config_get_num_searches (ip6)) + add_ip6_nameservers (ip6, globals); + } + } + + /* Get a list of global upstream servers with dupe checking */ + for (iter = (GSList *) dev_configs; iter;iter = g_slist_next (iter)) { + if (NM_IS_IP4_CONFIG (iter->data)) + add_ip4_nameservers (NM_IP4_CONFIG (iter->data), globals); + else if (NM_IS_IP6_CONFIG (iter->data)) + add_ip6_nameservers (NM_IP6_CONFIG (iter->data), globals); + } + + /* And any other random configs with dupe checking */ + for (iter = (GSList *) other_configs; iter;iter = g_slist_next (iter)) { + if (NM_IS_IP4_CONFIG (iter->data)) + add_ip4_nameservers (NM_IP4_CONFIG (iter->data), globals); + else if (NM_IS_IP6_CONFIG (iter->data)) + add_ip6_nameservers (NM_IP6_CONFIG (iter->data), globals); + } + + g_string_append (conf, + "options {\n" + " directory \"" LOCALSTATEDIR "/named\";\n" + " forward only;\n" + " recursion yes;\n" + " listen-on-v6 { ::1; };\n" + " listen-on { 127.0.0.1; };\n" + " forwarders {\n"); + + for (i = 0; i < globals->len; i++) { + char *ns = g_ptr_array_index (globals, i); + + g_string_append_printf (conf, " %s;\n", ns); + g_free (ns); + } + g_ptr_array_free (globals, TRUE); + + g_string_append (conf, + " };\n" + "};\n\n"); + + /* Build up the list of any split DNS zones, avoiding duplicates */ + zones = g_ptr_array_sized_new (4); + for (iter = (GSList *) vpn_configs; iter;iter = g_slist_next (iter)) { + if (NM_IS_IP4_CONFIG (iter->data)) + add_zone (G_OBJECT (iter->data), zones); + else if (NM_IS_IP6_CONFIG (iter->data)) + add_zone (G_OBJECT (iter->data), zones); + } + + /* Add all the zones to the config */ + for (i = 0; i < zones->len; i++) { + ZoneInfo *z = g_ptr_array_index (zones, i); + + g_string_append_printf (conf, + "zone \"%s\" IN {\n" + " type forward;\n" + " forward only;\n" + " forwarders {\n", + z->domain); + + /* Add each nameserver for this zone */ + for (j = 0; j < z->servers->len; j++) { + g_string_append_printf (conf, + " %s;\n", + (const char *) g_ptr_array_index (z->servers, j)); + } + + g_string_append (conf, + " };\n" + "};\n\n"); + + zone_free (z); + } + g_ptr_array_free (zones, TRUE); + + /* Write out the config file */ + if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) { + nm_log_warn (LOGD_DNS, "Failed to write named config file %s: (%d) %s", + CONFFILE, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + goto out; + } + ignored = chmod (CONFFILE, 0600); + + nm_log_dbg (LOGD_DNS, "BIND local caching DNS configuration:"); + nm_log_dbg (LOGD_DNS, "%s", conf->str); + + if (priv->pid) { + /* Send it SIGHUP to reload the new configuration */ + if (kill (priv->pid, SIGHUP) == 0) + success = TRUE; + else { + /* Sigh... some error. Kill it and restart */ + nm_dns_plugin_child_kill (NM_DNS_PLUGIN (self)); + priv->pid = 0; + } + } + + if (!success) { + /* Spawn it */ + priv->pid = start_bind (self); + if (priv->pid) + success = TRUE; + } + +out: + g_string_free (conf, TRUE); + return success; +} + +/****************************************************************/ + +static void +child_quit (NMDnsPlugin *plugin, gint status) +{ + NMDnsBind *self = NM_DNS_BIND (plugin); + gboolean failed = TRUE; + int err; + + if (WIFEXITED (status)) { + err = WEXITSTATUS (status); + if (err) { + nm_log_warn (LOGD_DNS, "named exited with error %d", err); + } else + failed = FALSE; + } else if (WIFSTOPPED (status)) { + nm_log_warn (LOGD_DNS, "named stopped unexpectedly with signal %d", WSTOPSIG (status)); + } else if (WIFSIGNALED (status)) { + nm_log_warn (LOGD_DNS, "named died with signal %d", WTERMSIG (status)); + } else { + nm_log_warn (LOGD_DNS, "named died from an unknown cause"); + } + unlink (CONFFILE); + + if (failed) + g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); +} + +/****************************************************************/ + +static gboolean +init (NMDnsPlugin *plugin) +{ + return TRUE; +} + +static gboolean +is_caching (NMDnsPlugin *plugin) +{ + return TRUE; +} + +static const char * +get_name (NMDnsPlugin *plugin) +{ + return "bind"; +} + +/****************************************************************/ + +NMDnsBind * +nm_dns_bind_new (void) +{ + return (NMDnsBind *) g_object_new (NM_TYPE_DNS_BIND, NULL); +} + +static void +nm_dns_bind_init (NMDnsBind *self) +{ +} + +static void +dispose (GObject *object) +{ + unlink (CONFFILE); + + G_OBJECT_CLASS (nm_dns_bind_parent_class)->dispose (object); +} + +static void +nm_dns_bind_class_init (NMDnsBindClass *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 (NMDnsBindPrivate)); + + object_class->dispose = dispose; + + plugin_class->init = init; + plugin_class->child_quit = child_quit; + plugin_class->is_caching = is_caching; + plugin_class->update = update; + plugin_class->get_name = get_name; +} + diff --git a/src/dns-manager/nm-dns-bind.h b/src/dns-manager/nm-dns-bind.h new file mode 100644 index 0000000000..7127265f18 --- /dev/null +++ b/src/dns-manager/nm-dns-bind.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#ifndef NM_DNS_BIND_H +#define NM_DNS_BIND_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-dns-plugin.h" + +#define NM_TYPE_DNS_BIND (nm_dns_bind_get_type ()) +#define NM_DNS_BIND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNS_BIND, NMDnsBind)) +#define NM_DNS_BIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNS_BIND, NMDnsBindClass)) +#define NM_IS_DNS_BIND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNS_BIND)) +#define NM_IS_DNS_BIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNS_BIND)) +#define NM_DNS_BIND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_BIND, NMDnsBindClass)) + +typedef struct { + NMDnsPlugin parent; +} NMDnsBind; + +typedef struct { + NMDnsPluginClass parent; +} NMDnsBindClass; + +GType nm_dns_bind_get_type (void); + +NMDnsBind *nm_dns_bind_new (void); + +#endif /* NM_DNS_BIND_H */ + diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c new file mode 100644 index 0000000000..254e2ffbb6 --- /dev/null +++ b/src/dns-manager/nm-dns-dnsmasq.c @@ -0,0 +1,370 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2010 Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <sys/stat.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "nm-dns-dnsmasq.h" +#include "nm-logging.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" + +G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) + +#define NM_DNS_DNSMASQ_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqPrivate)) + +#define PIDFILE LOCALSTATEDIR "/run/nm-dns-dnsmasq.pid" +#define CONFFILE LOCALSTATEDIR "/run/nm-dns-dnsmasq.conf" + +typedef struct { + guint32 foo; +} NMDnsDnsmasqPrivate; + +/*******************************************/ + +static inline const char * +find_dnsmasq (void) +{ + static const char *paths[] = { + "/usr/local/sbin/dnsmasq", + "/usr/sbin/dnsmasq", + "/sbin/dnsmasq", + NULL + }; + const char **binary = paths; + + while (*binary != NULL) { + if (g_file_test (*binary, G_FILE_TEST_EXISTS)) + return *binary; + binary++; + } + return NULL; +} + +static gboolean +add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split) +{ + char buf[INET_ADDRSTRLEN + 1]; + struct in_addr addr; + int n, i; + gboolean added = FALSE; + + if (split) { + /* FIXME: it appears that dnsmasq can only handle one nameserver + * per domain (at the manpage seems to indicate that) so only use + * the first nameserver here. + */ + addr.s_addr = nm_ip4_config_get_nameserver (ip4, 0); + memset (&buf[0], 0, sizeof (buf)); + if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf))) + return FALSE; + + /* searches are preferred over domains */ + n = nm_ip4_config_get_num_searches (ip4); + for (i = 0; i < n; i++) { + g_string_append_printf (str, "server=/%s/%s\n", + nm_ip4_config_get_search (ip4, i), + buf); + added = TRUE; + } + + if (n == 0) { + /* If not searches, use any domains */ + n = nm_ip4_config_get_num_domains (ip4); + for (i = 0; i < n; i++) { + g_string_append_printf (str, "server=/%s/%s\n", + nm_ip4_config_get_domain (ip4, i), + buf); + added = TRUE; + } + } + } + + /* If no searches or domains, just add the namservers */ + if (!added) { + n = nm_ip4_config_get_num_nameservers (ip4); + for (i = 0; i < n; i++) { + memset (&buf[0], 0, sizeof (buf)); + addr.s_addr = nm_ip4_config_get_nameserver (ip4, i); + if (inet_ntop (AF_INET, &addr, buf, sizeof (buf))) + g_string_append_printf (str, "server=%s\n", buf); + } + } + + return TRUE; +} + +static gboolean +ip6_addr_to_string (const struct in6_addr *addr, char *buf, size_t buflen) +{ + memset (buf, 0, buflen); + + /* inet_ntop is probably supposed to do this for us, but it doesn't */ + if (IN6_IS_ADDR_V4MAPPED (addr)) + return !!inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, buflen); + + return !!inet_ntop (AF_INET6, addr, buf, buflen); +} + +static gboolean +add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split) +{ + char buf[INET6_ADDRSTRLEN + 1]; + const struct in6_addr *addr; + int n, i; + gboolean added = FALSE; + + if (split) { + /* FIXME: it appears that dnsmasq can only handle one nameserver + * per domain (at the manpage seems to indicate that) so only use + * the first nameserver here. + */ + addr = nm_ip6_config_get_nameserver (ip6, 0); + if (!ip6_addr_to_string (addr, &buf[0], sizeof (buf))) + return FALSE; + + /* searches are preferred over domains */ + n = nm_ip6_config_get_num_searches (ip6); + for (i = 0; i < n; i++) { + g_string_append_printf (str, "server=/%s/%s\n", + nm_ip6_config_get_search (ip6, i), + buf); + added = TRUE; + } + + if (n == 0) { + /* If not searches, use any domains */ + n = nm_ip6_config_get_num_domains (ip6); + for (i = 0; i < n; i++) { + g_string_append_printf (str, "server=/%s/%s\n", + nm_ip6_config_get_domain (ip6, i), + buf); + added = TRUE; + } + } + } + + /* If no searches or domains, just add the namservers */ + if (!added) { + n = nm_ip6_config_get_num_nameservers (ip6); + for (i = 0; i < n; i++) { + addr = nm_ip6_config_get_nameserver (ip6, i); + if (ip6_addr_to_string (addr, &buf[0], sizeof (buf))) + g_string_append_printf (str, "server=%s\n", buf); + } + } + + return TRUE; +} + +static gboolean +update (NMDnsPlugin *plugin, + const GSList *vpn_configs, + const GSList *dev_configs, + const GSList *other_configs, + const char *hostname) +{ + NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); + 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); + + /* 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); + } + + /* 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 */ + if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) { + nm_log_warn (LOGD_DNS, "Failed to write dnsmasq config file %s: (%d) %s", + CONFFILE, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + goto out; + } + ignored = chmod (CONFFILE, 0600); + + nm_log_dbg (LOGD_DNS, "dnsmasq local caching DNS configuration:"); + nm_log_dbg (LOGD_DNS, "%s", conf->str); + + argv[0] = find_dnsmasq (); + argv[1] = "--no-resolv"; /* Use only commandline */ + argv[2] = "--keep-in-foreground"; + argv[3] = "--strict-order"; + argv[4] = "--bind-interfaces"; + argv[5] = "--pid-file=" PIDFILE; + argv[6] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */ + argv[7] = "--conf-file=" CONFFILE; + argv[8] = NULL; + + /* And finally spawn dnsmasq */ + pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq"); + +out: + g_string_free (conf, TRUE); + return pid ? TRUE : FALSE; +} + +/****************************************************************/ + +static const char * +dm_exit_code_to_msg (int status) +{ + if (status == 1) + return "Configuration problem"; + else if (status == 2) + return "Network access problem (address in use; permissions; etc)"; + else if (status == 3) + return "Filesystem problem (missing file/directory; permissions; etc)"; + else if (status == 4) + return "Memory allocation failure"; + else if (status == 5) + return "Other problem"; + else if (status >= 11) + return "Lease-script 'init' process failure"; + return "Unknown error"; +} + +static void +child_quit (NMDnsPlugin *plugin, gint status) +{ + NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); + gboolean failed = TRUE; + int err; + + if (WIFEXITED (status)) { + err = WEXITSTATUS (status); + if (err) { + nm_log_warn (LOGD_DNS, "dnsmasq exited with error: %s (%d)", + dm_exit_code_to_msg (err), + err); + } else + failed = FALSE; + } else if (WIFSTOPPED (status)) { + nm_log_warn (LOGD_DNS, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status)); + } else if (WIFSIGNALED (status)) { + nm_log_warn (LOGD_DNS, "dnsmasq died with signal %d", WTERMSIG (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); +} + +/****************************************************************/ + +static gboolean +init (NMDnsPlugin *plugin) +{ + return TRUE; +} + +static gboolean +is_caching (NMDnsPlugin *plugin) +{ + return TRUE; +} + +static const char * +get_name (NMDnsPlugin *plugin) +{ + return "dnsmasq"; +} + +/****************************************************************/ + +NMDnsDnsmasq * +nm_dns_dnsmasq_new (void) +{ + return (NMDnsDnsmasq *) g_object_new (NM_TYPE_DNS_DNSMASQ, NULL); +} + +static void +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_caching = is_caching; + plugin_class->update = update; + plugin_class->get_name = get_name; +} + diff --git a/src/dns-manager/nm-dns-dnsmasq.h b/src/dns-manager/nm-dns-dnsmasq.h new file mode 100644 index 0000000000..6491b271ae --- /dev/null +++ b/src/dns-manager/nm-dns-dnsmasq.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#ifndef NM_DNS_DNSMASQ_H +#define NM_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 ()) +#define NM_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq)) +#define NM_DNS_DNSMASQ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqClass)) +#define NM_IS_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNS_DNSMASQ)) +#define NM_IS_DNS_DNSMASQ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNS_DNSMASQ)) +#define NM_DNS_DNSMASQ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqClass)) + +typedef struct { + NMDnsPlugin parent; +} NMDnsDnsmasq; + +typedef struct { + NMDnsPluginClass parent; +} NMDnsDnsmasqClass; + +GType nm_dns_dnsmasq_get_type (void); + +NMDnsDnsmasq *nm_dns_dnsmasq_new (void); + +#endif /* NM_DNS_DNSMASQ_H */ + diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c index 6b4b67543c..77ad9d73eb 100644 --- a/src/dns-manager/nm-dns-manager.c +++ b/src/dns-manager/nm-dns-manager.c @@ -43,6 +43,10 @@ #include "nm-system.h" #include "NetworkManagerUtils.h" +#include "nm-dns-plugin.h" +#include "nm-dns-dnsmasq.h" +#include "nm-dns-bind.h" + #ifdef HAVE_SELINUX #include <selinux/selinux.h> #endif @@ -51,15 +55,12 @@ #define RESOLV_CONF "/etc/resolv.conf" #endif -#define ADDR_BUF_LEN 50 - G_DEFINE_TYPE(NMDnsManager, nm_dns_manager, G_TYPE_OBJECT) #define NM_DNS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ NM_TYPE_DNS_MANAGER, \ NMDnsManagerPrivate)) - struct NMDnsManagerPrivate { NMIP4Config *ip4_vpn_config; NMIP4Config *ip4_device_config; @@ -68,6 +69,16 @@ struct NMDnsManagerPrivate { GSList *configs; char *hostname; + /* poor man's hash; we assume that the IP4 config object won't change + * after it's given to us, which is (at this time) a fair assumption. So + * we track the order of the currently applied IP configs and if they + * haven't changed we don't need to rewrite resolv.conf. + */ + #define HLEN 6 + gpointer hash[HLEN]; + + GSList *plugins; + /* This is a hack because SUSE's netconfig always wants changes * associated with a network interface, but sometimes a change isn't * associated with a network interface (like hostnames). @@ -76,31 +87,6 @@ struct NMDnsManagerPrivate { }; -NMDnsManager * -nm_dns_manager_get (void) -{ - static NMDnsManager * singleton = NULL; - - if (!singleton) - singleton = NM_DNS_MANAGER (g_object_new (NM_TYPE_DNS_MANAGER, NULL)); - else - g_object_ref (singleton); - - g_assert (singleton); - return singleton; -} - - -GQuark -nm_dns_manager_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("nm_dns_manager_error"); - - return quark; -} - typedef struct { GPtrArray *nameservers; const char *domain; @@ -270,7 +256,7 @@ dispatch_netconfig (const char *domain, const char *iface, GError **error) { - char *str; + char *str, *tmp; GPid pid; gint fd; int ret; @@ -294,8 +280,6 @@ dispatch_netconfig (const char *domain, str = g_strjoinv (" ", searches); if (domain) { - char *tmp; - tmp = g_strconcat (domain, " ", str, NULL); g_free (str); str = tmp; @@ -350,6 +334,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, @@ -371,12 +356,10 @@ 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 (nameservers) { + int num = g_strv_length (nameservers); for (i = 0; i < num; i++) { if (i == 3) { @@ -391,14 +374,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); @@ -536,30 +519,67 @@ out: return *error ? FALSE : TRUE; } +static void +compute_hash (NMDnsManager *self, gpointer *hash) +{ + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); + gpointer check[HLEN]; + GSList *iter; + int i = 0; + + memset (check, 0, sizeof (check)); + + if (priv->ip4_vpn_config) + check[i++] = priv->ip4_vpn_config; + if (priv->ip4_device_config) + check[i++] = priv->ip4_device_config; + + if (priv->ip6_vpn_config) + check[i++] = priv->ip6_vpn_config; + if (priv->ip6_device_config) + check[i++] = priv->ip6_device_config; + + /* Add two more "other" configs if any exist */ + for (iter = priv->configs; iter && i < HLEN; 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)) + check[i++] = iter->data; + } + memcpy (hash, check, sizeof (check)); +} + 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); priv->last_iface = g_strdup (iface); } + /* Update hash with config we're applying */ + compute_hash (self, priv->hash); + rc.nameservers = g_ptr_array_new (); rc.domain = NULL; rc.searches = g_ptr_array_new (); @@ -640,6 +660,71 @@ 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); + + /* If caching was successful, we only send 127.0.0.1 to /etc/resolv.conf + * to ensure that the glibc resolver doesn't try to round-robin nameservers, + * but only uses the local caching nameserver. + */ + if (caching) { + if (nameservers) + g_strfreev (nameservers); + nameservers = g_new0 (char*, 2); + nameservers[0] = g_strdup ("127.0.0.1"); + } + #ifdef RESOLVCONF_PATH success = dispatch_resolvconf (domain, searches, nameservers, iface, error); #endif @@ -668,6 +753,43 @@ 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); + } +} + +static gboolean +config_changed (NMDnsManager *self) +{ + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); + gpointer check[HLEN]; + + /* We only store HLEN configs; so if there are actually more than that, + * we have to assume that the config has changed. + */ + if (g_slist_length (priv->configs) > HLEN) + return TRUE; + + /* Otherwise return TRUE if the configuration has changed */ + compute_hash (self, check); + return memcmp (check, priv->hash, sizeof (check)) ? TRUE : FALSE; +} + gboolean nm_dns_manager_add_ip4_config (NMDnsManager *mgr, const char *iface, @@ -698,9 +820,14 @@ 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 (!config_changed (mgr)) + return TRUE; + + 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; @@ -733,10 +860,14 @@ 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 (config_changed (mgr)) + return TRUE; + + 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; @@ -774,9 +905,14 @@ 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 (config_changed (mgr)) + return TRUE; + + 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; @@ -809,10 +945,14 @@ 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 (config_changed (mgr)) + return TRUE; + + 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; @@ -846,12 +986,85 @@ 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); } } +static void +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 */ + for (iter = plugins; iter && *iter; iter++) { + if (!strcasecmp (*iter, "dnsmasq")) + plugin = NM_DNS_PLUGIN (nm_dns_dnsmasq_new ()); + else if (!strcasecmp (*iter, "bind")) + plugin = NM_DNS_PLUGIN (nm_dns_bind_new ()); + else { + 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; + } + + 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 */ + } +} + +/******************************************************************/ + +NMDnsManager * +nm_dns_manager_get (const char **plugins) +{ + static NMDnsManager * singleton = NULL; + + if (!singleton) { + singleton = NM_DNS_MANAGER (g_object_new (NM_TYPE_DNS_MANAGER, NULL)); + g_assert (singleton); + load_plugins (singleton, plugins); + } else + g_object_ref (singleton); + + return singleton; +} + +GQuark +nm_dns_manager_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm_dns_manager_error"); + + return quark; +} static void nm_dns_manager_init (NMDnsManager *mgr) @@ -868,6 +1081,9 @@ nm_dns_manager_finalize (GObject *object) g_free (priv->hostname); g_free (priv->last_iface); + g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL); + g_slist_free (priv->plugins); + G_OBJECT_CLASS (nm_dns_manager_parent_class)->finalize (object); } diff --git a/src/dns-manager/nm-dns-manager.h b/src/dns-manager/nm-dns-manager.h index 0002d69990..eb1c73a7c2 100644 --- a/src/dns-manager/nm-dns-manager.h +++ b/src/dns-manager/nm-dns-manager.h @@ -67,7 +67,7 @@ typedef struct { GType nm_dns_manager_get_type (void); -NMDnsManager * nm_dns_manager_get (void); +NMDnsManager * nm_dns_manager_get (const char **plugins); gboolean nm_dns_manager_add_ip4_config (NMDnsManager *mgr, const char *iface, diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c new file mode 100644 index 0000000000..d47640b1c3 --- /dev/null +++ b/src/dns-manager/nm-dns-plugin.c @@ -0,0 +1,318 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + * + */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <glib.h> + +#include "nm-dns-plugin.h" +#include "nm-logging.h" + +typedef struct { + gboolean disposed; + + GPid pid; + guint32 watch_id; + char *progname; + char *pidfile; +} NMDnsPluginPrivate; + +#define NM_DNS_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_PLUGIN, NMDnsPluginPrivate)) + +G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {}) + +enum { + FAILED, + CHILD_QUIT, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +/********************************************/ + +gboolean +nm_dns_plugin_update (NMDnsPlugin *self, + const GSList *vpn_configs, + const GSList *dev_configs, + const GSList *other_configs, + const char *hostname) +{ + g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE); + + return NM_DNS_PLUGIN_GET_CLASS (self)->update (self, + vpn_configs, + dev_configs, + other_configs, + hostname); +} + +static gboolean +is_caching (NMDnsPlugin *self) +{ + return FALSE; +} + +gboolean +nm_dns_plugin_is_caching (NMDnsPlugin *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); +} + +/********************************************/ + +static void +kill_existing (const char *progname, const char *pidfile, const char *kill_match) +{ + char *contents = NULL; + glong pid; + char *proc_path = NULL; + char *cmdline_contents = NULL; + + if (!g_file_get_contents (pidfile, &contents, NULL, NULL)) + return; + + pid = strtol (contents, NULL, 10); + if (pid < 1 || pid > INT_MAX) + goto out; + + proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid); + if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL)) + goto out; + + if (strstr (cmdline_contents, kill_match)) { + if (kill (pid, 0)) { + nm_log_dbg (LOGD_DNS, "Killing stale %s child process %ld", progname, pid); + kill (pid, SIGKILL); + } + unlink (pidfile); + } + +out: + g_free (cmdline_contents); + g_free (proc_path); + g_free (contents); +} + +static void +watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMDnsPlugin *self = NM_DNS_PLUGIN (user_data); + NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); + + priv->pid = 0; + g_free (priv->progname); + priv->progname = NULL; + + g_signal_emit (self, signals[CHILD_QUIT], 0, status); +} + +static void +child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +GPid +nm_dns_plugin_child_spawn (NMDnsPlugin *self, + const char **argv, + const char *pidfile, + const char *kill_match) +{ + NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); + GError *error = NULL; + char *cmdline; + + g_return_val_if_fail (argv != NULL, 0); + g_return_val_if_fail (argv[0] != NULL, 0); + + g_warn_if_fail (priv->progname == NULL); + g_free (priv->progname); + priv->progname = g_path_get_basename (argv[0]); + + if (pidfile) { + g_return_val_if_fail (kill_match != NULL, 0); + kill_existing (priv->progname, pidfile, kill_match); + + g_free (priv->pidfile); + priv->pidfile = g_strdup (pidfile); + } + + nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname); + cmdline = g_strjoinv (" ", (char **) argv); + nm_log_dbg (LOGD_DNS, "DNS: command line: %s", cmdline); + g_free (cmdline); + + priv->pid = 0; + if (g_spawn_async (NULL, (char **) argv, NULL, + G_SPAWN_DO_NOT_REAP_CHILD, + child_setup, + NULL, &priv->pid, + &error)) { + nm_log_dbg (LOGD_DNS, "%s started with pid %d", priv->progname, priv->pid); + priv->watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) watch_cb, self); + } else { + nm_log_warn (LOGD_DNS, "Failed to spawn %s: (%d) %s", + priv->progname, error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } + + return priv->pid; +} + +typedef struct { + int pid; + char *progname; +} KillInfo; + +static gboolean +ensure_killed (gpointer data) +{ + KillInfo *info = data; + + if (kill (info->pid, 0) == 0) + kill (info->pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_DNS, "waiting for %s pid %d to exit", info->progname, info->pid); + waitpid (info->pid, NULL, 0); + nm_log_dbg (LOGD_DNS, "dnsmasq pid %d cleaned up", info->progname, info->pid); + + g_free (info->progname); + g_free (info); + return FALSE; +} + +gboolean 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; + } + + if (priv->pid) { + KillInfo *info; + + if (kill (priv->pid, SIGTERM) == 0) { + info = g_malloc0 (sizeof (KillInfo)); + info->pid = priv->pid; + info->progname = g_strdup (priv->progname); + g_timeout_add_seconds (2, ensure_killed, info); + } else { + kill (priv->pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_DNS, "waiting for %s pid %d to exit", priv->progname, priv->pid); + waitpid (priv->pid, NULL, 0); + nm_log_dbg (LOGD_DNS, "%s pid %d cleaned up", priv->progname, priv->pid); + } + priv->pid = 0; + g_free (priv->progname); + priv->progname = NULL; + } + + if (priv->pidfile) { + unlink (priv->pidfile); + g_free (priv->pidfile); + priv->pidfile = NULL; + } + + return TRUE; +} + +/********************************************/ + +static void +nm_dns_plugin_init (NMDnsPlugin *self) +{ +} + +static void +dispose (GObject *object) +{ + NMDnsPlugin *self = NM_DNS_PLUGIN (object); + NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); + + if (!priv->disposed) { + priv->disposed = TRUE; + + nm_dns_plugin_child_kill (self); + } + + G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDnsPlugin *self = NM_DNS_PLUGIN (object); + NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); + + g_free (priv->progname); + g_free (priv->pidfile); + + G_OBJECT_CLASS (nm_dns_plugin_parent_class)->finalize (object); +} + +static void +nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (plugin_class); + + g_type_class_add_private (plugin_class, sizeof (NMDnsPluginPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + object_class->finalize = finalize; + plugin_class->is_caching = is_caching; + + /* signals */ + signals[FAILED] = + g_signal_new (NM_DNS_PLUGIN_FAILED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDnsPluginClass, failed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[CHILD_QUIT] = + g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDnsPluginClass, child_quit), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); +} + diff --git a/src/dns-manager/nm-dns-plugin.h b/src/dns-manager/nm-dns-plugin.h new file mode 100644 index 0000000000..d4298b869d --- /dev/null +++ b/src/dns-manager/nm-dns-plugin.h @@ -0,0 +1,112 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#ifndef NM_DNS_PLUGIN_H +#define NM_DNS_PLUGIN_H + +#include <glib.h> +#include <glib-object.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)) +#define NM_DNS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass)) +#define NM_IS_DNS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNS_PLUGIN)) +#define NM_IS_DNS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNS_PLUGIN)) +#define NM_DNS_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass)) + +#define NM_DNS_PLUGIN_FAILED "failed" +#define NM_DNS_PLUGIN_CHILD_QUIT "child-quit" + +typedef struct { + GObject parent; +} NMDnsPlugin; + +typedef struct { + GObjectClass parent; + + /* Methods */ + gboolean (*init) (NMDnsPlugin *self); + + /* Called when DNS information is changed. 'vpn_configs' is a list of + * 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 + * order. + */ + gboolean (*update) (NMDnsPlugin *self, + const GSList *vpn_configs, + const GSList *dev_configs, + const GSList *other_configs, + const char *hostname); + + /* Subclasses should override and return TRUE if they start a local + * caching nameserver that listens on localhost and would block any + * other local caching nameserver from operating. + */ + gboolean (*is_caching) (NMDnsPlugin *self); + + /* Subclasses should override this and return their plugin name */ + const char *(*get_name) (NMDnsPlugin *self); + + /* Signals */ + + /* Emitted by the plugin and consumed by NMDnsManager when + * some error happens with the nameserver subprocess. Causes NM to fall + * back to writing out a non-local-caching resolv.conf until the next + * DNS update. + */ + void (*failed) (NMDnsPlugin *self); + + /* Emitted by the plugin base class when the nameserver subprocess + * quits. This signal is consumed by the plugin subclasses and not + * by NMDnsManager. If the subclass decides the exit status (as returned + * by waitpid(2)) is fatal it should then emit the 'failed' signal. + */ + void (*child_quit) (NMDnsPlugin *self, gint status); +} NMDnsPluginClass; + +GType nm_dns_plugin_get_type (void); + +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, + const GSList *dev_configs, + const GSList *other_configs, + const char *hostname); + +/* For subclasses/plugins */ + +/* Spawn a child process and watch for it to quit. 'argv' is the NULL-terminated + * argument vector to spawn the child with, where argv[0] is the full path to + * the child's executable. If 'pidfile' is given the process owning the PID + * contained in 'pidfile' will be killed if its command line matches 'kill_match' + * and the pidfile will be deleted. + */ +GPid nm_dns_plugin_child_spawn (NMDnsPlugin *self, + const char **argv, + const char *pidfile, + const char *kill_match); + +gboolean nm_dns_plugin_child_kill (NMDnsPlugin *self); + +#endif /* NM_DNS_PLUGIN_H */ + diff --git a/src/main.c b/src/main.c index 01bca9dc3e..7e75f05efb 100644 --- a/src/main.c +++ b/src/main.c @@ -300,6 +300,7 @@ static gboolean parse_config_file (const char *filename, char **plugins, char **dhcp_client, + char ***dns_plugins, char **log_level, char **log_domains, GError **error) @@ -322,6 +323,7 @@ parse_config_file (const char *filename, return FALSE; *dhcp_client = g_key_file_get_value (config, "main", "dhcp", NULL); + *dns_plugins = g_key_file_get_string_list (config, "main", "dns", NULL, NULL); *log_level = g_key_file_get_value (config, "logging", "level", NULL); *log_domains = g_key_file_get_value (config, "logging", "domains", NULL); @@ -442,6 +444,7 @@ main (int argc, char *argv[]) char *pidfile = NULL, *state_file = NULL, *dhcp = NULL; char *config = NULL, *plugins = NULL, *conf_plugins = NULL; char *log_level = NULL, *log_domains = NULL; + char **dns = NULL; gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE; gboolean success; NMPolicy *policy = NULL; @@ -515,7 +518,7 @@ main (int argc, char *argv[]) /* Parse the config file */ if (config) { - if (!parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error)) { + if (!parse_config_file (config, &conf_plugins, &dhcp, &dns, &cfg_log_level, &cfg_log_domains, &error)) { fprintf (stderr, "Config file %s invalid: (%d) %s\n", config, error ? error->code : -1, @@ -535,7 +538,7 @@ main (int argc, char *argv[]) /* Try deprecated nm-system-settings.conf first */ if (g_file_test (NM_OLD_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) { config = g_strdup (NM_OLD_SYSTEM_CONF_FILE); - parsed = parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error); + parsed = parse_config_file (config, &conf_plugins, &dhcp, &dns, &cfg_log_level, &cfg_log_domains, &error); if (!parsed) { fprintf (stderr, "Default config file %s invalid: (%d) %s\n", config, @@ -550,7 +553,7 @@ main (int argc, char *argv[]) /* Try the preferred NetworkManager.conf last */ if (!parsed && g_file_test (NM_DEFAULT_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) { config = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE); - parsed = parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error); + parsed = parse_config_file (config, &conf_plugins, &dhcp, &dns, &cfg_log_level, &cfg_log_domains, &error); if (!parsed) { fprintf (stderr, "Default config file %s invalid: (%d) %s\n", config, @@ -663,7 +666,7 @@ main (int argc, char *argv[]) goto done; } - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get ((const char **) dns); if (!dns_mgr) { nm_log_err (LOGD_CORE, "failed to start the DNS manager."); goto done; @@ -756,6 +759,7 @@ done: g_free (config); g_free (plugins); g_free (dhcp); + g_strfreev (dns); g_free (log_level); g_free (log_domains); g_free (cfg_log_level); diff --git a/src/nm-device.c b/src/nm-device.c index ad1976b03c..abffe1264f 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -3038,7 +3038,7 @@ nm_device_set_ip4_config (NMDevice *self, if (diff == NM_IP4_COMPARE_FLAG_NONE) return TRUE; - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get (NULL); if (old_config) { /* Remove any previous IP4 Config from the DNS manager */ nm_dns_manager_remove_ip4_config (dns_mgr, ip_iface, old_config); @@ -3141,7 +3141,7 @@ nm_device_set_ip6_config (NMDevice *self, if (diff == NM_IP6_COMPARE_FLAG_NONE) return TRUE; - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get (NULL); if (old_config) { /* Remove any previous IP6 Config from the DNS manager */ nm_dns_manager_remove_ip6_config (dns_mgr, ip_iface, old_config); diff --git a/src/nm-policy.c b/src/nm-policy.c index 28864a8780..3ab4db550c 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -241,7 +241,7 @@ _set_hostname (NMPolicy *policy, g_free (policy->cur_hostname); policy->cur_hostname = g_strdup (new_hostname); - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get (NULL); nm_dns_manager_set_hostname (dns_mgr, policy->cur_hostname); g_object_unref (dns_mgr); } @@ -553,7 +553,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) nm_act_request_set_default (req, FALSE); } - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get (NULL); nm_dns_manager_add_ip4_config (dns_mgr, ip_iface, ip4_config, dns_type); g_object_unref (dns_mgr); @@ -679,7 +679,7 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update) nm_act_request_set_default6 (req, FALSE); } - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get (NULL); nm_dns_manager_add_ip6_config (dns_mgr, ip_iface, ip6_config, dns_type); g_object_unref (dns_mgr); diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index d496e48ee3..cf844992c0 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -549,7 +549,7 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, config); /* Add the VPN to DNS */ - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get (NULL); nm_dns_manager_add_ip4_config (dns_mgr, priv->ip_iface, config, NM_DNS_IP_CONFIG_TYPE_VPN); g_object_unref (dns_mgr); @@ -902,7 +902,7 @@ vpn_cleanup (NMVPNConnection *connection) NMDnsManager *dns_mgr; /* Remove attributes of the VPN's IP4 Config */ - dns_mgr = nm_dns_manager_get (); + dns_mgr = nm_dns_manager_get (NULL); nm_dns_manager_remove_ip4_config (dns_mgr, priv->ip_iface, priv->ip4_config); g_object_unref (dns_mgr); |