[glib: 1/2] windows: fix multicast socket binding to specific network interfaces



commit 2b1a9219f1df72492f7975cd11f6e99bf655f25f
Author: Riccardo Bortolato <r bortolato sitepitalia it>
Date:   Mon Jun 3 11:37:57 2019 +0200

    windows: fix multicast socket binding to specific network interfaces
    
    v7, based on a patch by mrgard (GNOME/glib#1635)
    make w32_adapter_ipv4_addr() C90-compliant
    check for ERROR_BUFFER_OVERFLOW when calling GetAdaptersAddresses()
    code-style fixes
    indentation fixes
    use g_try_(re)alloc and g_free
    style suggestions by pwithnall
    drop uni_count variable
    cap maximum allowed interface name string length according to windows documentation
    
    Fixes: #1635

 gio/gsocket.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 119 insertions(+), 13 deletions(-)
---
diff --git a/gio/gsocket.c b/gio/gsocket.c
index 3cdce1053..34df3513c 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -2162,6 +2162,122 @@ g_socket_bind (GSocket         *socket,
   return TRUE;
 }
 
+#ifdef G_OS_WIN32
+static gulong
+g_socket_w32_get_adapter_ipv4_addr (const gchar *name_or_ip)
+{
+  ULONG bufsize = 15000; /* MS-recommended initial bufsize */
+  DWORD ret = ERROR_BUFFER_OVERFLOW;
+  unsigned int malloc_iterations = 0;
+  PIP_ADAPTER_ADDRESSES addr_buf = NULL, eth_adapter;
+  wchar_t *wchar_name_or_ip = NULL;
+  gulong ip_result;
+  NET_IFINDEX if_index;
+
+  /*
+   * For Windows OS only - return adapter IPv4 address in network byte order.
+   *
+   * Input string can be either friendly name of adapter, IP address of adapter,
+   * indextoname, or fullname of adapter.
+   * Example:
+   *    192.168.1.109   ===> IP address given directly,
+   *                         convert directly with inet_addr() function
+   *    Wi-Fi           ===> Adapter friendly name "Wi-Fi",
+   *                         scan with GetAdapterAddresses and adapter->FriendlyName
+   *    ethernet_32774  ===> Adapter name as returned by if_indextoname
+   *    {33E8F5CD-BAEA-4214-BE13-B79AB8080CAB} ===> Adaptername,
+   *                         as returned in GetAdapterAddresses and adapter->AdapterName
+   */
+
+  /* Step 1: Check if string is an IP address: */
+  ip_result = inet_addr (name_or_ip);
+  if (ip_result != INADDR_NONE)
+    return ip_result;  /* Success, IP address string was given directly */
+
+  /*
+   *  Step 2: Check if name represents a valid Interface index (e.g. ethernet_75521)
+   *  function if_nametoindex will return >=1 if a valid index, or 0=no match
+   *  valid index will be used later in GetAdaptersAddress loop for lookup of adapter IP address
+   */
+  if_index = if_nametoindex (name_or_ip);
+
+  /* Step 3: Prepare wchar string for friendly name comparision */
+  if (if_index == 0)
+    {
+      size_t if_name_len = strlen (name_or_ip);
+      if (if_name_len >= MAX_ADAPTER_NAME_LENGTH + 4)
+        return INADDR_NONE;
+      /* Name-check only needed if index=0... */
+      wchar_name_or_ip = (wchar_t *) g_try_malloc ((if_name_len + 1) * sizeof(wchar_t));
+      if (wchar_name_or_ip)
+        mbstowcs (wchar_name_or_ip, name_or_ip, if_name_len + 1);
+      /* NOTE: Even if malloc fails here, some comparisions can still be done later... so no exit here! */
+    }
+
+  /*
+   *  Step 4: Allocate memory and get adapter addresses.
+   *  Buffer allocation loop recommended by MS, since size can be dynamic
+   *  https://docs.microsoft.com/en-us/windows/desktop/api/iphlpapi/nf-iphlpapi-getadaptersaddresses
+   */
+  #define MAX_ALLOC_ITERATIONS 3
+  do
+    {
+      malloc_iterations++;
+      addr_buf = (PIP_ADAPTER_ADDRESSES) g_try_realloc (addr_buf, bufsize);
+      if (addr_buf)
+        ret = GetAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, addr_buf, &bufsize);
+    }
+  while (addr_buf &&
+           ret == ERROR_BUFFER_OVERFLOW &&
+           malloc_iterations < MAX_ALLOC_ITERATIONS);
+  #undef MAX_ALLOC_ITERATIONS
+
+  if (addr_buf == 0 || ret != NO_ERROR)
+    {
+      g_free (addr_buf);
+      g_free (wchar_name_or_ip);
+      return INADDR_NONE;
+    }
+
+  /* Step 5: Loop through adapters and check match for index or name */
+  for (eth_adapter = addr_buf; eth_adapter != NULL; eth_adapter = eth_adapter->Next)
+    {
+      /* Check if match for interface index/name: */
+      gboolean any_match = (if_index > 0) && (eth_adapter->IfIndex == if_index);
+
+      /* Check if match for friendly name - but only if NO if_index! */
+      if (!any_match && if_index == 0 && eth_adapter->FriendlyName &&
+          eth_adapter->FriendlyName[0] != 0 && wchar_name_or_ip != NULL)
+        any_match = (_wcsicmp (eth_adapter->FriendlyName, wchar_name_or_ip) == 0);
+
+      /* Check if match for adapter low level name - but only if NO if_index: */
+      if (!any_match && if_index == 0 && eth_adapter->AdapterName &&
+          eth_adapter->AdapterName[0] != 0)
+        any_match = (stricmp (eth_adapter->AdapterName, name_or_ip) == 0);
+
+      if (any_match)
+        {
+          /* We have match for this adapter, lets get its local unicast IP address! */
+          PIP_ADAPTER_UNICAST_ADDRESS uni_addr;
+          for (uni_addr = eth_adapter->FirstUnicastAddress;
+              uni_addr != NULL; uni_addr = uni_addr->Next)
+            {
+              if (uni_addr->Address.lpSockaddr->sa_family == AF_INET)
+                {
+                  ip_result = ((PSOCKADDR_IN) uni_addr->Address.lpSockaddr)->sin_addr.S_un.S_addr;
+                  break; /* finished, exit unicast addr loop */
+                }
+            }
+        }
+    }
+
+  g_free (addr_buf);
+  g_free (wchar_name_or_ip);
+
+  return ip_result;
+}
+#endif
+
 static gboolean
 g_socket_multicast_group_operation (GSocket       *socket,
                                    GInetAddress  *group,
@@ -2199,7 +2315,7 @@ g_socket_multicast_group_operation (GSocket       *socket,
         mc_req.imr_ifindex = 0;  /* Pick any.  */
 #elif defined(G_OS_WIN32)
       if (iface)
-        mc_req.imr_interface.s_addr = g_htonl (if_nametoindex (iface));
+        mc_req.imr_interface.s_addr = g_socket_w32_get_adapter_ipv4_addr (iface);
       else
         mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
 #else
@@ -2398,18 +2514,8 @@ g_socket_multicast_group_operation_ssm (GSocket       *socket,
 
         if (iface)
           {
-#if defined(G_OS_WIN32) && defined (HAVE_IF_NAMETOINDEX)
-            guint iface_index = if_nametoindex (iface);
-            if (iface_index == 0)
-              {
-                int errsv = errno;
-
-                g_set_error (error, G_IO_ERROR,  g_io_error_from_errno (errsv),
-                             _("Interface not found: %s"), g_strerror (errsv));
-                return FALSE;
-              }
-            /* (0.0.0.iface_index) only works on Windows. */
-            S_ADDR_FIELD(mc_req_src) = g_htonl (iface_index);
+#if defined(G_OS_WIN32)
+            S_ADDR_FIELD(mc_req_src) = g_socket_w32_get_adapter_ipv4_addr (iface);
 #elif defined (HAVE_SIOCGIFADDR)
             int ret;
             struct ifreq ifr;


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