diff --git a/gio/Makefile.am b/gio/Makefile.am index d0b8d10b9..bfe27c5fa 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -314,6 +314,7 @@ win32_actual_sources = \ gwin32inputstream.c \ gwin32outputstream.c \ gwin32outputstream.h \ + gwin32networking.h \ $(NULL) win32_more_sources_for_vcproj = \ diff --git a/gio/ginetaddress.c b/gio/ginetaddress.c index 9ff0c4417..8723c28fe 100644 --- a/gio/ginetaddress.c +++ b/gio/ginetaddress.c @@ -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); } diff --git a/gio/gnetworking.c b/gio/gnetworking.c index 30f4d18fa..c90aa9a15 100644 --- a/gio/gnetworking.c +++ b/gio/gnetworking.c @@ -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); } diff --git a/gio/gsocket.c b/gio/gsocket.c index f144674a1..06c7235ec 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -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) { diff --git a/gio/gwin32networking.h b/gio/gwin32networking.h new file mode 100644 index 000000000..086114edf --- /dev/null +++ b/gio/gwin32networking.h @@ -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 . + */ + +#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