diff options
Diffstat (limited to 'src/dns')
| -rw-r--r-- | src/dns/nm-dns-dnsmasq.c | 317 | ||||
| -rw-r--r-- | src/dns/nm-dns-manager.c | 1017 | ||||
| -rw-r--r-- | src/dns/nm-dns-manager.h | 37 | ||||
| -rw-r--r-- | src/dns/nm-dns-plugin.c | 4 | ||||
| -rw-r--r-- | src/dns/nm-dns-plugin.h | 4 | ||||
| -rw-r--r-- | src/dns/nm-dns-systemd-resolved.c | 295 | ||||
| -rw-r--r-- | src/dns/nm-dns-unbound.c | 2 |
7 files changed, 915 insertions, 761 deletions
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 %<zone-id>. Before dnsmasq 2.58, + * only '@' was supported as delimiter. Since 2.58, '@' and '%' are + * supported. Due to a bug, since 2.73 only '%' works properly as "server" + * address. + */ + 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 %<zone-id>. Before dnsmasq 2.58, - * only '@' was supported as delimiter. Since 2.58, '@' and '%' are - * supported. Due to a bug, since 2.73 only '%' works properly as "server" - * address. - */ - return g_strdup_printf ("%s%c%s", - buf, - IN6_IS_ADDR_LINKLOCAL (addr) ? '%' : '@', - iface); + 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 ("<unknown>"), + 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 @@ -282,24 +385,55 @@ add_dns_option_item (GPtrArray *array, const char *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 ? "<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 <sys/stat.h> #include <linux/if.h> +#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 }; |