From b40194865ae643b15b6aab1fabd5d2df52ef2f83 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Thu, 3 Jul 2025 16:51:55 -0500 Subject: [PATCH] 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);