policy,dns: fix a race in looking up hostname and updating DNS (rh #877084)

"config-changed" signal is added to dns-manager and emited when resolv.conf is
changed. Policy listens for the signal and restarts reverse-lookup in order to
get correct results.
This commit is contained in:
Jiří Klimeš 2013-02-05 17:29:15 +01:00
parent 32b38693f1
commit 07c5651a36
3 changed files with 93 additions and 6 deletions

View file

@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
* Copyright (C) 2004 - 2012 Red Hat, Inc.
* Copyright (C) 2004 - 2013 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
* and others
*/
@ -77,6 +77,14 @@ typedef struct {
gboolean dns_touched;
} NMDnsManagerPrivate;
enum {
CONFIG_CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
typedef struct {
GPtrArray *nameservers;
@ -746,6 +754,10 @@ update_dns (NMDnsManager *self,
if (success == FALSE)
success = update_resolv_conf (domain, searches, nameservers, error);
/* signal that resolv.conf was changed */
if (success)
g_signal_emit (self, signals[CONFIG_CHANGED], 0);
if (searches)
g_strfreev (searches);
if (nameservers)
@ -1145,9 +1157,20 @@ nm_dns_manager_class_init (NMDnsManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (NMDnsManagerPrivate));
/* virtual methods */
object_class->dispose = dispose;
object_class->finalize = finalize;
g_type_class_add_private (object_class, sizeof (NMDnsManagerPrivate));
/* signals */
signals[CONFIG_CHANGED] =
g_signal_new ("config-changed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMDnsManagerClass, config_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}

View file

@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
* Copyright (C) 2004 - 2010 Red Hat, Inc.
* Copyright (C) 2004 - 2013 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
* and others
*/
@ -60,6 +60,9 @@ typedef struct {
typedef struct {
GObjectClass parent;
/* Signals */
void (*config_changed) (NMDnsManager *mgr);
} NMDnsManagerClass;
GType nm_dns_manager_get_type (void);

View file

@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2012 Red Hat, Inc.
* Copyright (C) 2004 - 2013 Red Hat, Inc.
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
@ -62,6 +62,10 @@ struct NMPolicy {
NMDevice *default_device6;
HostnameThread *lookup;
guint32 lookup_ipv4_addr; /* IPv4 for reverse lookup */
struct in6_addr *lookup_ipv6_addr; /* IPv6 for reverse lookup */
NMDnsManager *dns_manager;
gulong config_changed_id;
gint reset_retries_id; /* idle handler for resetting the retries count */
@ -237,6 +241,15 @@ _set_hostname (NMPolicy *policy,
* there was no valid hostname to start with.
*/
/* Clear lookup adresses if we have a hostname, so that we didn't
* restart reverse lookup thread later.
*/
if (new_hostname) {
policy->lookup_ipv4_addr = 0;
g_free (policy->lookup_ipv6_addr);
policy->lookup_ipv6_addr = NULL;
}
/* Don't change the hostname or update DNS this is the first time we're
* trying to change the hostname, and it's not actually changing.
*/
@ -398,7 +411,8 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
g_assert (addr4); /* checked for > 1 address above */
/* Start the hostname lookup thread */
policy->lookup = hostname4_thread_new (nm_ip4_address_get_address (addr4), lookup_callback, policy);
policy->lookup_ipv4_addr = nm_ip4_address_get_address (addr4);
policy->lookup = hostname4_thread_new (policy->lookup_ipv4_addr, lookup_callback, policy);
} else if (best6) {
NMIP6Config *ip6_config;
NMIP6Address *addr6;
@ -416,7 +430,9 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
g_assert (addr6); /* checked for > 1 address above */
/* Start the hostname lookup thread */
policy->lookup = hostname6_thread_new (nm_ip6_address_get_address (addr6), lookup_callback, policy);
policy->lookup_ipv6_addr = g_malloc0 (sizeof (struct in6_addr));
memcpy (policy->lookup_ipv6_addr, nm_ip6_address_get_address (addr6), sizeof (struct in6_addr));
policy->lookup = hostname6_thread_new (policy->lookup_ipv6_addr, lookup_callback, policy);
}
if (!policy->lookup) {
@ -1791,6 +1807,43 @@ firewall_started (NMFirewallManager *manager,
}
}
static void
dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
{
NMPolicy *policy = (NMPolicy *) user_data;
/* Restart a thread for reverse-DNS lookup after we are signalled that
* DNS changed. Because the result from a previous run may not be right
* (race in updating DNS and doing the reverse lookup).
*/
/* Stop a lookup thread if any. */
if (policy->lookup) {
hostname_thread_kill (policy->lookup);
policy->lookup = NULL;
}
/* Re-start the hostname lookup thread if we don't have hostname yet. */
if (policy->lookup_ipv4_addr) {
char buf[INET_ADDRSTRLEN];
struct in_addr addr = { .s_addr = policy->lookup_ipv4_addr };
if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf)))
strcpy (buf, "(unknown)");
nm_log_dbg (LOGD_DNS, "restarting IPv4 reverse-lookup thread for address %s'", buf);
policy->lookup = hostname4_thread_new (policy->lookup_ipv4_addr, lookup_callback, policy);
} else if (policy->lookup_ipv6_addr) {
char buf[INET6_ADDRSTRLEN];
if (!inet_ntop (AF_INET6, policy->lookup_ipv6_addr, buf, sizeof (buf)))
strcpy (buf, "(unknown)");
nm_log_dbg (LOGD_DNS, "restarting IPv6 reverse-lookup thread for address %s'", buf);
policy->lookup = hostname6_thread_new (policy->lookup_ipv6_addr, lookup_callback, policy);
}
}
static void
connection_updated (NMSettings *settings,
NMConnection *connection,
@ -1917,6 +1970,10 @@ nm_policy_new (NMManager *manager, NMSettings *settings)
G_CALLBACK (firewall_started), policy);
policy->fw_started_id = id;
policy->dns_manager = nm_dns_manager_get (NULL);
policy->config_changed_id = g_signal_connect (policy->dns_manager, "config-changed",
G_CALLBACK (dns_config_changed), policy);
_connect_manager_signal (policy, "state-changed", global_state_changed);
_connect_manager_signal (policy, "notify::" NM_MANAGER_HOSTNAME, hostname_changed);
_connect_manager_signal (policy, "notify::" NM_MANAGER_SLEEPING, sleeping_changed);
@ -1955,6 +2012,7 @@ nm_policy_destroy (NMPolicy *policy)
hostname_thread_kill (policy->lookup);
policy->lookup = NULL;
}
g_free (policy->lookup_ipv6_addr);
g_slist_foreach (policy->pending_activation_checks, (GFunc) activate_data_free, NULL);
g_slist_free (policy->pending_activation_checks);
@ -1965,6 +2023,9 @@ nm_policy_destroy (NMPolicy *policy)
g_signal_handler_disconnect (policy->fw_manager, policy->fw_started_id);
g_object_unref (policy->fw_manager);
g_signal_handler_disconnect (policy->dns_manager, policy->config_changed_id);
g_object_unref (policy->dns_manager);
for (iter = policy->manager_ids; iter; iter = g_slist_next (iter))
g_signal_handler_disconnect (policy->manager, GPOINTER_TO_UINT (iter->data));
g_slist_free (policy->manager_ids);