[glib: 1/2] windows: fix multicast socket binding to specific network interfaces
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 1/2] windows: fix multicast socket binding to specific network interfaces
- Date: Mon, 29 Jul 2019 11:10:57 +0000 (UTC)
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]