summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2018-10-18 17:25:00 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2018-10-19 18:39:34 +0200
commit6b8c6e891b15c9f4ec8164227755dacbf005f8c2 (patch)
treed0807a3b3cda46178c9de46fb9dcb8dd4b7276f6
parent7d4c7e73df00b40d539cc7a64fb4f7c37d236ad8 (diff)
parent107c7f27af1a754cb94f65857d2e85f09cc50743 (diff)
downloadNetworkManager-6b8c6e891b15c9f4ec8164227755dacbf005f8c2.tar.gz
dns: merge branch 'bg/dns-bgo746422'
https://bugzilla.gnome.org/show_bug.cgi?id=746422 (cherry picked from commit d9782589248e61c0cb5aec90e3eb62612891116b)
-rw-r--r--clients/common/settings-docs.c.in4
-rw-r--r--libnm-core/nm-setting-ip-config.c42
-rw-r--r--src/dns/nm-dns-dnsmasq.c161
-rw-r--r--src/dns/nm-dns-manager.c199
-rw-r--r--src/dns/nm-dns-manager.h4
-rw-r--r--src/dns/nm-dns-systemd-resolved.c54
-rw-r--r--src/nm-ip4-config.c66
-rw-r--r--src/nm-ip4-config.h8
-rw-r--r--src/nm-ip6-config.c41
-rw-r--r--src/tests/test-ip4-config.c6
-rw-r--r--src/tests/test-ip6-config.c6
11 files changed, 322 insertions, 269 deletions
diff --git a/clients/common/settings-docs.c.in b/clients/common/settings-docs.c.in
index da1f24fa64..0abe983dde 100644
--- a/clients/common/settings-docs.c.in
+++ b/clients/common/settings-docs.c.in
@@ -216,7 +216,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("Intra-connection DNS priority. The relative priority to be used when determining the order of DNS servers in resolv.conf. A lower value means that servers will be on top of the file. Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. For that, just specify the DNS servers in the desired order. When multiple devices have configurations with the same priority, the one with an active default route will be preferred. Note that when using dns=dnsmasq the order is meaningless since dnsmasq forwards queries to all known servers at the same time. Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting. A lower value is better (higher priority). Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. When multiple devices have configurations with the same priority, the one with an active default route will be preferred. Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins. If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_SEARCH N_("Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
@@ -236,7 +236,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("Intra-connection DNS priority. The relative priority to be used when determining the order of DNS servers in resolv.conf. A lower value means that servers will be on top of the file. Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. For that, just specify the DNS servers in the desired order. When multiple devices have configurations with the same priority, the one with an active default route will be preferred. Note that when using dns=dnsmasq the order is meaningless since dnsmasq forwards queries to all known servers at the same time. Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting. A lower value is better (higher priority). Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. When multiple devices have configurations with the same priority, the one with an active default route will be preferred. Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins. If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_SEARCH N_("Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set.")
#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index 18d93f9deb..31da2dbe64 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -2993,24 +2993,34 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
/**
* NMSettingIPConfig:dns-priority:
*
- * Intra-connection DNS priority.
+ * DNS servers priority.
*
- * The relative priority to be used when determining the order of DNS
- * servers in resolv.conf. A lower value means that servers will be on top
- * of the file. Zero selects the default value, which is 50 for VPNs and
- * 100 for other connections. Note that the priority is to order DNS
- * settings for multiple active connections. It does not disambiguate
- * multiple DNS servers within the same connection profile. For that,
- * just specify the DNS servers in the desired order.
- * When multiple devices have configurations with the same priority, the
- * one with an active default route will be preferred.
- * Note that when using dns=dnsmasq the order is meaningless
- * since dnsmasq forwards queries to all known servers at the same time.
+ * The relative priority for DNS servers specified by this setting. A lower
+ * value is better (higher priority). Zero selects the default value, which
+ * is 50 for VPNs and 100 for other connections.
*
- * Negative values have the special effect of excluding other configurations
- * with a greater priority value; so in presence of at least a negative
- * priority, only DNS servers from connections with the lowest priority
- * value will be used.
+ * Note that the priority is to order DNS settings for multiple active
+ * connections. It does not disambiguate multiple DNS servers within the
+ * same connection profile.
+ *
+ * When using dns=default, servers with higher priority will be on top of
+ * resolv.conf. To prioritize a given server over another one within the
+ * same connection, just specify them in the desired order. When multiple
+ * devices have configurations with the same priority, the one with an
+ * active default route will be preferred. Negative values have the special
+ * effect of excluding other configurations with a greater priority value;
+ * so in presence of at least a negative priority, only DNS servers from
+ * connections with the lowest priority value will be used.
+ *
+ * When using a DNS resolver that supports split-DNS as dns=dnsmasq or
+ * dns=systemd-resolved, each connection is used to query domains in its
+ * search list. Queries for domains not present in any search list are
+ * routed through connections having the '~.' special wildcard domain, which
+ * is added automatically to connections with the default route (or can be
+ * added manually). When multiple connections specify the same domain, the
+ * one with the highest priority (lowest numerical value) wins. If a
+ * connection specifies a domain which is subdomain of another domain with a
+ * negative DNS priority value, the subdomain is ignored.
*
* Since: 1.4
**/
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c
index bc6ef4a26d..745d3d6742 100644
--- a/src/dns/nm-dns-dnsmasq.c
+++ b/src/dns/nm-dns-dnsmasq.c
@@ -75,53 +75,6 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
/*****************************************************************************/
-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);
-}
-
static void
add_dnsmasq_nameserver (NMDnsDnsmasq *self,
GVariantBuilder *servers,
@@ -206,91 +159,34 @@ 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)
+add_ip_config (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigData *ip_data)
{
- int addr_family;
+ NMIPConfig *ip_config = ip_data->ip_config;
gconstpointer addr;
- gboolean added = FALSE;
- guint nnameservers, i_nameserver, n, i;
+ const char *iface, *domain;
char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN];
- char **domains, **iter;
- gboolean iface_resolved = FALSE;
- const char *iface = NULL, *domain;
+ 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);
- 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);
-
- if (split) {
- if (nnameservers == 0)
- return;
-
- if (!iface_resolved) {
- iface = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex);
- iface_resolved = TRUE;
+ 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 (iface) {
- for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) {
- addr = nm_ip_config_get_nameserver (ip_config, i_nameserver);
-
- ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf);
-
- /* searches are preferred over domains */
- n = nm_ip_config_get_num_searches (ip_config);
- 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);
- 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);
- }
- }
- }
- }
-
- /* 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 (j = 0; ip_data->domains.reverse[j]; j++) {
+ add_dnsmasq_nameserver (self, servers,
+ ip_addr_to_string_buf,
+ ip_data->domains.reverse[j]);
}
}
}
@@ -489,9 +385,7 @@ update (NMDnsPlugin *plugin,
NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
GVariantBuilder servers;
- int prio, first_prio;
const NMDnsIPConfigData *ip_data;
- gboolean is_first = TRUE;
start_dnsmasq (self);
@@ -500,19 +394,8 @@ 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;
- 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);
- }
+ 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 6813e9b86c..657a99a5d8 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -274,6 +274,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);
@@ -1130,6 +1133,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,
@@ -1192,6 +1386,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),
@@ -1203,6 +1398,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 663a3ad258..75a29b9a2d 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/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 4692f978fe..1317662adf 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -141,47 +141,36 @@ static void
update_add_ip_config (NMDnsSystemdResolved *self,
GVariantBuilder *dns,
GVariantBuilder *domains,
- NMIPConfig *config)
+ NMDnsIPConfigData *data)
{
int addr_family;
gsize addr_size;
guint i, n;
gboolean is_routing;
+ const char **iter;
const char *domain;
- addr_family = nm_ip_config_get_addr_family (config);
+ addr_family = nm_ip_config_get_addr_family (data->ip_config);
addr_size = nm_utils_addr_family_to_size (addr_family);
- n = nm_ip_config_get_num_nameservers (config);
+ 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,
- nm_ip_config_get_nameserver (config, i),
+ nm_ip_config_get_nameserver (data->ip_config, i),
addr_size,
1));
g_variant_builder_close (dns);
}
- n = nm_ip_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);
- }
- } else {
- n = nm_ip_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);
- }
+ 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);
}
}
@@ -216,9 +205,10 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic)
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;
+ NMDnsIPConfigData *data = elem->data;
+ NMIPConfig *ip_config = data->ip_config;
- update_add_ip_config (self, &dns, &domains, 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)));
@@ -294,25 +284,15 @@ update (NMDnsPlugin *plugin,
gs_free gpointer *interfaces_keys = NULL;
guint interfaces_len;
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) {
- 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;
- 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));
@@ -324,10 +304,8 @@ update (NMDnsPlugin *plugin,
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);
- }
+ c_list_link_tail (&ic->configs_lst_head,
+ &nm_c_list_elem_new_stale (ip_data)->lst);
}
free_pending_updates (self);
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index aeb5bbe8a8..803a7de05e 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -2286,6 +2286,31 @@ _nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i)
/*****************************************************************************/
+gboolean
+_nm_ip_config_check_and_add_domain (GPtrArray *array, const char *domain)
+{
+ char *copy = NULL;
+ size_t len;
+
+ g_return_val_if_fail (domain, FALSE);
+ g_return_val_if_fail (domain[0] != '\0', FALSE);
+
+ if (domain[0] == '.' || strstr (domain, ".."))
+ return FALSE;
+
+ len = strlen (domain);
+ if (domain[len - 1] == '.')
+ domain = copy = g_strndup (domain, len - 1);
+
+ if (nm_utils_strv_find_first ((char **) array->pdata, array->len, domain) >= 0) {
+ g_free (copy);
+ return FALSE;
+ }
+
+ g_ptr_array_add (array, copy ?: g_strdup (domain));
+ return TRUE;
+}
+
void
nm_ip4_config_reset_domains (NMIP4Config *self)
{
@@ -2301,17 +2326,9 @@ void
nm_ip4_config_add_domain (NMIP4Config *self, const char *domain)
{
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
- int i;
-
- g_return_if_fail (domain != NULL);
- g_return_if_fail (domain[0] != '\0');
-
- for (i = 0; i < priv->domains->len; i++)
- if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain))
- return;
- g_ptr_array_add (priv->domains, g_strdup (domain));
- _notify (self, PROP_DOMAINS);
+ if (_nm_ip_config_check_and_add_domain (priv->domains, domain))
+ _notify (self, PROP_DOMAINS);
}
void
@@ -2355,35 +2372,12 @@ nm_ip4_config_reset_searches (NMIP4Config *self)
}
void
-nm_ip4_config_add_search (NMIP4Config *self, const char *new)
+nm_ip4_config_add_search (NMIP4Config *self, const char *search)
{
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
- char *search;
- size_t len;
-
- g_return_if_fail (new != NULL);
- g_return_if_fail (new[0] != '\0');
-
- search = g_strdup (new);
-
- /* Remove trailing dot as it has no effect */
- len = strlen (search);
- if (search[len - 1] == '.')
- search[len - 1] = 0;
- if (!search[0]) {
- g_free (search);
- return;
- }
-
- if (nm_utils_strv_find_first ((char **) priv->searches->pdata,
- priv->searches->len, search) >= 0) {
- g_free (search);
- return;
- }
-
- g_ptr_array_add (priv->searches, search);
- _notify (self, PROP_SEARCHES);
+ if (_nm_ip_config_check_and_add_domain (priv->searches, search))
+ _notify (self, PROP_SEARCHES);
}
void
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index c2ead0c66e..88f22daffc 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -280,6 +280,8 @@ gboolean nm_ip4_config_nmpobj_remove (NMIP4Config *self,
void nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only);
gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b);
+gboolean _nm_ip_config_check_and_add_domain (GPtrArray *array, const char *domain);
+
/*****************************************************************************/
#include "nm-ip6-config.h"
@@ -435,4 +437,10 @@ 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);
+}
+
#endif /* __NETWORKMANAGER_IP4_CONFIG_H__ */
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index ea3a202987..7f360921dc 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -2054,17 +2054,9 @@ void
nm_ip6_config_add_domain (NMIP6Config *self, const char *domain)
{
NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
- int i;
-
- g_return_if_fail (domain != NULL);
- g_return_if_fail (domain[0] != '\0');
- for (i = 0; i < priv->domains->len; i++)
- if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain))
- return;
-
- g_ptr_array_add (priv->domains, g_strdup (domain));
- _notify (self, PROP_DOMAINS);
+ if (_nm_ip_config_check_and_add_domain (priv->domains, domain))
+ _notify (self, PROP_DOMAINS);
}
void
@@ -2108,35 +2100,12 @@ nm_ip6_config_reset_searches (NMIP6Config *self)
}
void
-nm_ip6_config_add_search (NMIP6Config *self, const char *new)
+nm_ip6_config_add_search (NMIP6Config *self, const char *search)
{
NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
- char *search;
- size_t len;
-
- g_return_if_fail (new != NULL);
- g_return_if_fail (new[0] != '\0');
-
- search = g_strdup (new);
- /* Remove trailing dot as it has no effect */
- len = strlen (search);
- if (search[len - 1] == '.')
- search[len - 1] = 0;
-
- if (!search[0]) {
- g_free (search);
- return;
- }
-
- if (nm_utils_strv_find_first ((char **) priv->searches->pdata,
- priv->searches->len, search) >= 0) {
- g_free (search);
- return;
- }
-
- g_ptr_array_add (priv->searches, search);
- _notify (self, PROP_SEARCHES);
+ if (_nm_ip_config_check_and_add_domain (priv->searches, search))
+ _notify (self, PROP_SEARCHES);
}
void
diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c
index 4c3c344e46..9fea6af5b1 100644
--- a/src/tests/test-ip4-config.c
+++ b/src/tests/test-ip4-config.c
@@ -319,11 +319,15 @@ test_strip_search_trailing_dot (void)
nm_ip4_config_add_search (config, "bar.");
nm_ip4_config_add_search (config, "baz.com");
nm_ip4_config_add_search (config, "baz.com.");
+ nm_ip4_config_add_search (config, "foobar..");
+ nm_ip4_config_add_search (config, ".foobar");
+ nm_ip4_config_add_search (config, "~.");
- g_assert_cmpuint (nm_ip4_config_get_num_searches (config), ==, 3);
+ g_assert_cmpuint (nm_ip4_config_get_num_searches (config), ==, 4);
g_assert_cmpstr (nm_ip4_config_get_search (config, 0), ==, "foo");
g_assert_cmpstr (nm_ip4_config_get_search (config, 1), ==, "bar");
g_assert_cmpstr (nm_ip4_config_get_search (config, 2), ==, "baz.com");
+ g_assert_cmpstr (nm_ip4_config_get_search (config, 3), ==, "~");
g_object_unref (config);
}
diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c
index bcbeee3e10..eb322b4789 100644
--- a/src/tests/test-ip6-config.c
+++ b/src/tests/test-ip6-config.c
@@ -338,11 +338,15 @@ test_strip_search_trailing_dot (void)
nm_ip6_config_add_search (config, "bar.");
nm_ip6_config_add_search (config, "baz.com");
nm_ip6_config_add_search (config, "baz.com.");
+ nm_ip6_config_add_search (config, "foobar..");
+ nm_ip6_config_add_search (config, ".foobar");
+ nm_ip6_config_add_search (config, "~.");
- g_assert_cmpuint (nm_ip6_config_get_num_searches (config), ==, 3);
+ g_assert_cmpuint (nm_ip6_config_get_num_searches (config), ==, 4);
g_assert_cmpstr (nm_ip6_config_get_search (config, 0), ==, "foo");
g_assert_cmpstr (nm_ip6_config_get_search (config, 1), ==, "bar");
g_assert_cmpstr (nm_ip6_config_get_search (config, 2), ==, "baz.com");
+ g_assert_cmpstr (nm_ip6_config_get_search (config, 3), ==, "~");
g_object_unref (config);
}