mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 00:12:19 +01:00 
			
		
		
		
	Windows: Use Standard Networking Functions If Possible
Currently, the Windows code use Winsock2-specific APIs to try to emulate calls such as inet_pton(), inet_ntop() and if_nametoindex(), which may not do the job all the time. On Vista and later, Winsock2 does provide a proper implementation for these functions, so we can use them if they exist on the system, by querying for them during g_networking_init(). Otherwise, we continue to use the original code path for these, in the case of XP and Server 2003. This enables many of the network-address tests to pass on Windows as a result, when the native Winsock2 implementations can be used. https://bugzilla.gnome.org/show_bug.cgi?id=730352
This commit is contained in:
		| @@ -314,6 +314,7 @@ win32_actual_sources = \ | ||||
| 	gwin32inputstream.c \ | ||||
| 	gwin32outputstream.c \ | ||||
| 	gwin32outputstream.h \ | ||||
| 	gwin32networking.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| win32_more_sources_for_vcproj = \ | ||||
|   | ||||
| @@ -31,6 +31,13 @@ | ||||
| #include "glibintl.h" | ||||
| #include "gnetworkingprivate.h" | ||||
|  | ||||
| #ifdef G_OS_WIN32 | ||||
| /* Ensure Windows XP runtime compatibility, while using | ||||
|  * inet_pton() and inet_ntop() if available | ||||
|  */ | ||||
| #include "gwin32networking.h" | ||||
| #endif | ||||
|  | ||||
| struct _GInetAddressPrivate | ||||
| { | ||||
|   GSocketFamily family; | ||||
| @@ -369,6 +376,109 @@ g_inet_address_init (GInetAddress *address) | ||||
|   address->priv = g_inet_address_get_instance_private (address); | ||||
| } | ||||
|  | ||||
| /* These are provided so that we can use inet_pton() and inet_ntop() on Windows | ||||
|  * if they are available (i.e. Vista and later), and use the existing code path | ||||
|  * on Windows XP/Server 2003.  We can drop this portion when we drop support for | ||||
|  * XP/Server 2003. | ||||
|  */ | ||||
| #if defined(G_OS_WIN32) && _WIN32_WINNT < 0x0600 | ||||
| static gint | ||||
| inet_pton (gint family, | ||||
|            const gchar *addr_string, | ||||
|            gpointer addr) | ||||
| { | ||||
|   /* For Vista/Server 2008 and later, there is native inet_pton() in Winsock2 */ | ||||
|   if (ws2funcs.pInetPton != NULL) | ||||
|     return ws2funcs.pInetPton (family, addr_string, addr); | ||||
|   else | ||||
|     { | ||||
|       /* Fallback codepath for XP/Server 2003 */ | ||||
|       struct sockaddr_storage sa; | ||||
|       struct sockaddr_in *sin = (struct sockaddr_in *)&sa; | ||||
|       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; | ||||
|       gint len = sizeof (sa); | ||||
|  | ||||
|       /* We need to make sure to not pass a string of the form | ||||
|        * "IPv4addr:port" or "[IPv6addr]:port" to WSAStringToAddress(), | ||||
|        * since it would accept them (returning both the address and the | ||||
|        * port), but we only want to accept standalone IP addresses. (In | ||||
|        * the IPv6 case, WINE actually only checks for the ']', not the | ||||
|        * '[', which is why we do the same here.) | ||||
|        */ | ||||
|       if (family != AF_INET && family != AF_INET6) | ||||
|         { | ||||
|           WSASetLastError (WSAEAFNOSUPPORT); | ||||
|           return -1; | ||||
|         } | ||||
|       if (!strchr (addr_string, ':')) | ||||
|         { | ||||
|           if (WSAStringToAddress ((LPTSTR) addr_string, AF_INET, NULL, (LPSOCKADDR) &sa, &len) == 0) | ||||
|             { | ||||
|               /* XXX: Figure out when WSAStringToAddress() accepts a IPv4 address but the | ||||
|                       numbers-and-dots address is actually not complete.  This code will be | ||||
|                       removed once XP/Server 2003 support is dropped... */ | ||||
|               addr = &sin->sin_addr; | ||||
|               return 1; | ||||
|             } | ||||
|         } | ||||
|       if (!strchr (addr_string, ']')) | ||||
|         { | ||||
|           if (WSAStringToAddress ((LPTSTR) addr_string, AF_INET6, NULL, (LPSOCKADDR) &sa, &len) == 0) | ||||
|             { | ||||
|               addr = &sin6->sin6_addr; | ||||
|               return 1; | ||||
|             } | ||||
|         } | ||||
|       return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static const gchar * | ||||
| inet_ntop (gint family, | ||||
|            const gpointer addr, | ||||
|            gchar *addr_str, | ||||
|            socklen_t size) | ||||
| { | ||||
|   /* On Vista/Server 2008 and later, there is native inet_ntop() in Winsock2 */ | ||||
|   if (ws2funcs.pInetNtop != NULL) | ||||
|     return ws2funcs.pInetNtop (family, addr, addr_str, size); | ||||
|   else | ||||
|     { | ||||
|       /* Fallback codepath for XP/Server 2003 */ | ||||
|       DWORD buflen = sizeof (addr_str), addrlen; | ||||
|       struct sockaddr_storage sa; | ||||
|       struct sockaddr_in *sin = (struct sockaddr_in *)&sa; | ||||
|       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; | ||||
|  | ||||
|       memset (&sa, 0, sizeof (sa)); | ||||
|       sa.ss_family = family; | ||||
|       if (sa.ss_family == AF_INET) | ||||
|         { | ||||
|           struct in_addr *addrv4 = (struct in_addr *) addr; | ||||
|  | ||||
|           addrlen = sizeof (*sin); | ||||
|           memcpy (&sin->sin_addr, addrv4, sizeof (sin->sin_addr)); | ||||
|         } | ||||
|       else if (sa.ss_family == AF_INET6) | ||||
|         { | ||||
|           struct in6_addr *addrv6 = (struct in6_addr *) addr; | ||||
|  | ||||
|           addrlen = sizeof (*sin6); | ||||
|           memcpy (&sin6->sin6_addr, addrv6, sizeof (sin6->sin6_addr)); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           WSASetLastError (WSAEAFNOSUPPORT); | ||||
|           return NULL; | ||||
|         } | ||||
|       if (WSAAddressToString ((LPSOCKADDR) &sa, addrlen, NULL, addr_str, &buflen) == 0) | ||||
|         return addr_str; | ||||
|       else | ||||
|         return NULL; | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * g_inet_address_new_from_string: | ||||
|  * @string: a string representation of an IP address | ||||
| @@ -383,15 +493,8 @@ g_inet_address_init (GInetAddress *address) | ||||
| GInetAddress * | ||||
| g_inet_address_new_from_string (const gchar *string) | ||||
| { | ||||
| #ifdef G_OS_WIN32 | ||||
|   struct sockaddr_storage sa; | ||||
|   struct sockaddr_in *sin = (struct sockaddr_in *)&sa; | ||||
|   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; | ||||
|   gint len; | ||||
| #else /* !G_OS_WIN32 */ | ||||
|   struct in_addr in_addr; | ||||
|   struct in6_addr in6_addr; | ||||
| #endif | ||||
|  | ||||
|   g_return_val_if_fail (string != NULL, NULL); | ||||
|  | ||||
| @@ -401,35 +504,10 @@ g_inet_address_new_from_string (const gchar *string) | ||||
|    */ | ||||
|   g_networking_init (); | ||||
|  | ||||
| #ifdef G_OS_WIN32 | ||||
|   /* We need to make sure to not pass a string of the form | ||||
|    * "IPv4addr:port" or "[IPv6addr]:port" to WSAStringToAddress(), | ||||
|    * since it would accept them (returning both the address and the | ||||
|    * port), but we only want to accept standalone IP addresses. (In | ||||
|    * the IPv6 case, WINE actually only checks for the ']', not the | ||||
|    * '[', which is why we do the same here.) | ||||
|    */ | ||||
|   if (!strchr (string, ':')) | ||||
|     { | ||||
|       len = sizeof (sa); | ||||
|       if (WSAStringToAddress ((LPTSTR) string, AF_INET, NULL, (LPSOCKADDR) &sa, &len) == 0) | ||||
|         return g_inet_address_new_from_bytes ((guint8 *)&sin->sin_addr, AF_INET); | ||||
|     } | ||||
|  | ||||
|   if (!strchr (string, ']')) | ||||
|     { | ||||
|       len = sizeof (sa); | ||||
|       if (WSAStringToAddress ((LPTSTR) string, AF_INET6, NULL, (LPSOCKADDR) &sa, &len) == 0) | ||||
|         return g_inet_address_new_from_bytes ((guint8 *)&sin6->sin6_addr, AF_INET6); | ||||
|     } | ||||
|  | ||||
| #else /* !G_OS_WIN32 */ | ||||
|  | ||||
|   if (inet_pton (AF_INET, string, &in_addr) > 0) | ||||
|     return g_inet_address_new_from_bytes ((guint8 *)&in_addr, AF_INET); | ||||
|   else if (inet_pton (AF_INET6, string, &in6_addr) > 0) | ||||
|     return g_inet_address_new_from_bytes ((guint8 *)&in6_addr, AF_INET6); | ||||
| #endif | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
| @@ -530,40 +608,13 @@ gchar * | ||||
| g_inet_address_to_string (GInetAddress *address) | ||||
| { | ||||
|   gchar buffer[INET6_ADDRSTRLEN]; | ||||
| #ifdef G_OS_WIN32 | ||||
|   DWORD buflen = sizeof (buffer), addrlen; | ||||
|   struct sockaddr_storage sa; | ||||
|   struct sockaddr_in *sin = (struct sockaddr_in *)&sa; | ||||
|   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; | ||||
| #endif | ||||
|  | ||||
|   g_return_val_if_fail (G_IS_INET_ADDRESS (address), NULL); | ||||
|  | ||||
| #ifdef G_OS_WIN32 | ||||
|   memset (&sa, 0, sizeof (sa)); | ||||
|   sa.ss_family = address->priv->family; | ||||
|   if (address->priv->family == AF_INET) | ||||
|     { | ||||
|       addrlen = sizeof (*sin); | ||||
|       memcpy (&sin->sin_addr, &address->priv->addr.ipv4, | ||||
| 	      sizeof (sin->sin_addr)); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       addrlen = sizeof (*sin6); | ||||
|       memcpy (&sin6->sin6_addr, &address->priv->addr.ipv6, | ||||
| 	      sizeof (sin6->sin6_addr)); | ||||
|     } | ||||
|   if (WSAAddressToString ((LPSOCKADDR) &sa, addrlen, NULL, buffer, &buflen) != 0) | ||||
|     return NULL; | ||||
|  | ||||
| #else /* !G_OS_WIN32 */ | ||||
|  | ||||
|   if (address->priv->family == AF_INET) | ||||
|     inet_ntop (AF_INET, &address->priv->addr.ipv4, buffer, sizeof (buffer)); | ||||
|   else | ||||
|     inet_ntop (AF_INET6, &address->priv->addr.ipv6, buffer, sizeof (buffer)); | ||||
| #endif | ||||
|  | ||||
|   return g_strdup (buffer); | ||||
| } | ||||
|   | ||||
| @@ -22,6 +22,13 @@ | ||||
|  | ||||
| #include "gnetworking.h" | ||||
|  | ||||
| #ifdef G_OS_WIN32 | ||||
| /* For Windows XP run-time compatibility */ | ||||
| #include "gwin32networking.h" | ||||
|  | ||||
| GWin32WinsockFuncs ws2funcs = {0}; | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * SECTION:gnetworking | ||||
|  * @title: gnetworking.h | ||||
| @@ -66,9 +73,39 @@ g_networking_init (void) | ||||
|   if (g_once_init_enter (&inited)) | ||||
|     { | ||||
|       WSADATA wsadata; | ||||
|       HMODULE ws2dll, iphlpapidll; | ||||
|  | ||||
|       if (WSAStartup (MAKEWORD (2, 0), &wsadata) != 0) | ||||
| 	g_error ("Windows Sockets could not be initialized"); | ||||
|         g_error ("Windows Sockets could not be initialized"); | ||||
|  | ||||
|       /* We want to use these functions if they are available, but | ||||
|        * still need to make sure the code still runs on Windows XP | ||||
|        */ | ||||
|       ws2dll = LoadLibraryW (L"ws2_32.dll"); | ||||
|       iphlpapidll = LoadLibraryW (L"iphlpapi.dll"); | ||||
|  | ||||
|       if (ws2dll != NULL) | ||||
|         { | ||||
|           ws2funcs.pInetNtop = | ||||
|             (PFN_InetNtop) GetProcAddress (ws2dll, "inet_ntop"); | ||||
|           ws2funcs.pInetPton = | ||||
|             (PFN_InetPton) GetProcAddress (ws2dll, "inet_pton"); | ||||
|           FreeLibrary (ws2dll); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           ws2funcs.pInetNtop = NULL; | ||||
|           ws2funcs.pInetPton = NULL; | ||||
|         } | ||||
|  | ||||
|       if (iphlpapidll != NULL) | ||||
|         { | ||||
|           ws2funcs.pIfNameToIndex = | ||||
|             (PFN_IfNameToIndex) GetProcAddress (iphlpapidll, "if_nametoindex"); | ||||
|           FreeLibrary (iphlpapidll); | ||||
|         } | ||||
|       else | ||||
|         ws2funcs.pIfNameToIndex = NULL; | ||||
|        | ||||
|       g_once_init_leave (&inited, 1); | ||||
|     } | ||||
|   | ||||
| @@ -64,6 +64,11 @@ | ||||
| #include "gcredentialsprivate.h" | ||||
| #include "glibintl.h" | ||||
|  | ||||
| #ifdef G_OS_WIN32 | ||||
| /* For Windows XP runtime compatibility, but use the system's if_nametoindex() if available */ | ||||
| #include "gwin32networking.h" | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * SECTION:gsocket | ||||
|  * @short_description: Low-level socket object | ||||
| @@ -1938,6 +1943,9 @@ if_nametoindex (const gchar *iface) | ||||
|   guint idx = 0; | ||||
|   DWORD res; | ||||
|  | ||||
|   if (ws2funcs.pIfNameToIndex != NULL) | ||||
|     return ws2funcs.pIfNameToIndex (iface); | ||||
|  | ||||
|   res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, NULL, &addresses_len); | ||||
|   if (res != NO_ERROR && res != ERROR_BUFFER_OVERFLOW) | ||||
|     { | ||||
|   | ||||
							
								
								
									
										42
									
								
								gio/gwin32networking.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								gio/gwin32networking.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /* GIO - GLib Input, Output and Streaming Library | ||||
|  * | ||||
|  * Copyright (C) 2015 Chun-wei Fan | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General | ||||
|  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #ifndef __G_WIN32_NETWORKING_H__ | ||||
| #define __G_WIN32_NETWORKING_H__ | ||||
|  | ||||
| G_BEGIN_DECLS | ||||
|  | ||||
| /* Check if more ANSI-compliant Winsock2 functions are provided */ | ||||
| /* For run-time compatibility with Windows XP, remove when XP support dropped */ | ||||
|  | ||||
| typedef INT (WSAAPI *PFN_InetPton) (INT, PCTSTR, PVOID); | ||||
| typedef PCTSTR (WSAAPI *PFN_InetNtop) (INT, PVOID, PTSTR, size_t); | ||||
| typedef NET_IFINDEX (WINAPI *PFN_IfNameToIndex) (PCSTR); | ||||
|  | ||||
| typedef struct _GWin32WinsockFuncs | ||||
| { | ||||
|   PFN_InetPton pInetPton; | ||||
|   PFN_InetNtop pInetNtop; | ||||
|   PFN_IfNameToIndex pIfNameToIndex; | ||||
| } GWin32WinsockFuncs; | ||||
|  | ||||
| extern GWin32WinsockFuncs ws2funcs; | ||||
|  | ||||
| G_END_DECLS /* __G_WIN32_NETWORKING_H__ */ | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user