diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2020-11-12 08:47:25 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2020-11-16 16:43:40 +0100 |
commit | 09c83871144a5154a0b3003b77fbbd69f2c758db (patch) | |
tree | b325b2afdd838ce8090c15151d5e34d1c9e8b801 | |
parent | abd002642f19026c899611e3a7741e04f6c4e1f2 (diff) | |
download | NetworkManager-bg/hostname-setting-rh1766944.tar.gz |
policy: use the hostname settingbg/hostname-setting-rh1766944
Rework update_system_hostname() to use the new properties from the
hostname setting.
In the default configuration where all the 3 boolean properties
hostname.{from-dhcp,from-dns,only-from-default} are true, the behavior
is the same as before.
-rw-r--r-- | src/devices/nm-device.c | 228 | ||||
-rw-r--r-- | src/devices/nm-device.h | 4 | ||||
-rw-r--r-- | src/nm-policy.c | 308 |
3 files changed, 464 insertions, 76 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index b905855cf5..98b4d339e4 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -206,6 +206,23 @@ typedef struct { NMEthtoolRingState * ring; } EthtoolState; +typedef enum { + RESOLVER_WAIT_ADDRESS = 0, + RESOLVER_IN_PROGRESS, + RESOLVER_DONE, +} ResolverState; + +typedef struct { + ResolverState state; + GResolver * resolver; + GInetAddress *address; + GCancellable *cancellable; + char * hostname; + NMDevice * device; + guint timeout_id; /* Used when waiting for the address */ + int addr_family; +} HostnameResolver; + /*****************************************************************************/ enum { @@ -218,6 +235,7 @@ enum { REMOVED, RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, + DNS_LOOKUP_DONE, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; @@ -325,6 +343,14 @@ typedef struct _NMDevicePrivate { int auth_retries; union { + struct { + HostnameResolver *hostname_resolver_6; + HostnameResolver *hostname_resolver_4; + }; + HostnameResolver *hostname_resolver_x[2]; + }; + + union { const guint8 hw_addr_len; /* read-only */ guint8 hw_addr_len_; }; @@ -866,6 +892,22 @@ static NM_UTILS_LOOKUP_STR_DEFINE(mtu_source_to_str, /*****************************************************************************/ +static void +_hostname_resolver_free(HostnameResolver *resolver) +{ + if (!resolver) + return; + + nm_clear_g_source(&resolver->timeout_id); + nm_clear_g_cancellable(&resolver->cancellable); + nm_g_object_unref(resolver->resolver); + nm_g_object_unref(resolver->address); + g_free(resolver->hostname); + nm_g_slice_free(resolver); +} + +/*****************************************************************************/ + static NMSettingIP6ConfigPrivacy _ip6_privacy_clamp(NMSettingIP6ConfigPrivacy use_tempaddr) { @@ -15618,6 +15660,7 @@ static void _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + guint i; _cancel_activation(self); @@ -15642,6 +15685,9 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type) nm_clear_pointer(&priv->shared_ip_handle, nm_netns_shared_ip_release); + for (i = 0; i < 2; i++) + nm_clear_pointer(&priv->hostname_resolver_x[i], _hostname_resolver_free); + _cleanup_ip_pre(self, AF_INET, cleanup_type); _cleanup_ip_pre(self, AF_INET6, cleanup_type); } @@ -17467,6 +17513,178 @@ nm_device_auth_retries_try_next(NMDevice *self) return TRUE; } +static void +hostname_dns_lookup_callback(GObject *source, GAsyncResult *result, gpointer user_data) +{ + HostnameResolver *resolver; + NMDevice * self; + gs_free char * hostname = NULL; + gs_free char * addr_str = NULL; + gs_free_error GError *error = NULL; + + hostname = g_resolver_lookup_by_address_finish(G_RESOLVER(source), result, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + resolver = user_data; + self = resolver->device; + resolver->state = RESOLVER_DONE; + resolver->hostname = g_strdup(hostname); + + _LOGD(LOGD_DNS, + "hostname-from-dns: lookup done for %s, result %s%s%s", + (addr_str = g_inet_address_to_string(resolver->address)), + NM_PRINT_FMT_QUOTE_STRING(hostname)); + + nm_clear_g_cancellable(&resolver->cancellable); + g_signal_emit(self, signals[DNS_LOOKUP_DONE], 0); +} + +static gboolean +hostname_dns_address_timeout(gpointer user_data) +{ + HostnameResolver *resolver = user_data; + NMDevice * self = resolver->device; + + g_return_val_if_fail(NM_IS_DEVICE(self), G_SOURCE_REMOVE); + + nm_assert(resolver->state == RESOLVER_WAIT_ADDRESS); + nm_assert(!resolver->address); + nm_assert(!resolver->cancellable); + + _LOGT(LOGD_DNS, + "hostname-from-dns: timed out while waiting IPv%c address", + nm_utils_addr_family_to_char(resolver->addr_family)); + + resolver->timeout_id = 0; + resolver->state = RESOLVER_DONE; + g_signal_emit(self, signals[DNS_LOOKUP_DONE], 0); + + return G_SOURCE_REMOVE; +} + +/* return value is valid only immediately */ +const char * +nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean *out_wait) +{ + NMDevicePrivate * priv; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + HostnameResolver *resolver; + NMIPConfig * ip_config; + const char * method; + gboolean address_changed = FALSE; + gs_unref_object GInetAddress *new_address = NULL; + + g_return_val_if_fail(NM_IS_DEVICE(self), NULL); + priv = NM_DEVICE_GET_PRIVATE(self); + + /* If the device is not supposed to have addresses, + * return an immediate empty result.*/ + method = nm_device_get_effective_ip_config_method(self, addr_family); + if (IS_IPv4) { + if (NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { + nm_clear_pointer(&priv->hostname_resolver_x[IS_IPv4], _hostname_resolver_free); + NM_SET_OUT(out_wait, FALSE); + return NULL; + } + } else { + if (NM_IN_STRSET(method, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + nm_clear_pointer(&priv->hostname_resolver_x[IS_IPv4], _hostname_resolver_free); + NM_SET_OUT(out_wait, FALSE); + return NULL; + } + } + + resolver = priv->hostname_resolver_x[IS_IPv4]; + if (!resolver) { + resolver = g_slice_new(HostnameResolver); + *resolver = (HostnameResolver){ + .resolver = g_resolver_get_default(), + .device = self, + .addr_family = addr_family, + .state = RESOLVER_WAIT_ADDRESS, + }; + priv->hostname_resolver_x[IS_IPv4] = resolver; + } + + /* Determine the first address of the interface and + * whether it changed from the previous lookup */ + ip_config = priv->ip_config_x[IS_IPv4]; + if (ip_config) { + const NMPlatformIPAddress *addr; + + addr = nm_ip_config_get_first_address(ip_config); + if (addr) { + new_address = g_inet_address_new_from_bytes(addr->address_ptr, + IS_IPv4 ? G_SOCKET_FAMILY_IPV4 + : G_SOCKET_FAMILY_IPV6); + } + } + + if (new_address && resolver->address) { + if (!g_inet_address_equal(new_address, resolver->address)) + address_changed = TRUE; + } else if (new_address != resolver->address) + address_changed = TRUE; + + { + gs_free char *old_str = NULL; + gs_free char *new_str = NULL; + + _LOGT(LOGD_DNS, + "hostname-from-dns: ipv%c resolver state %d, old address %s, new address %s", + nm_utils_addr_family_to_char(resolver->addr_family), + resolver->state, + resolver->address ? (old_str = g_inet_address_to_string(resolver->address)) + : "(null)", + new_address ? (new_str = g_inet_address_to_string(new_address)) : "(null)"); + } + + /* In every state, if the address changed, we restart + * the resolution with the new address */ + if (address_changed) { + nm_clear_g_cancellable(&resolver->cancellable); + nm_g_object_unref(resolver->address); + resolver->state = RESOLVER_WAIT_ADDRESS; + } + + if (address_changed && new_address) { + gs_free char *str = NULL; + + _LOGT(LOGD_DNS, + "hostname-from-dns: starting lookup for address %s", + (str = g_inet_address_to_string(new_address))); + + resolver->state = RESOLVER_IN_PROGRESS; + resolver->cancellable = g_cancellable_new(); + resolver->address = g_steal_pointer(&new_address); + g_resolver_lookup_by_address_async(resolver->resolver, + resolver->address, + resolver->cancellable, + hostname_dns_lookup_callback, + resolver); + nm_clear_g_source(&resolver->timeout_id); + } + + switch (resolver->state) { + case RESOLVER_WAIT_ADDRESS: + if (!resolver->timeout_id) + resolver->timeout_id = g_timeout_add(30000, hostname_dns_address_timeout, resolver); + NM_SET_OUT(out_wait, TRUE); + return NULL; + case RESOLVER_IN_PROGRESS: + NM_SET_OUT(out_wait, TRUE); + return NULL; + case RESOLVER_DONE: + NM_SET_OUT(out_wait, FALSE); + return resolver->hostname; + } + + return nm_assert_unreachable_val(NULL); +} + /*****************************************************************************/ static const char * @@ -18644,6 +18862,16 @@ nm_device_class_init(NMDeviceClass *klass) NULL, G_TYPE_NONE, 0); + + signals[DNS_LOOKUP_DONE] = g_signal_new(NM_DEVICE_DNS_LOOKUP_DONE, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); } /* Connection defaults from plugins */ diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index d29a6a1506..c330411bc4 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -116,6 +116,7 @@ nm_device_state_reason_check(NMDeviceStateReason reason) #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */ /* Internal signals */ +#define NM_DEVICE_DNS_LOOKUP_DONE "dns-lookup-done" #define NM_DEVICE_IP4_CONFIG_CHANGED "ip4-config-changed" #define NM_DEVICE_IP6_CONFIG_CHANGED "ip6-config-changed" #define NM_DEVICE_IP6_PREFIX_DELEGATED "ip6-prefix-delegated" @@ -863,4 +864,7 @@ const char *nm_device_state_reason_to_str(NMDeviceStateReason reason); gboolean nm_device_is_vpn(NMDevice *self); +const char * +nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean *out_pending); + #endif /* __NETWORKMANAGER_DEVICE_H__ */ diff --git a/src/nm-policy.c b/src/nm-policy.c index 5a14a663ee..bf54ede1a2 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -117,6 +117,8 @@ _PRIV_TO_SELF(NMPolicyPrivate *priv) /*****************************************************************************/ #define _NMLOG_PREFIX_NAME "policy" +#undef _NMLOG_ENABLED +#define _NMLOG_ENABLED(level, domain) (nm_logging_enabled((level), (domain))) #define _NMLOG(level, domain, ...) \ G_STMT_START \ { \ @@ -131,6 +133,7 @@ _PRIV_TO_SELF(NMPolicyPrivate *priv) /*****************************************************************************/ +static void update_system_hostname(NMPolicy *self, const char *msg); static void schedule_activate_all(NMPolicy *self); static void schedule_activate_check(NMPolicy *self, NMDevice *device); static NMDevice *get_default_device(NMPolicy *self, int addr_family); @@ -671,20 +674,168 @@ lookup_by_address(NMPolicy *self) self); } +typedef struct { + NMDevice *device; + int priority; + bool from_dhcp : 1; + bool from_dns : 1; + + union { + struct { + bool ip_6; + bool ip_4; + }; + bool ip_x[2]; + }; +} DeviceHostnameInfo; + +static int +device_hostname_info_compare(gconstpointer a, gconstpointer b) +{ + const DeviceHostnameInfo *info1 = a; + const DeviceHostnameInfo *info2 = b; + + NM_CMP_FIELD(info1, info2, priority); + + return 0; +} + +NM_CON_DEFAULT_NOP("hostname.from-dhcp"); +NM_CON_DEFAULT_NOP("hostname.from-dns-lookup"); +NM_CON_DEFAULT_NOP("hostname.only-from-default"); + +static gboolean +device_get_hostname_property_boolean(NMDevice *device, const char *name) +{ + NMSettingHostname *s_hostname; + char buf[128]; + int value; + + nm_assert(NM_IN_STRSET(name, + NM_SETTING_HOSTNAME_FROM_DHCP, + NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP, + NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT)); + + s_hostname = nm_device_get_applied_setting(device, NM_TYPE_SETTING_HOSTNAME); + + if (s_hostname) { + g_object_get(s_hostname, name, &value, NULL); + if (NM_IN_SET(value, NM_TERNARY_FALSE, NM_TERNARY_TRUE)) + return value; + } + + return nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + nm_sprintf_buf(buf, "hostname.%s", name), + device, + NM_TERNARY_FALSE, + NM_TERNARY_TRUE, + NM_TERNARY_TRUE); +} + +static int +device_get_hostname_priority(NMDevice *device) +{ + NMSettingHostname *s_hostname; + int priority; + + s_hostname = nm_device_get_applied_setting(device, NM_TYPE_SETTING_HOSTNAME); + if (s_hostname) { + priority = nm_setting_hostname_get_priority(s_hostname); + if (priority != 0) + return priority; + } + + return nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("hostname.priority"), + device, + G_MININT, + G_MAXINT, + 100); +} + +static GArray * +build_device_hostname_infos(NMPolicy *self) +{ + NMPolicyPrivate * priv = NM_POLICY_GET_PRIVATE(self); + const CList * tmp_clist; + NMActiveConnection *ac; + GArray * array = NULL; + + nm_manager_for_each_active_connection (priv->manager, ac, tmp_clist) { + DeviceHostnameInfo *info; + NMDevice * device; + gboolean only_from_default; + + device = nm_active_connection_get_device(ac); + if (!device) + continue; + + only_from_default = + device_get_hostname_property_boolean(device, NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT); + if (only_from_default && ac != priv->default_ac4 && ac != priv->default_ac6) + continue; + + if (!array) + array = g_array_sized_new(FALSE, FALSE, sizeof(DeviceHostnameInfo), 4); + + info = nm_g_array_append_new(array, DeviceHostnameInfo); + *info = (DeviceHostnameInfo){ + .device = device, + .priority = device_get_hostname_priority(device), + .from_dhcp = + device_get_hostname_property_boolean(device, NM_SETTING_HOSTNAME_FROM_DHCP), + .from_dns = + device_get_hostname_property_boolean(device, NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP), + .ip_4 = priv->default_ac4 || !only_from_default, + .ip_6 = priv->default_ac6 || !only_from_default, + }; + } + + if (array && array->len > 1) { + const DeviceHostnameInfo *info0; + guint i; + + g_array_sort(array, device_hostname_info_compare); + + info0 = &g_array_index(array, DeviceHostnameInfo, 0); + if (info0->priority < 0) { + for (i = 1; i < array->len; i++) { + const DeviceHostnameInfo *info = &g_array_index(array, DeviceHostnameInfo, i); + + if (info->priority > info0->priority) { + g_array_set_size(array, i); + break; + } + } + } + } + + return array; +} + +static void +device_dns_lookup_done(NMDevice *device, gpointer user_data) +{ + NMPolicy *self = user_data; + + g_signal_handlers_disconnect_by_func(device, device_dns_lookup_done, self); + + update_system_hostname(self, "lookup finished"); +} + static void update_system_hostname(NMPolicy *self, const char *msg) { - NMPolicyPrivate * priv = NM_POLICY_GET_PRIVATE(self); - const char * configured_hostname; - gs_free char * temp_hostname = NULL; - const char * dhcp_hostname, *p; - NMIP4Config * ip4_config; - NMIP6Config * ip6_config; - gboolean external_hostname = FALSE; - const NMPlatformIP4Address *addr4; - const NMPlatformIP6Address *addr6; - NMDevice * device; - NMDhcpConfig * dhcp_config; + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); + const char * configured_hostname; + gs_free char * temp_hostname = NULL; + const char * dhcp_hostname, *p; + gboolean external_hostname = FALSE; + NMDhcpConfig * dhcp_config; + gs_unref_array GArray *infos = NULL; + DeviceHostnameInfo * info; + guint i; + int IS_IPv4; g_return_if_fail(self != NULL); @@ -724,10 +875,9 @@ update_system_hostname(NMPolicy *self, const char *msg) /* Hostname precedence order: * * 1) a configured hostname (from settings) - * 2) automatic hostname from the default device's config (DHCP, VPN, etc) - * 3) the last hostname set outside NM - * 4) reverse-DNS of the best device's IPv4 address - * + * 2) automatic hostname from DHCP of eligible interfaces + * 3) reverse-DNS lookup of the first address on eligible interfaces + * 4) the last hostname set outside NM */ /* Try a persistent hostname first */ @@ -738,40 +888,73 @@ update_system_hostname(NMPolicy *self, const char *msg) return; } - if (priv->default_ac4) { - /* Grab a hostname out of the device's DHCP4 config */ - dhcp_config = nm_device_get_dhcp_config(get_default_device(self, AF_INET), AF_INET); - if (dhcp_config) { - dhcp_hostname = nm_dhcp_config_get_option(dhcp_config, "host_name"); - if (dhcp_hostname && dhcp_hostname[0]) { - p = nm_str_skip_leading_spaces(dhcp_hostname); - if (p[0]) { - _set_hostname(self, p, "from DHCPv4"); - priv->dhcp_hostname = TRUE; - return; + infos = build_device_hostname_infos(self); + + if (infos && _LOGT_ENABLED(LOGD_DNS)) { + _LOGT(LOGD_DNS, "device hostname info:"); + for (i = 0; i < infos->len; i++) { + info = &g_array_index(infos, DeviceHostnameInfo, i); + _LOGT(LOGD_DNS, + " - prio:%4d ipv:%c%c dhcp:%d dns:%d dev:%s", + info->priority, + info->ip_4 ? '4' : '-', + info->ip_6 ? '6' : '-', + info->from_dhcp, + info->from_dns, + nm_device_get_iface(info->device)); + } + } + + for (i = 0; infos && i < infos->len; i++) { + info = &g_array_index(infos, DeviceHostnameInfo, i); + g_signal_handlers_disconnect_by_func(info->device, device_dns_lookup_done, self); + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + + if (info->from_dhcp && info->ip_x[IS_IPv4]) { + dhcp_config = nm_device_get_dhcp_config(info->device, addr_family); + if (dhcp_config) { + dhcp_hostname = + nm_dhcp_config_get_option(dhcp_config, IS_IPv4 ? "host_name" : "fqdn_fqdn"); + if (dhcp_hostname && dhcp_hostname[0]) { + p = nm_str_skip_leading_spaces(dhcp_hostname); + if (p[0]) { + _set_hostname(self, p, IS_IPv4 ? "from DHCPv4" : "from DHCPv6"); + priv->dhcp_hostname = TRUE; + return; + } + _LOGW(LOGD_DNS, + "set-hostname: DHCPv%c-provided hostname '%s' looks invalid; " + "ignoring it", + nm_utils_addr_family_to_char(addr_family), + dhcp_hostname); + } } - _LOGW(LOGD_DNS, - "set-hostname: DHCPv4-provided hostname '%s' looks invalid; ignoring it", - dhcp_hostname); } } - } - if (priv->default_ac6) { - /* Grab a hostname out of the device's DHCP6 config */ - dhcp_config = nm_device_get_dhcp_config(get_default_device(self, AF_INET6), AF_INET6); - if (dhcp_config) { - dhcp_hostname = nm_dhcp_config_get_option(dhcp_config, "fqdn_fqdn"); - if (dhcp_hostname && dhcp_hostname[0]) { - p = nm_str_skip_leading_spaces(dhcp_hostname); - if (p[0]) { - _set_hostname(self, p, "from DHCPv6"); - priv->dhcp_hostname = TRUE; - return; + if (priv->hostname_mode != NM_POLICY_HOSTNAME_MODE_DHCP) { + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET6 : AF_INET; + + if (info->from_dns && info->ip_x[IS_IPv4]) { + const char *result; + gboolean wait; + + result = + nm_device_get_hostname_from_dns_lookup(info->device, addr_family, &wait); + if (result) { + _set_hostname(self, result, "from address lookup"); + return; + } + if (wait) { + g_signal_connect(info->device, + NM_DEVICE_DNS_LOOKUP_DONE, + (GCallback) device_dns_lookup_done, + self); + return; + } } - _LOGW(LOGD_DNS, - "set-hostname: DHCPv6-provided hostname '%s' looks invalid; ignoring it", - dhcp_hostname); } } } @@ -795,14 +978,6 @@ update_system_hostname(NMPolicy *self, const char *msg) priv->dhcp_hostname = FALSE; - if (!priv->default_ac4 && !priv->default_ac6) { - /* No best device; fall back to the last hostname set externally - * to NM or if there wasn't one, 'localhost.localdomain' - */ - _set_hostname(self, priv->orig_hostname, "no default device"); - return; - } - /* If no automatically-configured hostname, try using the last hostname * set externally to NM */ @@ -811,30 +986,7 @@ update_system_hostname(NMPolicy *self, const char *msg) return; } - /* No configured hostname, no automatically determined hostname, and no - * bootup hostname. Start reverse DNS of the current IPv4 or IPv6 address. - */ - device = get_default_device(self, AF_INET); - ip4_config = device ? nm_device_get_ip4_config(device) : NULL; - - device = get_default_device(self, AF_INET6); - ip6_config = device ? nm_device_get_ip6_config(device) : NULL; - - if (ip4_config && (addr4 = nm_ip4_config_get_first_address(ip4_config))) { - g_clear_object(&priv->lookup.addr); - priv->lookup.addr = - g_inet_address_new_from_bytes((guint8 *) &addr4->address, G_SOCKET_FAMILY_IPV4); - } else if (ip6_config && (addr6 = nm_ip6_config_get_first_address(ip6_config))) { - g_clear_object(&priv->lookup.addr); - priv->lookup.addr = - g_inet_address_new_from_bytes((guint8 *) &addr6->address, G_SOCKET_FAMILY_IPV6); - } else { - /* No valid IP config; fall back to localhost.localdomain */ - _set_hostname(self, NULL, "no IP config"); - return; - } - - lookup_by_address(self); + _set_hostname(self, NULL, "no hostname found"); } static void @@ -1796,6 +1948,8 @@ device_state_changed(NMDevice * device, switch (new_state) { case NM_DEVICE_STATE_FAILED: + g_signal_handlers_disconnect_by_func(device, device_dns_lookup_done, self); + /* Mark the connection invalid if it failed during activation so that * it doesn't get automatically chosen over and over and over again. */ @@ -1934,6 +2088,8 @@ device_state_changed(NMDevice * device, ip6_remove_device_prefix_delegations(self, device); break; case NM_DEVICE_STATE_DISCONNECTED: + g_signal_handlers_disconnect_by_func(device, device_dns_lookup_done, self); + /* Reset retry counts for a device's connections when carrier on; if cable * was unplugged and plugged in again, we should try to reconnect. */ |