From 73d0627bdae56bae8d9b2e699c0b620d51a6f1d6 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Wed, 2 Jul 2025 09:15:51 -0500 Subject: [PATCH 1/8] ginetaddress: Add scope-id and flowinfo properties This will be used by GResolver as DNS responses include this information along with the address. --- gio/ginetaddress.c | 133 +++++++++++++++++++++++++++++++++++++++++++++ gio/ginetaddress.h | 12 ++++ 2 files changed, 145 insertions(+) diff --git a/gio/ginetaddress.c b/gio/ginetaddress.c index c0ba2507a..0005b2e7c 100644 --- a/gio/ginetaddress.c +++ b/gio/ginetaddress.c @@ -42,6 +42,10 @@ struct _GInetAddressPrivate struct in6_addr ipv6; #endif } addr; +#ifdef HAVE_IPV6 + guint32 flowinfo; + guint32 scope_id; +#endif }; /** @@ -78,6 +82,8 @@ enum PROP_IS_MC_NODE_LOCAL, PROP_IS_MC_ORG_LOCAL, PROP_IS_MC_SITE_LOCAL, + PROP_FLOWINFO, + PROP_SCOPE_ID, }; static void @@ -107,6 +113,18 @@ g_inet_address_set_property (GObject *object, #endif break; + case PROP_SCOPE_ID: +#ifdef HAVE_IPV6 + address->priv->scope_id = g_value_get_uint (value); +#endif + break; + + case PROP_FLOWINFO: +#ifdef HAVE_IPV6 + address->priv->flowinfo = g_value_get_uint (value); +#endif + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -172,6 +190,14 @@ g_inet_address_get_property (GObject *object, g_value_set_boolean (value, g_inet_address_get_is_mc_site_local (address)); break; + case PROP_FLOWINFO: + g_value_set_uint (value, g_inet_address_get_flowinfo (address)); + break; + + case PROP_SCOPE_ID: + g_value_set_uint (value, g_inet_address_get_scope_id (address)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -353,6 +379,38 @@ g_inet_address_class_init (GInetAddressClass *klass) FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * GInetAddress:flowinfo: + * + * The flowinfo for an IPv6 address. + * See [method@Gio.InetAddress.get_flowinfo]. + * + * Since: 2.86 + */ + g_object_class_install_property (gobject_class, PROP_FLOWINFO, + g_param_spec_uint ("flowinfo", NULL, NULL, + 0, G_MAXUINT32, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GInetAddress:scope-id: + * + * The scope-id for an IPv6 address. + * See [method@Gio.InetAddress.get_scope_id]. + * + * Since: 2.86 + */ + g_object_class_install_property (gobject_class, PROP_SCOPE_ID, + g_param_spec_uint ("scope-id", NULL, NULL, + 0, G_MAXUINT32, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); } static void @@ -490,6 +548,38 @@ g_inet_address_new_any (GSocketFamily family) #endif } +/** + * g_inet_address_new_from_bytes_with_ipv6_info: + * @bytes: (array) (element-type guint8): raw address data + * @family: the address family of @bytes + * @scope_id: the scope-id of the address + * + * Creates a new [class@Gio.InetAddress] from the given @family, @bytes + * and @scope_id. + * + * @bytes must be 4 bytes for [enum@Gio.SocketFamily.IPV4] and 16 bytes for + * [enum@Gio.SocketFamily.IPV6]. + * + * Returns: (transfer full): a new internet address corresponding to + * @family, @bytes and @scope_id + * + * Since: 2.86 + */ +GInetAddress * +g_inet_address_new_from_bytes_with_ipv6_info (const guint8 *bytes, + GSocketFamily family, + guint32 flowinfo, + guint32 scope_id) +{ + g_return_val_if_fail (G_INET_ADDRESS_FAMILY_IS_VALID (family), NULL); + + return g_object_new (G_TYPE_INET_ADDRESS, + "family", family, + "bytes", bytes, + "flowinfo", flowinfo, + "scope-id", scope_id, + NULL); +} /** * g_inet_address_to_string: @@ -866,6 +956,49 @@ g_inet_address_get_is_mc_site_local (GInetAddress *address) #endif } +/** + * g_inet_address_get_scope_id: + * @address: a #GInetAddress + * + * Gets the value of [property@Gio.InetAddress:scope-id]. + * + * Returns: The scope-id for the address, `0` if unset or not IPv6 address. + * Since: 2.86 + */ +guint32 +g_inet_address_get_scope_id (GInetAddress *address) +{ + g_return_val_if_fail (G_IS_INET_ADDRESS (address), 0); + +#ifdef HAVE_IPV6 + if (address->priv->family == AF_INET6) + return address->priv->scope_id; +#endif + return 0; +} + +/** + * g_inet_address_get_flowinfo: + * @address: a #GInetAddress + * + * Gets the value of [property@Gio.InetAddress:flowinfo]. + * + * Returns: The flowinfo for the address, `0` if unset or not IPv6 address. + * Since: 2.86 + */ +guint32 +g_inet_address_get_flowinfo (GInetAddress *address) +{ + g_return_val_if_fail (G_IS_INET_ADDRESS (address), 0); + +#ifdef HAVE_IPV6 + if (address->priv->family == AF_INET6) + return address->priv->flowinfo; +#endif + return 0; +} + + /** * g_inet_address_equal: * @address: A #GInetAddress. diff --git a/gio/ginetaddress.h b/gio/ginetaddress.h index ea503a927..9d2f21b1e 100644 --- a/gio/ginetaddress.h +++ b/gio/ginetaddress.h @@ -71,6 +71,12 @@ GInetAddress * g_inet_address_new_from_bytes (const guint8 GIO_AVAILABLE_IN_ALL GInetAddress * g_inet_address_new_loopback (GSocketFamily family); +GIO_AVAILABLE_IN_2_86 +GInetAddress * g_inet_address_new_from_bytes_with_ipv6_info (const guint8 *bytes, + GSocketFamily family, + guint32 flowinfo, + guint32 scope_id); + GIO_AVAILABLE_IN_ALL GInetAddress * g_inet_address_new_any (GSocketFamily family); @@ -120,6 +126,12 @@ gboolean g_inet_address_get_is_mc_org_local (GInetAddress GIO_AVAILABLE_IN_ALL gboolean g_inet_address_get_is_mc_site_local (GInetAddress *address); +GIO_AVAILABLE_IN_2_86 +guint32 g_inet_address_get_scope_id (GInetAddress *address); + +GIO_AVAILABLE_IN_2_86 +guint32 g_inet_address_get_flowinfo (GInetAddress *address); + G_END_DECLS #endif /* __G_INET_ADDRESS_H__ */ From d03f495b3182cc31085348619c2c6205964433b4 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Wed, 2 Jul 2025 09:30:05 -0500 Subject: [PATCH 2/8] gsocketaddress: Set scope-id and flowinfo of ginetaddress --- gio/ginetsocketaddress.c | 20 ++++++++++++++------ gio/gsocketaddress.c | 8 +++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/gio/ginetsocketaddress.c b/gio/ginetsocketaddress.c index c17bd1497..31b09b33c 100644 --- a/gio/ginetsocketaddress.c +++ b/gio/ginetsocketaddress.c @@ -97,12 +97,12 @@ g_inet_socket_address_get_property (GObject *object, case PROP_FLOWINFO: g_return_if_fail (g_inet_address_get_family (address->priv->address) == G_SOCKET_FAMILY_IPV6); - g_value_set_uint (value, address->priv->flowinfo); + g_value_set_uint (value, g_inet_socket_address_get_flowinfo (address)); break; case PROP_SCOPE_ID: g_return_if_fail (g_inet_address_get_family (address->priv->address) == G_SOCKET_FAMILY_IPV6); - g_value_set_uint (value, address->priv->scope_id); + g_value_set_uint (value, g_inet_socket_address_get_scope_id (address)); break; default: @@ -220,8 +220,8 @@ g_inet_socket_address_to_native (GSocketAddress *address, memset (sock, 0, sizeof (*sock)); sock->sin6_family = AF_INET6; sock->sin6_port = g_htons (addr->priv->port); - sock->sin6_flowinfo = addr->priv->flowinfo; - sock->sin6_scope_id = addr->priv->scope_id; + sock->sin6_flowinfo = g_inet_socket_address_get_flowinfo (addr); + sock->sin6_scope_id = g_inet_socket_address_get_scope_id (addr); memcpy (&(sock->sin6_addr.s6_addr), g_inet_address_to_bytes (addr->priv->address), sizeof (sock->sin6_addr)); return TRUE; } @@ -282,6 +282,8 @@ g_inet_socket_address_class_init (GInetSocketAddressClass *klass) * * The `sin6_flowinfo` field, for IPv6 addresses. * + * If unset this property is inherited from [property@Gio.InetSocketAddress:address]. + * * Since: 2.32 */ g_object_class_install_property (gobject_class, PROP_FLOWINFO, @@ -298,6 +300,8 @@ g_inet_socket_address_class_init (GInetSocketAddressClass *klass) * * The `sin6_scope_id` field, for IPv6 addresses. * + * If unset this property is inherited from [property@Gio.InetSocketAddress:address]. + * * Since: 2.32 */ g_object_class_install_property (gobject_class, PROP_SCOPE_ID, @@ -511,6 +515,8 @@ g_inet_socket_address_get_port (GInetSocketAddress *address) * Gets the `sin6_flowinfo` field from @address, * which must be an IPv6 address. * + * If not overridden this value will be inherited from [property@Gio.InetSocketAddress:address]. + * * Returns: the flowinfo field * * Since: 2.32 @@ -521,7 +527,7 @@ g_inet_socket_address_get_flowinfo (GInetSocketAddress *address) g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (address), 0); g_return_val_if_fail (g_inet_address_get_family (address->priv->address) == G_SOCKET_FAMILY_IPV6, 0); - return address->priv->flowinfo; + return address->priv->flowinfo ? address->priv->flowinfo : g_inet_address_get_flowinfo (address->priv->address); } /** @@ -531,6 +537,8 @@ g_inet_socket_address_get_flowinfo (GInetSocketAddress *address) * Gets the `sin6_scope_id` field from @address, * which must be an IPv6 address. * + * If not overridden this value will be inherited from [property@Gio.InetSocketAddress:address]. + * * Returns: the scope id field * * Since: 2.32 @@ -541,5 +549,5 @@ g_inet_socket_address_get_scope_id (GInetSocketAddress *address) g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (address), 0); g_return_val_if_fail (g_inet_address_get_family (address->priv->address) == G_SOCKET_FAMILY_IPV6, 0); - return address->priv->scope_id; + return address->priv->scope_id ? address->priv->scope_id : g_inet_address_get_scope_id (address->priv->address); } diff --git a/gio/gsocketaddress.c b/gio/gsocketaddress.c index defd965e0..195644b53 100644 --- a/gio/gsocketaddress.c +++ b/gio/gsocketaddress.c @@ -252,15 +252,13 @@ g_socket_address_new_from_native (gpointer native, iaddr = g_inet_address_new_from_bytes ((guint8 *) &(sin_addr.sin_addr), G_SOCKET_FAMILY_IPV4); } else - { - iaddr = g_inet_address_new_from_bytes ((guint8 *) &(addr->sin6_addr), G_SOCKET_FAMILY_IPV6); - } + { + iaddr = g_inet_address_new_from_bytes_with_ipv6_info ((guint8 *) &(addr->sin6_addr), G_SOCKET_FAMILY_IPV6, addr->sin6_flowinfo, addr->sin6_scope_id); + } sockaddr = g_object_new (G_TYPE_INET_SOCKET_ADDRESS, "address", iaddr, "port", g_ntohs (addr->sin6_port), - "flowinfo", addr->sin6_flowinfo, - "scope_id", addr->sin6_scope_id, NULL); g_object_unref (iaddr); return sockaddr; From 984d5cb199b61417c29039617f38efaa07f256a1 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Wed, 2 Jul 2025 09:49:37 -0500 Subject: [PATCH 3/8] socket-testclient: Support connecting to IPv6 addresses Each enumerated address can be of a different family type, so you have to make a socket after you have that information. As this is just a test client we make a new socket each time for simplicity. --- gio/tests/socket-testclient.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/gio/tests/socket-testclient.c b/gio/tests/socket-testclient.c index 025632767..6cf6ba306 100644 --- a/gio/tests/socket-testclient.c +++ b/gio/tests/socket-testclient.c @@ -103,6 +103,17 @@ lookup_client_certificate (GTlsClientConnection *conn, return certificate; } +static GSocket * +make_socket (GSocketFamily socket_family, GSocketType socket_type, GError **error) +{ + GSocket *socket = g_socket_new (socket_family, socket_type, 0, error); + + if (socket && read_timeout) + g_socket_set_timeout (socket, read_timeout); + + return socket; +} + static gboolean make_connection (const char *argument, GTlsCertificate *certificate, @@ -115,7 +126,6 @@ make_connection (const char *argument, GError **error) { GSocketType socket_type; - GSocketFamily socket_family; GSocketAddressEnumerator *enumerator; GSocketConnectable *connectable; GSocketAddress *src_address; @@ -127,17 +137,6 @@ make_connection (const char *argument, else socket_type = G_SOCKET_TYPE_STREAM; - if (unix_socket) - socket_family = G_SOCKET_FAMILY_UNIX; - else - socket_family = G_SOCKET_FAMILY_IPV4; - - *socket = g_socket_new (socket_family, socket_type, 0, error); - if (*socket == NULL) - return FALSE; - - if (read_timeout) - g_socket_set_timeout (*socket, read_timeout); if (unix_socket) { @@ -171,11 +170,21 @@ make_connection (const char *argument, return FALSE; } + *socket = make_socket (unix_socket ? G_SOCKET_FAMILY_UNIX : g_socket_address_get_family (*address), + socket_type, error); + if (*socket == NULL) + { + g_object_unref (*address); + g_object_unref (enumerator); + return FALSE; + } + if (g_socket_connect (*socket, *address, cancellable, &err)) break; g_message ("Connection to %s failed: %s, trying next", socket_address_to_string (*address), err->message); g_clear_error (&err); + g_object_unref (*socket); g_object_unref (*address); } g_object_unref (enumerator); From bb8281fa7297722474946c91dc403a44cdb1e8c4 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Thu, 3 Jul 2025 16:00:21 -0500 Subject: [PATCH 4/8] socket-testclient: Include scope-id in debug output --- gio/tests/socket-common.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/gio/tests/socket-common.c b/gio/tests/socket-common.c index b740f68e7..fb59f3f6d 100644 --- a/gio/tests/socket-common.c +++ b/gio/tests/socket-common.c @@ -17,14 +17,23 @@ socket_address_to_string (GSocketAddress *address) if (G_IS_INET_SOCKET_ADDRESS (address)) { + GInetSocketAddress *socket_address; GInetAddress *inet_address; char *str; int port; + guint32 scope_id; - inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)); + socket_address = G_INET_SOCKET_ADDRESS (address); + scope_id = g_inet_socket_address_get_scope_id (socket_address); + inet_address = g_inet_socket_address_get_address (socket_address); str = g_inet_address_to_string (inet_address); - port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); - res = g_strdup_printf ("%s:%d", str, port); + port = g_inet_socket_address_get_port (socket_address); + if (scope_id) + res = g_strdup_printf ("[%s%%%u]:%d", str, scope_id, port); + else if (g_inet_address_get_family (inet_address) == G_SOCKET_FAMILY_IPV6) + res = g_strdup_printf ("[%s]:%d", str, port); + else + res = g_strdup_printf ("%s:%d", str, port); g_free (str); } #ifdef G_OS_UNIX From ed8bae5483be82f2ff7e8792494b0daca591e2b3 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Thu, 3 Jul 2025 16:05:20 -0500 Subject: [PATCH 5/8] socket-testclient: Fix leaking debug strings --- gio/tests/socket-testclient.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/gio/tests/socket-testclient.c b/gio/tests/socket-testclient.c index 6cf6ba306..4b8aa6582 100644 --- a/gio/tests/socket-testclient.c +++ b/gio/tests/socket-testclient.c @@ -131,6 +131,7 @@ make_connection (const char *argument, GSocketAddress *src_address; GTlsInteraction *interaction; GError *err = NULL; + char *socket_string; if (use_udp) socket_type = G_SOCKET_TYPE_DATAGRAM; @@ -181,7 +182,9 @@ make_connection (const char *argument, if (g_socket_connect (*socket, *address, cancellable, &err)) break; - g_message ("Connection to %s failed: %s, trying next", socket_address_to_string (*address), err->message); + socket_string = socket_address_to_string (*address); + g_message ("Connection to %s failed: %s, trying next", socket_string, err->message); + g_free (socket_string); g_clear_error (&err); g_object_unref (*socket); @@ -189,8 +192,9 @@ make_connection (const char *argument, } g_object_unref (enumerator); - g_print ("Connected to %s\n", - socket_address_to_string (*address)); + socket_string = socket_address_to_string (*address); + g_print ("Connected to %s\n", socket_string); + g_free (socket_string); src_address = g_socket_get_local_address (*socket, error); if (!src_address) @@ -199,8 +203,9 @@ make_connection (const char *argument, return FALSE; } - g_print ("local address: %s\n", - socket_address_to_string (src_address)); + socket_string = socket_address_to_string (src_address); + g_print ("local address: %s\n", socket_string); + g_free (socket_string); g_object_unref (src_address); if (use_udp) From b40194865ae643b15b6aab1fabd5d2df52ef2f83 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Thu, 3 Jul 2025 16:51:55 -0500 Subject: [PATCH 6/8] Unify address parsing for GInetAddress and GInetSocketAddress This allows scope-ids to work for GInetAddress --- gio/ginetaddress.c | 52 ++++++++++++++++++++++++++++++++------ gio/ginetsocketaddress.c | 54 ++++------------------------------------ gio/tests/inet-address.c | 6 +++-- 3 files changed, 54 insertions(+), 58 deletions(-) diff --git a/gio/ginetaddress.c b/gio/ginetaddress.c index 0005b2e7c..fbf32dfee 100644 --- a/gio/ginetaddress.c +++ b/gio/ginetaddress.c @@ -435,9 +435,6 @@ GInetAddress * g_inet_address_new_from_string (const gchar *string) { struct in_addr in_addr; -#ifdef HAVE_IPV6 - struct in6_addr in6_addr; -#endif g_return_val_if_fail (string != NULL, NULL); @@ -447,13 +444,54 @@ g_inet_address_new_from_string (const gchar *string) */ g_networking_init (); - if (inet_pton (AF_INET, string, &in_addr) > 0) - return g_inet_address_new_from_bytes ((guint8 *)&in_addr, AF_INET); #ifdef HAVE_IPV6 - else if (inet_pton (AF_INET6, string, &in6_addr) > 0) - return g_inet_address_new_from_bytes ((guint8 *)&in6_addr, AF_INET6); + /* IPv6 address (or it's invalid). We use getaddrinfo() because + * it will handle parsing a scope_id as well. + */ + if (strchr (string, ':')) + { + struct addrinfo *res; + struct addrinfo hints = { + .ai_family = AF_INET6, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_NUMERICHOST, + }; + int status; + GInetAddress *address = NULL; + + status = getaddrinfo (string, NULL, &hints, &res); + if (status == 0) + { + g_assert (res->ai_addrlen == sizeof (struct sockaddr_in6)); + struct sockaddr_in6 *sockaddr6 = (struct sockaddr_in6 *)res->ai_addr; + address = g_inet_address_new_from_bytes_with_ipv6_info (((guint8 *)&sockaddr6->sin6_addr), + G_SOCKET_FAMILY_IPV6, + sockaddr6->sin6_flowinfo, + sockaddr6->sin6_scope_id); + freeaddrinfo (res); + } + else + { + struct in6_addr in6_addr; + g_debug ("getaddrinfo failed to resolve host string %s", string); + + if (inet_pton (AF_INET6, string, &in6_addr) > 0) + address = g_inet_address_new_from_bytes ((guint8 *)&in6_addr, G_SOCKET_FAMILY_IPV6); + } + + return address; + } #endif + /* IPv4 (or invalid). We don't want to use getaddrinfo() here, + * because it accepts the stupid "IPv4 numbers-and-dots + * notation" addresses that are never used for anything except + * phishing. Since we don't have to worry about scope IDs for + * IPv4, we can just use inet_pton(). + */ + if (inet_pton (AF_INET, string, &in_addr) > 0) + return g_inet_address_new_from_bytes ((guint8 *)&in_addr, G_SOCKET_FAMILY_IPV4); + return NULL; } diff --git a/gio/ginetsocketaddress.c b/gio/ginetsocketaddress.c index 31b09b33c..ca03890f6 100644 --- a/gio/ginetsocketaddress.c +++ b/gio/ginetsocketaddress.c @@ -413,60 +413,16 @@ GSocketAddress * g_inet_socket_address_new_from_string (const char *address, guint port) { - static struct addrinfo *hints, hints_struct; GSocketAddress *saddr; GInetAddress *iaddr; - struct addrinfo *res; - gint status; - if (strchr (address, ':')) - { - /* IPv6 address (or it's invalid). We use getaddrinfo() because - * it will handle parsing a scope_id as well. - */ + iaddr = g_inet_address_new_from_string (address); + if (!iaddr) + return NULL; - if (G_UNLIKELY (g_once_init_enter_pointer (&hints))) - { - hints_struct.ai_family = AF_UNSPEC; - hints_struct.ai_socktype = SOCK_STREAM; - hints_struct.ai_protocol = 0; - hints_struct.ai_flags = AI_NUMERICHOST; - g_once_init_leave_pointer (&hints, &hints_struct); - } - - status = getaddrinfo (address, NULL, hints, &res); - if (status != 0) - return NULL; - - if (res->ai_family == AF_INET6 && - res->ai_addrlen == sizeof (struct sockaddr_in6)) - { - ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = g_htons (port); - saddr = g_socket_address_new_from_native (res->ai_addr, res->ai_addrlen); - } - else - saddr = NULL; - - freeaddrinfo (res); - } - else - { - /* IPv4 (or invalid). We don't want to use getaddrinfo() here, - * because it accepts the stupid "IPv4 numbers-and-dots - * notation" addresses that are never used for anything except - * phishing. Since we don't have to worry about scope IDs for - * IPv4, we can just use g_inet_address_new_from_string(). - */ - iaddr = g_inet_address_new_from_string (address); - if (!iaddr) - return NULL; - - g_warn_if_fail (g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV4); - - saddr = g_inet_socket_address_new (iaddr, port); - g_object_unref (iaddr); - } + saddr = g_inet_socket_address_new (iaddr, port); + g_object_unref (iaddr); return saddr; } diff --git a/gio/tests/inet-address.c b/gio/tests/inet-address.c index 5c663fd67..f43640ca8 100644 --- a/gio/tests/inet-address.c +++ b/gio/tests/inet-address.c @@ -53,6 +53,9 @@ test_parse (void) addr = g_inet_address_new_from_string ("204.152.189.116"); g_assert (addr != NULL); g_object_unref (addr); + addr = g_inet_address_new_from_string ("::1%0"); + g_assert (addr != NULL); + g_object_unref (addr); addr = g_inet_address_new_from_string ("::1::2"); g_assert (addr == NULL); @@ -206,12 +209,11 @@ test_socket_address (void) g_object_unref (saddr); - addr = g_inet_address_new_from_string ("::1"); + addr = g_inet_address_new_from_string ("::1%25"); saddr = G_INET_SOCKET_ADDRESS (g_object_new (G_TYPE_INET_SOCKET_ADDRESS, "address", addr, "port", 308, "flowinfo", 10, - "scope-id", 25, NULL)); g_object_unref (addr); From 7e85ae9d5fb3270d2372d432abd3fb20add35b48 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Mon, 7 Jul 2025 11:24:11 -0500 Subject: [PATCH 7/8] ginetaddress: Scope ID parsing is not supported on win32 getaddrinfo doesn't parse it on Windows. This could be replaced by more manual parsing in the future. --- gio/ginetaddress.c | 4 ++++ gio/ginetsocketaddress.c | 3 ++- gio/tests/inet-address.c | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/gio/ginetaddress.c b/gio/ginetaddress.c index fbf32dfee..c5274a0f5 100644 --- a/gio/ginetaddress.c +++ b/gio/ginetaddress.c @@ -425,6 +425,10 @@ g_inet_address_init (GInetAddress *address) * * Parses @string as an IP address and creates a new #GInetAddress. * + * If @address is an IPv6 address, it can also contain a scope ID + * (separated from the address by a `%`). Note that currently this + * behavior is platform specific. This may change in a future release. + * * Returns: (nullable) (transfer full): a new #GInetAddress corresponding * to @string, or %NULL if @string could not be parsed. * Free the returned object with g_object_unref(). diff --git a/gio/ginetsocketaddress.c b/gio/ginetsocketaddress.c index ca03890f6..95a4eb164 100644 --- a/gio/ginetsocketaddress.c +++ b/gio/ginetsocketaddress.c @@ -402,7 +402,8 @@ g_inet_socket_address_new (GInetAddress *address, * Creates a new #GInetSocketAddress for @address and @port. * * If @address is an IPv6 address, it can also contain a scope ID - * (separated from the address by a `%`). + * (separated from the address by a `%`). Note that currently this + * behavior is platform specific. This may change in a future release. * * Returns: (nullable) (transfer full): a new #GInetSocketAddress, * or %NULL if @address cannot be parsed. diff --git a/gio/tests/inet-address.c b/gio/tests/inet-address.c index f43640ca8..ea9faecd8 100644 --- a/gio/tests/inet-address.c +++ b/gio/tests/inet-address.c @@ -53,9 +53,11 @@ test_parse (void) addr = g_inet_address_new_from_string ("204.152.189.116"); g_assert (addr != NULL); g_object_unref (addr); +#ifndef G_OS_WIN32 addr = g_inet_address_new_from_string ("::1%0"); g_assert (addr != NULL); g_object_unref (addr); +#endif addr = g_inet_address_new_from_string ("::1::2"); g_assert (addr == NULL); @@ -209,11 +211,12 @@ test_socket_address (void) g_object_unref (saddr); - addr = g_inet_address_new_from_string ("::1%25"); + addr = g_inet_address_new_from_string ("::1"); saddr = G_INET_SOCKET_ADDRESS (g_object_new (G_TYPE_INET_SOCKET_ADDRESS, "address", addr, "port", 308, "flowinfo", 10, + "scope-id", 25, NULL)); g_object_unref (addr); From e419c1e10d938efa66fd23d785b8c20bd3c7a39f Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Mon, 7 Jul 2025 12:55:11 -0500 Subject: [PATCH 8/8] inet-address: Ignore windows specific failure In this case getaddrinfo on Windows succeeds at parsing something that fails on Linux. --- gio/tests/inet-address.c | 4 +++- gio/tests/network-address.c | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/gio/tests/inet-address.c b/gio/tests/inet-address.c index ea9faecd8..799f66b5f 100644 --- a/gio/tests/inet-address.c +++ b/gio/tests/inet-address.c @@ -53,7 +53,7 @@ test_parse (void) addr = g_inet_address_new_from_string ("204.152.189.116"); g_assert (addr != NULL); g_object_unref (addr); -#ifndef G_OS_WIN32 +#ifndef G_OS_WIN32 /* getaddrinfo on Windows does not support scope-id */ addr = g_inet_address_new_from_string ("::1%0"); g_assert (addr != NULL); g_object_unref (addr); @@ -65,10 +65,12 @@ test_parse (void) g_assert (addr == NULL); addr = g_inet_address_new_from_string ("[2001:1:2:3:4:5:6:7"); g_assert (addr == NULL); +#ifndef G_OS_WIN32 /* getaddrinfo on Windows is more forgiving about format and accepts these strings */ addr = g_inet_address_new_from_string ("[2001:1:2:3:4:5:6:7]"); g_assert (addr == NULL); addr = g_inet_address_new_from_string ("[2001:1:2:3:4:5:6:7]:80"); g_assert (addr == NULL); +#endif addr = g_inet_address_new_from_string ("0:1:2:3:4:5:6:7:8:9"); g_assert (addr == NULL); addr = g_inet_address_new_from_string ("::FFFFFFF"); diff --git a/gio/tests/network-address.c b/gio/tests/network-address.c index 61fa41468..81dcda7a9 100644 --- a/gio/tests/network-address.c +++ b/gio/tests/network-address.c @@ -176,8 +176,13 @@ static ResolveTest address_tests[] = { * (just) IP addresses. */ { "192.168.1.2:80", TRUE, FALSE, FALSE }, +#ifndef G_OS_WIN32 /* getaddrinfo on Windows is more forgiving about format and accepts these strings */ { "[fe80::42]", TRUE, FALSE, FALSE }, { "[fe80::42]:80", TRUE, FALSE, FALSE }, +#else + { "[fe80::42]", TRUE, TRUE, FALSE }, + { "[fe80::42]:80", TRUE, TRUE, FALSE }, +#endif /* These should not be considered IP addresses by anyone. */ { "192.168.258", FALSE, FALSE, FALSE },