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;