summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2018-05-14 15:25:27 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2018-05-14 15:25:27 +0200
commitd9782589248e61c0cb5aec90e3eb62612891116b (patch)
tree65b4e7118c434a77d51f933a151d26de6fbb6d6c
parentdb80d5f62a1edf39c5970887ef7b9ec62dd4163f (diff)
parent1fbadecdbc00f3a61afe98790e5a21e6cb9b7933 (diff)
dns: merge branch 'bg/dns-bgo746422'
https://bugzilla.gnome.org/show_bug.cgi?id=746422
-rw-r--r--clients/common/settings-docs.h.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.h.in b/clients/common/settings-docs.h.in
index 71ff2f083b..1b94905b4d 100644
--- a/clients/common/settings-docs.h.in
+++ b/clients/common/settings-docs.h.in
@@ -218,7 +218,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.")
@@ -238,7 +238,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 a695051f8a..c7f8c55557 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -2963,24 +2963,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 d075307867..b5b93280d7 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]);
}
}
}
@@ -487,9 +383,7 @@ update (NMDnsPlugin *plugin,
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;
start_dnsmasq (self);
@@ -498,19 +392,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 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/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 7da27e5f7f..1a73aae127 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -139,47 +139,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);
}
}
@@ -214,9 +203,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)));
@@ -292,25 +282,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));
@@ -322,10 +302,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 0a9591d28e..6ae81b5730 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -2333,6 +2333,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)
{
@@ -2348,17 +2373,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
@@ -2402,35 +2419,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 f8ff74f236..e2f40fd345 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -288,6 +288,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"
@@ -499,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); \
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 77a3eee2d0..9807d3882c 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -2117,17 +2117,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
@@ -2171,35 +2163,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 816a816f0b..a03d89b07b 100644
--- a/src/tests/test-ip6-config.c
+++ b/src/tests/test-ip6-config.c
@@ -340,11 +340,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);
}