diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2018-03-23 18:49:46 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2018-05-14 15:22:50 +0200 |
commit | dd1e671fe5cf28ee1b8cf701895f041f6f3b8b1a (patch) | |
tree | ebfe60c5afc6c1ac6a3cd3cf15e86f9f2a99830b /src | |
parent | 82ebfa73514d853f4da39c5108b30f4643ce727e (diff) | |
download | NetworkManager-dd1e671fe5cf28ee1b8cf701895f041f6f3b8b1a.tar.gz |
dns: use dns-priority to provide a preprocessed domain list to plugins
Do some preprocessing on the DNS configuration sent to plugins:
- add the '~' default routing (lookup) domain to IP configurations
with the default route or, when there is none, to all non-VPN
IP configurations
- use the dns-priority to decide which connection to use in case
multiple connections have the same domain
- consider a negative dns-priority value as a way to 'shadow' all
subdomains from other connections
- compute reverse DNS domains
and add the resulting domain list to NMDnsIPConfigData so that
split-DNS plugins can use that directly instead of reimplementing the
same logic themselves.
Diffstat (limited to 'src')
-rw-r--r-- | src/dns/nm-dns-dnsmasq.c | 1 | ||||
-rw-r--r-- | src/dns/nm-dns-manager.c | 199 | ||||
-rw-r--r-- | src/dns/nm-dns-manager.h | 4 | ||||
-rw-r--r-- | src/nm-ip4-config.h | 6 |
4 files changed, 210 insertions, 0 deletions
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index 1f3a58e477..36d4c85b1c 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -75,6 +75,7 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) /*****************************************************************************/ +/* FIXME: this is a duplicate of the function in nm-dns-manager.c */ static char ** get_ip_rdns_domains (NMIPConfig *ip_config) { diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index c4ba54caf3..669371378d 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -273,6 +273,9 @@ _ip_config_data_free (NMDnsIPConfigData *ip_data) 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_signal_handlers_disconnect_by_func (ip_data->ip_config, _ip_config_dns_priority_changed, ip_data); @@ -1136,6 +1139,197 @@ _collect_resolv_conf_data (NMDnsManager *self, *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, @@ -1198,6 +1392,7 @@ update_dns (NMDnsManager *self, } _LOGD ("update-dns: updating plugin %s", plugin_name); + rebuild_domain_lists (self); if (!nm_dns_plugin_update (plugin, global_config, _ip_config_lst_head (self), @@ -1209,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: ; diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index 5688936736..ed1974a566 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -50,6 +50,10 @@ typedef struct { CList data_lst; CList ip_config_lst; NMDnsIPConfigType ip_config_type; + struct { + const char **search; + char **reverse; + } domains; } NMDnsIPConfigData; typedef struct _NMDnsConfigData { diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index a7c2b78bd6..e2f40fd345 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -501,6 +501,12 @@ nm_ip_config_get_dns_option (const NMIPConfig *self, guint i) _NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_dns_option, nm_ip6_config_get_dns_option, i); } +static inline const NMPObject * +nm_ip_config_best_default_route_get (const NMIPConfig *self) +{ + _NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_best_default_route_get, nm_ip6_config_best_default_route_get); +} + #define _NM_IP_CONFIG_DISPATCH_SET_OP(_return, dst, src, v4_func, v6_func, ...) \ G_STMT_START { \ gpointer _dst = (dst); \ |