GNetworkAddress: drop cached addresses on resolver reload
authorDan Winship <danw@gnome.org>
Tue, 19 Feb 2013 20:19:22 +0000 (15:19 -0500)
committerDan Winship <danw@gnome.org>
Wed, 20 Feb 2013 12:33:58 +0000 (07:33 -0500)
If the resolver reloads (ie, if /etc/resolv.conf changes),
GNetworkAddress needs to re-resolve its addresses the next time it's
enumerated. Otherwise hosts that have different IP addresses inside
and outside a VPN won't work correctly if you hold on to a
GNetworkAddress for them for a long time.

https://bugzilla.gnome.org/show_bug.cgi?id=694181

gio/gnetworkaddress.c
gio/gnetworkingprivate.h
gio/gresolver.c

index 1335d94..2e4042c 100644 (file)
@@ -65,6 +65,8 @@ struct _GNetworkAddressPrivate {
   guint16 port;
   GList *sockaddrs;
   gchar *scheme;
+
+  gint64 resolver_serial;
 };
 
 enum {
@@ -226,7 +228,8 @@ g_network_address_get_property (GObject    *object,
 
 static void
 g_network_address_set_addresses (GNetworkAddress *addr,
-                                 GList           *addresses)
+                                 GList           *addresses,
+                                 guint64          resolver_serial)
 {
   GList *a;
   GSocketAddress *sockaddr;
@@ -241,6 +244,8 @@ g_network_address_set_addresses (GNetworkAddress *addr,
     }
   g_list_free (addresses);
   addr->priv->sockaddrs = g_list_reverse (addr->priv->sockaddrs);
+
+  addr->priv->resolver_serial = resolver_serial;
 }
 
 static gboolean
@@ -846,26 +851,39 @@ g_network_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator
 
   if (addr_enum->addresses == NULL)
     {
-      if (!addr_enum->addr->priv->sockaddrs)
-        g_network_address_parse_sockaddr (addr_enum->addr);
-      if (!addr_enum->addr->priv->sockaddrs)
+      GNetworkAddress *addr = addr_enum->addr;
+      GResolver *resolver = g_resolver_get_default ();
+      gint64 serial = g_resolver_get_serial (resolver);
+
+      if (addr->priv->resolver_serial != 0 &&
+          addr->priv->resolver_serial != serial)
+        {
+          /* Resolver has reloaded, discard cached addresses */
+          g_list_free_full (addr->priv->sockaddrs, g_object_unref);
+          addr->priv->sockaddrs = NULL;
+        }
+
+      if (!addr->priv->sockaddrs)
+        g_network_address_parse_sockaddr (addr);
+      if (!addr->priv->sockaddrs)
         {
-          GResolver *resolver = g_resolver_get_default ();
           GList *addresses;
 
           addresses = g_resolver_lookup_by_name (resolver,
-                                                 addr_enum->addr->priv->hostname,
+                                                 addr->priv->hostname,
                                                  cancellable, error);
-          g_object_unref (resolver);
-
           if (!addresses)
-            return NULL;
+            {
+              g_object_unref (resolver);
+              return NULL;
+            }
 
-          g_network_address_set_addresses (addr_enum->addr, addresses);
+          g_network_address_set_addresses (addr, addresses, serial);
         }
           
-      addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
+      addr_enum->addresses = addr->priv->sockaddrs;
       addr_enum->next = addr_enum->addresses;
+      g_object_unref (resolver);
     }
 
   if (addr_enum->next == NULL)
@@ -916,7 +934,10 @@ got_addresses (GObject      *source_object,
       addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
 
       if (!error)
-        g_network_address_set_addresses (addr_enum->addr, addresses);
+        {
+          g_network_address_set_addresses (addr_enum->addr, addresses,
+                                           g_resolver_get_serial (resolver));
+        }
     }
   have_addresses (addr_enum, task, error);
 }
@@ -936,25 +957,36 @@ g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enum
 
   if (addr_enum->addresses == NULL)
     {
-      if (!addr_enum->addr->priv->sockaddrs)
+      GNetworkAddress *addr = addr_enum->addr;
+      GResolver *resolver = g_resolver_get_default ();
+      gint64 serial = g_resolver_get_serial (resolver);
+
+      if (addr->priv->resolver_serial != 0 &&
+          addr->priv->resolver_serial != serial)
+        {
+          /* Resolver has reloaded, discard cached addresses */
+          g_list_free_full (addr->priv->sockaddrs, g_object_unref);
+          addr->priv->sockaddrs = NULL;
+        }
+
+      if (!addr->priv->sockaddrs)
         {
-          if (g_network_address_parse_sockaddr (addr_enum->addr))
+          if (g_network_address_parse_sockaddr (addr))
             have_addresses (addr_enum, task, NULL);
           else
             {
-              GResolver *resolver = g_resolver_get_default ();
-
               g_resolver_lookup_by_name_async (resolver,
-                                               addr_enum->addr->priv->hostname,
+                                               addr->priv->hostname,
                                                cancellable,
                                                got_addresses, task);
-              g_object_unref (resolver);
             }
+          g_object_unref (resolver);
           return;
         }
 
-      addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
+      addr_enum->addresses = addr->priv->sockaddrs;
       addr_enum->next = addr_enum->addresses;
+      g_object_unref (resolver);
     }
 
   if (addr_enum->next)
index 1a299c9..4341dc7 100644 (file)
@@ -34,6 +34,8 @@ gchar *  _g_uri_from_authority             (const gchar      *protocol,
                                            guint             port,
                                            const gchar      *userinfo);
 
+guint64  g_resolver_get_serial             (GResolver        *resolver);
+
 gint g_socket (gint     domain,
                gint     type,
                gint     protocol,
index 589e324..5549e4f 100644 (file)
@@ -846,6 +846,20 @@ g_resolver_lookup_records_finish (GResolver     *resolver,
     lookup_records_finish (resolver, result, error);
 }
 
+guint64
+g_resolver_get_serial (GResolver *resolver)
+{
+  g_return_val_if_fail (G_IS_RESOLVER (resolver), 0);
+
+  g_resolver_maybe_reload (resolver);
+
+#ifdef G_OS_UNIX
+  return (guint64) resolver->priv->resolv_conf_timestamp;
+#else
+  return 1;
+#endif
+}
+
 /**
  * g_resolver_error_quark:
  *