Merge branch 'master' into 'master'

windows: fix multicast socket binding to specific network interfaces

Closes #1635

See merge request GNOME/glib!887
This commit is contained in:
Philip Withnall 2019-07-29 11:10:40 +00:00
commit 8f385b8cf5

View File

@ -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;