[glib] gnetworkaddress: preserve IPv6 scope ID in IP literals



commit 8a77f7bb181f3341a6866cd0e64779c35b3a3f61
Author: Dan Winship <danw gnome org>
Date:   Wed Dec 12 16:12:09 2012 +0100

    gnetworkaddress: preserve IPv6 scope ID in IP literals
    
    If a GNetworkAddress is created with a hostname like "fe80::xxx%em1",
    make sure that the scope_id corresponding to "em1" is present in the
    GSocketAddresses it returns when used as a GSocketConnectable.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=684404

 gio/gnetworkaddress.c    |   88 ++++++++++++++++++++++++++++++++-------------
 gio/tests/inet-address.c |   38 ++++++++++++++++++++
 2 files changed, 100 insertions(+), 26 deletions(-)
---
diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c
index b67a86d..44c39f1 100644
--- a/gio/gnetworkaddress.c
+++ b/gio/gnetworkaddress.c
@@ -243,6 +243,29 @@ g_network_address_set_addresses (GNetworkAddress *addr,
   addr->priv->sockaddrs = g_list_reverse (addr->priv->sockaddrs);
 }
 
+static gboolean
+g_network_address_parse_sockaddr (GNetworkAddress *addr)
+{
+  struct addrinfo hints, *res = NULL;
+  GSocketAddress *sockaddr;
+  gchar port[32];
+
+  memset (&hints, 0, sizeof (hints));
+  hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+  g_snprintf (port, sizeof (port), "%u", addr->priv->port);
+
+  if (getaddrinfo (addr->priv->hostname, port, &hints, &res) != 0)
+    return FALSE;
+
+  sockaddr = g_socket_address_new_from_native (res->ai_addr, res->ai_addrlen);
+  freeaddrinfo (res);
+  if (!sockaddr || !G_IS_INET_SOCKET_ADDRESS (sockaddr))
+    return FALSE;
+
+  addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
+  return TRUE;
+}
+
 /**
  * g_network_address_new:
  * @hostname: the hostname
@@ -820,6 +843,8 @@ 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)
         {
           GResolver *resolver = g_resolver_get_default ();
           GList *addresses;
@@ -848,29 +873,11 @@ g_network_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator
 }
 
 static void
-got_addresses (GObject      *source_object,
-               GAsyncResult *result,
-               gpointer      user_data)
+have_addresses (GNetworkAddressAddressEnumerator *addr_enum,
+                GTask *task, GError *error)
 {
-  GTask *task = user_data;
-  GNetworkAddressAddressEnumerator *addr_enum = g_task_get_source_object (task);
-  GResolver *resolver = G_RESOLVER (source_object);
-  GList *addresses;
-  GError *error = NULL;
   GSocketAddress *sockaddr;
 
-  if (!addr_enum->addr->priv->sockaddrs)
-    {
-      addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
-
-      if (error)
-        g_task_return_error (task, error);
-      else
-        g_network_address_set_addresses (addr_enum->addr, addresses);
-    }
-
-  g_object_unref (resolver);
-
   addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
   addr_enum->next = addr_enum->addresses;
 
@@ -882,12 +889,35 @@ got_addresses (GObject      *source_object,
   else
     sockaddr = NULL;
 
-  if (!error)
+  if (error)
+    g_task_return_error (task, error);
+  else
     g_task_return_pointer (task, sockaddr, g_object_unref);
   g_object_unref (task);
 }
 
 static void
+got_addresses (GObject      *source_object,
+               GAsyncResult *result,
+               gpointer      user_data)
+{
+  GTask *task = user_data;
+  GNetworkAddressAddressEnumerator *addr_enum = g_task_get_source_object (task);
+  GResolver *resolver = G_RESOLVER (source_object);
+  GList *addresses;
+  GError *error = NULL;
+
+  if (!addr_enum->addr->priv->sockaddrs)
+    {
+      addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
+
+      if (!error)
+        g_network_address_set_addresses (addr_enum->addr, addresses);
+    }
+  have_addresses (addr_enum, task, error);
+}
+
+static void
 g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enumerator,
                                                  GCancellable              *cancellable,
                                                  GAsyncReadyCallback        callback,
@@ -904,12 +934,18 @@ g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enum
     {
       if (!addr_enum->addr->priv->sockaddrs)
         {
-          GResolver *resolver = g_resolver_get_default ();
+          if (g_network_address_parse_sockaddr (addr_enum->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,
-                                           cancellable,
-                                           got_addresses, task);
+              g_resolver_lookup_by_name_async (resolver,
+                                               addr_enum->addr->priv->hostname,
+                                               cancellable,
+                                               got_addresses, task);
+              g_object_unref (resolver);
+            }
           return;
         }
 
diff --git a/gio/tests/inet-address.c b/gio/tests/inet-address.c
index 566078b..0e82b06 100644
--- a/gio/tests/inet-address.c
+++ b/gio/tests/inet-address.c
@@ -223,6 +223,43 @@ test_socket_address (void)
 }
 
 static void
+test_scope_id (void)
+{
+  GSocketConnectable *addr;
+  GSocketAddressEnumerator *addr_enum;
+  GSocketAddress *saddr;
+  GInetSocketAddress *isaddr;
+  GInetAddress *iaddr;
+  char *tostring;
+  GError *error = NULL;
+
+  addr = g_network_address_new ("fe80::42%1", 99);
+  addr_enum = g_socket_connectable_enumerate (addr);
+  saddr = g_socket_address_enumerator_next (addr_enum, NULL, &error);
+  g_assert_no_error (error);
+
+  g_assert (saddr != NULL);
+  g_assert (G_IS_INET_SOCKET_ADDRESS (saddr));
+
+  isaddr = G_INET_SOCKET_ADDRESS (saddr);
+  g_assert_cmpint (g_inet_socket_address_get_scope_id (isaddr), ==, 1);
+  g_assert_cmpint (g_inet_socket_address_get_port (isaddr), ==, 99);
+
+  iaddr = g_inet_socket_address_get_address (isaddr);
+  tostring = g_inet_address_to_string (iaddr);
+  g_assert_cmpstr (tostring, ==, "fe80::42");
+  g_free (tostring);
+
+  g_object_unref (saddr);
+  saddr = g_socket_address_enumerator_next (addr_enum, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (saddr == NULL);
+
+  g_object_unref (addr_enum);
+  g_object_unref (addr);
+}
+
+static void
 test_mask_parse (void)
 {
   GInetAddressMask *mask;
@@ -355,6 +392,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/inet-address/bytes", test_bytes);
   g_test_add_func ("/inet-address/property", test_property);
   g_test_add_func ("/socket-address/basic", test_socket_address);
+  g_test_add_func ("/socket-address/scope-id", test_scope_id);
   g_test_add_func ("/address-mask/parse", test_mask_parse);
   g_test_add_func ("/address-mask/property", test_mask_property);
   g_test_add_func ("/address-mask/equal", test_mask_equal);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]