[glib/glib-2-42] Windows: Use Standard Networking Functions If Possible



commit 83bf89c39dc7cd9b72f0c5ec1b0e6b4dbd807398
Author: Chun-wei Fan <fanchunwei src gnome org>
Date:   Thu Mar 5 16:13:03 2015 +0800

    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

 gio/Makefile.am        |    1 +
 gio/ginetaddress.c     |  169 +++++++++++++++++++++++++++++++-----------------
 gio/gnetworking.c      |   39 +++++++++++-
 gio/gsocket.c          |    8 ++
 gio/gwin32networking.h |   42 ++++++++++++
 5 files changed, 199 insertions(+), 60 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index e993e2f..22e3315 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -312,6 +312,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 9ff0c44..8723c28 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 30f4d18..c90aa9a 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 4d863ce..5e094c6 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 0000000..086114e
--- /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 <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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]