From 8a77f7bb181f3341a6866cd0e64779c35b3a3f61 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 12 Dec 2012 16:12:09 +0100 Subject: [PATCH] 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 b67a86d2b..44c39f17f 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 @@ -819,6 +842,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 (); @@ -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,11 +889,34 @@ 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, @@ -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 566078bc4..0e82b0652 100644 --- a/gio/tests/inet-address.c +++ b/gio/tests/inet-address.c @@ -222,6 +222,43 @@ test_socket_address (void) g_object_unref (saddr); } +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) { @@ -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);