mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-20 07:38:54 +02:00
gnetworkaddress: Interleave ipv4 and ipv6 addresses
This commit is contained in:
committed by
Patrick Griffis
parent
c1e32b9057
commit
4b29e55097
@@ -884,6 +884,7 @@ typedef struct {
|
|||||||
|
|
||||||
GNetworkAddress *addr; /* (owned) */
|
GNetworkAddress *addr; /* (owned) */
|
||||||
GList *addresses; /* (owned) (nullable) */
|
GList *addresses; /* (owned) (nullable) */
|
||||||
|
GList *last_tail; /* (unowned) (nullable) */
|
||||||
GList *current_item; /* (unowned) (nullable) */
|
GList *current_item; /* (unowned) (nullable) */
|
||||||
GTask *queued_task; /* (owned) (nullable) */
|
GTask *queued_task; /* (owned) (nullable) */
|
||||||
GError *last_error; /* (owned) (nullable) */
|
GError *last_error; /* (owned) (nullable) */
|
||||||
@@ -919,6 +920,125 @@ g_network_address_address_enumerator_finalize (GObject *object)
|
|||||||
G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
|
G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline GSocketFamily
|
||||||
|
get_address_family (GInetSocketAddress *address)
|
||||||
|
{
|
||||||
|
return g_inet_address_get_family (g_inet_socket_address_get_address (address));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
list_split_families (GList *list,
|
||||||
|
GList **out_ipv4,
|
||||||
|
GList **out_ipv6)
|
||||||
|
{
|
||||||
|
g_assert (out_ipv4);
|
||||||
|
g_assert (out_ipv6);
|
||||||
|
|
||||||
|
while (list)
|
||||||
|
{
|
||||||
|
GSocketFamily family = get_address_family (list->data);
|
||||||
|
switch (family)
|
||||||
|
{
|
||||||
|
case G_SOCKET_FAMILY_IPV4:
|
||||||
|
*out_ipv4 = g_list_prepend (*out_ipv4, list->data);
|
||||||
|
break;
|
||||||
|
case G_SOCKET_FAMILY_IPV6:
|
||||||
|
*out_ipv6 = g_list_prepend (*out_ipv6, list->data);
|
||||||
|
break;
|
||||||
|
case G_SOCKET_FAMILY_INVALID:
|
||||||
|
case G_SOCKET_FAMILY_UNIX:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
list = g_list_next (list);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_ipv4 = g_list_reverse (*out_ipv4);
|
||||||
|
*out_ipv6 = g_list_reverse (*out_ipv6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GList *
|
||||||
|
list_interleave_families (GList *list1,
|
||||||
|
GList *list2)
|
||||||
|
{
|
||||||
|
GList *interleaved = NULL;
|
||||||
|
|
||||||
|
while (list1 || list2)
|
||||||
|
{
|
||||||
|
if (list1)
|
||||||
|
{
|
||||||
|
interleaved = g_list_append (interleaved, list1->data);
|
||||||
|
list1 = g_list_delete_link (list1, list1);
|
||||||
|
}
|
||||||
|
if (list2)
|
||||||
|
{
|
||||||
|
interleaved = g_list_append (interleaved, list2->data);
|
||||||
|
list2 = g_list_delete_link (list2, list2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return interleaved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* list_copy_interleaved:
|
||||||
|
* @list: (transfer container): List to copy
|
||||||
|
*
|
||||||
|
* Does a shallow copy of a list with address families interleaved.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* Input: [ipv6, ipv6, ipv4, ipv4]
|
||||||
|
* Output: [ipv6, ipv4, ipv6, ipv4]
|
||||||
|
*
|
||||||
|
* Returns: (transfer container): A new list
|
||||||
|
*/
|
||||||
|
static GList *
|
||||||
|
list_copy_interleaved (GList *list)
|
||||||
|
{
|
||||||
|
GList *ipv4 = NULL, *ipv6 = NULL;
|
||||||
|
|
||||||
|
list_split_families (list, &ipv4, &ipv6);
|
||||||
|
return list_interleave_families (ipv6, ipv4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* list_concat_interleaved:
|
||||||
|
* @current_item: (transfer container): Already existing list
|
||||||
|
* @new_list: (transfer none): New list to be interleaved and concatenated
|
||||||
|
*
|
||||||
|
* This differs from g_list_concat() + list_copy_interleaved() in that it sorts
|
||||||
|
* items in the previous list starting from @current_item.
|
||||||
|
*
|
||||||
|
* Returns: (transfer container): New start of list
|
||||||
|
*/
|
||||||
|
static GList *
|
||||||
|
list_concat_interleaved (GList *current_item,
|
||||||
|
GList *new_list)
|
||||||
|
{
|
||||||
|
GList *ipv4 = NULL, *ipv6 = NULL, *interleaved, *trailing = NULL;
|
||||||
|
GSocketFamily last_family = G_SOCKET_FAMILY_IPV4; /* Default to starting with ipv6 */
|
||||||
|
|
||||||
|
if (current_item)
|
||||||
|
{
|
||||||
|
last_family = get_address_family (current_item->data);
|
||||||
|
|
||||||
|
/* Unused addresses will get removed, resorted, then readded */
|
||||||
|
trailing = g_list_next (current_item);
|
||||||
|
current_item->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_split_families (trailing, &ipv4, &ipv6);
|
||||||
|
list_split_families (new_list, &ipv4, &ipv6);
|
||||||
|
|
||||||
|
if (trailing)
|
||||||
|
g_list_free (trailing);
|
||||||
|
|
||||||
|
if (last_family == G_SOCKET_FAMILY_IPV4)
|
||||||
|
interleaved = list_interleave_families (ipv6, ipv4);
|
||||||
|
else
|
||||||
|
interleaved = list_interleave_families (ipv4, ipv6);
|
||||||
|
|
||||||
|
return g_list_concat (current_item, interleaved);
|
||||||
|
}
|
||||||
|
|
||||||
static GSocketAddress *
|
static GSocketAddress *
|
||||||
g_network_address_address_enumerator_next (GSocketAddressEnumerator *enumerator,
|
g_network_address_address_enumerator_next (GSocketAddressEnumerator *enumerator,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
@@ -960,8 +1080,8 @@ g_network_address_address_enumerator_next (GSocketAddressEnumerator *enumerator
|
|||||||
g_network_address_add_addresses (addr, g_steal_pointer (&addresses), serial);
|
g_network_address_add_addresses (addr, g_steal_pointer (&addresses), serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_enum->addresses = addr->priv->sockaddrs;
|
addr_enum->current_item = addr_enum->addresses = list_copy_interleaved (addr->priv->sockaddrs);
|
||||||
addr_enum->current_item = addr_enum->addresses;
|
addr_enum->last_tail = g_list_last (addr->priv->sockaddrs);
|
||||||
g_object_unref (resolver);
|
g_object_unref (resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,13 +1100,11 @@ complete_queued_task (GNetworkAddressAddressEnumerator *addr_enum,
|
|||||||
{
|
{
|
||||||
GSocketAddress *sockaddr;
|
GSocketAddress *sockaddr;
|
||||||
|
|
||||||
addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
|
addr_enum->current_item = addr_enum->addresses = list_copy_interleaved (addr_enum->addr->priv->sockaddrs);
|
||||||
addr_enum->current_item = addr_enum->addresses;
|
addr_enum->last_tail = g_list_last (addr_enum->addr->priv->sockaddrs);
|
||||||
|
|
||||||
if (addr_enum->current_item)
|
if (addr_enum->current_item)
|
||||||
{
|
sockaddr = g_object_ref (addr_enum->current_item->data);
|
||||||
sockaddr = g_object_ref (addr_enum->current_item->data);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
sockaddr = NULL;
|
sockaddr = NULL;
|
||||||
|
|
||||||
@@ -1176,17 +1294,28 @@ g_network_address_address_enumerator_next_async (GSocketAddressEnumerator *enum
|
|||||||
if (addr_enum->addresses == NULL)
|
if (addr_enum->addresses == NULL)
|
||||||
{
|
{
|
||||||
g_assert (addr->priv->sockaddrs);
|
g_assert (addr->priv->sockaddrs);
|
||||||
addr_enum->addresses = addr->priv->sockaddrs;
|
|
||||||
addr_enum->current_item = addr_enum->addresses;
|
addr_enum->current_item = addr_enum->addresses = list_copy_interleaved (addr->priv->sockaddrs);
|
||||||
sockaddr = g_object_ref (addr_enum->current_item->data);
|
|
||||||
}
|
|
||||||
else if (addr_enum->current_item->next)
|
|
||||||
{
|
|
||||||
addr_enum->current_item = g_list_next (addr_enum->current_item);
|
|
||||||
sockaddr = g_object_ref (addr_enum->current_item->data);
|
sockaddr = g_object_ref (addr_enum->current_item->data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sockaddr = NULL;
|
{
|
||||||
|
GList *parent_tail = g_list_last (addr_enum->addr->priv->sockaddrs);
|
||||||
|
|
||||||
|
if (addr_enum->last_tail != parent_tail)
|
||||||
|
{
|
||||||
|
addr_enum->current_item = list_concat_interleaved (addr_enum->current_item, g_list_next (addr_enum->last_tail));
|
||||||
|
addr_enum->last_tail = parent_tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_enum->current_item->next)
|
||||||
|
{
|
||||||
|
addr_enum->current_item = g_list_next (addr_enum->current_item);
|
||||||
|
sockaddr = g_object_ref (addr_enum->current_item->data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sockaddr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
g_task_return_pointer (task, sockaddr, g_object_unref);
|
g_task_return_pointer (task, sockaddr, g_object_unref);
|
||||||
g_object_unref (task);
|
g_object_unref (task);
|
||||||
|
@@ -707,8 +707,6 @@ test_happy_eyeballs_slow_connection_and_ipv4 (HappyEyeballsFixture *fixture,
|
|||||||
g_main_loop_run (fixture->loop);
|
g_main_loop_run (fixture->loop);
|
||||||
|
|
||||||
assert_list_matches_expected (data.addrs, fixture->input_all_results);
|
assert_list_matches_expected (data.addrs, fixture->input_all_results);
|
||||||
/* Note that interleaving will not happen here because ipv6 was used before ipv4
|
|
||||||
* responded */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Reference in New Issue
Block a user