mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +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:
parent
ec1edef3ab
commit
6fe28eef3c
@ -314,6 +314,7 @@ win32_actual_sources = \
|
|||||||
gwin32inputstream.c \
|
gwin32inputstream.c \
|
||||||
gwin32outputstream.c \
|
gwin32outputstream.c \
|
||||||
gwin32outputstream.h \
|
gwin32outputstream.h \
|
||||||
|
gwin32networking.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
win32_more_sources_for_vcproj = \
|
win32_more_sources_for_vcproj = \
|
||||||
|
@ -31,6 +31,13 @@
|
|||||||
#include "glibintl.h"
|
#include "glibintl.h"
|
||||||
#include "gnetworkingprivate.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
|
struct _GInetAddressPrivate
|
||||||
{
|
{
|
||||||
GSocketFamily family;
|
GSocketFamily family;
|
||||||
@ -369,6 +376,109 @@ g_inet_address_init (GInetAddress *address)
|
|||||||
address->priv = g_inet_address_get_instance_private (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:
|
* g_inet_address_new_from_string:
|
||||||
* @string: a string representation of an IP address
|
* @string: a string representation of an IP address
|
||||||
@ -383,15 +493,8 @@ g_inet_address_init (GInetAddress *address)
|
|||||||
GInetAddress *
|
GInetAddress *
|
||||||
g_inet_address_new_from_string (const gchar *string)
|
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 in_addr in_addr;
|
||||||
struct in6_addr in6_addr;
|
struct in6_addr in6_addr;
|
||||||
#endif
|
|
||||||
|
|
||||||
g_return_val_if_fail (string != NULL, NULL);
|
g_return_val_if_fail (string != NULL, NULL);
|
||||||
|
|
||||||
@ -401,35 +504,10 @@ g_inet_address_new_from_string (const gchar *string)
|
|||||||
*/
|
*/
|
||||||
g_networking_init ();
|
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)
|
if (inet_pton (AF_INET, string, &in_addr) > 0)
|
||||||
return g_inet_address_new_from_bytes ((guint8 *)&in_addr, AF_INET);
|
return g_inet_address_new_from_bytes ((guint8 *)&in_addr, AF_INET);
|
||||||
else if (inet_pton (AF_INET6, string, &in6_addr) > 0)
|
else if (inet_pton (AF_INET6, string, &in6_addr) > 0)
|
||||||
return g_inet_address_new_from_bytes ((guint8 *)&in6_addr, AF_INET6);
|
return g_inet_address_new_from_bytes ((guint8 *)&in6_addr, AF_INET6);
|
||||||
#endif
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -530,40 +608,13 @@ gchar *
|
|||||||
g_inet_address_to_string (GInetAddress *address)
|
g_inet_address_to_string (GInetAddress *address)
|
||||||
{
|
{
|
||||||
gchar buffer[INET6_ADDRSTRLEN];
|
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);
|
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)
|
if (address->priv->family == AF_INET)
|
||||||
inet_ntop (AF_INET, &address->priv->addr.ipv4, buffer, sizeof (buffer));
|
inet_ntop (AF_INET, &address->priv->addr.ipv4, buffer, sizeof (buffer));
|
||||||
else
|
else
|
||||||
inet_ntop (AF_INET6, &address->priv->addr.ipv6, buffer, sizeof (buffer));
|
inet_ntop (AF_INET6, &address->priv->addr.ipv6, buffer, sizeof (buffer));
|
||||||
#endif
|
|
||||||
|
|
||||||
return g_strdup (buffer);
|
return g_strdup (buffer);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,13 @@
|
|||||||
|
|
||||||
#include "gnetworking.h"
|
#include "gnetworking.h"
|
||||||
|
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
/* For Windows XP run-time compatibility */
|
||||||
|
#include "gwin32networking.h"
|
||||||
|
|
||||||
|
GWin32WinsockFuncs ws2funcs = {0};
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:gnetworking
|
* SECTION:gnetworking
|
||||||
* @title: gnetworking.h
|
* @title: gnetworking.h
|
||||||
@ -66,9 +73,39 @@ g_networking_init (void)
|
|||||||
if (g_once_init_enter (&inited))
|
if (g_once_init_enter (&inited))
|
||||||
{
|
{
|
||||||
WSADATA wsadata;
|
WSADATA wsadata;
|
||||||
|
HMODULE ws2dll, iphlpapidll;
|
||||||
|
|
||||||
if (WSAStartup (MAKEWORD (2, 0), &wsadata) != 0)
|
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);
|
g_once_init_leave (&inited, 1);
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,11 @@
|
|||||||
#include "gcredentialsprivate.h"
|
#include "gcredentialsprivate.h"
|
||||||
#include "glibintl.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
|
* SECTION:gsocket
|
||||||
* @short_description: Low-level socket object
|
* @short_description: Low-level socket object
|
||||||
@ -1938,6 +1943,9 @@ if_nametoindex (const gchar *iface)
|
|||||||
guint idx = 0;
|
guint idx = 0;
|
||||||
DWORD res;
|
DWORD res;
|
||||||
|
|
||||||
|
if (ws2funcs.pIfNameToIndex != NULL)
|
||||||
|
return ws2funcs.pIfNameToIndex (iface);
|
||||||
|
|
||||||
res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, NULL, &addresses_len);
|
res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, NULL, &addresses_len);
|
||||||
if (res != NO_ERROR && res != ERROR_BUFFER_OVERFLOW)
|
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
|
Loading…
Reference in New Issue
Block a user