From ee9c73a923909e23a649407be77e25235d769e25 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Fri, 11 May 2018 22:08:45 +0200 Subject: New upstream version 1.10.8 --- src/dns/nm-dns-dnsmasq.c | 344 +++++++++------- src/dns/nm-dns-manager.c | 819 ++++++++++++++++---------------------- src/dns/nm-dns-manager.h | 33 +- src/dns/nm-dns-plugin.c | 4 +- src/dns/nm-dns-plugin.h | 4 +- src/dns/nm-dns-systemd-resolved.c | 289 ++++++-------- src/dns/nm-dns-unbound.c | 2 +- 7 files changed, 702 insertions(+), 793 deletions(-) (limited to 'src/dns') diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index d0753078..e6436c79 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -35,7 +35,7 @@ #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-dbus-manager.h" +#include "nm-bus-manager.h" #include "NetworkManagerUtils.h" #define PIDFILE NMRUNDIR "/dnsmasq.pid" @@ -76,41 +76,54 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) /*****************************************************************************/ static char ** -get_ip_rdns_domains (NMIPConfig *ip_config) +get_ip4_rdns_domains (NMIP4Config *ip4) { - int addr_family = nm_ip_config_get_addr_family (ip_config); char **strv; GPtrArray *domains = NULL; NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; - nm_assert_addr_family (addr_family); + g_return_val_if_fail (ip4 != NULL, NULL); domains = g_ptr_array_sized_new (5); - if (addr_family == AF_INET) { - NMIP4Config *ip4 = (gpointer) ip_config; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route *route; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) + nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) - nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); + } - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); - } - } else { - NMIP6Config *ip6 = (gpointer) ip_config; - const NMPlatformIP6Address *address; - const NMPlatformIP6Route *route; + /* Terminating NULL so we can use g_strfreev() to free it */ + g_ptr_array_add (domains, NULL); - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) - nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); + /* Free the array and return NULL if the only element was the ending NULL */ + strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); - } + return _nm_utils_strv_cleanup (strv, FALSE, FALSE, TRUE); +} + +static char ** +get_ip6_rdns_domains (NMIP6Config *ip6) +{ + char **strv; + GPtrArray *domains = NULL; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *route; + + g_return_val_if_fail (ip6 != NULL, NULL); + + domains = g_ptr_array_sized_new (5); + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) + nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); } /* Terminating NULL so we can use g_strfreev() to free it */ @@ -142,43 +155,96 @@ add_dnsmasq_nameserver (NMDnsDnsmasq *self, g_variant_builder_close (servers); } -#define IP_ADDR_TO_STRING_BUFLEN (NM_UTILS_INET_ADDRSTRLEN + 1 + IFNAMSIZ) - -static const char * -ip_addr_to_string (int addr_family, gconstpointer addr, const char *iface, char *out_buf) +static gboolean +add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, + const char *iface, gboolean split) { - int n_written; - char buf2[NM_UTILS_INET_ADDRSTRLEN]; - const char *separator; + char buf[INET_ADDRSTRLEN + 1 + IFNAMSIZ]; + char buf2[INET_ADDRSTRLEN]; + in_addr_t addr; + int nnameservers, i_nameserver, n, i; + gboolean added = FALSE; - nm_assert_addr_family (addr_family); - nm_assert (addr); - nm_assert (out_buf); + g_return_val_if_fail (iface, FALSE); + nnameservers = nm_ip4_config_get_num_nameservers (ip4); - if (addr_family == AF_INET) { - nm_utils_inet_ntop (addr_family, addr, buf2); - separator = "@"; - } else { - if (IN6_IS_ADDR_V4MAPPED (addr)) - nm_utils_inet4_ntop (((const struct in6_addr *) addr)->s6_addr32[3], buf2); - else - nm_utils_inet6_ntop (addr, buf2); - /* Need to scope link-local addresses with %. 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. - */ - separator = IN6_IS_ADDR_LINKLOCAL (addr) ? "%" : "@"; + if (split) { + char **domains, **iter; + + if (nnameservers == 0) + return FALSE; + + for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { + addr = nm_ip4_config_get_nameserver (ip4, i_nameserver); + 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); + for (i = 0; i < n; i++) { + add_dnsmasq_nameserver (self, + servers, + buf, + nm_ip4_config_get_search (ip4, i)); + 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++) { + add_dnsmasq_nameserver (self, + servers, + buf, + nm_ip4_config_get_domain (ip4, i)); + added = TRUE; + } + } + + /* Ensure reverse-DNS works by directing queries for in-addr.arpa + * domains to the split domain's nameserver. + */ + domains = get_ip4_rdns_domains (ip4); + if (domains) { + for (iter = domains; iter && *iter; iter++) + add_dnsmasq_nameserver (self, servers, buf, *iter); + g_strfreev (domains); + } + } + } + + /* 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); + g_snprintf (buf, sizeof (buf), "%s@%s", + nm_utils_inet4_ntop (addr, buf2), iface); + add_dnsmasq_nameserver (self, servers, buf, NULL); + } } - n_written = g_snprintf (out_buf, - IP_ADDR_TO_STRING_BUFLEN, - "%s%s%s", - buf2, - iface ? separator : "", - iface ?: ""); - nm_assert (n_written < IP_ADDR_TO_STRING_BUFLEN); - return out_buf; + return TRUE; +} + +static char * +ip6_addr_to_string (const struct in6_addr *addr, const char *iface) +{ + char buf[NM_UTILS_INET_ADDRSTRLEN]; + + if (IN6_IS_ADDR_V4MAPPED (addr)) + nm_utils_inet4_ntop (addr->s6_addr32[3], buf); + else + nm_utils_inet6_ntop (addr, buf); + + /* Need to scope link-local addresses with %. 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 @@ -205,100 +271,103 @@ add_global_config (NMDnsDnsmasq *self, GVariantBuilder *dnsmasq_servers, const N } } -static void -add_ip_config (NMDnsDnsmasq *self, - GVariantBuilder *servers, - int ifindex, - NMIPConfig *ip_config, - gboolean split) +static gboolean +add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, + const char *iface, gboolean split) { - int addr_family; - gconstpointer addr; + const struct in6_addr *addr; + char *buf = NULL; + int nnameservers, i_nameserver, n, i; gboolean added = FALSE; - guint nnameservers, i_nameserver, n, i; - char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; - char **domains, **iter; - gboolean iface_resolved = FALSE; - const char *iface = NULL, *domain; - addr_family = nm_ip_config_get_addr_family (ip_config); - g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - - nm_assert (ifindex > 0); - nm_assert (ifindex == nm_ip_config_get_ifindex (ip_config)); - - nnameservers = nm_ip_config_get_num_nameservers (ip_config); + g_return_val_if_fail (iface, FALSE); + nnameservers = nm_ip6_config_get_num_nameservers (ip6); if (split) { - if (nnameservers == 0) - return; - - if (!iface_resolved) { - iface = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); - iface_resolved = TRUE; - } - - if (iface) { - for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { - addr = nm_ip_config_get_nameserver (ip_config, i_nameserver); + char **domains, **iter; - ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf); + if (nnameservers == 0) + return FALSE; + + for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { + addr = nm_ip6_config_get_nameserver (ip6, i_nameserver); + buf = ip6_addr_to_string (addr, iface); + + /* searches are preferred over domains */ + n = nm_ip6_config_get_num_searches (ip6); + for (i = 0; i < n; i++) { + add_dnsmasq_nameserver (self, + servers, + buf, + nm_ip6_config_get_search (ip6, i)); + added = TRUE; + } - /* searches are preferred over domains */ - n = nm_ip_config_get_num_searches (ip_config); + if (n == 0) { + /* If not searches, use any domains */ + n = nm_ip6_config_get_num_domains (ip6); for (i = 0; i < n; i++) { - domain = nm_utils_parse_dns_domain (nm_ip_config_get_search (ip_config, i), NULL); add_dnsmasq_nameserver (self, servers, - ip_addr_to_string_buf, - domain); + buf, + nm_ip6_config_get_domain (ip6, i)); added = TRUE; } + } - if (n == 0) { - /* If not searches, use any domains */ - n = nm_ip_config_get_num_domains (ip_config); - domain = nm_utils_parse_dns_domain (nm_ip_config_get_domain (ip_config, i), NULL); - for (i = 0; i < n; i++) { - add_dnsmasq_nameserver (self, - servers, - ip_addr_to_string_buf, - domain); - added = TRUE; - } - } - - /* Ensure reverse-DNS works by directing queries for in-addr4.arpa/ip6.arpa - * domains to the split domain's nameserver. - */ - domains = get_ip_rdns_domains (ip_config); - if (domains) { - for (iter = domains; *iter; iter++) - add_dnsmasq_nameserver (self, servers, ip_addr_to_string_buf, *iter); - g_strfreev (domains); - } + /* 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 nameservers */ if (!added) { - if (!iface_resolved) - iface = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); - if (iface) { - for (i = 0; i < nnameservers; i++) { - addr = nm_ip_config_get_nameserver (ip_config, i); - ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf); - add_dnsmasq_nameserver (self, servers, ip_addr_to_string_buf, NULL); + for (i = 0; i < nnameservers; i++) { + addr = nm_ip6_config_get_nameserver (ip6, i); + buf = ip6_addr_to_string (addr, iface); + if (buf) { + add_dnsmasq_nameserver (self, servers, buf, NULL); + g_free (buf); } } } + + 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 (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { NMDnsDnsmasq *self; + NMDnsDnsmasqPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *response = NULL; @@ -307,6 +376,7 @@ dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) return; self = NM_DNS_DNSMASQ (user_data); + priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); if (!response) _LOGW ("dnsmasq update failed: %s", error->message); @@ -408,7 +478,7 @@ start_dnsmasq (NMDnsDnsmasq *self) const char *argv[15]; GPid pid = 0; guint idx = 0; - NMDBusManager *dbus_mgr; + NMBusManager *dbus_mgr; GDBusConnection *connection; if (priv->running) { @@ -460,10 +530,10 @@ start_dnsmasq (NMDnsDnsmasq *self) return; } - dbus_mgr = nm_dbus_manager_get (); + dbus_mgr = nm_bus_manager_get (); g_return_if_fail (dbus_mgr); - connection = nm_dbus_manager_get_connection (dbus_mgr); + connection = nm_bus_manager_get_connection (dbus_mgr); g_return_if_fail (connection); priv->dnsmasq_cancellable = g_cancellable_new (); @@ -480,16 +550,15 @@ start_dnsmasq (NMDnsDnsmasq *self) static gboolean update (NMDnsPlugin *plugin, + const GPtrArray *configs, const NMGlobalDnsConfig *global_config, - const CList *ip_config_lst_head, const char *hostname) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); GVariantBuilder servers; - int prio, first_prio = 0; - const NMDnsIPConfigData *ip_data; - gboolean is_first = TRUE; + guint i; + int prio, first_prio; start_dnsmasq (self); @@ -498,18 +567,15 @@ update (NMDnsPlugin *plugin, if (global_config) add_global_config (self, &servers, global_config); else { - c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { - prio = nm_ip_config_get_dns_priority (ip_data->ip_config); - if (is_first) { - is_first = FALSE; + for (i = 0; i < configs->len; i++) { + const NMDnsIPConfigData *data = configs->pdata[i]; + + prio = nm_ip_config_get_dns_priority (data->config); + if (i == 0) first_prio = prio; - } else if (first_prio < 0 && first_prio != prio) + else if (first_prio < 0 && first_prio != prio) break; - add_ip_config (self, - &servers, - ip_data->data->ifindex, - ip_data->ip_config, - ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN); + add_ip_config_data (self, &servers, data); } } diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 10a16e57..dc545470 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -46,7 +46,6 @@ #include "nm-ip6-config.h" #include "NetworkManagerUtils.h" #include "nm-config.h" -#include "nm-dbus-object.h" #include "devices/nm-device.h" #include "nm-manager.h" @@ -55,6 +54,8 @@ #include "nm-dns-systemd-resolved.h" #include "nm-dns-unbound.h" +#include "introspection/org.freedesktop.NetworkManager.DnsManager.h" + #define HASH_LEN 20 #ifndef RESOLVCONF_PATH @@ -69,24 +70,6 @@ #define PLUGIN_RATELIMIT_BURST 5 #define PLUGIN_RATELIMIT_DELAY 300 -/*****************************************************************************/ - -typedef enum { - SR_SUCCESS, - SR_NOTFOUND, - SR_ERROR -} SpawnResult; - -typedef struct { - GPtrArray *nameservers; - GPtrArray *searches; - GPtrArray *options; - const char *nis_domain; - GPtrArray *nis_servers; -} NMResolvConfData; - -/*****************************************************************************/ - enum { CONFIG_CHANGED, @@ -101,16 +84,44 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDnsManager, static guint signals[LAST_SIGNAL] = { 0 }; -typedef struct { - GHashTable *configs; - CList ip_config_lst_head; - GVariant *config_variant; +typedef enum { + SR_SUCCESS, + SR_NOTFOUND, + SR_ERROR +} SpawnResult; - NMDnsIPConfigData *best_ip_config_4; - NMDnsIPConfigData *best_ip_config_6; +NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); - bool ip_config_lst_need_sort:1; +/*****************************************************************************/ +#define _NMLOG_PREFIX_NAME "dns-mgr" +#define _NMLOG_DOMAIN LOGD_DNS +#define _NMLOG(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + char __prefix[20]; \ + const NMDnsManager *const __self = (self); \ + \ + _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + ((!__self || __self == singleton_instance) \ + ? "" \ + : nm_sprintf_buf (__prefix, "[%p]", __self)) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +typedef struct { + GPtrArray *configs; + GVariant *config_variant; + NMDnsIPConfigData *best_conf4, *best_conf6; + + bool need_sort:1; bool dns_touched:1; bool is_stopped:1; @@ -134,52 +145,20 @@ typedef struct { } NMDnsManagerPrivate; struct _NMDnsManager { - NMDBusObject parent; + NMExportedObject parent; NMDnsManagerPrivate _priv; }; struct _NMDnsManagerClass { - NMDBusObjectClass parent; + NMExportedObjectClass parent; }; -G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_DBUS_OBJECT) +G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_EXPORTED_OBJECT) #define NM_DNS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsManager, NM_IS_DNS_MANAGER) -NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "dns-mgr" -#define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - G_STMT_START { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ - char __prefix[20]; \ - const NMDnsManager *const __self = (self); \ - \ - _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((!__self || __self == singleton_instance) \ - ? "" \ - : nm_sprintf_buf (__prefix, "[%p]", __self)) \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - -static void _ip_config_dns_priority_changed (gpointer config, - GParamSpec *pspec, - NMDnsIPConfigData *ip_data); - -/*****************************************************************************/ - static gboolean -domain_is_valid (const char *domain, gboolean check_public_suffix) +domain_is_valid (const gchar *domain, gboolean check_public_suffix) { if (*domain == '\0') return FALSE; @@ -190,14 +169,16 @@ domain_is_valid (const char *domain, gboolean check_public_suffix) return TRUE; } -static gboolean -domain_is_routing (const char *domain) -{ - return domain[0] == '~'; -} - /*****************************************************************************/ +typedef struct { + GPtrArray *nameservers; + GPtrArray *searches; + GPtrArray *options; + const char *nis_domain; + GPtrArray *nis_servers; +} NMResolvConfData; + NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConfManager, NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN, "unknown"), @@ -211,109 +192,44 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConf NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_config_type_to_string, NMDnsIPConfigType, NM_UTILS_LOOKUP_DEFAULT_WARN (""), - NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_REMOVED, "removed"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_DEFAULT, "default"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, "best"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_VPN, "vpn"), ); -/*****************************************************************************/ - -static void -_ASSERT_config_data (const NMDnsConfigData *data) -{ - nm_assert (data); - nm_assert (NM_IS_DNS_MANAGER (data->self)); - nm_assert (data->ifindex > 0); -} - -static void -_ASSERT_ip_config_data (const NMDnsIPConfigData *ip_data) -{ - nm_assert (ip_data); - _ASSERT_config_data (ip_data->data); - nm_assert (NM_IS_IP_CONFIG (ip_data->ip_config)); - nm_assert (c_list_contains (&ip_data->data->data_lst_head, &ip_data->data_lst)); - nm_assert (ip_data->data->ifindex == nm_ip_config_get_ifindex (ip_data->ip_config)); -} - static NMDnsIPConfigData * -_ip_config_data_new (NMDnsConfigData *data, - NMIPConfig *ip_config, - NMDnsIPConfigType ip_config_type) -{ - NMDnsIPConfigData *ip_data; - - _ASSERT_config_data (data); - nm_assert (NM_IS_IP_CONFIG (ip_config)); - nm_assert (ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED); - - ip_data = g_slice_new0 (NMDnsIPConfigData); - ip_data->data = data; - ip_data->ip_config = g_object_ref (ip_config); - ip_data->ip_config_type = ip_config_type; - c_list_link_tail (&data->data_lst_head, &ip_data->data_lst); - c_list_link_tail (&NM_DNS_MANAGER_GET_PRIVATE (data->self)->ip_config_lst_head, &ip_data->ip_config_lst); - - g_signal_connect (ip_config, - NM_IS_IP4_CONFIG (ip_config) - ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY - : "notify::" NM_IP6_CONFIG_DNS_PRIORITY, - (GCallback) _ip_config_dns_priority_changed, ip_data); - - _ASSERT_ip_config_data (ip_data); - return ip_data; -} - -static void -_ip_config_data_free (NMDnsIPConfigData *ip_data) +ip_config_data_new (gpointer config, NMDnsIPConfigType type, const char *iface) { - _ASSERT_ip_config_data (ip_data); + NMDnsIPConfigData *data; - c_list_unlink_stale (&ip_data->data_lst); - c_list_unlink_stale (&ip_data->ip_config_lst); + data = g_slice_new0 (NMDnsIPConfigData); + data->config = g_object_ref (config); + data->iface = g_strdup (iface); + data->type = type; - g_signal_handlers_disconnect_by_func (ip_data->ip_config, - _ip_config_dns_priority_changed, - ip_data); - - g_object_unref (ip_data->ip_config); - g_slice_free (NMDnsIPConfigData, ip_data); -} - -static NMDnsIPConfigData * -_config_data_find_ip_config (NMDnsConfigData *data, - NMIPConfig *ip_config) -{ - NMDnsIPConfigData *ip_data; - - _ASSERT_config_data (data); - - c_list_for_each_entry (ip_data, &data->data_lst_head, data_lst) { - _ASSERT_ip_config_data (ip_data); - - if (ip_data->ip_config == ip_config) - return ip_data; - } - return NULL; + return data; } static void -_config_data_free (NMDnsConfigData *data) +ip_config_data_destroy (gpointer ptr) { - _ASSERT_config_data (data); + NMDnsIPConfigData *data = ptr; + + if (!data) + return; - nm_assert (c_list_is_empty (&data->data_lst_head)); - g_slice_free (NMDnsConfigData, data); + g_object_unref (data->config); + g_free (data->iface); + g_slice_free (NMDnsIPConfigData, data); } static gint -_ip_config_data_cmp (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) +ip_config_data_compare (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) { int a_prio, b_prio; - a_prio = nm_ip_config_get_dns_priority (a->ip_config); - b_prio = nm_ip_config_get_dns_priority (b->ip_config); + a_prio = nm_ip_config_get_dns_priority (a->config); + b_prio = nm_ip_config_get_dns_priority (b->config); /* Configurations with lower priority value first */ if (a_prio < b_prio) @@ -322,40 +238,24 @@ _ip_config_data_cmp (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) return 1; /* Sort also according to type */ - if (a->ip_config_type > b->ip_config_type) + if (a->type > b->type) return -1; - else if (a->ip_config_type < b->ip_config_type) + else if (a->type < b->type) return 1; return 0; } static gint -_ip_config_lst_cmp (const CList *a, - const CList *b, - const void *user_data) +ip_config_data_ptr_compare (gconstpointer a, gconstpointer b) { - return _ip_config_data_cmp (c_list_entry (a, NMDnsIPConfigData, ip_config_lst), - c_list_entry (b, NMDnsIPConfigData, ip_config_lst)); -} + const NMDnsIPConfigData *const *ptr_a = a, *const *ptr_b = b; -static CList * -_ip_config_lst_head (NMDnsManager *self) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - if (priv->ip_config_lst_need_sort) { - priv->ip_config_lst_need_sort = FALSE; - c_list_sort (&priv->ip_config_lst_head, _ip_config_lst_cmp, NULL); - } - - return &priv->ip_config_lst_head; + return ip_config_data_compare (*ptr_a, *ptr_b); } -/*****************************************************************************/ - static void -add_string_item (GPtrArray *array, const char *str, gboolean dup) +add_string_item (GPtrArray *array, const char *str) { int i; @@ -371,7 +271,7 @@ add_string_item (GPtrArray *array, const char *str, gboolean dup) } /* No dupes, add the new item */ - g_ptr_array_add (array, dup ? g_strdup (str): (gpointer) str); + g_ptr_array_add (array, g_strdup (str)); } static void @@ -381,56 +281,25 @@ add_dns_option_item (GPtrArray *array, const char *str) g_ptr_array_add (array, g_strdup (str)); } -static void -add_dns_domains (GPtrArray *array, const NMIPConfig *ip_config, - gboolean include_routing, gboolean dup) -{ - guint num_domains, num_searches, i; - const char *str; - - num_domains = nm_ip_config_get_num_domains (ip_config); - num_searches = nm_ip_config_get_num_searches (ip_config); - - for (i = 0; i < num_searches; i++) { - str = nm_ip_config_get_search (ip_config, i); - if (!include_routing && domain_is_routing (str)) - continue; - if (!domain_is_valid (nm_utils_parse_dns_domain (str, NULL), FALSE)) - continue; - add_string_item (array, str, dup); - } - if (num_domains > 1 || !num_searches) { - for (i = 0; i < num_domains; i++) { - str = nm_ip_config_get_domain (ip_config, i); - if (!include_routing && domain_is_routing (str)) - continue; - if (!domain_is_valid (nm_utils_parse_dns_domain (str, NULL), FALSE)) - continue; - add_string_item (array, str, dup); - } - } -} - static void merge_one_ip_config (NMResolvConfData *rc, - int ifindex, - const NMIPConfig *ip_config) + const NMIPConfig *config, + const char *iface) { int addr_family; - guint num, i; + guint num, num_domains, num_searches, i; char buf[NM_UTILS_INET_ADDRSTRLEN + 50]; + const char *str; - addr_family = nm_ip_config_get_addr_family (ip_config); + addr_family = nm_ip_config_get_addr_family (config); nm_assert_addr_family (addr_family); - nm_assert (ifindex > 0); - nm_assert (ifindex == nm_ip_config_get_ifindex (ip_config)); - num = nm_ip_config_get_num_nameservers (ip_config); + num = nm_ip_config_get_num_nameservers (config); for (i = 0; i < num; i++) { const NMIPAddr *addr; - addr = nm_ip_config_get_nameserver (ip_config, i); + addr = nm_ip_config_get_nameserver (config, i); if (addr_family == AF_INET) nm_utils_inet_ntop (addr_family, addr, buf); else if (IN6_IS_ADDR_V4MAPPED (addr)) @@ -438,42 +307,49 @@ merge_one_ip_config (NMResolvConfData *rc, else { nm_utils_inet6_ntop (&addr->addr6, buf); if (IN6_IS_ADDR_LINKLOCAL (addr)) { - const char *ifname; - - ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); - if (ifname) { - g_strlcat (buf, "%", sizeof (buf)); - g_strlcat (buf, ifname, sizeof (buf)); - } + g_strlcat (buf, "%", sizeof (buf)); + g_strlcat (buf, iface, sizeof (buf)); } } - add_string_item (rc->nameservers, buf, TRUE); + add_string_item (rc->nameservers, buf); } - add_dns_domains (rc->searches, ip_config, FALSE, TRUE); + num_domains = nm_ip_config_get_num_domains (config); + num_searches = nm_ip_config_get_num_searches (config); + for (i = 0; i < num_searches; i++) { + str = nm_ip_config_get_search (config, i); + if (domain_is_valid (str, FALSE)) + add_string_item (rc->searches, str); + } + if (num_domains > 1 || !num_searches) { + for (i = 0; i < num_domains; i++) { + str = nm_ip_config_get_domain (config, i); + if (domain_is_valid (str, FALSE)) + add_string_item (rc->searches, str); + } + } - num = nm_ip_config_get_num_dns_options (ip_config); + num = nm_ip_config_get_num_dns_options (config); for (i = 0; i < num; i++) { add_dns_option_item (rc->options, - nm_ip_config_get_dns_option (ip_config, i)); + nm_ip_config_get_dns_option (config, i)); } if (addr_family == AF_INET) { - const NMIP4Config *ip4_config = (const NMIP4Config *) ip_config; + const NMIP4Config *config4 = (const NMIP4Config *) config; /* NIS stuff */ - num = nm_ip4_config_get_num_nis_servers (ip4_config); + num = nm_ip4_config_get_num_nis_servers (config4); for (i = 0; i < num; i++) { add_string_item (rc->nis_servers, - nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (ip4_config, i), buf), - TRUE); + nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (config4, i), buf)); } - if (nm_ip4_config_get_nis_domain (ip4_config)) { + if (nm_ip4_config_get_nis_domain (config4)) { /* FIXME: handle multiple domains */ if (!rc->nis_domain) - rc->nis_domain = nm_ip4_config_get_nis_domain (ip4_config); + rc->nis_domain = nm_ip4_config_get_nis_domain (config4); } } } @@ -502,59 +378,58 @@ run_netconfig (NMDnsManager *self, GError **error, gint *stdin_fd) } static void -netconfig_construct_str (NMDnsManager *self, GString *str, const char *key, const char *value) +write_to_netconfig (NMDnsManager *self, gint fd, const char *key, const char *value) { - if (value) { - _LOGD ("writing to netconfig: %s='%s'", key, value); - g_string_append_printf (str, "%s='%s'\n", key, value); - } -} + char *str; + int x; -static void -netconfig_construct_strv (NMDnsManager *self, GString *str, const char *key, const char *const*values) -{ - if (values) { - gs_free char *value = NULL; - - value = g_strjoinv (" ", (char **) values); - netconfig_construct_str (self, str, key, value); - } + str = g_strdup_printf ("%s='%s'\n", key, value); + _LOGD ("writing to netconfig: %s", str); + x = write (fd, str, strlen (str)); + g_free (str); } static SpawnResult dispatch_netconfig (NMDnsManager *self, - const char *const*searches, - const char *const*nameservers, + char **searches, + char **nameservers, const char *nis_domain, - const char *const*nis_servers, + char **nis_servers, GError **error) { + char *str; GPid pid; gint fd; int status; - gssize l; - nm_auto_free_gstring GString *str = NULL; pid = run_netconfig (self, error, &fd); if (pid <= 0) return SR_NOTFOUND; - str = g_string_new (""); - /* NM is writing already-merged DNS information to netconfig, so it * does not apply to a specific network interface. */ - netconfig_construct_str (self, str, "INTERFACE", "NetworkManager"); - netconfig_construct_strv (self, str, "DNSSEARCH", searches); - netconfig_construct_strv (self, str, "DNSSERVERS", nameservers); - netconfig_construct_str (self, str, "NISDOMAIN", nis_domain); - netconfig_construct_strv (self, str, "NISSERVERS", nis_servers); + write_to_netconfig (self, fd, "INTERFACE", "NetworkManager"); -again: - l = write (fd, str->str, str->len); - if (l == -1) { - if (errno == EINTR) - goto again; + if (searches) { + str = g_strjoinv (" ", searches); + write_to_netconfig (self, fd, "DNSSEARCH", str); + g_free (str); + } + + if (nameservers) { + str = g_strjoinv (" ", nameservers); + write_to_netconfig (self, fd, "DNSSERVERS", str); + g_free (str); + } + + if (nis_domain) + write_to_netconfig (self, fd, "NISDOMAIN", nis_domain); + + if (nis_servers) { + str = g_strjoinv (" ", nis_servers); + write_to_netconfig (self, fd, "NISSERVERS", str); + g_free (str); } nm_close (fd); @@ -868,7 +743,7 @@ update_resolv_conf (NMDnsManager *self, if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) { _LOGT ("update-resolv-conf: write internal file %s succeeded (rc-manager=%s)", - MY_RESOLV_CONF, _rc_manager_to_string (rc_manager)); + rc_path, _rc_manager_to_string (rc_manager)); return write_file_result; } @@ -942,21 +817,25 @@ update_resolv_conf (NMDnsManager *self, static void compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN]) { + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); GChecksum *sum; gsize len = HASH_LEN; - NMDnsIPConfigData *ip_data; + guint i; sum = g_checksum_new (G_CHECKSUM_SHA1); - nm_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); + g_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); if (global) nm_global_dns_config_update_checksum (global, sum); else { - const CList *head; + for (i = 0; i < priv->configs->len; i++) { + NMDnsIPConfigData *data = priv->configs->pdata[i]; - head = _ip_config_lst_head (self); - c_list_for_each_entry (ip_data, head, ip_config_lst) - nm_ip_config_hash (ip_data->ip_config, sum, TRUE); + if (NM_IS_IP4_CONFIG (data->config)) + nm_ip4_config_hash ((NMIP4Config *) data->config, sum, TRUE); + else if (NM_IS_IP6_CONFIG (data->config)) + nm_ip6_config_hash ((NMIP6Config *) data->config, sum, TRUE); + } } g_checksum_get_digest (sum, buffer, &len); @@ -970,36 +849,27 @@ merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) const char *const *searches; const char *const *options; const char *const *servers; - guint i; + gint i; if (!global_conf) return FALSE; searches = nm_global_dns_config_get_searches (global_conf); - if (searches) { - for (i = 0; searches[i]; i++) { - if (domain_is_routing (searches[i])) - continue; - if (!domain_is_valid (searches[i], FALSE)) - continue; - add_string_item (rc->searches, searches[i], TRUE); - } - } - options = nm_global_dns_config_get_options (global_conf); - if (options) { - for (i = 0; options[i]; i++) - add_string_item (rc->options, options[i], TRUE); + + for (i = 0; searches && searches[i]; i++) { + if (domain_is_valid (searches[i], FALSE)) + add_string_item (rc->searches, searches[i]); } - default_domain = nm_global_dns_config_lookup_domain (global_conf, "*"); - nm_assert (default_domain); + for (i = 0; options && options[i]; i++) + add_string_item (rc->options, options[i]); + default_domain = nm_global_dns_config_lookup_domain (global_conf, "*"); + g_assert (default_domain); servers = nm_global_dns_domain_get_servers (default_domain); - if (servers) { - for (i = 0; servers[i]; i++) - add_string_item (rc->nameservers, servers[i], TRUE); - } + for (i = 0; servers && servers[i]; i++) + add_string_item (rc->nameservers, servers[i]); return TRUE; } @@ -1039,16 +909,17 @@ _ptrarray_to_strv (GPtrArray *parray) } static void -_collect_resolv_conf_data (NMDnsManager *self, +_collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no other side-effects */ NMGlobalDnsConfig *global_config, + const GPtrArray *configs, + const char *hostname, char ***out_searches, char ***out_options, char ***out_nameservers, char ***out_nis_servers, const char **out_nis_domain) { - NMDnsManagerPrivate *priv; - guint i, num, len; + guint i, j, num, len; NMResolvConfData rc = { .nameservers = g_ptr_array_new (), .searches = g_ptr_array_new (), @@ -1057,44 +928,37 @@ _collect_resolv_conf_data (NMDnsManager *self, .nis_servers = g_ptr_array_new (), }; - priv = NM_DNS_MANAGER_GET_PRIVATE (self); - if (global_config) merge_global_dns_config (&rc, global_config); else { nm_auto_free_gstring GString *tmp_gstring = NULL; int prio, first_prio = 0; - const NMDnsIPConfigData *ip_data; - const CList *head; - gboolean is_first = TRUE; + NMDnsIPConfigData *current; - head = _ip_config_lst_head (self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { + for (i = 0, j = 0; i < configs->len; i++) { gboolean skip = FALSE; - _ASSERT_ip_config_data (ip_data); + current = configs->pdata[i]; - prio = nm_ip_config_get_dns_priority (ip_data->ip_config); + prio = nm_ip_config_get_dns_priority (current->config); - if (is_first) { - is_first = FALSE; + if (i == 0) first_prio = prio; - } else if ( first_prio < 0 - && first_prio != prio) + else if (first_prio < 0 && first_prio != prio) skip = TRUE; - if (nm_ip_config_get_num_nameservers (ip_data->ip_config)) { - _LOGT ("config: %8d %-7s v%c %-5d %s: %s", + if (nm_ip_config_get_num_nameservers (current->config)) { + _LOGT ("config: %8d %-7s v%c %-16s %s: %s", prio, - _config_type_to_string (ip_data->ip_config_type), - nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (ip_data->ip_config)), - ip_data->data->ifindex, + _config_type_to_string (current->type), + nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (current->config)), + current->iface, skip ? "" : "", - get_nameserver_list (ip_data->ip_config, &tmp_gstring)); + get_nameserver_list (current->config, &tmp_gstring)); } if (!skip) - merge_one_ip_config (&rc, ip_data->data->ifindex, ip_data->ip_config); + merge_one_ip_config (&rc, current->config, current->iface); } } @@ -1105,16 +969,16 @@ _collect_resolv_conf_data (NMDnsManager *self, * (eg, "example.com"), then use the hostname itself as the search (since the user is * unlikely to want "com" as a search domain). */ - if (priv->hostname) { - const char *hostdomain = strchr (priv->hostname, '.'); + if (hostname) { + const char *hostdomain = strchr (hostname, '.'); if ( hostdomain - && !nm_utils_ipaddr_valid (AF_UNSPEC, priv->hostname)) { + && !nm_utils_ipaddr_valid (AF_UNSPEC, hostname)) { hostdomain++; if (domain_is_valid (hostdomain, TRUE)) - add_string_item (rc.searches, hostdomain, TRUE); - else if (domain_is_valid (priv->hostname, TRUE)) - add_string_item (rc.searches, priv->hostname, TRUE); + add_string_item (rc.searches, hostdomain); + else if (domain_is_valid (hostname, TRUE)) + add_string_item (rc.searches, hostname); } } @@ -1176,12 +1040,16 @@ update_dns (NMDnsManager *self, data = nm_config_get_data (priv->config); global_config = nm_config_data_get_global_dns_config (data); + if (priv->need_sort) { + g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare); + priv->need_sort = FALSE; + } + /* Update hash with config we're applying */ compute_hash (self, global_config, priv->hash); - _collect_resolv_conf_data (self, global_config, - &searches, &options, &nameservers, - &nis_servers, &nis_domain); + _collect_resolv_conf_data (self, global_config, priv->configs, priv->hostname, + &searches, &options, &nameservers, &nis_servers, &nis_domain); /* Let any plugins do their thing first */ if (priv->plugin) { @@ -1199,8 +1067,8 @@ update_dns (NMDnsManager *self, _LOGD ("update-dns: updating plugin %s", plugin_name); if (!nm_dns_plugin_update (plugin, + priv->configs, global_config, - _ip_config_lst_head (self), priv->hostname)) { _LOGW ("update-dns: plugin %s update failed", plugin_name); @@ -1246,12 +1114,8 @@ update_dns (NMDnsManager *self, result = dispatch_resolvconf (self, searches, nameservers, options, error); break; case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: - result = dispatch_netconfig (self, - (const char *const*) searches, - (const char *const*) nameservers, - nis_domain, - (const char *const*) nis_servers, - error); + result = dispatch_netconfig (self, searches, nameservers, nis_domain, + nis_servers, error); break; default: g_assert_not_reached (); @@ -1341,96 +1205,82 @@ plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data) } static void -_ip_config_dns_priority_changed (gpointer config, - GParamSpec *pspec, - NMDnsIPConfigData *ip_data) +ip_config_dns_priority_changed (gpointer config, + GParamSpec *pspec, + NMDnsManager *self) { - _ASSERT_ip_config_data (ip_data); + NM_DNS_MANAGER_GET_PRIVATE (self)->need_sort = TRUE; +} - NM_DNS_MANAGER_GET_PRIVATE (ip_data->data->self)->ip_config_lst_need_sort = TRUE; +static void +forget_data (NMDnsManager *self, NMDnsIPConfigData *data) +{ + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); + + if (data == priv->best_conf4) + priv->best_conf4 = NULL; + else if (data == priv->best_conf6) + priv->best_conf6 = NULL; + + g_signal_handlers_disconnect_by_func (data->config, ip_config_dns_priority_changed, self); } gboolean -nm_dns_manager_set_ip_config (NMDnsManager *self, - NMIPConfig *ip_config, - NMDnsIPConfigType ip_config_type) +nm_dns_manager_add_ip_config (NMDnsManager *self, + const char *iface, + gpointer config, + NMDnsIPConfigType cfg_type) { NMDnsManagerPrivate *priv; GError *error = NULL; - NMDnsIPConfigData *ip_data; - NMDnsConfigData *data; - int ifindex; - NMDnsIPConfigData **p_best; + NMDnsIPConfigData *data; + gboolean v4 = NM_IS_IP4_CONFIG (config); + guint i; g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); - g_return_val_if_fail (NM_IS_IP_CONFIG (ip_config), FALSE); - - ifindex = nm_ip_config_get_ifindex (ip_config); - g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (config, FALSE); + g_return_val_if_fail (iface && iface[0], FALSE); + nm_assert (NM_IS_IP_CONFIG (config)); priv = NM_DNS_MANAGER_GET_PRIVATE (self); - data = g_hash_table_lookup (priv->configs, GINT_TO_POINTER (ifindex)); - if (!data) - ip_data = NULL; - else - ip_data = _config_data_find_ip_config (data, ip_config); - - if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) { - if (!ip_data) - return FALSE; - if (priv->best_ip_config_4 == ip_data) - priv->best_ip_config_4 = NULL; - if (priv->best_ip_config_6 == ip_data) - priv->best_ip_config_6 = NULL; - /* deleting a config doesn't invalidate the configs' sort order. */ - _ip_config_data_free (ip_data); - if (c_list_is_empty (&data->data_lst_head)) - g_hash_table_remove (priv->configs, GINT_TO_POINTER (ifindex)); - goto changed; - } - - if ( ip_data - && ip_data->ip_config_type == ip_config_type) { - /* nothing to do. */ - return FALSE; - } - - if (!data) { - data = g_slice_new0 (NMDnsConfigData); - data->ifindex = ifindex; - data->self = self; - c_list_init (&data->data_lst_head); - _ASSERT_config_data (data); - g_hash_table_insert (priv->configs, GINT_TO_POINTER (ifindex), data); + for (i = 0; i < priv->configs->len; i++) { + data = priv->configs->pdata[i]; + if (data->config == config) { + if ( nm_streq (data->iface, iface) + && data->type == cfg_type) + return FALSE; + else { + forget_data (self, data); + g_ptr_array_remove_index_fast (priv->configs, i); + break; + } + } } - if (!ip_data) - ip_data = _ip_config_data_new (data, ip_config, ip_config_type); - else - ip_data->ip_config_type = ip_config_type; - - priv->ip_config_lst_need_sort = TRUE; - - p_best = NM_IS_IP4_CONFIG (ip_config) - ? &priv->best_ip_config_4 - : &priv->best_ip_config_6; + data = ip_config_data_new (config, cfg_type, iface); + g_ptr_array_add (priv->configs, data); + g_signal_connect (config, + v4 ? + "notify::" NM_IP4_CONFIG_DNS_PRIORITY : + "notify::" NM_IP6_CONFIG_DNS_PRIORITY, + (GCallback) ip_config_dns_priority_changed, self); + priv->need_sort = TRUE; - if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { + if (cfg_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { /* Only one best-device per IP version is allowed */ - if (*p_best != ip_data) { - if (*p_best) - (*p_best)->ip_config_type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; - *p_best = ip_data; + if (v4) { + if (priv->best_conf4) + priv->best_conf4->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; + priv->best_conf4 = data; + } else { + if (priv->best_conf6) + priv->best_conf6->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; + priv->best_conf6 = data; } - } else { - if (*p_best == ip_data) - *p_best = NULL; } -changed: - if ( !priv->updates_queue - && !update_dns (self, FALSE, &error)) { + if (!priv->updates_queue && !update_dns (self, FALSE, &error)) { _LOGW ("could not commit DNS changes: %s", error->message); g_clear_error (&error); } @@ -1438,6 +1288,38 @@ changed: return TRUE; } +gboolean +nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config) +{ + NMDnsManagerPrivate *priv; + GError *error = NULL; + NMDnsIPConfigData *data; + guint i; + + g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); + g_return_val_if_fail (config, FALSE); + nm_assert (NM_IS_IP_CONFIG (config)); + + priv = NM_DNS_MANAGER_GET_PRIVATE (self); + + for (i = 0; i < priv->configs->len; i++) { + data = priv->configs->pdata[i]; + + if (data->config == config) { + forget_data (self, data); + g_ptr_array_remove_index (priv->configs, i); + + if (!priv->updates_queue && !update_dns (self, FALSE, &error)) { + _LOGW ("could not commit DNS changes: %s", error->message); + g_clear_error (&error); + } + + return TRUE; + } + } + return FALSE; +} + void nm_dns_manager_set_initial_hostname (NMDnsManager *self, const char *hostname) @@ -1480,6 +1362,23 @@ nm_dns_manager_set_hostname (NMDnsManager *self, } } +gboolean +nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self) +{ + NMDnsManagerPrivate *priv; + + g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); + + priv = NM_DNS_MANAGER_GET_PRIVATE (self); + + if ( NM_IN_SET (priv->rc_manager, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED, + NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE) + || priv->plugin) + return FALSE; + + return TRUE; +} + void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func) { @@ -1510,6 +1409,11 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func) priv = NM_DNS_MANAGER_GET_PRIVATE (self); g_return_if_fail (priv->updates_queue > 0); + if (priv->need_sort) { + g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare); + priv->need_sort = FALSE; + } + compute_hash (self, nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)), new); changed = (memcmp (new, priv->prev_hash, sizeof (new)) != 0) ? TRUE : FALSE; _LOGD ("(%s): DNS configuration %s", func, changed ? "changed" : "did not change"); @@ -1906,14 +1810,14 @@ _get_config_variant (NMDnsManager *self) NMGlobalDnsConfig *global_config; gs_free char *str = NULL; GVariantBuilder builder; - NMDnsIPConfigData *ip_data; - const CList *head; - gs_unref_ptrarray GPtrArray *array_domains = NULL; + NMConfigData *data; + guint i, j; if (priv->config_variant) return priv->config_variant; - global_config = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)); + data = nm_config_get_data (priv->config); + global_config = nm_config_data_get_global_dns_config (data); if (global_config) { priv->config_variant = _get_global_config_variant (global_config); _LOGT ("current configuration: %s", (str = g_variant_print (priv->config_variant, TRUE))); @@ -1922,26 +1826,25 @@ _get_config_variant (NMDnsManager *self) g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); - head = _ip_config_lst_head (self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { - const NMIPConfig *ip_config = ip_data->ip_config; + for (i = 0; i < priv->configs->len; i++) { + NMDnsIPConfigData *current = priv->configs->pdata[i]; + const NMIPConfig *config = current->config; GVariantBuilder entry_builder; GVariantBuilder strv_builder; - guint i, num; - const int addr_family = nm_ip_config_get_addr_family (ip_config); + guint num; + const int addr_family = nm_ip_config_get_addr_family (config); char buf[NM_UTILS_INET_ADDRSTRLEN]; const NMIPAddr *addr; - const char *ifname; - num = nm_ip_config_get_num_nameservers (ip_config); + num = nm_ip_config_get_num_nameservers (config); if (!num) continue; g_variant_builder_init (&entry_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); - for (i = 0; i < num; i++) { - addr = nm_ip_config_get_nameserver (ip_config, i); + for (j = 0; j < num; j++) { + addr = nm_ip_config_get_nameserver (config, j); g_variant_builder_add (&strv_builder, "s", nm_utils_inet_ntop (addr_family, addr, buf)); @@ -1951,47 +1854,36 @@ _get_config_variant (NMDnsManager *self) "nameservers", g_variant_builder_end (&strv_builder)); - - num = nm_ip_config_get_num_domains (ip_config); - num += nm_ip_config_get_num_searches (ip_config); + num = nm_ip_config_get_num_domains (config); if (num > 0) { - if (!array_domains) - array_domains = g_ptr_array_sized_new (num); - else - g_ptr_array_set_size (array_domains, 0); - - add_dns_domains (array_domains, ip_config, TRUE, FALSE); - if (array_domains->len) { - g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); - for (i = 0; i < array_domains->len; i++) { - g_variant_builder_add (&strv_builder, - "s", - array_domains->pdata[i]); - } - g_variant_builder_add (&entry_builder, - "{sv}", - "domains", - g_variant_builder_end (&strv_builder)); + g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); + for (j = 0; j < num; j++) { + g_variant_builder_add (&strv_builder, + "s", + nm_ip_config_get_domain (config, j)); } + g_variant_builder_add (&entry_builder, + "{sv}", + "domains", + g_variant_builder_end (&strv_builder)); } - ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ip_data->data->ifindex); - if (ifname) { + if (current->iface) { g_variant_builder_add (&entry_builder, "{sv}", "interface", - g_variant_new_string (ifname)); + g_variant_new_string (current->iface)); } g_variant_builder_add (&entry_builder, "{sv}", "priority", - g_variant_new_int32 (nm_ip_config_get_dns_priority (ip_config))); + g_variant_new_int32 (nm_ip_config_get_dns_priority (config))); g_variant_builder_add (&entry_builder, "{sv}", "vpn", - g_variant_new_boolean (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN)); + g_variant_new_boolean (current->type == NM_DNS_IP_CONFIG_TYPE_VPN)); g_variant_builder_add (&builder, "a{sv}", &entry_builder); } @@ -2032,12 +1924,8 @@ nm_dns_manager_init (NMDnsManager *self) _LOGT ("creating..."); - c_list_init (&priv->ip_config_lst_head); - priv->config = g_object_ref (nm_config_get ()); - - priv->configs = g_hash_table_new_full (nm_direct_hash, NULL, - NULL, (GDestroyNotify) _config_data_free); + priv->configs = g_ptr_array_new_full (8, ip_config_data_destroy); /* Set the initial hash */ compute_hash (self, NULL, NM_DNS_MANAGER_GET_PRIVATE (self)->hash); @@ -2054,33 +1942,33 @@ dispose (GObject *object) { NMDnsManager *self = NM_DNS_MANAGER (object); NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - NMDnsIPConfigData *ip_data, *ip_data_safe; + NMDnsIPConfigData *data; + guint i; _LOGT ("disposing"); if (!priv->is_stopped) nm_dns_manager_stop (self); - if (priv->config) - g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); - _clear_plugin (self); - priv->best_ip_config_4 = NULL; - priv->best_ip_config_6 = NULL; - - c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst) - _ip_config_data_free (ip_data); + if (priv->config) { + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + g_clear_object (&priv->config); + } - g_clear_pointer (&priv->configs, g_hash_table_destroy); + if (priv->configs) { + for (i = 0; i < priv->configs->len; i++) { + data = priv->configs->pdata[i]; + forget_data (self, data); + } + g_ptr_array_free (priv->configs, TRUE); + priv->configs = NULL; + } nm_clear_g_source (&priv->plugin_ratelimit.timer); - g_clear_object (&priv->config); - G_OBJECT_CLASS (nm_dns_manager_parent_class)->dispose (object); - - g_clear_pointer (&priv->config_variant, g_variant_unref); } static void @@ -2095,30 +1983,18 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_dns_manager_parent_class)->finalize (object); } -static const NMDBusInterfaceInfoExtended interface_info_dns_manager = { - .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( - NM_DBUS_INTERFACE_DNS_MANAGER, - .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "s", NM_DNS_MANAGER_MODE), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RcManager", "s", NM_DNS_MANAGER_RC_MANAGER), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Configuration", "aa{sv}", NM_DNS_MANAGER_CONFIGURATION), - ), - ), -}; - static void nm_dns_manager_class_init (NMDnsManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); + NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass); object_class->dispose = dispose; object_class->finalize = finalize; object_class->get_property = get_property; - dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH "/DnsManager"); - dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dns_manager); - dbus_object_class->export_on_construction = TRUE; + exported_object_class->export_path = NM_DBUS_PATH "/DnsManager"; + exported_object_class->export_on_construction = TRUE; obj_properties[PROP_MODE] = g_param_spec_string (NM_DNS_MANAGER_MODE, "", "", @@ -2148,4 +2024,9 @@ nm_dns_manager_class_init (NMDnsManagerClass *klass) 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DNS_MANAGER_SKELETON, + NULL); } + diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index 56889367..b38ea701 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -26,14 +26,11 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-setting-connection.h" typedef enum { - NM_DNS_IP_CONFIG_TYPE_REMOVED = -1, - NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0, NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, - NM_DNS_IP_CONFIG_TYPE_VPN, + NM_DNS_IP_CONFIG_TYPE_VPN } NMDnsIPConfigType; enum { @@ -41,23 +38,12 @@ enum { NM_DNS_PRIORITY_DEFAULT_VPN = 50, }; -struct _NMDnsConfigData; -struct _NMDnsManager; - typedef struct { - struct _NMDnsConfigData *data; - NMIPConfig *ip_config; - CList data_lst; - CList ip_config_lst; - NMDnsIPConfigType ip_config_type; + gpointer config; + NMDnsIPConfigType type; + char *iface; } NMDnsIPConfigData; -typedef struct _NMDnsConfigData { - struct _NMDnsManager *self; - CList data_lst_head; - int ifindex; -} NMDnsConfigData; - #define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type ()) #define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DNS_MANAGER, NMDnsManager)) #define NM_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) @@ -84,9 +70,12 @@ NMDnsManager * nm_dns_manager_get (void); void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func); void nm_dns_manager_end_updates (NMDnsManager *self, const char *func); -gboolean nm_dns_manager_set_ip_config (NMDnsManager *self, - NMIPConfig *ip_config, - NMDnsIPConfigType ip_config_type); +gboolean nm_dns_manager_add_ip_config (NMDnsManager *self, + const char *iface, + gpointer config, + NMDnsIPConfigType cfg_type); + +gboolean nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config); void nm_dns_manager_set_initial_hostname (NMDnsManager *self, const char *hostname); @@ -123,6 +112,8 @@ typedef enum { NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, } NMDnsManagerResolvConfManager; +gboolean nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self); + void nm_dns_manager_stop (NMDnsManager *self); #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */ diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c index d9400e3e..5805b7d8 100644 --- a/src/dns/nm-dns-plugin.c +++ b/src/dns/nm-dns-plugin.c @@ -77,15 +77,15 @@ G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_A gboolean nm_dns_plugin_update (NMDnsPlugin *self, + const GPtrArray *configs, const NMGlobalDnsConfig *global_config, - const CList *ip_config_lst_head, 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, + configs, global_config, - ip_config_lst_head, hostname); } diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h index 80b77d95..996695c0 100644 --- a/src/dns/nm-dns-plugin.h +++ b/src/dns/nm-dns-plugin.h @@ -50,8 +50,8 @@ typedef struct { * configuration. */ gboolean (*update) (NMDnsPlugin *self, + const GPtrArray *configs, const NMGlobalDnsConfig *global_config, - const CList *ip_config_lst_head, const char *hostname); /* Subclasses should override and return TRUE if they start a local @@ -80,8 +80,8 @@ 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 GPtrArray *configs, const NMGlobalDnsConfig *global_config, - const CList *ip_config_lst_head, const char *hostname); void nm_dns_plugin_stop (NMDnsPlugin *self); diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index 7da27e5f..6ab2ea18 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -31,15 +31,13 @@ #include #include -#include "nm-utils/nm-c-list.h" #include "nm-core-internal.h" #include "platform/nm-platform.h" #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-dbus-manager.h" +#include "nm-bus-manager.h" #include "nm-manager.h" -#include "nm-setting-connection.h" #include "devices/nm-device.h" #include "NetworkManagerUtils.h" @@ -50,23 +48,17 @@ typedef struct { int ifindex; - CList configs_lst_head; + GList *configs; } InterfaceConfig; -typedef struct { - CList request_queue_lst; - const char *operation; - GVariant *argument; -} RequestItem; - /*****************************************************************************/ typedef struct { GDBusProxy *resolve; GCancellable *init_cancellable; GCancellable *update_cancellable; - GCancellable *mdns_cancellable; - CList request_queue_lst_head; + GQueue dns_updates; + GQueue domain_updates; } NMDnsSystemdResolvedPrivate; struct _NMDnsSystemdResolved { @@ -89,36 +81,6 @@ G_DEFINE_TYPE (NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN /*****************************************************************************/ -static void -_request_item_free (RequestItem *request_item) -{ - c_list_unlink_stale (&request_item->request_queue_lst); - g_variant_unref (request_item->argument); - g_slice_free (RequestItem, request_item); -} - -static void -_request_item_append (CList *request_queue_lst_head, - const char *operation, - GVariant *argument) -{ - RequestItem *request_item; - - request_item = g_slice_new (RequestItem); - request_item->operation = operation; - request_item->argument = g_variant_ref_sink (argument); - c_list_link_tail (request_queue_lst_head, &request_item->request_queue_lst); -} - -/*****************************************************************************/ - -static void -_interface_config_free (InterfaceConfig *config) -{ - nm_c_list_elem_free_all (&config->configs_lst_head, NULL); - g_slice_free (InterfaceConfig, config); -} - static void call_done (GObject *source, GAsyncResult *r, gpointer user_data) { @@ -127,58 +89,122 @@ call_done (GObject *source, GAsyncResult *r, gpointer user_data) NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data; v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error); - if (!v) { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + if (error != NULL) { _LOGW ("Failed: %s\n", error->message); g_error_free (error); } } +static void +add_interface_configuration (NMDnsSystemdResolved *self, + GArray *interfaces, + const NMDnsIPConfigData *data, + gboolean skip) +{ + int i; + InterfaceConfig *ic = NULL; + int ifindex; + + if (NM_IS_IP4_CONFIG (data->config)) + ifindex = nm_ip4_config_get_ifindex (data->config); + else if (NM_IS_IP6_CONFIG (data->config)) + ifindex = nm_ip6_config_get_ifindex (data->config); + else + g_return_if_reached (); + + for (i = 0; i < interfaces->len; i++) { + InterfaceConfig *tic = &g_array_index (interfaces, InterfaceConfig, i); + if (ifindex == tic->ifindex) { + ic = tic; + break; + } + } + + if (!ic) { + g_array_set_size (interfaces, interfaces->len + 1); + ic = &g_array_index (interfaces, InterfaceConfig, + interfaces->len - 1); + ic->ifindex = ifindex; + } + + if (!skip) + ic->configs = g_list_append (ic->configs, data->config); +} + static void update_add_ip_config (NMDnsSystemdResolved *self, GVariantBuilder *dns, GVariantBuilder *domains, - NMIPConfig *config) + gpointer config) { int addr_family; gsize addr_size; guint i, n; - gboolean is_routing; - const char *domain; + gboolean route_only; + + if (NM_IS_IP4_CONFIG (config)) + addr_family = AF_INET; + else if (NM_IS_IP6_CONFIG (config)) + addr_family = AF_INET6; + else + g_return_if_reached (); - addr_family = nm_ip_config_get_addr_family (config); addr_size = nm_utils_addr_family_to_size (addr_family); - n = nm_ip_config_get_num_nameservers (config); + n = addr_family == AF_INET + ? nm_ip4_config_get_num_nameservers (config) + : nm_ip6_config_get_num_nameservers (config); for (i = 0 ; i < n; i++) { + in_addr_t ns4; + gconstpointer ns; + + if (addr_family == AF_INET) { + ns4 = nm_ip4_config_get_nameserver (config, i); + ns = &ns4; + } else + ns = nm_ip6_config_get_nameserver (config, i); + g_variant_builder_open (dns, G_VARIANT_TYPE ("(iay)")); g_variant_builder_add (dns, "i", addr_family); g_variant_builder_add_value (dns, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, - nm_ip_config_get_nameserver (config, i), + ns, addr_size, 1)); g_variant_builder_close (dns); } - n = nm_ip_config_get_num_searches (config); + /* If this link is never the default (e.g. only used for resources on this + * network) add a routing domain. */ + route_only = addr_family == AF_INET + ? !nm_ip4_config_best_default_route_get (config) + : !nm_ip6_config_best_default_route_get (config); + + n = addr_family == AF_INET + ? nm_ip4_config_get_num_searches (config) + : nm_ip6_config_get_num_searches (config); if (n > 0) { for (i = 0; i < n; i++) { - domain = nm_utils_parse_dns_domain (nm_ip_config_get_search (config, i), - &is_routing); g_variant_builder_add (domains, "(sb)", - domain, - is_routing); + addr_family == AF_INET + ? nm_ip4_config_get_search (config, i) + : nm_ip6_config_get_search (config, i), + route_only); } } else { - n = nm_ip_config_get_num_domains (config); + n = addr_family == AF_INET + ? nm_ip4_config_get_num_domains (config) + : nm_ip6_config_get_num_domains (config); for (i = 0; i < n; i++) { - domain = nm_utils_parse_dns_domain (nm_ip_config_get_domain (config, i), - &is_routing); g_variant_builder_add (domains, "(sb)", - domain, - is_routing); + addr_family == AF_INET + ? nm_ip4_config_get_domain (config, i) + : nm_ip6_config_get_domain (config, i), + route_only); } } } @@ -187,13 +213,13 @@ static void free_pending_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - RequestItem *request_item, *request_item_safe; + GVariant *v; - c_list_for_each_entry_safe (request_item, - request_item_safe, - &priv->request_queue_lst_head, - request_queue_lst) - _request_item_free (request_item); + while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL) + g_variant_unref (v); + + while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL) + g_variant_unref (v); } static void @@ -201,9 +227,7 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); GVariantBuilder dns, domains; - NMCListElem *elem; - NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; - const char *mdns_arg = NULL; + GList *l; g_variant_builder_init (&dns, G_VARIANT_TYPE ("(ia(iay))")); g_variant_builder_add (&dns, "i", ic->ifindex); @@ -213,50 +237,23 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic) g_variant_builder_add (&domains, "i", ic->ifindex); g_variant_builder_open (&domains, G_VARIANT_TYPE ("a(sb)")); - c_list_for_each_entry (elem, &ic->configs_lst_head, lst) { - NMIPConfig *ip_config = elem->data; - - update_add_ip_config (self, &dns, &domains, ip_config); - - if (NM_IS_IP4_CONFIG (ip_config)) - mdns = NM_MAX (mdns, nm_ip4_config_mdns_get (NM_IP4_CONFIG (ip_config))); - } + for (l = ic->configs; l; l = l->next) + update_add_ip_config (self, &dns, &domains, l->data); g_variant_builder_close (&dns); g_variant_builder_close (&domains); - switch (mdns) { - case NM_SETTING_CONNECTION_MDNS_NO: - mdns_arg = "no"; - break; - case NM_SETTING_CONNECTION_MDNS_RESOLVE: - mdns_arg = "resolve"; - break; - case NM_SETTING_CONNECTION_MDNS_YES: - mdns_arg = "yes"; - break; - case NM_SETTING_CONNECTION_MDNS_DEFAULT: - mdns_arg = ""; - break; - } - nm_assert (mdns_arg); - - _request_item_append (&priv->request_queue_lst_head, - "SetLinkDNS", - g_variant_builder_end (&dns)); - _request_item_append (&priv->request_queue_lst_head, - "SetLinkDomains", - g_variant_builder_end (&domains)); - _request_item_append (&priv->request_queue_lst_head, - "SetLinkMulticastDNS", - g_variant_new ("(is)", ic->ifindex, mdns_arg ?: "")); + g_queue_push_tail (&priv->dns_updates, + g_variant_ref_sink (g_variant_builder_end (&dns))); + g_queue_push_tail (&priv->domain_updates, + g_variant_ref_sink (g_variant_builder_end (&domains))); } static void send_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - RequestItem *request_item, *request_item_safe; + GVariant *v; nm_clear_g_cancellable (&priv->update_cancellable); @@ -265,81 +262,55 @@ send_updates (NMDnsSystemdResolved *self) priv->update_cancellable = g_cancellable_new (); - c_list_for_each_entry_safe (request_item, - request_item_safe, - &priv->request_queue_lst_head, - request_queue_lst) { - g_dbus_proxy_call (priv->resolve, - request_item->operation, - request_item->argument, + while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL) { + g_dbus_proxy_call (priv->resolve, "SetLinkDNS", v, G_DBUS_CALL_FLAGS_NONE, - -1, - priv->update_cancellable, - call_done, - self); - _request_item_free (request_item); + -1, priv->update_cancellable, call_done, self); + g_variant_unref (v); + } + + while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL) { + g_dbus_proxy_call (priv->resolve, "SetLinkDomains", v, + G_DBUS_CALL_FLAGS_NONE, + -1, priv->update_cancellable, call_done, self); + g_variant_unref (v); } } static gboolean update (NMDnsPlugin *plugin, + const GPtrArray *configs, const NMGlobalDnsConfig *global_config, - const CList *ip_config_lst_head, const char *hostname) { NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED (plugin); - gs_unref_hashtable GHashTable *interfaces = NULL; - gs_free gpointer *interfaces_keys = NULL; - guint interfaces_len; + GArray *interfaces = g_array_new (TRUE, TRUE, sizeof (InterfaceConfig)); guint i; int prio, first_prio = 0; - NMDnsIPConfigData *ip_data; - gboolean is_first = TRUE; - - interfaces = g_hash_table_new_full (nm_direct_hash, NULL, - NULL, (GDestroyNotify) _interface_config_free); - c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { + for (i = 0; i < configs->len; i++) { + const NMDnsIPConfigData *data = configs->pdata[i]; gboolean skip = FALSE; - InterfaceConfig *ic = NULL; - int ifindex; - prio = nm_ip_config_get_dns_priority (ip_data->ip_config); - if (is_first) { - is_first = FALSE; + prio = nm_ip_config_get_dns_priority (data->config); + if (i == 0) first_prio = prio; - } else if (first_prio < 0 && first_prio != prio) + else if (first_prio < 0 && first_prio != prio) skip = TRUE; - - ifindex = ip_data->data->ifindex; - nm_assert (ifindex == nm_ip_config_get_ifindex (ip_data->ip_config)); - - ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (ifindex)); - if (!ic) { - ic = g_slice_new (InterfaceConfig); - ic->ifindex = ifindex; - c_list_init (&ic->configs_lst_head); - g_hash_table_insert (interfaces, GINT_TO_POINTER (ifindex), ic); - } - - if (!skip) { - c_list_link_tail (&ic->configs_lst_head, - &nm_c_list_elem_new_stale (ip_data->ip_config)->lst); - } + add_interface_configuration (self, interfaces, data, skip); } free_pending_updates (self); - interfaces_keys = nm_utils_hash_keys_to_array (interfaces, - nm_cmp_int2ptr_p_with_data, - NULL, - &interfaces_len); - for (i = 0; i < interfaces_len; i++) { - InterfaceConfig *ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (interfaces_keys[i])); + for (i = 0; i < interfaces->len; i++) { + InterfaceConfig *ic = &g_array_index (interfaces, InterfaceConfig, i); prepare_one_interface (self, ic); + g_list_free (ic->configs); } + g_array_free (interfaces, TRUE); + send_updates (self); return TRUE; @@ -392,15 +363,16 @@ static void nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - NMDBusManager *dbus_mgr; + NMBusManager *dbus_mgr; GDBusConnection *connection; - c_list_init (&priv->request_queue_lst_head); + g_queue_init (&priv->dns_updates); + g_queue_init (&priv->domain_updates); - dbus_mgr = nm_dbus_manager_get (); + dbus_mgr = nm_bus_manager_get (); g_return_if_fail (dbus_mgr); - connection = nm_dbus_manager_get_connection (dbus_mgr); + connection = nm_bus_manager_get_connection (dbus_mgr); g_return_if_fail (connection); priv->init_cancellable = g_cancellable_new (); @@ -432,7 +404,6 @@ dispose (GObject *object) g_clear_object (&priv->resolve); nm_clear_g_cancellable (&priv->init_cancellable); nm_clear_g_cancellable (&priv->update_cancellable); - nm_clear_g_cancellable (&priv->mdns_cancellable); G_OBJECT_CLASS (nm_dns_systemd_resolved_parent_class)->dispose (object); } diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c index e06128aa..0b80055f 100644 --- a/src/dns/nm-dns-unbound.c +++ b/src/dns/nm-dns-unbound.c @@ -39,8 +39,8 @@ G_DEFINE_TYPE (NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN) static gboolean update (NMDnsPlugin *plugin, + const GPtrArray *configs, const NMGlobalDnsConfig *global_config, - const CList *ip_config_lst_head, const char *hostname) { char *argv[] = { DNSSEC_TRIGGER_SCRIPT, "--async", "--update", NULL }; -- cgit 1.3.0-6-gf8a5 From 04bc9e1cd3544445d883ad29ea108c1645c8e7b7 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Mon, 4 Jun 2018 00:07:45 +0200 Subject: New upstream version 1.11.4 --- src/dns/nm-dns-dnsmasq.c | 317 +++--------- src/dns/nm-dns-manager.c | 1017 ++++++++++++++++++++++++------------- src/dns/nm-dns-manager.h | 37 +- src/dns/nm-dns-plugin.c | 4 +- src/dns/nm-dns-plugin.h | 4 +- src/dns/nm-dns-systemd-resolved.c | 295 +++++------ src/dns/nm-dns-unbound.c | 2 +- 7 files changed, 915 insertions(+), 761 deletions(-) (limited to 'src/dns') diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index e6436c79..b5b93280 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -35,7 +35,7 @@ #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "NetworkManagerUtils.h" #define PIDFILE NMRUNDIR "/dnsmasq.pid" @@ -75,66 +75,6 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) /*****************************************************************************/ -static char ** -get_ip4_rdns_domains (NMIP4Config *ip4) -{ - char **strv; - GPtrArray *domains = NULL; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route *route; - - g_return_val_if_fail (ip4 != NULL, NULL); - - domains = g_ptr_array_sized_new (5); - - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) - nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - 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; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *address; - const NMPlatformIP6Route *route; - - g_return_val_if_fail (ip6 != NULL, NULL); - - domains = g_ptr_array_sized_new (5); - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) - nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - 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, @@ -155,96 +95,43 @@ add_dnsmasq_nameserver (NMDnsDnsmasq *self, g_variant_builder_close (servers); } -static gboolean -add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, - const char *iface, gboolean split) -{ - 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) { - char **domains, **iter; - - if (nnameservers == 0) - return FALSE; - - for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { - addr = nm_ip4_config_get_nameserver (ip4, i_nameserver); - 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); - for (i = 0; i < n; i++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip4_config_get_search (ip4, i)); - 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++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip4_config_get_domain (ip4, i)); - added = TRUE; - } - } - - /* Ensure reverse-DNS works by directing queries for in-addr.arpa - * domains to the split domain's nameserver. - */ - domains = get_ip4_rdns_domains (ip4); - if (domains) { - for (iter = domains; iter && *iter; iter++) - add_dnsmasq_nameserver (self, servers, buf, *iter); - g_strfreev (domains); - } - } - } +#define IP_ADDR_TO_STRING_BUFLEN (NM_UTILS_INET_ADDRSTRLEN + 1 + IFNAMSIZ) - /* 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); - g_snprintf (buf, sizeof (buf), "%s@%s", - nm_utils_inet4_ntop (addr, buf2), iface); - add_dnsmasq_nameserver (self, servers, buf, NULL); - } - } +static const char * +ip_addr_to_string (int addr_family, gconstpointer addr, const char *iface, char *out_buf) +{ + int n_written; + char buf2[NM_UTILS_INET_ADDRSTRLEN]; + const char *separator; - return TRUE; -} + nm_assert_addr_family (addr_family); + nm_assert (addr); + nm_assert (out_buf); -static char * -ip6_addr_to_string (const struct in6_addr *addr, const char *iface) -{ - char buf[NM_UTILS_INET_ADDRSTRLEN]; + if (addr_family == AF_INET) { + nm_utils_inet_ntop (addr_family, addr, buf2); + separator = "@"; + } else { + if (IN6_IS_ADDR_V4MAPPED (addr)) + nm_utils_inet4_ntop (((const struct in6_addr *) addr)->s6_addr32[3], buf2); + else + nm_utils_inet6_ntop (addr, buf2); + /* Need to scope link-local addresses with %. 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. + */ + separator = IN6_IS_ADDR_LINKLOCAL (addr) ? "%" : "@"; + } - if (IN6_IS_ADDR_V4MAPPED (addr)) - nm_utils_inet4_ntop (addr->s6_addr32[3], buf); - else - nm_utils_inet6_ntop (addr, buf); - - /* Need to scope link-local addresses with %. 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); + n_written = g_snprintf (out_buf, + IP_ADDR_TO_STRING_BUFLEN, + "%s%s%s", + buf2, + iface ? separator : "", + iface ?: ""); + nm_assert (n_written < IP_ADDR_TO_STRING_BUFLEN); + return out_buf; } static void @@ -271,103 +158,43 @@ add_global_config (NMDnsDnsmasq *self, GVariantBuilder *dnsmasq_servers, const N } } -static gboolean -add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, - const char *iface, gboolean split) +static void +add_ip_config (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigData *ip_data) { - const struct in6_addr *addr; - char *buf = NULL; - int nnameservers, i_nameserver, n, i; - gboolean added = FALSE; - - g_return_val_if_fail (iface, FALSE); - nnameservers = nm_ip6_config_get_num_nameservers (ip6); - - if (split) { - char **domains, **iter; - - if (nnameservers == 0) - return FALSE; - - for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { - addr = nm_ip6_config_get_nameserver (ip6, i_nameserver); - buf = ip6_addr_to_string (addr, iface); - - /* searches are preferred over domains */ - n = nm_ip6_config_get_num_searches (ip6); - for (i = 0; i < n; i++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip6_config_get_search (ip6, i)); - 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++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip6_config_get_domain (ip6, i)); - added = TRUE; - } - } - - /* 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); + NMIPConfig *ip_config = ip_data->ip_config; + gconstpointer addr; + const char *iface, *domain; + char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; + int addr_family; + guint i, j, num; + + iface = nm_platform_link_get_name (NM_PLATFORM_GET, ip_data->data->ifindex); + addr_family = nm_ip_config_get_addr_family (ip_config); + + num = nm_ip_config_get_num_nameservers (ip_config); + for (i = 0; i < num; i++) { + addr = nm_ip_config_get_nameserver (ip_config, i); + ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf); + for (j = 0; ip_data->domains.search[j]; j++) { + domain = nm_utils_parse_dns_domain (ip_data->domains.search[j], NULL); + add_dnsmasq_nameserver (self, + servers, + ip_addr_to_string_buf, + domain[0] ? domain : NULL); } - } - /* 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); - buf = ip6_addr_to_string (addr, iface); - if (buf) { - add_dnsmasq_nameserver (self, servers, buf, NULL); - g_free (buf); - } + for (j = 0; ip_data->domains.reverse[j]; j++) { + add_dnsmasq_nameserver (self, servers, + ip_addr_to_string_buf, + ip_data->domains.reverse[j]); } } - - 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 (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { NMDnsDnsmasq *self; - NMDnsDnsmasqPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *response = NULL; @@ -376,7 +203,6 @@ dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) return; self = NM_DNS_DNSMASQ (user_data); - priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); if (!response) _LOGW ("dnsmasq update failed: %s", error->message); @@ -478,7 +304,7 @@ start_dnsmasq (NMDnsDnsmasq *self) const char *argv[15]; GPid pid = 0; guint idx = 0; - NMBusManager *dbus_mgr; + NMDBusManager *dbus_mgr; GDBusConnection *connection; if (priv->running) { @@ -530,10 +356,10 @@ start_dnsmasq (NMDnsDnsmasq *self) return; } - dbus_mgr = nm_bus_manager_get (); + dbus_mgr = nm_dbus_manager_get (); g_return_if_fail (dbus_mgr); - connection = nm_bus_manager_get_connection (dbus_mgr); + connection = nm_dbus_manager_get_connection (dbus_mgr); g_return_if_fail (connection); priv->dnsmasq_cancellable = g_cancellable_new (); @@ -550,15 +376,14 @@ start_dnsmasq (NMDnsDnsmasq *self) static gboolean update (NMDnsPlugin *plugin, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); GVariantBuilder servers; - guint i; - int prio, first_prio; + const NMDnsIPConfigData *ip_data; start_dnsmasq (self); @@ -567,16 +392,8 @@ update (NMDnsPlugin *plugin, if (global_config) add_global_config (self, &servers, global_config); else { - for (i = 0; i < configs->len; i++) { - const NMDnsIPConfigData *data = configs->pdata[i]; - - prio = nm_ip_config_get_dns_priority (data->config); - if (i == 0) - first_prio = prio; - else if (first_prio < 0 && first_prio != prio) - break; - add_ip_config_data (self, &servers, data); - } + c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) + add_ip_config (self, &servers, ip_data); } g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index dc545470..f5500ee7 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -46,6 +46,7 @@ #include "nm-ip6-config.h" #include "NetworkManagerUtils.h" #include "nm-config.h" +#include "nm-dbus-object.h" #include "devices/nm-device.h" #include "nm-manager.h" @@ -54,8 +55,6 @@ #include "nm-dns-systemd-resolved.h" #include "nm-dns-unbound.h" -#include "introspection/org.freedesktop.NetworkManager.DnsManager.h" - #define HASH_LEN 20 #ifndef RESOLVCONF_PATH @@ -70,6 +69,24 @@ #define PLUGIN_RATELIMIT_BURST 5 #define PLUGIN_RATELIMIT_DELAY 300 +/*****************************************************************************/ + +typedef enum { + SR_SUCCESS, + SR_NOTFOUND, + SR_ERROR +} SpawnResult; + +typedef struct { + GPtrArray *nameservers; + GPtrArray *searches; + GPtrArray *options; + const char *nis_domain; + GPtrArray *nis_servers; +} NMResolvConfData; + +/*****************************************************************************/ + enum { CONFIG_CHANGED, @@ -84,44 +101,16 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDnsManager, static guint signals[LAST_SIGNAL] = { 0 }; -typedef enum { - SR_SUCCESS, - SR_NOTFOUND, - SR_ERROR -} SpawnResult; - -NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "dns-mgr" -#define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - G_STMT_START { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ - char __prefix[20]; \ - const NMDnsManager *const __self = (self); \ - \ - _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((!__self || __self == singleton_instance) \ - ? "" \ - : nm_sprintf_buf (__prefix, "[%p]", __self)) \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - typedef struct { - GPtrArray *configs; + GHashTable *configs; + CList ip_config_lst_head; GVariant *config_variant; - NMDnsIPConfigData *best_conf4, *best_conf6; - bool need_sort:1; + NMDnsIPConfigData *best_ip_config_4; + NMDnsIPConfigData *best_ip_config_6; + + bool ip_config_lst_need_sort:1; + bool dns_touched:1; bool is_stopped:1; @@ -145,20 +134,52 @@ typedef struct { } NMDnsManagerPrivate; struct _NMDnsManager { - NMExportedObject parent; + NMDBusObject parent; NMDnsManagerPrivate _priv; }; struct _NMDnsManagerClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_DBUS_OBJECT) #define NM_DNS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsManager, NM_IS_DNS_MANAGER) +NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); + +/*****************************************************************************/ + +#define _NMLOG_PREFIX_NAME "dns-mgr" +#define _NMLOG_DOMAIN LOGD_DNS +#define _NMLOG(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + char __prefix[20]; \ + const NMDnsManager *const __self = (self); \ + \ + _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + ((!__self || __self == singleton_instance) \ + ? "" \ + : nm_sprintf_buf (__prefix, "[%p]", __self)) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +static void _ip_config_dns_priority_changed (gpointer config, + GParamSpec *pspec, + NMDnsIPConfigData *ip_data); + +/*****************************************************************************/ + static gboolean -domain_is_valid (const gchar *domain, gboolean check_public_suffix) +domain_is_valid (const char *domain, gboolean check_public_suffix) { if (*domain == '\0') return FALSE; @@ -169,15 +190,13 @@ domain_is_valid (const gchar *domain, gboolean check_public_suffix) return TRUE; } -/*****************************************************************************/ +static gboolean +domain_is_routing (const char *domain) +{ + return domain[0] == '~'; +} -typedef struct { - GPtrArray *nameservers; - GPtrArray *searches; - GPtrArray *options; - const char *nis_domain; - GPtrArray *nis_servers; -} NMResolvConfData; +/*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConfManager, NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), @@ -192,44 +211,112 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConf NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_config_type_to_string, NMDnsIPConfigType, NM_UTILS_LOOKUP_DEFAULT_WARN (""), + NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_REMOVED, "removed"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_DEFAULT, "default"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, "best"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_VPN, "vpn"), ); -static NMDnsIPConfigData * -ip_config_data_new (gpointer config, NMDnsIPConfigType type, const char *iface) +/*****************************************************************************/ + +static void +_ASSERT_config_data (const NMDnsConfigData *data) { - NMDnsIPConfigData *data; + nm_assert (data); + nm_assert (NM_IS_DNS_MANAGER (data->self)); + nm_assert (data->ifindex > 0); +} - data = g_slice_new0 (NMDnsIPConfigData); - data->config = g_object_ref (config); - data->iface = g_strdup (iface); - data->type = type; +static void +_ASSERT_ip_config_data (const NMDnsIPConfigData *ip_data) +{ + nm_assert (ip_data); + _ASSERT_config_data (ip_data->data); + nm_assert (NM_IS_IP_CONFIG (ip_data->ip_config, AF_UNSPEC)); + nm_assert (c_list_contains (&ip_data->data->data_lst_head, &ip_data->data_lst)); + nm_assert (ip_data->data->ifindex == nm_ip_config_get_ifindex (ip_data->ip_config)); +} - return data; +static NMDnsIPConfigData * +_ip_config_data_new (NMDnsConfigData *data, + NMIPConfig *ip_config, + NMDnsIPConfigType ip_config_type) +{ + NMDnsIPConfigData *ip_data; + + _ASSERT_config_data (data); + nm_assert (NM_IS_IP_CONFIG (ip_config, AF_UNSPEC)); + nm_assert (ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED); + + ip_data = g_slice_new0 (NMDnsIPConfigData); + ip_data->data = data; + ip_data->ip_config = g_object_ref (ip_config); + ip_data->ip_config_type = ip_config_type; + c_list_link_tail (&data->data_lst_head, &ip_data->data_lst); + c_list_link_tail (&NM_DNS_MANAGER_GET_PRIVATE (data->self)->ip_config_lst_head, &ip_data->ip_config_lst); + + g_signal_connect (ip_config, + NM_IS_IP4_CONFIG (ip_config) + ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY + : "notify::" NM_IP6_CONFIG_DNS_PRIORITY, + (GCallback) _ip_config_dns_priority_changed, ip_data); + + _ASSERT_ip_config_data (ip_data); + return ip_data; } static void -ip_config_data_destroy (gpointer ptr) +_ip_config_data_free (NMDnsIPConfigData *ip_data) { - NMDnsIPConfigData *data = ptr; + _ASSERT_ip_config_data (ip_data); - if (!data) - return; + c_list_unlink_stale (&ip_data->data_lst); + c_list_unlink_stale (&ip_data->ip_config_lst); + + g_free (ip_data->domains.search); + g_strfreev (ip_data->domains.reverse); - g_object_unref (data->config); - g_free (data->iface); - g_slice_free (NMDnsIPConfigData, data); + g_signal_handlers_disconnect_by_func (ip_data->ip_config, + _ip_config_dns_priority_changed, + ip_data); + + g_object_unref (ip_data->ip_config); + g_slice_free (NMDnsIPConfigData, ip_data); +} + +static NMDnsIPConfigData * +_config_data_find_ip_config (NMDnsConfigData *data, + NMIPConfig *ip_config) +{ + NMDnsIPConfigData *ip_data; + + _ASSERT_config_data (data); + + c_list_for_each_entry (ip_data, &data->data_lst_head, data_lst) { + _ASSERT_ip_config_data (ip_data); + + if (ip_data->ip_config == ip_config) + return ip_data; + } + return NULL; +} + +static void +_config_data_free (NMDnsConfigData *data) +{ + _ASSERT_config_data (data); + + nm_assert (c_list_is_empty (&data->data_lst_head)); + g_slice_free (NMDnsConfigData, data); } static gint -ip_config_data_compare (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) +_ip_config_data_cmp (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) { int a_prio, b_prio; - a_prio = nm_ip_config_get_dns_priority (a->config); - b_prio = nm_ip_config_get_dns_priority (b->config); + a_prio = nm_ip_config_get_dns_priority (a->ip_config); + b_prio = nm_ip_config_get_dns_priority (b->ip_config); /* Configurations with lower priority value first */ if (a_prio < b_prio) @@ -238,24 +325,40 @@ ip_config_data_compare (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) return 1; /* Sort also according to type */ - if (a->type > b->type) + if (a->ip_config_type > b->ip_config_type) return -1; - else if (a->type < b->type) + else if (a->ip_config_type < b->ip_config_type) return 1; return 0; } static gint -ip_config_data_ptr_compare (gconstpointer a, gconstpointer b) +_ip_config_lst_cmp (const CList *a, + const CList *b, + const void *user_data) +{ + return _ip_config_data_cmp (c_list_entry (a, NMDnsIPConfigData, ip_config_lst), + c_list_entry (b, NMDnsIPConfigData, ip_config_lst)); +} + +static CList * +_ip_config_lst_head (NMDnsManager *self) { - const NMDnsIPConfigData *const *ptr_a = a, *const *ptr_b = b; + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - return ip_config_data_compare (*ptr_a, *ptr_b); + if (priv->ip_config_lst_need_sort) { + priv->ip_config_lst_need_sort = FALSE; + c_list_sort (&priv->ip_config_lst_head, _ip_config_lst_cmp, NULL); + } + + return &priv->ip_config_lst_head; } +/*****************************************************************************/ + static void -add_string_item (GPtrArray *array, const char *str) +add_string_item (GPtrArray *array, const char *str, gboolean dup) { int i; @@ -271,7 +374,7 @@ add_string_item (GPtrArray *array, const char *str) } /* No dupes, add the new item */ - g_ptr_array_add (array, g_strdup (str)); + g_ptr_array_add (array, dup ? g_strdup (str): (gpointer) str); } static void @@ -281,25 +384,56 @@ add_dns_option_item (GPtrArray *array, const char *str) g_ptr_array_add (array, g_strdup (str)); } +static void +add_dns_domains (GPtrArray *array, const NMIPConfig *ip_config, + gboolean include_routing, gboolean dup) +{ + guint num_domains, num_searches, i; + const char *str; + + num_domains = nm_ip_config_get_num_domains (ip_config); + num_searches = nm_ip_config_get_num_searches (ip_config); + + for (i = 0; i < num_searches; i++) { + str = nm_ip_config_get_search (ip_config, i); + if (!include_routing && domain_is_routing (str)) + continue; + if (!domain_is_valid (nm_utils_parse_dns_domain (str, NULL), FALSE)) + continue; + add_string_item (array, str, dup); + } + if (num_domains > 1 || !num_searches) { + for (i = 0; i < num_domains; i++) { + str = nm_ip_config_get_domain (ip_config, i); + if (!include_routing && domain_is_routing (str)) + continue; + if (!domain_is_valid (nm_utils_parse_dns_domain (str, NULL), FALSE)) + continue; + add_string_item (array, str, dup); + } + } +} + static void merge_one_ip_config (NMResolvConfData *rc, - const NMIPConfig *config, - const char *iface) + int ifindex, + const NMIPConfig *ip_config) { int addr_family; - guint num, num_domains, num_searches, i; + guint num, i; char buf[NM_UTILS_INET_ADDRSTRLEN + 50]; - const char *str; - addr_family = nm_ip_config_get_addr_family (config); + addr_family = nm_ip_config_get_addr_family (ip_config); nm_assert_addr_family (addr_family); + nm_assert (ifindex > 0); + nm_assert (ifindex == nm_ip_config_get_ifindex (ip_config)); - num = nm_ip_config_get_num_nameservers (config); + num = nm_ip_config_get_num_nameservers (ip_config); for (i = 0; i < num; i++) { const NMIPAddr *addr; - addr = nm_ip_config_get_nameserver (config, i); + addr = nm_ip_config_get_nameserver (ip_config, i); if (addr_family == AF_INET) nm_utils_inet_ntop (addr_family, addr, buf); else if (IN6_IS_ADDR_V4MAPPED (addr)) @@ -307,49 +441,42 @@ merge_one_ip_config (NMResolvConfData *rc, else { nm_utils_inet6_ntop (&addr->addr6, buf); if (IN6_IS_ADDR_LINKLOCAL (addr)) { - g_strlcat (buf, "%", sizeof (buf)); - g_strlcat (buf, iface, sizeof (buf)); + const char *ifname; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + if (ifname) { + g_strlcat (buf, "%", sizeof (buf)); + g_strlcat (buf, ifname, sizeof (buf)); + } } } - add_string_item (rc->nameservers, buf); + add_string_item (rc->nameservers, buf, TRUE); } - num_domains = nm_ip_config_get_num_domains (config); - num_searches = nm_ip_config_get_num_searches (config); - for (i = 0; i < num_searches; i++) { - str = nm_ip_config_get_search (config, i); - if (domain_is_valid (str, FALSE)) - add_string_item (rc->searches, str); - } - if (num_domains > 1 || !num_searches) { - for (i = 0; i < num_domains; i++) { - str = nm_ip_config_get_domain (config, i); - if (domain_is_valid (str, FALSE)) - add_string_item (rc->searches, str); - } - } + add_dns_domains (rc->searches, ip_config, FALSE, TRUE); - num = nm_ip_config_get_num_dns_options (config); + num = nm_ip_config_get_num_dns_options (ip_config); for (i = 0; i < num; i++) { add_dns_option_item (rc->options, - nm_ip_config_get_dns_option (config, i)); + nm_ip_config_get_dns_option (ip_config, i)); } if (addr_family == AF_INET) { - const NMIP4Config *config4 = (const NMIP4Config *) config; + const NMIP4Config *ip4_config = (const NMIP4Config *) ip_config; /* NIS stuff */ - num = nm_ip4_config_get_num_nis_servers (config4); + num = nm_ip4_config_get_num_nis_servers (ip4_config); for (i = 0; i < num; i++) { add_string_item (rc->nis_servers, - nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (config4, i), buf)); + nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (ip4_config, i), buf), + TRUE); } - if (nm_ip4_config_get_nis_domain (config4)) { + if (nm_ip4_config_get_nis_domain (ip4_config)) { /* FIXME: handle multiple domains */ if (!rc->nis_domain) - rc->nis_domain = nm_ip4_config_get_nis_domain (config4); + rc->nis_domain = nm_ip4_config_get_nis_domain (ip4_config); } } } @@ -378,58 +505,59 @@ run_netconfig (NMDnsManager *self, GError **error, gint *stdin_fd) } static void -write_to_netconfig (NMDnsManager *self, gint fd, const char *key, const char *value) +netconfig_construct_str (NMDnsManager *self, GString *str, const char *key, const char *value) +{ + if (value) { + _LOGD ("writing to netconfig: %s='%s'", key, value); + g_string_append_printf (str, "%s='%s'\n", key, value); + } +} + +static void +netconfig_construct_strv (NMDnsManager *self, GString *str, const char *key, const char *const*values) { - char *str; - int x; + if (values) { + gs_free char *value = NULL; - str = g_strdup_printf ("%s='%s'\n", key, value); - _LOGD ("writing to netconfig: %s", str); - x = write (fd, str, strlen (str)); - g_free (str); + value = g_strjoinv (" ", (char **) values); + netconfig_construct_str (self, str, key, value); + } } static SpawnResult dispatch_netconfig (NMDnsManager *self, - char **searches, - char **nameservers, + const char *const*searches, + const char *const*nameservers, const char *nis_domain, - char **nis_servers, + const char *const*nis_servers, GError **error) { - char *str; GPid pid; gint fd; int status; + gssize l; + nm_auto_free_gstring GString *str = NULL; pid = run_netconfig (self, error, &fd); if (pid <= 0) return SR_NOTFOUND; + str = g_string_new (""); + /* NM is writing already-merged DNS information to netconfig, so it * does not apply to a specific network interface. */ - write_to_netconfig (self, fd, "INTERFACE", "NetworkManager"); - - if (searches) { - str = g_strjoinv (" ", searches); - write_to_netconfig (self, fd, "DNSSEARCH", str); - g_free (str); - } - - if (nameservers) { - str = g_strjoinv (" ", nameservers); - write_to_netconfig (self, fd, "DNSSERVERS", str); - g_free (str); - } + netconfig_construct_str (self, str, "INTERFACE", "NetworkManager"); + netconfig_construct_strv (self, str, "DNSSEARCH", searches); + netconfig_construct_strv (self, str, "DNSSERVERS", nameservers); + netconfig_construct_str (self, str, "NISDOMAIN", nis_domain); + netconfig_construct_strv (self, str, "NISSERVERS", nis_servers); - if (nis_domain) - write_to_netconfig (self, fd, "NISDOMAIN", nis_domain); - - if (nis_servers) { - str = g_strjoinv (" ", nis_servers); - write_to_netconfig (self, fd, "NISSERVERS", str); - g_free (str); +again: + l = write (fd, str->str, str->len); + if (l == -1) { + if (errno == EINTR) + goto again; } nm_close (fd); @@ -743,7 +871,7 @@ update_resolv_conf (NMDnsManager *self, if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) { _LOGT ("update-resolv-conf: write internal file %s succeeded (rc-manager=%s)", - rc_path, _rc_manager_to_string (rc_manager)); + MY_RESOLV_CONF, _rc_manager_to_string (rc_manager)); return write_file_result; } @@ -817,25 +945,21 @@ update_resolv_conf (NMDnsManager *self, static void compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN]) { - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); GChecksum *sum; gsize len = HASH_LEN; - guint i; + NMDnsIPConfigData *ip_data; sum = g_checksum_new (G_CHECKSUM_SHA1); - g_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); + nm_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); if (global) nm_global_dns_config_update_checksum (global, sum); else { - for (i = 0; i < priv->configs->len; i++) { - NMDnsIPConfigData *data = priv->configs->pdata[i]; + const CList *head; - if (NM_IS_IP4_CONFIG (data->config)) - nm_ip4_config_hash ((NMIP4Config *) data->config, sum, TRUE); - else if (NM_IS_IP6_CONFIG (data->config)) - nm_ip6_config_hash ((NMIP6Config *) data->config, sum, TRUE); - } + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) + nm_ip_config_hash (ip_data->ip_config, sum, TRUE); } g_checksum_get_digest (sum, buffer, &len); @@ -849,27 +973,36 @@ merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) const char *const *searches; const char *const *options; const char *const *servers; - gint i; + guint i; if (!global_conf) return FALSE; searches = nm_global_dns_config_get_searches (global_conf); - options = nm_global_dns_config_get_options (global_conf); - - for (i = 0; searches && searches[i]; i++) { - if (domain_is_valid (searches[i], FALSE)) - add_string_item (rc->searches, searches[i]); + if (searches) { + for (i = 0; searches[i]; i++) { + if (domain_is_routing (searches[i])) + continue; + if (!domain_is_valid (searches[i], FALSE)) + continue; + add_string_item (rc->searches, searches[i], TRUE); + } } - for (i = 0; options && options[i]; i++) - add_string_item (rc->options, options[i]); + options = nm_global_dns_config_get_options (global_conf); + if (options) { + for (i = 0; options[i]; i++) + add_string_item (rc->options, options[i], TRUE); + } default_domain = nm_global_dns_config_lookup_domain (global_conf, "*"); - g_assert (default_domain); + nm_assert (default_domain); + servers = nm_global_dns_domain_get_servers (default_domain); - for (i = 0; servers && servers[i]; i++) - add_string_item (rc->nameservers, servers[i]); + if (servers) { + for (i = 0; servers[i]; i++) + add_string_item (rc->nameservers, servers[i], TRUE); + } return TRUE; } @@ -909,17 +1042,16 @@ _ptrarray_to_strv (GPtrArray *parray) } static void -_collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no other side-effects */ +_collect_resolv_conf_data (NMDnsManager *self, NMGlobalDnsConfig *global_config, - const GPtrArray *configs, - const char *hostname, char ***out_searches, char ***out_options, char ***out_nameservers, char ***out_nis_servers, const char **out_nis_domain) { - guint i, j, num, len; + NMDnsManagerPrivate *priv; + guint i, num, len; NMResolvConfData rc = { .nameservers = g_ptr_array_new (), .searches = g_ptr_array_new (), @@ -928,37 +1060,44 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o .nis_servers = g_ptr_array_new (), }; + priv = NM_DNS_MANAGER_GET_PRIVATE (self); + if (global_config) merge_global_dns_config (&rc, global_config); else { nm_auto_free_gstring GString *tmp_gstring = NULL; int prio, first_prio = 0; - NMDnsIPConfigData *current; + const NMDnsIPConfigData *ip_data; + const CList *head; + gboolean is_first = TRUE; - for (i = 0, j = 0; i < configs->len; i++) { + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) { gboolean skip = FALSE; - current = configs->pdata[i]; + _ASSERT_ip_config_data (ip_data); - prio = nm_ip_config_get_dns_priority (current->config); + prio = nm_ip_config_get_dns_priority (ip_data->ip_config); - if (i == 0) + if (is_first) { + is_first = FALSE; first_prio = prio; - else if (first_prio < 0 && first_prio != prio) + } else if ( first_prio < 0 + && first_prio != prio) skip = TRUE; - if (nm_ip_config_get_num_nameservers (current->config)) { - _LOGT ("config: %8d %-7s v%c %-16s %s: %s", + if (nm_ip_config_get_num_nameservers (ip_data->ip_config)) { + _LOGT ("config: %8d %-7s v%c %-5d %s: %s", prio, - _config_type_to_string (current->type), - nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (current->config)), - current->iface, + _config_type_to_string (ip_data->ip_config_type), + nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (ip_data->ip_config)), + ip_data->data->ifindex, skip ? "" : "", - get_nameserver_list (current->config, &tmp_gstring)); + get_nameserver_list (ip_data->ip_config, &tmp_gstring)); } if (!skip) - merge_one_ip_config (&rc, current->config, current->iface); + merge_one_ip_config (&rc, ip_data->data->ifindex, ip_data->ip_config); } } @@ -969,16 +1108,16 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o * (eg, "example.com"), then use the hostname itself as the search (since the user is * unlikely to want "com" as a search domain). */ - if (hostname) { - const char *hostdomain = strchr (hostname, '.'); + if (priv->hostname) { + const char *hostdomain = strchr (priv->hostname, '.'); if ( hostdomain - && !nm_utils_ipaddr_valid (AF_UNSPEC, hostname)) { + && !nm_utils_ipaddr_valid (AF_UNSPEC, priv->hostname)) { hostdomain++; if (domain_is_valid (hostdomain, TRUE)) - add_string_item (rc.searches, hostdomain); - else if (domain_is_valid (hostname, TRUE)) - add_string_item (rc.searches, hostname); + add_string_item (rc.searches, hostdomain, TRUE); + else if (domain_is_valid (priv->hostname, TRUE)) + add_string_item (rc.searches, priv->hostname, TRUE); } } @@ -1000,6 +1139,197 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o *out_nis_domain = rc.nis_domain; } +static char ** +get_ip_rdns_domains (NMIPConfig *ip_config) +{ + int addr_family = nm_ip_config_get_addr_family (ip_config); + char **strv; + GPtrArray *domains = NULL; + NMDedupMultiIter ipconf_iter; + + nm_assert_addr_family (addr_family); + + domains = g_ptr_array_sized_new (5); + + if (addr_family == AF_INET) { + NMIP4Config *ip4 = (gpointer) ip_config; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; + + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) + nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); + } + } else { + NMIP6Config *ip6 = (gpointer) ip_config; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *route; + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) + nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + 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); +} + +/* Check if the domain is shadowed by a parent domain with more negative priority */ +static gboolean +domain_is_shadowed (GHashTable *ht, + const char *domain, int priority, + const char **out_parent, int *out_parent_priority) +{ + char *parent; + int parent_priority; + + nm_assert (!g_hash_table_contains (ht, domain)); + + parent_priority = GPOINTER_TO_INT (g_hash_table_lookup (ht, "")); + if (parent_priority < 0 && parent_priority < priority) { + *out_parent = ""; + *out_parent_priority = parent_priority; + return TRUE; + } + + parent = strchr (domain, '.'); + while (parent && parent[1]) { + parent++; + parent_priority = GPOINTER_TO_INT (g_hash_table_lookup (ht, parent)); + if (parent_priority < 0 && parent_priority < priority) { + *out_parent = parent; + *out_parent_priority = parent_priority; + return TRUE; + } + parent = strchr (parent, '.'); + } + + return FALSE; +} + +static void +rebuild_domain_lists (NMDnsManager *self) +{ + NMDnsIPConfigData *ip_data; + gs_unref_hashtable GHashTable *ht = NULL; + gboolean default_route_found = FALSE; + CList *head; + + ht = g_hash_table_new (nm_str_hash, g_str_equal); + + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) { + NMIPConfig *ip_config = ip_data->ip_config; + + if (!nm_ip_config_get_num_nameservers (ip_config)) + continue; + if (nm_ip_config_best_default_route_get (ip_config)) { + default_route_found = TRUE; + break; + } + } + + c_list_for_each_entry (ip_data, head, ip_config_lst) { + NMIPConfig *ip_config = ip_data->ip_config; + int priority, old_priority; + guint i, n, n_domains = 0; + const char **domains; + + if (!nm_ip_config_get_num_nameservers (ip_config)) + continue; + + priority = nm_ip_config_get_dns_priority (ip_config); + nm_assert (priority != 0); + g_free (ip_data->domains.search); + domains = g_new0 (const char *, + 2 + NM_MAX (nm_ip_config_get_num_searches (ip_config), + nm_ip_config_get_num_domains (ip_config))); + ip_data->domains.search = domains; + + /* Add wildcard lookup domain to connections with the default route. + * If there is no default route, add the wildcard domain to all non-VPN + * connections */ + if (default_route_found) { + if (nm_ip_config_best_default_route_get (ip_config)) + domains[n_domains++] = "~"; + } else { + if (ip_data->ip_config_type != NM_DNS_IP_CONFIG_TYPE_VPN) + domains[n_domains++] = "~"; + } + + /* searches are preferred over domains */ + n = nm_ip_config_get_num_searches (ip_config); + for (i = 0; i < n; i++) + domains[n_domains++] = nm_ip_config_get_search (ip_config, i); + + if (n == 0) { + /* If not searches, use any domains */ + n = nm_ip_config_get_num_domains (ip_config); + for (i = 0; i < n; i++) + domains[n_domains++] = nm_ip_config_get_domain (ip_config, i); + } + + n = 0; + for (i = 0; i < n_domains; i++) { + const char *domain_clean; + const char *parent; + int parent_priority; + + domain_clean = nm_utils_parse_dns_domain (domains[i], NULL); + + /* Remove domains with lower priority */ + old_priority = GPOINTER_TO_INT (g_hash_table_lookup (ht, domain_clean)); + if (old_priority) { + if (old_priority < priority) { + _LOGT ("plugin: drop domain '%s' (i=%d, p=%d) because it already exists with p=%d", + domains[i], ip_data->data->ifindex, + priority, old_priority); + continue; + } + } else if (domain_is_shadowed (ht, domain_clean, priority, &parent, &parent_priority)) { + _LOGT ("plugin: drop domain '%s' (i=%d, p=%d) shadowed by '%s' (p=%d)", + domains[i], + ip_data->data->ifindex, priority, + parent, parent_priority); + continue; + } + + _LOGT ("plugin: add domain '%s' (i=%d, p=%d)", domains[i], ip_data->data->ifindex, priority); + g_hash_table_insert (ht, (gpointer) domain_clean, GINT_TO_POINTER (priority)); + domains[n++] = domains[i]; + } + domains[n] = NULL; + + g_strfreev (ip_data->domains.reverse); + ip_data->domains.reverse = get_ip_rdns_domains (ip_config); + } +} + +static void +clear_domain_lists (NMDnsManager *self) +{ + NMDnsIPConfigData *ip_data; + CList *head; + + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) { + g_clear_pointer (&ip_data->domains.search, g_free); + g_clear_pointer (&ip_data->domains.reverse, g_strfreev); + } +} + static gboolean update_dns (NMDnsManager *self, gboolean no_caching, @@ -1040,16 +1370,12 @@ update_dns (NMDnsManager *self, data = nm_config_get_data (priv->config); global_config = nm_config_data_get_global_dns_config (data); - if (priv->need_sort) { - g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare); - priv->need_sort = FALSE; - } - /* Update hash with config we're applying */ compute_hash (self, global_config, priv->hash); - _collect_resolv_conf_data (self, global_config, priv->configs, priv->hostname, - &searches, &options, &nameservers, &nis_servers, &nis_domain); + _collect_resolv_conf_data (self, global_config, + &searches, &options, &nameservers, + &nis_servers, &nis_domain); /* Let any plugins do their thing first */ if (priv->plugin) { @@ -1066,9 +1392,10 @@ update_dns (NMDnsManager *self, } _LOGD ("update-dns: updating plugin %s", plugin_name); + rebuild_domain_lists (self); if (!nm_dns_plugin_update (plugin, - priv->configs, global_config, + _ip_config_lst_head (self), priv->hostname)) { _LOGW ("update-dns: plugin %s update failed", plugin_name); @@ -1077,6 +1404,10 @@ update_dns (NMDnsManager *self, */ caching = FALSE; } + /* Clear the generated search list as it points to + * strings owned by IP configurations and we can't + * guarantee they stay alive. */ + clear_domain_lists (self); skip: ; @@ -1114,8 +1445,12 @@ update_dns (NMDnsManager *self, result = dispatch_resolvconf (self, searches, nameservers, options, error); break; case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: - result = dispatch_netconfig (self, searches, nameservers, nis_domain, - nis_servers, error); + result = dispatch_netconfig (self, + (const char *const*) searches, + (const char *const*) nameservers, + nis_domain, + (const char *const*) nis_servers, + error); break; default: g_assert_not_reached (); @@ -1205,82 +1540,96 @@ plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data) } static void -ip_config_dns_priority_changed (gpointer config, - GParamSpec *pspec, - NMDnsManager *self) +_ip_config_dns_priority_changed (gpointer config, + GParamSpec *pspec, + NMDnsIPConfigData *ip_data) { - NM_DNS_MANAGER_GET_PRIVATE (self)->need_sort = TRUE; -} + _ASSERT_ip_config_data (ip_data); -static void -forget_data (NMDnsManager *self, NMDnsIPConfigData *data) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - if (data == priv->best_conf4) - priv->best_conf4 = NULL; - else if (data == priv->best_conf6) - priv->best_conf6 = NULL; - - g_signal_handlers_disconnect_by_func (data->config, ip_config_dns_priority_changed, self); + NM_DNS_MANAGER_GET_PRIVATE (ip_data->data->self)->ip_config_lst_need_sort = TRUE; } gboolean -nm_dns_manager_add_ip_config (NMDnsManager *self, - const char *iface, - gpointer config, - NMDnsIPConfigType cfg_type) +nm_dns_manager_set_ip_config (NMDnsManager *self, + NMIPConfig *ip_config, + NMDnsIPConfigType ip_config_type) { NMDnsManagerPrivate *priv; GError *error = NULL; - NMDnsIPConfigData *data; - gboolean v4 = NM_IS_IP4_CONFIG (config); - guint i; + NMDnsIPConfigData *ip_data; + NMDnsConfigData *data; + int ifindex; + NMDnsIPConfigData **p_best; g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); - g_return_val_if_fail (config, FALSE); - g_return_val_if_fail (iface && iface[0], FALSE); - nm_assert (NM_IS_IP_CONFIG (config)); + g_return_val_if_fail (NM_IS_IP_CONFIG (ip_config, AF_UNSPEC), FALSE); + + ifindex = nm_ip_config_get_ifindex (ip_config); + g_return_val_if_fail (ifindex > 0, FALSE); priv = NM_DNS_MANAGER_GET_PRIVATE (self); - for (i = 0; i < priv->configs->len; i++) { - data = priv->configs->pdata[i]; - if (data->config == config) { - if ( nm_streq (data->iface, iface) - && data->type == cfg_type) - return FALSE; - else { - forget_data (self, data); - g_ptr_array_remove_index_fast (priv->configs, i); - break; - } - } + data = g_hash_table_lookup (priv->configs, GINT_TO_POINTER (ifindex)); + if (!data) + ip_data = NULL; + else + ip_data = _config_data_find_ip_config (data, ip_config); + + if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) { + if (!ip_data) + return FALSE; + if (priv->best_ip_config_4 == ip_data) + priv->best_ip_config_4 = NULL; + if (priv->best_ip_config_6 == ip_data) + priv->best_ip_config_6 = NULL; + /* deleting a config doesn't invalidate the configs' sort order. */ + _ip_config_data_free (ip_data); + if (c_list_is_empty (&data->data_lst_head)) + g_hash_table_remove (priv->configs, GINT_TO_POINTER (ifindex)); + goto changed; + } + + if ( ip_data + && ip_data->ip_config_type == ip_config_type) { + /* nothing to do. */ + return FALSE; + } + + if (!data) { + data = g_slice_new0 (NMDnsConfigData); + data->ifindex = ifindex; + data->self = self; + c_list_init (&data->data_lst_head); + _ASSERT_config_data (data); + g_hash_table_insert (priv->configs, GINT_TO_POINTER (ifindex), data); } - data = ip_config_data_new (config, cfg_type, iface); - g_ptr_array_add (priv->configs, data); - g_signal_connect (config, - v4 ? - "notify::" NM_IP4_CONFIG_DNS_PRIORITY : - "notify::" NM_IP6_CONFIG_DNS_PRIORITY, - (GCallback) ip_config_dns_priority_changed, self); - priv->need_sort = TRUE; + if (!ip_data) + ip_data = _ip_config_data_new (data, ip_config, ip_config_type); + else + ip_data->ip_config_type = ip_config_type; + + priv->ip_config_lst_need_sort = TRUE; - if (cfg_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { + p_best = NM_IS_IP4_CONFIG (ip_config) + ? &priv->best_ip_config_4 + : &priv->best_ip_config_6; + + if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { /* Only one best-device per IP version is allowed */ - if (v4) { - if (priv->best_conf4) - priv->best_conf4->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; - priv->best_conf4 = data; - } else { - if (priv->best_conf6) - priv->best_conf6->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; - priv->best_conf6 = data; + if (*p_best != ip_data) { + if (*p_best) + (*p_best)->ip_config_type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; + *p_best = ip_data; } + } else { + if (*p_best == ip_data) + *p_best = NULL; } - if (!priv->updates_queue && !update_dns (self, FALSE, &error)) { +changed: + if ( !priv->updates_queue + && !update_dns (self, FALSE, &error)) { _LOGW ("could not commit DNS changes: %s", error->message); g_clear_error (&error); } @@ -1288,38 +1637,6 @@ nm_dns_manager_add_ip_config (NMDnsManager *self, return TRUE; } -gboolean -nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config) -{ - NMDnsManagerPrivate *priv; - GError *error = NULL; - NMDnsIPConfigData *data; - guint i; - - g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); - g_return_val_if_fail (config, FALSE); - nm_assert (NM_IS_IP_CONFIG (config)); - - priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - for (i = 0; i < priv->configs->len; i++) { - data = priv->configs->pdata[i]; - - if (data->config == config) { - forget_data (self, data); - g_ptr_array_remove_index (priv->configs, i); - - if (!priv->updates_queue && !update_dns (self, FALSE, &error)) { - _LOGW ("could not commit DNS changes: %s", error->message); - g_clear_error (&error); - } - - return TRUE; - } - } - return FALSE; -} - void nm_dns_manager_set_initial_hostname (NMDnsManager *self, const char *hostname) @@ -1362,23 +1679,6 @@ nm_dns_manager_set_hostname (NMDnsManager *self, } } -gboolean -nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self) -{ - NMDnsManagerPrivate *priv; - - g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); - - priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - if ( NM_IN_SET (priv->rc_manager, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED, - NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE) - || priv->plugin) - return FALSE; - - return TRUE; -} - void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func) { @@ -1409,11 +1709,6 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func) priv = NM_DNS_MANAGER_GET_PRIVATE (self); g_return_if_fail (priv->updates_queue > 0); - if (priv->need_sort) { - g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare); - priv->need_sort = FALSE; - } - compute_hash (self, nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)), new); changed = (memcmp (new, priv->prev_hash, sizeof (new)) != 0) ? TRUE : FALSE; _LOGD ("(%s): DNS configuration %s", func, changed ? "changed" : "did not change"); @@ -1810,14 +2105,14 @@ _get_config_variant (NMDnsManager *self) NMGlobalDnsConfig *global_config; gs_free char *str = NULL; GVariantBuilder builder; - NMConfigData *data; - guint i, j; + NMDnsIPConfigData *ip_data; + const CList *head; + gs_unref_ptrarray GPtrArray *array_domains = NULL; if (priv->config_variant) return priv->config_variant; - data = nm_config_get_data (priv->config); - global_config = nm_config_data_get_global_dns_config (data); + global_config = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)); if (global_config) { priv->config_variant = _get_global_config_variant (global_config); _LOGT ("current configuration: %s", (str = g_variant_print (priv->config_variant, TRUE))); @@ -1826,25 +2121,26 @@ _get_config_variant (NMDnsManager *self) g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); - for (i = 0; i < priv->configs->len; i++) { - NMDnsIPConfigData *current = priv->configs->pdata[i]; - const NMIPConfig *config = current->config; + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) { + const NMIPConfig *ip_config = ip_data->ip_config; GVariantBuilder entry_builder; GVariantBuilder strv_builder; - guint num; - const int addr_family = nm_ip_config_get_addr_family (config); + guint i, num; + const int addr_family = nm_ip_config_get_addr_family (ip_config); char buf[NM_UTILS_INET_ADDRSTRLEN]; const NMIPAddr *addr; + const char *ifname; - num = nm_ip_config_get_num_nameservers (config); + num = nm_ip_config_get_num_nameservers (ip_config); if (!num) continue; g_variant_builder_init (&entry_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); - for (j = 0; j < num; j++) { - addr = nm_ip_config_get_nameserver (config, j); + for (i = 0; i < num; i++) { + addr = nm_ip_config_get_nameserver (ip_config, i); g_variant_builder_add (&strv_builder, "s", nm_utils_inet_ntop (addr_family, addr, buf)); @@ -1854,36 +2150,46 @@ _get_config_variant (NMDnsManager *self) "nameservers", g_variant_builder_end (&strv_builder)); - num = nm_ip_config_get_num_domains (config); + num = nm_ip_config_get_num_domains (ip_config); + num += nm_ip_config_get_num_searches (ip_config); if (num > 0) { - g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); - for (j = 0; j < num; j++) { - g_variant_builder_add (&strv_builder, - "s", - nm_ip_config_get_domain (config, j)); + if (!array_domains) + array_domains = g_ptr_array_sized_new (num); + else + g_ptr_array_set_size (array_domains, 0); + + add_dns_domains (array_domains, ip_config, TRUE, FALSE); + if (array_domains->len) { + g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); + for (i = 0; i < array_domains->len; i++) { + g_variant_builder_add (&strv_builder, + "s", + array_domains->pdata[i]); + } + g_variant_builder_add (&entry_builder, + "{sv}", + "domains", + g_variant_builder_end (&strv_builder)); } - g_variant_builder_add (&entry_builder, - "{sv}", - "domains", - g_variant_builder_end (&strv_builder)); } - if (current->iface) { + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ip_data->data->ifindex); + if (ifname) { g_variant_builder_add (&entry_builder, "{sv}", "interface", - g_variant_new_string (current->iface)); + g_variant_new_string (ifname)); } g_variant_builder_add (&entry_builder, "{sv}", "priority", - g_variant_new_int32 (nm_ip_config_get_dns_priority (config))); + g_variant_new_int32 (nm_ip_config_get_dns_priority (ip_config))); g_variant_builder_add (&entry_builder, "{sv}", "vpn", - g_variant_new_boolean (current->type == NM_DNS_IP_CONFIG_TYPE_VPN)); + g_variant_new_boolean (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN)); g_variant_builder_add (&builder, "a{sv}", &entry_builder); } @@ -1924,8 +2230,12 @@ nm_dns_manager_init (NMDnsManager *self) _LOGT ("creating..."); + c_list_init (&priv->ip_config_lst_head); + priv->config = g_object_ref (nm_config_get ()); - priv->configs = g_ptr_array_new_full (8, ip_config_data_destroy); + + priv->configs = g_hash_table_new_full (nm_direct_hash, NULL, + NULL, (GDestroyNotify) _config_data_free); /* Set the initial hash */ compute_hash (self, NULL, NM_DNS_MANAGER_GET_PRIVATE (self)->hash); @@ -1942,33 +2252,33 @@ dispose (GObject *object) { NMDnsManager *self = NM_DNS_MANAGER (object); NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - NMDnsIPConfigData *data; - guint i; + NMDnsIPConfigData *ip_data, *ip_data_safe; _LOGT ("disposing"); if (!priv->is_stopped) nm_dns_manager_stop (self); + if (priv->config) + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + _clear_plugin (self); - if (priv->config) { - g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); - g_clear_object (&priv->config); - } + priv->best_ip_config_4 = NULL; + priv->best_ip_config_6 = NULL; - if (priv->configs) { - for (i = 0; i < priv->configs->len; i++) { - data = priv->configs->pdata[i]; - forget_data (self, data); - } - g_ptr_array_free (priv->configs, TRUE); - priv->configs = NULL; - } + c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst) + _ip_config_data_free (ip_data); + + g_clear_pointer (&priv->configs, g_hash_table_destroy); nm_clear_g_source (&priv->plugin_ratelimit.timer); + g_clear_object (&priv->config); + G_OBJECT_CLASS (nm_dns_manager_parent_class)->dispose (object); + + g_clear_pointer (&priv->config_variant, g_variant_unref); } static void @@ -1983,18 +2293,30 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_dns_manager_parent_class)->finalize (object); } +static const NMDBusInterfaceInfoExtended interface_info_dns_manager = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DNS_MANAGER, + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "s", NM_DNS_MANAGER_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RcManager", "s", NM_DNS_MANAGER_RC_MANAGER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Configuration", "aa{sv}", NM_DNS_MANAGER_CONFIGURATION), + ), + ), +}; + static void nm_dns_manager_class_init (NMDnsManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); object_class->dispose = dispose; object_class->finalize = finalize; object_class->get_property = get_property; - exported_object_class->export_path = NM_DBUS_PATH "/DnsManager"; - exported_object_class->export_on_construction = TRUE; + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH "/DnsManager"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dns_manager); + dbus_object_class->export_on_construction = TRUE; obj_properties[PROP_MODE] = g_param_spec_string (NM_DNS_MANAGER_MODE, "", "", @@ -2024,9 +2346,4 @@ nm_dns_manager_class_init (NMDnsManagerClass *klass) 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DNS_MANAGER_SKELETON, - NULL); } - diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index b38ea701..ed1974a5 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -26,11 +26,14 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" +#include "nm-setting-connection.h" typedef enum { + NM_DNS_IP_CONFIG_TYPE_REMOVED = -1, + NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0, NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, - NM_DNS_IP_CONFIG_TYPE_VPN + NM_DNS_IP_CONFIG_TYPE_VPN, } NMDnsIPConfigType; enum { @@ -38,12 +41,27 @@ enum { NM_DNS_PRIORITY_DEFAULT_VPN = 50, }; +struct _NMDnsConfigData; +struct _NMDnsManager; + typedef struct { - gpointer config; - NMDnsIPConfigType type; - char *iface; + struct _NMDnsConfigData *data; + NMIPConfig *ip_config; + CList data_lst; + CList ip_config_lst; + NMDnsIPConfigType ip_config_type; + struct { + const char **search; + char **reverse; + } domains; } NMDnsIPConfigData; +typedef struct _NMDnsConfigData { + struct _NMDnsManager *self; + CList data_lst_head; + int ifindex; +} NMDnsConfigData; + #define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type ()) #define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DNS_MANAGER, NMDnsManager)) #define NM_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) @@ -70,12 +88,9 @@ NMDnsManager * nm_dns_manager_get (void); void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func); void nm_dns_manager_end_updates (NMDnsManager *self, const char *func); -gboolean nm_dns_manager_add_ip_config (NMDnsManager *self, - const char *iface, - gpointer config, - NMDnsIPConfigType cfg_type); - -gboolean nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config); +gboolean nm_dns_manager_set_ip_config (NMDnsManager *self, + NMIPConfig *ip_config, + NMDnsIPConfigType ip_config_type); void nm_dns_manager_set_initial_hostname (NMDnsManager *self, const char *hostname); @@ -112,8 +127,6 @@ typedef enum { NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, } NMDnsManagerResolvConfManager; -gboolean nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self); - void nm_dns_manager_stop (NMDnsManager *self); #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */ diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c index 5805b7d8..d9400e3e 100644 --- a/src/dns/nm-dns-plugin.c +++ b/src/dns/nm-dns-plugin.c @@ -77,15 +77,15 @@ G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_A gboolean nm_dns_plugin_update (NMDnsPlugin *self, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, 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, - configs, global_config, + ip_config_lst_head, hostname); } diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h index 996695c0..80b77d95 100644 --- a/src/dns/nm-dns-plugin.h +++ b/src/dns/nm-dns-plugin.h @@ -50,8 +50,8 @@ typedef struct { * configuration. */ gboolean (*update) (NMDnsPlugin *self, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname); /* Subclasses should override and return TRUE if they start a local @@ -80,8 +80,8 @@ 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 GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname); void nm_dns_plugin_stop (NMDnsPlugin *self); diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index 6ab2ea18..1a73aae1 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -31,13 +31,15 @@ #include #include +#include "nm-utils/nm-c-list.h" #include "nm-core-internal.h" #include "platform/nm-platform.h" #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "nm-manager.h" +#include "nm-setting-connection.h" #include "devices/nm-device.h" #include "NetworkManagerUtils.h" @@ -48,17 +50,23 @@ typedef struct { int ifindex; - GList *configs; + CList configs_lst_head; } InterfaceConfig; +typedef struct { + CList request_queue_lst; + const char *operation; + GVariant *argument; +} RequestItem; + /*****************************************************************************/ typedef struct { GDBusProxy *resolve; GCancellable *init_cancellable; GCancellable *update_cancellable; - GQueue dns_updates; - GQueue domain_updates; + GCancellable *mdns_cancellable; + CList request_queue_lst_head; } NMDnsSystemdResolvedPrivate; struct _NMDnsSystemdResolved { @@ -82,130 +90,85 @@ G_DEFINE_TYPE (NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN /*****************************************************************************/ static void -call_done (GObject *source, GAsyncResult *r, gpointer user_data) +_request_item_free (RequestItem *request_item) { - GVariant *v; - GError *error = NULL; - NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data; - - v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error); + c_list_unlink_stale (&request_item->request_queue_lst); + g_variant_unref (request_item->argument); + g_slice_free (RequestItem, request_item); +} - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; +static void +_request_item_append (CList *request_queue_lst_head, + const char *operation, + GVariant *argument) +{ + RequestItem *request_item; - if (error != NULL) { - _LOGW ("Failed: %s\n", error->message); - g_error_free (error); - } + request_item = g_slice_new (RequestItem); + request_item->operation = operation; + request_item->argument = g_variant_ref_sink (argument); + c_list_link_tail (request_queue_lst_head, &request_item->request_queue_lst); } +/*****************************************************************************/ + static void -add_interface_configuration (NMDnsSystemdResolved *self, - GArray *interfaces, - const NMDnsIPConfigData *data, - gboolean skip) +_interface_config_free (InterfaceConfig *config) { - int i; - InterfaceConfig *ic = NULL; - int ifindex; + nm_c_list_elem_free_all (&config->configs_lst_head, NULL); + g_slice_free (InterfaceConfig, config); +} - if (NM_IS_IP4_CONFIG (data->config)) - ifindex = nm_ip4_config_get_ifindex (data->config); - else if (NM_IS_IP6_CONFIG (data->config)) - ifindex = nm_ip6_config_get_ifindex (data->config); - else - g_return_if_reached (); - - for (i = 0; i < interfaces->len; i++) { - InterfaceConfig *tic = &g_array_index (interfaces, InterfaceConfig, i); - if (ifindex == tic->ifindex) { - ic = tic; - break; - } - } +static void +call_done (GObject *source, GAsyncResult *r, gpointer user_data) +{ + GVariant *v; + GError *error = NULL; + NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data; - if (!ic) { - g_array_set_size (interfaces, interfaces->len + 1); - ic = &g_array_index (interfaces, InterfaceConfig, - interfaces->len - 1); - ic->ifindex = ifindex; + v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error); + if (!v) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + _LOGW ("Failed: %s\n", error->message); + g_error_free (error); } - - if (!skip) - ic->configs = g_list_append (ic->configs, data->config); } static void update_add_ip_config (NMDnsSystemdResolved *self, GVariantBuilder *dns, GVariantBuilder *domains, - gpointer config) + NMDnsIPConfigData *data) { int addr_family; gsize addr_size; guint i, n; - gboolean route_only; - - if (NM_IS_IP4_CONFIG (config)) - addr_family = AF_INET; - else if (NM_IS_IP6_CONFIG (config)) - addr_family = AF_INET6; - else - g_return_if_reached (); + gboolean is_routing; + const char **iter; + const char *domain; + addr_family = nm_ip_config_get_addr_family (data->ip_config); addr_size = nm_utils_addr_family_to_size (addr_family); - n = addr_family == AF_INET - ? nm_ip4_config_get_num_nameservers (config) - : nm_ip6_config_get_num_nameservers (config); - for (i = 0 ; i < n; i++) { - in_addr_t ns4; - gconstpointer ns; - - if (addr_family == AF_INET) { - ns4 = nm_ip4_config_get_nameserver (config, i); - ns = &ns4; - } else - ns = nm_ip6_config_get_nameserver (config, i); + if (!data->domains.search || !data->domains.search[0]) + return; + n = nm_ip_config_get_num_nameservers (data->ip_config); + for (i = 0 ; i < n; i++) { g_variant_builder_open (dns, G_VARIANT_TYPE ("(iay)")); g_variant_builder_add (dns, "i", addr_family); g_variant_builder_add_value (dns, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, - ns, + nm_ip_config_get_nameserver (data->ip_config, i), addr_size, 1)); g_variant_builder_close (dns); } - /* If this link is never the default (e.g. only used for resources on this - * network) add a routing domain. */ - route_only = addr_family == AF_INET - ? !nm_ip4_config_best_default_route_get (config) - : !nm_ip6_config_best_default_route_get (config); - - n = addr_family == AF_INET - ? nm_ip4_config_get_num_searches (config) - : nm_ip6_config_get_num_searches (config); - if (n > 0) { - for (i = 0; i < n; i++) { - g_variant_builder_add (domains, "(sb)", - addr_family == AF_INET - ? nm_ip4_config_get_search (config, i) - : nm_ip6_config_get_search (config, i), - route_only); - } - } else { - n = addr_family == AF_INET - ? nm_ip4_config_get_num_domains (config) - : nm_ip6_config_get_num_domains (config); - for (i = 0; i < n; i++) { - g_variant_builder_add (domains, "(sb)", - addr_family == AF_INET - ? nm_ip4_config_get_domain (config, i) - : nm_ip6_config_get_domain (config, i), - route_only); - } + for (iter = data->domains.search; *iter; iter++) { + domain = nm_utils_parse_dns_domain (*iter, &is_routing); + g_variant_builder_add (domains, "(sb)", domain[0] ? domain : ".", is_routing); } } @@ -213,13 +176,13 @@ static void free_pending_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - GVariant *v; + RequestItem *request_item, *request_item_safe; - while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL) - g_variant_unref (v); - - while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL) - g_variant_unref (v); + c_list_for_each_entry_safe (request_item, + request_item_safe, + &priv->request_queue_lst_head, + request_queue_lst) + _request_item_free (request_item); } static void @@ -227,7 +190,9 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); GVariantBuilder dns, domains; - GList *l; + NMCListElem *elem; + NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; + const char *mdns_arg = NULL; g_variant_builder_init (&dns, G_VARIANT_TYPE ("(ia(iay))")); g_variant_builder_add (&dns, "i", ic->ifindex); @@ -237,23 +202,51 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic) g_variant_builder_add (&domains, "i", ic->ifindex); g_variant_builder_open (&domains, G_VARIANT_TYPE ("a(sb)")); - for (l = ic->configs; l; l = l->next) - update_add_ip_config (self, &dns, &domains, l->data); + c_list_for_each_entry (elem, &ic->configs_lst_head, lst) { + NMDnsIPConfigData *data = elem->data; + NMIPConfig *ip_config = data->ip_config; + + update_add_ip_config (self, &dns, &domains, data); + + if (NM_IS_IP4_CONFIG (ip_config)) + mdns = NM_MAX (mdns, nm_ip4_config_mdns_get (NM_IP4_CONFIG (ip_config))); + } g_variant_builder_close (&dns); g_variant_builder_close (&domains); - g_queue_push_tail (&priv->dns_updates, - g_variant_ref_sink (g_variant_builder_end (&dns))); - g_queue_push_tail (&priv->domain_updates, - g_variant_ref_sink (g_variant_builder_end (&domains))); + switch (mdns) { + case NM_SETTING_CONNECTION_MDNS_NO: + mdns_arg = "no"; + break; + case NM_SETTING_CONNECTION_MDNS_RESOLVE: + mdns_arg = "resolve"; + break; + case NM_SETTING_CONNECTION_MDNS_YES: + mdns_arg = "yes"; + break; + case NM_SETTING_CONNECTION_MDNS_DEFAULT: + mdns_arg = ""; + break; + } + nm_assert (mdns_arg); + + _request_item_append (&priv->request_queue_lst_head, + "SetLinkDNS", + g_variant_builder_end (&dns)); + _request_item_append (&priv->request_queue_lst_head, + "SetLinkDomains", + g_variant_builder_end (&domains)); + _request_item_append (&priv->request_queue_lst_head, + "SetLinkMulticastDNS", + g_variant_new ("(is)", ic->ifindex, mdns_arg ?: "")); } static void send_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - GVariant *v; + RequestItem *request_item, *request_item_safe; nm_clear_g_cancellable (&priv->update_cancellable); @@ -262,55 +255,69 @@ send_updates (NMDnsSystemdResolved *self) priv->update_cancellable = g_cancellable_new (); - while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL) { - g_dbus_proxy_call (priv->resolve, "SetLinkDNS", v, + c_list_for_each_entry_safe (request_item, + request_item_safe, + &priv->request_queue_lst_head, + request_queue_lst) { + g_dbus_proxy_call (priv->resolve, + request_item->operation, + request_item->argument, G_DBUS_CALL_FLAGS_NONE, - -1, priv->update_cancellable, call_done, self); - g_variant_unref (v); - } - - while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL) { - g_dbus_proxy_call (priv->resolve, "SetLinkDomains", v, - G_DBUS_CALL_FLAGS_NONE, - -1, priv->update_cancellable, call_done, self); - g_variant_unref (v); + -1, + priv->update_cancellable, + call_done, + self); + _request_item_free (request_item); } } static gboolean update (NMDnsPlugin *plugin, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname) { NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED (plugin); - GArray *interfaces = g_array_new (TRUE, TRUE, sizeof (InterfaceConfig)); + gs_unref_hashtable GHashTable *interfaces = NULL; + gs_free gpointer *interfaces_keys = NULL; + guint interfaces_len; guint i; - int prio, first_prio = 0; - - for (i = 0; i < configs->len; i++) { - const NMDnsIPConfigData *data = configs->pdata[i]; - gboolean skip = FALSE; - - prio = nm_ip_config_get_dns_priority (data->config); - if (i == 0) - first_prio = prio; - else if (first_prio < 0 && first_prio != prio) - skip = TRUE; - add_interface_configuration (self, interfaces, data, skip); + NMDnsIPConfigData *ip_data; + + interfaces = g_hash_table_new_full (nm_direct_hash, NULL, + NULL, (GDestroyNotify) _interface_config_free); + + c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { + InterfaceConfig *ic = NULL; + int ifindex; + + ifindex = ip_data->data->ifindex; + nm_assert (ifindex == nm_ip_config_get_ifindex (ip_data->ip_config)); + + ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (ifindex)); + if (!ic) { + ic = g_slice_new (InterfaceConfig); + ic->ifindex = ifindex; + c_list_init (&ic->configs_lst_head); + g_hash_table_insert (interfaces, GINT_TO_POINTER (ifindex), ic); + } + + c_list_link_tail (&ic->configs_lst_head, + &nm_c_list_elem_new_stale (ip_data)->lst); } free_pending_updates (self); - for (i = 0; i < interfaces->len; i++) { - InterfaceConfig *ic = &g_array_index (interfaces, InterfaceConfig, i); + interfaces_keys = nm_utils_hash_keys_to_array (interfaces, + nm_cmp_int2ptr_p_with_data, + NULL, + &interfaces_len); + for (i = 0; i < interfaces_len; i++) { + InterfaceConfig *ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (interfaces_keys[i])); prepare_one_interface (self, ic); - g_list_free (ic->configs); } - g_array_free (interfaces, TRUE); - send_updates (self); return TRUE; @@ -363,16 +370,15 @@ static void nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - NMBusManager *dbus_mgr; + NMDBusManager *dbus_mgr; GDBusConnection *connection; - g_queue_init (&priv->dns_updates); - g_queue_init (&priv->domain_updates); + c_list_init (&priv->request_queue_lst_head); - dbus_mgr = nm_bus_manager_get (); + dbus_mgr = nm_dbus_manager_get (); g_return_if_fail (dbus_mgr); - connection = nm_bus_manager_get_connection (dbus_mgr); + connection = nm_dbus_manager_get_connection (dbus_mgr); g_return_if_fail (connection); priv->init_cancellable = g_cancellable_new (); @@ -404,6 +410,7 @@ dispose (GObject *object) g_clear_object (&priv->resolve); nm_clear_g_cancellable (&priv->init_cancellable); nm_clear_g_cancellable (&priv->update_cancellable); + nm_clear_g_cancellable (&priv->mdns_cancellable); G_OBJECT_CLASS (nm_dns_systemd_resolved_parent_class)->dispose (object); } diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c index 0b80055f..e06128aa 100644 --- a/src/dns/nm-dns-unbound.c +++ b/src/dns/nm-dns-unbound.c @@ -39,8 +39,8 @@ G_DEFINE_TYPE (NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN) static gboolean update (NMDnsPlugin *plugin, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname) { char *argv[] = { DNSSEC_TRIGGER_SCRIPT, "--async", "--update", NULL }; -- cgit 1.3.0-6-gf8a5