diff options
Diffstat (limited to 'src/dns-manager/nm-dns-dnsmasq.c')
| -rw-r--r-- | src/dns-manager/nm-dns-dnsmasq.c | 286 |
1 files changed, 199 insertions, 87 deletions
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c index 4cee1608..69b26068 100644 --- a/src/dns-manager/nm-dns-dnsmasq.c +++ b/src/dns-manager/nm-dns-dnsmasq.c @@ -26,12 +26,14 @@ #include <sys/wait.h> #include <arpa/inet.h> #include <sys/stat.h> +#include <linux/if.h> #include "nm-dns-dnsmasq.h" +#include "nm-core-internal.h" +#include "nm-platform.h" #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-dns-utils.h" #include "nm-bus-manager.h" #include "NetworkManagerUtils.h" @@ -48,6 +50,7 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) typedef struct { GDBusProxy *dnsmasq; GCancellable *dnsmasq_cancellable; + GCancellable *update_cancellable; gboolean running; GVariant *set_server_ex_args; @@ -68,6 +71,70 @@ typedef struct { /*****************************************************************************/ +static char ** +get_ip4_rdns_domains (NMIP4Config *ip4) +{ + char **strv; + GPtrArray *domains = NULL; + int i; + + g_return_val_if_fail (ip4 != NULL, NULL); + + domains = g_ptr_array_sized_new (5); + + for (i = 0; i < nm_ip4_config_get_num_addresses (ip4); i++) { + const NMPlatformIP4Address *address = nm_ip4_config_get_address (ip4, i); + + nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); + } + + for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) { + const NMPlatformIP4Route *route = nm_ip4_config_get_route (ip4, i); + + nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); + } + + /* Terminating NULL so we can use g_strfreev() to free it */ + g_ptr_array_add (domains, NULL); + + /* Free the array and return NULL if the only element was the ending NULL */ + strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); + + return _nm_utils_strv_cleanup (strv, FALSE, FALSE, TRUE); +} + +static char ** +get_ip6_rdns_domains (NMIP6Config *ip6) +{ + char **strv; + GPtrArray *domains = NULL; + int i; + + g_return_val_if_fail (ip6 != NULL, NULL); + + domains = g_ptr_array_sized_new (5); + + for (i = 0; i < nm_ip6_config_get_num_addresses (ip6); i++) { + const NMPlatformIP6Address *address = nm_ip6_config_get_address (ip6, i); + + nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); + } + + for (i = 0; i < nm_ip6_config_get_num_routes (ip6); i++) { + const NMPlatformIP6Route *route = nm_ip6_config_get_route (ip6, i); + + nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); + } + + /* Terminating NULL so we can use g_strfreev() to free it */ + g_ptr_array_add (domains, NULL); + + /* Free the array and return NULL if the only element was the ending NULL */ + strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); + + return _nm_utils_strv_cleanup (strv, FALSE, FALSE, TRUE); +} + static void add_dnsmasq_nameserver (NMDnsDnsmasq *self, GVariantBuilder *servers, @@ -89,13 +156,16 @@ add_dnsmasq_nameserver (NMDnsDnsmasq *self, } static gboolean -add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, gboolean split) +add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, + const char *iface, gboolean split) { - char buf[INET_ADDRSTRLEN]; + char buf[INET_ADDRSTRLEN + 1 + IFNAMSIZ]; + char buf2[INET_ADDRSTRLEN]; in_addr_t addr; int nnameservers, i_nameserver, n, i; gboolean added = FALSE; + g_return_val_if_fail (iface, FALSE); nnameservers = nm_ip4_config_get_num_nameservers (ip4); if (split) { @@ -106,7 +176,8 @@ add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { addr = nm_ip4_config_get_nameserver (ip4, i_nameserver); - nm_utils_inet4_ntop (addr, buf); + g_snprintf (buf, sizeof (buf), "%s@%s", + nm_utils_inet4_ntop (addr, buf2), iface); /* searches are preferred over domains */ n = nm_ip4_config_get_num_searches (ip4); @@ -133,22 +204,22 @@ add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, /* Ensure reverse-DNS works by directing queries for in-addr.arpa * domains to the split domain's nameserver. */ - domains = nm_dns_utils_get_ip4_rdns_domains (ip4); + domains = get_ip4_rdns_domains (ip4); if (domains) { for (iter = domains; iter && *iter; iter++) add_dnsmasq_nameserver (self, servers, buf, *iter); g_strfreev (domains); - added = TRUE; } } } - /* If no searches or domains, just add the namservers */ + /* If no searches or domains, just add the nameservers */ if (!added) { for (i = 0; i < nnameservers; i++) { addr = nm_ip4_config_get_nameserver (ip4, i); - add_dnsmasq_nameserver (self, servers, - nm_utils_inet4_ntop (addr, NULL), NULL); + g_snprintf (buf, sizeof (buf), "%s@%s", + nm_utils_inet4_ntop (addr, buf2), iface); + add_dnsmasq_nameserver (self, servers, buf, NULL); } } @@ -158,23 +229,22 @@ add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, static char * ip6_addr_to_string (const struct in6_addr *addr, const char *iface) { - char *buf; + char buf[NM_UTILS_INET_ADDRSTRLEN]; - if (IN6_IS_ADDR_V4MAPPED (addr)) { - buf = g_malloc (INET_ADDRSTRLEN); + if (IN6_IS_ADDR_V4MAPPED (addr)) nm_utils_inet4_ntop (addr->s6_addr32[3], buf); - } else if (!iface || !iface[0] || !IN6_IS_ADDR_LINKLOCAL (addr)) { - buf = g_malloc (INET6_ADDRSTRLEN); + else nm_utils_inet6_ntop (addr, buf); - } else { - /* Need to scope the address with %<zone-id>. Before dnsmasq 2.58, - * only '@' was supported as delimiter. Since 2.58, '@' and '%' - * are supported. Due to a bug, since 2.73 only '%' works properly - * as "server" address. - */ - buf = g_strconcat (nm_utils_inet6_ntop (addr, NULL), "%", iface, NULL); - } - return buf; + + /* Need to scope link-local addresses with %<zone-id>. Before dnsmasq 2.58, + * only '@' was supported as delimiter. Since 2.58, '@' and '%' are + * supported. Due to a bug, since 2.73 only '%' works properly as "server" + * address. + */ + return g_strdup_printf ("%s%c%s", + buf, + IN6_IS_ADDR_LINKLOCAL (addr) ? '%' : '@', + iface); } static void @@ -202,20 +272,20 @@ add_global_config (NMDnsDnsmasq *self, GVariantBuilder *dnsmasq_servers, const N } static gboolean -add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, gboolean split) +add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, + const char *iface, gboolean split) { const struct in6_addr *addr; char *buf = NULL; int nnameservers, i_nameserver, n, i; gboolean added = FALSE; - const char *iface; + g_return_val_if_fail (iface, FALSE); nnameservers = nm_ip6_config_get_num_nameservers (ip6); - iface = g_object_get_data (G_OBJECT (ip6), IP_CONFIG_IFACE_TAG); - g_assert (iface); - if (split) { + char **domains, **iter; + if (nnameservers == 0) return FALSE; @@ -245,11 +315,21 @@ add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, } } + /* Ensure reverse-DNS works by directing queries for ip6.arpa + * domains to the split domain's nameserver. + */ + domains = get_ip6_rdns_domains (ip6); + if (domains) { + for (iter = domains; iter && *iter; iter++) + add_dnsmasq_nameserver (self, servers, buf, *iter); + g_strfreev (domains); + } + g_free (buf); } } - /* If no searches or domains, just add the namservers */ + /* If no searches or domains, just add the nameservers */ if (!added) { for (i = 0; i < nnameservers; i++) { addr = nm_ip6_config_get_nameserver (ip6, i); @@ -264,19 +344,71 @@ add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, return TRUE; } +static gboolean +add_ip_config_data (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigData *data) +{ + if (NM_IS_IP4_CONFIG (data->config)) { + return add_ip4_config (self, + servers, + (NMIP4Config *) data->config, + data->iface, + data->type == NM_DNS_IP_CONFIG_TYPE_VPN); + } else if (NM_IS_IP6_CONFIG (data->config)) { + return add_ip6_config (self, + servers, + (NMIP6Config *) data->config, + data->iface, + data->type == NM_DNS_IP_CONFIG_TYPE_VPN); + } else + g_return_val_if_reached (FALSE); +} + static void -dnsmasq_update_done (GObject *source, GAsyncResult *res, gpointer user_data) +dnsmasq_clear_cache_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { - NMDnsDnsmasq *self = NM_DNS_DNSMASQ (user_data); - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + NMDnsDnsmasq *self; gs_free_error GError *error = NULL; gs_unref_variant GVariant *response = NULL; - response = g_dbus_proxy_call_finish (priv->dnsmasq, res, &error); + response = g_dbus_proxy_call_finish (proxy, res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_DNS_DNSMASQ (user_data); + if (!response) - _LOGW ("dnsmasq update failed: %s", error->message); + _LOGW ("dnsmasq cache clear failed: %s", error->message); else - _LOGD ("dnsmasq update successful"); + _LOGD ("dnsmasq update successful, cache cleared"); +} + +static void +dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) +{ + NMDnsDnsmasq *self; + NMDnsDnsmasqPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *response = NULL; + + response = g_dbus_proxy_call_finish (proxy, res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_DNS_DNSMASQ (user_data); + priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + + if (!response) + _LOGW ("dnsmasq update failed: %s", error->message); + else { + g_dbus_proxy_call (priv->dnsmasq, + "ClearCache", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->update_cancellable, + (GAsyncReadyCallback) dnsmasq_clear_cache_done, + self); + } } static void @@ -290,12 +422,15 @@ send_dnsmasq_update (NMDnsDnsmasq *self) if (priv->running) { _LOGD ("trying to update dnsmasq nameservers"); + nm_clear_g_cancellable (&priv->update_cancellable); + priv->update_cancellable = g_cancellable_new (); + g_dbus_proxy_call (priv->dnsmasq, "SetServersEx", priv->set_server_ex_args, G_DBUS_CALL_FLAGS_NONE, -1, - NULL, + priv->update_cancellable, (GAsyncReadyCallback) dnsmasq_update_done, self); g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); @@ -373,11 +508,16 @@ start_dnsmasq (NMDnsDnsmasq *self) NMBusManager *dbus_mgr; GDBusConnection *connection; + if (priv->running) { + /* the dnsmasq process is running. Nothing to do. */ + return; + } - if ( priv->running - || priv->dnsmasq - || priv->dnsmasq_cancellable) + if (nm_dns_plugin_child_pid ((NMDnsPlugin *) self) > 0) { + /* if we already have a child process spawned, don't do + * it again. */ return; + } dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL); if (!dm_binary) { @@ -409,6 +549,13 @@ start_dnsmasq (NMDnsDnsmasq *self) if (!pid) return; + if ( priv->dnsmasq + || priv->dnsmasq_cancellable) { + /* we already have a proxy or are about to create it. + * We are done. */ + return; + } + dbus_mgr = nm_bus_manager_get (); g_return_if_fail (dbus_mgr); @@ -429,15 +576,12 @@ start_dnsmasq (NMDnsDnsmasq *self) static gboolean update (NMDnsPlugin *plugin, - const GSList *vpn_configs, - const GSList *dev_configs, - const GSList *other_configs, + const NMDnsIPConfigData **configs, const NMGlobalDnsConfig *global_config, const char *hostname) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); - const GSList *iter; GVariantBuilder servers; start_dnsmasq (self); @@ -447,28 +591,9 @@ update (NMDnsPlugin *plugin, if (global_config) add_global_config (self, &servers, global_config); else { - /* Use split DNS for VPN configs */ - for (iter = vpn_configs; iter; iter = g_slist_next (iter)) { - if (NM_IS_IP4_CONFIG (iter->data)) - add_ip4_config (self, &servers, iter->data, TRUE); - else if (NM_IS_IP6_CONFIG (iter->data)) - add_ip6_config (self, &servers, iter->data, TRUE); - } - - /* Now add interface configs without split DNS */ - for (iter = dev_configs; iter; iter = g_slist_next (iter)) { - if (NM_IS_IP4_CONFIG (iter->data)) - add_ip4_config (self, &servers, iter->data, FALSE); - else if (NM_IS_IP6_CONFIG (iter->data)) - add_ip6_config (self, &servers, iter->data, FALSE); - } - - /* And any other random configs */ - for (iter = other_configs; iter; iter = g_slist_next (iter)) { - if (NM_IS_IP4_CONFIG (iter->data)) - add_ip4_config (self, &servers, iter->data, FALSE); - else if (NM_IS_IP6_CONFIG (iter->data)) - add_ip6_config (self, &servers, iter->data, FALSE); + while (*configs) { + add_ip_config_data (self, &servers, *configs); + configs++; } } @@ -482,39 +607,23 @@ update (NMDnsPlugin *plugin, /****************************************************************/ -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); + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); gboolean failed = TRUE; int err; if (WIFEXITED (status)) { err = WEXITSTATUS (status); if (err) { - _LOGW ("dnsmasq exited with error: %s (%d)", - dm_exit_code_to_msg (err), - err); - } else + _LOGW ("dnsmasq exited with error: %s", + nm_utils_dnsmasq_status_to_string (err, NULL, 0)); + } else { + _LOGD ("dnsmasq exited normally"); failed = FALSE; + } } else if (WIFSTOPPED (status)) _LOGW ("dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status)); else if (WIFSIGNALED (status)) @@ -522,6 +631,8 @@ child_quit (NMDnsPlugin *plugin, gint status) else _LOGW ("dnsmasq died from an unknown cause"); + priv->running = FALSE; + if (failed) g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); } @@ -559,6 +670,7 @@ dispose (GObject *object) NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (object); nm_clear_g_cancellable (&priv->dnsmasq_cancellable); + nm_clear_g_cancellable (&priv->update_cancellable); g_clear_object (&priv->dnsmasq); |