In order to avoid accessing iphlpapi.
We will need to move this code to ntdll or the server, but we can't access iphlpapi from either. Even accessing a lower-level interface (i.e. ndis) is hard—we have no good way to enumerate ndis devices from either (without duplicating large parts of setupapi).
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- In terms of ws2_32 complexity, this code paths seems no more complex, or even simpler. However, it omits the fallback path using SIOCGIFCON, making getifaddrs() a hard requirement for UDP binding. I don't know what if anything this risks losing support for, but current versions of all major operating systems seem to support it (BSD, Linux, Mac, Android), and we're gated on IP_BOUND_IF or IP_UNICAST_IF anyway.
v2: Intentionally link to and call the iphlpapi if_nametoindex(), which is a simple wrapper around libc anyway, by redefining the unix version. Eventually this code will be moved out of ws2_32 and this step will be unnecessary.
dlls/ws2_32/socket.c | 79 ++++++++++++++++++++---------------- dlls/ws2_32/ws2_32_private.h | 8 ++++ 2 files changed, 51 insertions(+), 36 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index bfe5ab186ee..ad2e3a1d683 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1975,62 +1975,65 @@ static int WINAPI WS2_WSARecvMsg( SOCKET s, LPWSAMSG msg, LPDWORD lpNumberOfByte */ static BOOL interface_bind( SOCKET s, int fd, struct sockaddr *addr ) { +#if defined(HAVE_GETIFADDRS) && (defined(IP_BOUND_IF) || defined(LINUX_BOUND_IF)) struct sockaddr_in *in_sock = (struct sockaddr_in *) addr; in_addr_t bind_addr = in_sock->sin_addr.s_addr; - PIP_ADAPTER_INFO adapters = NULL, adapter; + struct ifaddrs *ifaddrs, *ifaddr; + unsigned int index; BOOL ret = FALSE; - DWORD adap_size; int enable = 1;
if (bind_addr == htonl(INADDR_ANY) || bind_addr == htonl(INADDR_LOOPBACK)) return FALSE; /* Not binding to a network adapter, special interface binding unnecessary. */ if (_get_fd_type(fd) != SOCK_DGRAM) return FALSE; /* Special interface binding is only necessary for UDP datagrams. */ - if (GetAdaptersInfo(NULL, &adap_size) != ERROR_BUFFER_OVERFLOW) - goto cleanup; - adapters = HeapAlloc(GetProcessHeap(), 0, adap_size); - if (adapters == NULL || GetAdaptersInfo(adapters, &adap_size) != NO_ERROR) - goto cleanup; - /* Search the IPv4 adapter list for the appropriate binding interface */ - for (adapter = adapters; adapter != NULL; adapter = adapter->Next) - { - in_addr_t adapter_addr = (in_addr_t) inet_addr(adapter->IpAddressList.IpAddress.String);
- if (bind_addr == adapter_addr) + if (getifaddrs( &ifaddrs ) < 0) return FALSE; + + for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) + { + if (ifaddr->ifa_addr && ifaddr->ifa_addr->sa_family == AF_INET + && ((struct sockaddr_in *)ifaddr->ifa_addr)->sin_addr.s_addr == bind_addr) { + index = if_nametoindex( ifaddr->ifa_name ); + if (!index) + { + ERR( "Unable to look up interface index for %s: %s\n", ifaddr->ifa_name, strerror( errno ) ); + continue; + } + #if defined(IP_BOUND_IF) /* IP_BOUND_IF sets both the incoming and outgoing restriction at once */ - if (setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &adapter->Index, sizeof(adapter->Index)) != 0) + if (setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &index, sizeof(index)) != 0) goto cleanup; ret = TRUE; #elif defined(LINUX_BOUND_IF) - in_addr_t ifindex = (in_addr_t) htonl(adapter->Index); - struct interface_filter specific_interface_filter; - struct sock_fprog filter_prog; + { + in_addr_t ifindex = (in_addr_t) htonl(index); + struct interface_filter specific_interface_filter; + struct sock_fprog filter_prog;
- if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)) != 0) - goto cleanup; /* Failed to suggest egress interface */ - specific_interface_filter = generic_interface_filter; - specific_interface_filter.iface_rule.k = adapter->Index; - specific_interface_filter.ip_rule.k = htonl(adapter_addr); - filter_prog.len = sizeof(generic_interface_filter)/sizeof(struct sock_filter); - filter_prog.filter = (struct sock_filter *) &specific_interface_filter; - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) != 0) - goto cleanup; /* Failed to specify incoming packet filter */ - ret = TRUE; -#else - FIXME("Broadcast packets on interface-bound sockets are not currently supported on this platform, " - "receiving broadcast packets will not work on socket %04lx.\n", s); + if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)) != 0) + goto cleanup; /* Failed to suggest egress interface */ + specific_interface_filter = generic_interface_filter; + specific_interface_filter.iface_rule.k = index; + specific_interface_filter.ip_rule.k = htonl(bind_addr); + filter_prog.len = sizeof(generic_interface_filter)/sizeof(struct sock_filter); + filter_prog.filter = (struct sock_filter *) &specific_interface_filter; + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) != 0) + goto cleanup; /* Failed to specify incoming packet filter */ + ret = TRUE; + } #endif if (ret) { EnterCriticalSection(&cs_if_addr_cache); - if (if_addr_cache_size <= adapter->Index) + if (if_addr_cache_size <= index) { unsigned int new_size; in_addr_t *new;
- new_size = max(if_addr_cache_size * 2, adapter->Index + 1); + new_size = max(if_addr_cache_size * 2, index + 1); if (!(new = heap_realloc(if_addr_cache, sizeof(*if_addr_cache) * new_size))) { ERR("No memory.\n"); @@ -2043,10 +2046,10 @@ static BOOL interface_bind( SOCKET s, int fd, struct sockaddr *addr ) if_addr_cache = new; if_addr_cache_size = new_size; } - if (if_addr_cache[adapter->Index] && if_addr_cache[adapter->Index] != adapter_addr) - WARN("Adapter addr for iface index %u has changed.\n", adapter->Index); + if (if_addr_cache[index] && if_addr_cache[index] != bind_addr) + WARN("Adapter addr for iface index %u has changed.\n", index);
- if_addr_cache[adapter->Index] = adapter_addr; + if_addr_cache[index] = bind_addr; LeaveCriticalSection(&cs_if_addr_cache); } break; @@ -2054,15 +2057,19 @@ static BOOL interface_bind( SOCKET s, int fd, struct sockaddr *addr ) } /* Will soon be switching to INADDR_ANY: permit address reuse */ if (ret && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == 0) - TRACE("Socket %04lx bound to interface index %d\n", s, adapter->Index); + TRACE("Socket %04lx bound to interface index %d\n", s, index); else ret = FALSE;
cleanup: if(!ret) ERR("Failed to bind to interface, receiving broadcast packets will not work on socket %04lx.\n", s); - HeapFree(GetProcessHeap(), 0, adapters); + freeifaddrs( ifaddrs ); return ret; +#else + FIXME( "Broadcast packets on interface-bound sockets are not currently supported on this platform.\n" ); + return FALSE; +#endif }
/*********************************************************************** diff --git a/dlls/ws2_32/ws2_32_private.h b/dlls/ws2_32/ws2_32_private.h index 527dbe903b4..c27c697a8b6 100644 --- a/dlls/ws2_32/ws2_32_private.h +++ b/dlls/ws2_32/ws2_32_private.h @@ -83,11 +83,18 @@ # include <resolv.h> #endif #ifdef HAVE_NET_IF_H +# define if_indextoname unix_if_indextoname +# define if_nametoindex unix_if_nametoindex # include <net/if.h> +# undef if_indextoname +# undef if_nametoindex #endif #ifdef HAVE_LINUX_FILTER_H # include <linux/filter.h> #endif +#ifdef HAVE_IFADDRS_H +# include <ifaddrs.h> +#endif
#ifdef HAVE_NETIPX_IPX_H # include <netipx/ipx.h> @@ -144,6 +151,7 @@ #include "winnt.h" #define USE_WC_PREFIX /* For CMSG_DATA */ #include "iphlpapi.h" +#include "netioapi.h" #include "ip2string.h" #include "wine/afd.h" #include "wine/server.h"