From b39dbdf341752d31ddac9f2e2236070b15cd6743 Mon Sep 17 00:00:00 2001
From: Erich Hoover <ehoover@mines.edu>
Date: Tue, 7 Dec 2010 18:07:38 -0700
Subject: ws2_32: Selectively bind UDP sockets to interfaces while still allowing broadcast packets.

---
 dlls/ws2_32/Makefile.in      |    3 +-
 dlls/ws2_32/socket.c         |   44 +++--
 dlls/ws2_32/socket_private.h |   42 ++++
 dlls/ws2_32/socket_shim.c    |  548 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 621 insertions(+), 16 deletions(-)
 create mode 100644 dlls/ws2_32/socket_private.h
 create mode 100644 dlls/ws2_32/socket_shim.c

diff --git a/dlls/ws2_32/Makefile.in b/dlls/ws2_32/Makefile.in
index 1d7e550..50b647a 100644
--- a/dlls/ws2_32/Makefile.in
+++ b/dlls/ws2_32/Makefile.in
@@ -2,11 +2,12 @@ EXTRADEFS = -DUSE_WS_PREFIX
 MODULE    = ws2_32.dll
 IMPORTLIB = ws2_32
 DELAYIMPORTS = iphlpapi user32
-EXTRALIBS = @LIBPOLL@
+EXTRALIBS = @LIBPOLL@ @LIBPTHREAD@
 
 C_SRCS = \
 	async.c \
 	protocol.c \
+	socket_shim.c \
 	socket.c
 
 RC_SRCS = version.rc
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index f1c689e..80e2f02 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -152,6 +152,8 @@
 #include "wine/exception.h"
 #include "wine/unicode.h"
 
+#include "socket_private.h"
+
 #ifdef HAVE_IPX
 # include "wsnwlink.h"
 #endif
@@ -244,10 +246,6 @@ static inline const char *debugstr_sockaddr( const struct WS_sockaddr *a )
     }
 }
 
-/* HANDLE<->SOCKET conversion (SOCKET is UINT_PTR). */
-#define SOCKET2HANDLE(s) ((HANDLE)(s))
-#define HANDLE2SOCKET(h) ((SOCKET)(h))
-
 /****************************************************************
  * Async IO declarations
  ****************************************************************/
@@ -990,15 +988,19 @@ static inline int get_rcvsnd_timeo( int fd, int optname)
 #endif
 
 /* utility: given an fd, will block until one of the events occurs */
-static inline int do_block( int fd, int events, int timeout )
+static inline int do_block( SOCKET s, int fd, int events, int timeout )
 {
+  WS_fd_set rd_fds;
   struct pollfd pfd;
   int ret;
 
   pfd.fd = fd;
   pfd.events = events;
 
-  while ((ret = poll(&pfd, 1, timeout)) < 0)
+  WS_FD_ZERO(&rd_fds);
+  WS_FD_SET(s, &rd_fds);
+
+  while ((ret = WS2_shim_poll(&rd_fds, &pfd, 1, timeout)) < 0)
   {
       if (errno != EINTR)
           return -1;
@@ -1518,7 +1520,7 @@ static int WS2_recv( int fd, struct ws2_async *wsa )
     hdr.msg_flags = 0;
 #endif
 
-    if ( (n = recvmsg(fd, &hdr, wsa->flags)) == -1 )
+    if ( (n = WS2_shim_recvmsg(HANDLE2SOCKET(wsa->hSocket), fd, &hdr, wsa->flags)) == -1 )
         return -1;
 
 #ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
@@ -1569,6 +1571,16 @@ static NTSTATUS WS2_async_recv( void* user, IO_STATUS_BLOCK* iosb, NTSTATUS stat
         if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_READ_DATA, &fd, NULL ) ))
             break;
 
+        /* Check for packets that caused a false wakeup */
+        if (WS2_ifacefilter_check_mismatch(HANDLE2SOCKET(wsa->hSocket), fd))
+        {
+            WARN("Packet not destined for this interface, thrown away.\n");
+            wine_server_release_fd( wsa->hSocket, fd );
+            status = STATUS_PENDING;
+            _enable_event( wsa->hSocket, FD_READ, 0, 0 );
+            break;
+        }
+
         result = WS2_recv( fd, wsa );
         wine_server_release_fd( wsa->hSocket, fd );
         if (result >= 0)
@@ -1768,7 +1780,7 @@ static int WS2_send( int fd, struct ws2_async *wsa )
     hdr.msg_flags = 0;
 #endif
 
-    return sendmsg(fd, &hdr, wsa->flags);
+    return WS2_shim_sendmsg(HANDLE2SOCKET(wsa->hSocket), fd, &hdr, wsa->flags);
 }
 
 /***********************************************************************
@@ -1929,7 +1941,7 @@ SOCKET WINAPI WS_accept(SOCKET s, struct WS_sockaddr *addr,
         {
             int fd = get_sock_fd( s, FILE_READ_DATA, NULL );
             /* block here */
-            do_block(fd, POLLIN, -1);
+            do_block(s, fd, POLLIN, -1);
             _sync_sock_state(s); /* let wineserver notice connection */
             release_sock_fd( s, fd );
         }
@@ -2136,7 +2148,7 @@ int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen)
                         in4->sin_addr.s_addr = htonl(WS_INADDR_ANY);
                     }
                 }
-                if (bind(fd, &uaddr.addr, uaddrlen) < 0)
+                if (WS2_shim_bind(s, fd, &uaddr.addr, uaddrlen) < 0)
                 {
                     int loc_errno = errno;
                     WARN("\tfailure - errno = %i\n", errno);
@@ -2227,7 +2239,7 @@ int WINAPI WS_connect(SOCKET s, const struct WS_sockaddr* name, int namelen)
             {
                 int result;
                 /* block here */
-                do_block(fd, POLLIN | POLLOUT, -1);
+                do_block(s, fd, POLLIN | POLLOUT, -1);
                 _sync_sock_state(s); /* let wineserver notice connection */
                 /* retrieve any error codes from it */
                 result = _get_sock_error(s, FD_CONNECT_BIT);
@@ -3760,7 +3772,7 @@ int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds,
         gettimeofday( &tv1, 0 );
     }
 
-    while ((ret = poll( pollfds, count, timeout )) < 0)
+    while ((ret = WS2_shim_poll( ws_readfds, pollfds, count, timeout )) < 0)
     {
         if (errno == EINTR)
         {
@@ -3981,7 +3993,7 @@ static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
             pfd.fd = fd;
             pfd.events = POLLOUT;
 
-            if (!timeout || !poll( &pfd, 1, timeout ))
+            if (!timeout || !WS2_shim_poll( NULL, &pfd, 1, timeout ))
             {
                 err = WSAETIMEDOUT;
                 goto error; /* msdn says a timeout in send is fatal */
@@ -5768,6 +5780,7 @@ static int WS2_recv_base( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
 
         if ( _is_blocking(s) )
         {
+            WS_fd_set rd_fds;
             struct pollfd pfd;
             int timeout = GET_RCVTIMEO(fd);
             if (timeout != -1)
@@ -5779,8 +5792,9 @@ static int WS2_recv_base( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
             pfd.fd = fd;
             pfd.events = POLLIN;
             if (*lpFlags & WS_MSG_OOB) pfd.events |= POLLPRI;
-
-            if (!timeout || !poll( &pfd, 1, timeout ))
+            WS_FD_ZERO(&rd_fds);
+            WS_FD_SET(s, &rd_fds);
+            if (!timeout || !WS2_shim_poll( &rd_fds, &pfd, 1, timeout ))
             {
                 err = WSAETIMEDOUT;
                 /* a timeout is not fatal */
diff --git a/dlls/ws2_32/socket_private.h b/dlls/ws2_32/socket_private.h
new file mode 100644
index 0000000..e1b9a29
--- /dev/null
+++ b/dlls/ws2_32/socket_private.h
@@ -0,0 +1,42 @@
+/*
+ * Private socket definitions
+ *
+ * Copyright (C) 2010 Erich Hoover
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WINE_SOCKET_PRIVATE_H
+#define __WINE_SOCKET_PRIVATE_H
+
+int WS2_shim_recvmsg( SOCKET s, int fd, struct msghdr *hdr, int flags );
+int WS2_shim_sendmsg( SOCKET s, int fd, struct msghdr *hdr, int flags );
+int WS2_shim_bind( SOCKET s, int fd, struct sockaddr *addr, socklen_t addrlen );
+int WS2_shim_poll( WS_fd_set *ws_readfds, struct pollfd *pollfds, nfds_t count, int timeout );
+
+int WS2_ifacefilter_check_mismatch( SOCKET sock, int fd );
+
+/* HANDLE<->SOCKET conversion (SOCKET is UINT_PTR). */
+#define SOCKET2HANDLE(s) ((HANDLE)(s))
+#define HANDLE2SOCKET(h) ((SOCKET)(h))
+
+#ifndef WS2_BYPASS_SHIM
+#   define recvmsg ERROR_do_not_call_recvmsg_directly
+#   define sendmsg ERROR_do_not_call_sendmsg_directly
+#   define bind    ERROR_do_not_call_bind_directly
+#   define poll    ERROR_do_not_call_poll_directly
+#endif
+
+#endif /* __WINE_SOCKET_PRIVATE_H */
diff --git a/dlls/ws2_32/socket_shim.c b/dlls/ws2_32/socket_shim.c
new file mode 100644
index 0000000..b888507
--- /dev/null
+++ b/dlls/ws2_32/socket_shim.c
@@ -0,0 +1,548 @@
+/*
+ * A shim for socket calls that permits UDP broadcast packets bound to a
+ * specific interface.
+ *
+ * Copyright (C) 2010 Erich Hoover
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTE: If you make any changes to fix a particular app, make sure
+ * they don't break something else like Netscape or telnet and ftp
+ * clients and servers (www.winsite.com got a lot of those).
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#include <errno.h>
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winsock2.h"
+#include "iphlpapi.h"
+
+#include "wine/server.h"
+#include "wine/debug.h"
+
+#define WS2_BYPASS_SHIM
+#include "socket_private.h"
+
+#if !defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
+# if defined(IP_PKTINFO)
+#  define IP_UDPBRDIF_FILTER IP_PKTINFO
+# endif
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(winsock);
+
+/* Set and retrieve the interface for UDP interface filtered sockets */
+#ifdef IP_UDPBRDIF_FILTER
+static void _set_sock_iface(SOCKET s, int index)
+{
+    SERVER_START_REQ( set_socket_iface )
+    {
+        req->handle = wine_server_obj_handle( SOCKET2HANDLE(s) );
+        req->iface = index;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+
+static int _get_sock_iface(SOCKET s)
+{
+    int ret;
+
+    SERVER_START_REQ( get_socket_iface )
+    {
+        req->handle = wine_server_obj_handle( SOCKET2HANDLE(s) );
+        wine_server_call( req );
+        ret = reply->iface;
+    }
+    SERVER_END_REQ;
+    return ret;
+}
+
+ /***********************************************************************
+ *              WS2_get_pktiface                (INTERNAL)
+ *
+ * check_iface is enabled, so this is a socket that might receive broadcast
+ * packets and it is bound to a particular network interface. Therefore, we
+ * need to check the socket's interface to make sure that the datagram
+ * is meant for this interface and throw it out if it is not.
+ */
+static int WS2_get_pktiface(struct msghdr *hdr)
+{
+    struct cmsghdr *cmsg;
+
+    for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != NULL;
+         cmsg = CMSG_NXTHDR(hdr, cmsg))
+    {
+#if defined(IP_PKTINFO)
+        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
+        {
+            struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+
+            if (pktinfo)
+                return pktinfo->ipi_ifindex;
+        }
+#else
+#error "IP_UDPBRDIF_FILTER is defined incorrectly."
+#endif
+    }
+    WARN("Failed to get interface index for a socket with a filter enabled.\n");
+    return -1;
+}
+
+/***********************************************************************
+ *              WS2_lock_sock                (INTERNAL)
+ *
+ * Place a semaphore lock on the socket so that throwing away packets
+ * cannot be interrupted.
+ */
+static sem_t *WS2_lock_sock(int fd)
+{
+    char lockname[100];
+    struct stat fdinfo;
+    sem_t *lock;
+
+
+    if (fstat(fd, &fdinfo) != 0)
+    {
+        ERR("failed to create socket semaphore\n");
+        return NULL;
+    }
+    snprintf( lockname, sizeof(lockname), "/wine-ifacelock-%d.%d-%ld",
+        major(fdinfo.st_dev), minor(fdinfo.st_dev), (long)fdinfo.st_ino );
+    lock = sem_open( lockname, 0 );
+    if (!lock)
+        return NULL; /* no lock on this file descriptor */
+    sem_wait( lock );
+    return lock;
+}
+
+/***********************************************************************
+ *              WS2_unlock_sock                (INTERNAL)
+ *
+ * Remove the semaphore lock on a socket.
+ */
+static void WS2_unlock_sock(sem_t *lock)
+{
+    if (!lock)
+        return;
+    sem_post( lock );
+    sem_close( lock );
+}
+
+/***********************************************************************
+ *               WS2_fd_sets_with_ifacefilter        (INTERNAL)
+ *
+ * Check to see if any of the read sockets have filtering enabled,
+ * if this option is enabled and "select" is called then it
+ * may cause the application to attempt to collect data on an
+ * interface for which the socket is not bound (which will fail).
+ */
+static int WS2_fd_sets_with_ifacefilter( struct pollfd *fds, int count )
+{
+    unsigned int i, check_iface, optlen;
+    int sets = 0;
+
+    optlen = sizeof(check_iface);
+    for (i = 0; i < count; i++)
+    {
+        if (fds[i].events == POLLIN)
+        {
+            int fd = fds[i].fd;
+
+            check_iface = 0;
+            getsockopt(fd, IPPROTO_IP, IP_UDPBRDIF_FILTER, (void *) &check_iface, &optlen);
+            if (check_iface)
+                sets++;
+        }
+    }
+    return sets;
+}
+
+/***********************************************************************
+ *               WS2_check_ifacefilter_fds        (INTERNAL)
+ *
+ * A poll succeeded, if any of the sockets have filtering enabled
+ * then peek the data from that socket and throw the packet away
+ * if there is an interface mismatch.
+ */
+static int WS2_check_ifacefilter_fds( WS_fd_set *readfds, struct pollfd *fds, int count )
+{
+    unsigned int i;
+    int events = 0;
+
+    for (i = 0; i < count; i++)
+    {
+        if (fds[i].events == POLLIN && fds[i].revents)
+        {
+            SOCKET sock = readfds->fd_array[i];
+            int fd = fds[i].fd;
+
+            if (WS2_ifacefilter_check_mismatch(sock, fd))
+            {
+                WARN("Packet not destined for this interface, thrown away.\n");
+                fds[i].revents = 0;
+            }
+            else
+                events++;
+        }
+        else
+        {
+            if (fds[i].revents)
+                events++;
+        }
+    }
+    return (events == 0);
+}
+
+#endif /* IP_UDPBRDIF_FILTER */
+
+/***********************************************************************
+ *              WS2_shim_recvmsg                (INTERNAL)
+ *
+ * Properly lock and filter UDP interace-specific packets.
+ */
+int WS2_shim_recvmsg( SOCKET s, int fd, struct msghdr *hdr, int flags )
+{
+#ifdef IP_UDPBRDIF_FILTER
+    unsigned int check_iface = 0, optlen;
+    int correct_iface = FALSE;
+    int iface_index = 0;
+    sem_t *lock = NULL;
+    int n;
+
+    /* Note: WS2_shim_recvmsg must be called with a control message buffer */
+    assert(hdr->msg_control != NULL);
+
+    optlen = sizeof(check_iface);
+    getsockopt(fd, IPPROTO_IP, IP_UDPBRDIF_FILTER, (void *) &check_iface, &optlen);
+    if (check_iface)
+    {
+        iface_index = _get_sock_iface(s);
+        check_iface &= (iface_index != -1);
+    }
+    /* If interface filtering is enabled then throw out any packet that is
+     * not meant for the interface associated with this socket.
+     */
+    lock = WS2_lock_sock(fd);
+    while (!correct_iface)
+    {
+        if ( (n = recvmsg(fd, hdr, flags)) == -1 )
+            goto cleanup;
+        correct_iface = TRUE;
+        if (check_iface)
+        {
+            int packet_index = WS2_get_pktiface(hdr);
+
+            if(packet_index != -1 && packet_index != iface_index)
+            {
+                correct_iface = FALSE;
+                WARN("Packet not destined for this interface (%d != %d), thrown away.\n", packet_index, iface_index);
+            }
+        }
+    }
+cleanup:
+    WS2_unlock_sock(lock);
+    return n;
+#else
+    return recvmsg(fd, hdr, flags);
+#endif /* IP_UDPBRDIF_FILTER */
+}
+
+/***********************************************************************
+ *              WS2_shim_sendmsg                (INTERNAL)
+ *
+ * Set the suggested outgoing interface for filtered packets.
+ */
+int WS2_shim_sendmsg( SOCKET s, int fd, struct msghdr *hdr, int flags )
+{
+#ifdef IP_UDPBRDIF_FILTER
+    unsigned int force_iface = 0, optlen;
+    char *buf = NULL, pktbuf[512];
+    unsigned int buflen = 0;
+    int iface_index;
+
+    hdr->msg_control = pktbuf;
+    hdr->msg_controllen = sizeof(pktbuf);
+
+    optlen = sizeof(force_iface);
+    getsockopt(fd, IPPROTO_IP, IP_UDPBRDIF_FILTER, (void *) &force_iface, &optlen);
+    iface_index = _get_sock_iface(s);
+    force_iface &= (iface_index != -1);
+
+    /* If force_iface is enabled then this is a socket that might send broadcast
+     * packets and is bound to a particular network interface. Therefore, we
+     * must setup the packet routing to use the correct interface.
+     */
+    if (force_iface)
+    {
+#if defined(IP_PKTINFO)
+        /* IP_PKTINFO allows sending all packets through a specific interface */
+        struct in_pktinfo pktinfo;
+        struct cmsghdr *cmsg;
+
+        memset(&pktinfo, 0, sizeof(pktinfo));
+        pktinfo.ipi_ifindex = iface_index;
+        cmsg = CMSG_FIRSTHDR(hdr);
+        cmsg->cmsg_level = IPPROTO_IP;
+        cmsg->cmsg_type = IP_PKTINFO;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(pktinfo));
+        memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo));
+        buf = pktbuf;
+        buflen = cmsg->cmsg_len;
+#else
+#error "IP_UDPBRDIF_FILTER is defined incorrectly."
+#endif
+    }
+    else
+        buf = NULL;
+
+    hdr->msg_control = buf;
+    hdr->msg_controllen = buflen;
+    hdr->msg_flags = 0;
+#endif /* IP_UDPBRDIF_FILTER */
+    return sendmsg(fd, hdr, flags);
+}
+
+/***********************************************************************
+ *               WS2_shim_bind          (INTERNAL)
+ *
+ * Replace any interface bind with a UDP interface-specific filter.
+ */
+int WS2_shim_bind( SOCKET s, int fd, struct sockaddr *addr, socklen_t addrlen )
+{
+#ifdef IP_UDPBRDIF_FILTER
+    struct sockaddr_in *in_sock = (struct sockaddr_in *) addr;
+    PIP_ADAPTER_INFO adapters = NULL, adapter;
+    unsigned int sock_type = 0, optlen;
+    DWORD adap_size;
+
+    if (addr->sa_family != AF_INET)
+    {
+        /* Not an IPv4 networking socket */
+        goto cleanup;
+    }
+    if (in_sock->sin_addr.s_addr == htonl(WS_INADDR_ANY))
+    {
+        /* Not binding to specific interface, uses default route */
+        goto cleanup;
+    }
+    optlen = sizeof(sock_type);
+    if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &optlen) == -1
+        || sock_type != SOCK_DGRAM)
+    {
+        /* Filtering is only valid for UDP datagrams. */
+        goto cleanup;
+    }
+    /* Obtain the size of the IPv4 adapter list, also allocate memory */
+    if (GetAdaptersInfo(NULL, &adap_size) != ERROR_BUFFER_OVERFLOW)
+    {
+        FIXME("Failed to get the size of the adapter list, bound broadcast packets will not work on this socket.\n");
+        goto cleanup;
+    }
+    adapters = HeapAlloc(GetProcessHeap(), 0, adap_size);
+    if (adapters == NULL)
+    {
+        FIXME("Failed to allocate the adapter list, bound broadcast packets will not work on this socket.\n");
+        goto cleanup;
+    }
+    /* Obtain the IPv4 adapter list */
+    if (GetAdaptersInfo(adapters, &adap_size) != NO_ERROR)
+    {
+        FIXME("Failed to obtain the adapter list, bound broadcast packets will not work on this socket.\n");
+        goto cleanup;
+    }
+    /* Search the IPv4 adapter list for the appropriate binding IP */
+    for (adapter = adapters; adapter != NULL; adapter = adapter->Next)
+    {
+        char *ip = adapter->IpAddressList.IpAddress.String;
+        in_addr_t adapter_addr = (in_addr_t) inet_addr(ip);
+
+        if (in_sock->sin_addr.s_addr == adapter_addr)
+        {
+            int enable = 1;
+
+            if (setsockopt(fd, IPPROTO_IP, IP_UDPBRDIF_FILTER, &enable, sizeof(enable)) < 0)
+            {
+                /* If we are unable to enable interface filtering then we cannot
+                 * switch over to INADDR_ANY or we will handle packets destined
+                 * for the wrong interfaces.
+                 */
+                FIXME("setsockopt: setting interface filter failed, broadcast packets will not work on this socket.\n");
+            }
+            else
+            {
+                /* Success! Store the interface ID to the socket for proper
+                 * filtering later on and bind this socket to INADDR_ANY.
+                 */
+                TRACE("Bind to local address %s corresponds to interface %d.\n", inet_ntoa(in_sock->sin_addr), adapter->Index);
+                _set_sock_iface(s, adapter->Index);
+                in_sock->sin_addr.s_addr = htonl(WS_INADDR_ANY);
+                addr = (struct sockaddr *) in_sock;
+            }
+            break;
+        }
+    }
+
+cleanup:
+    HeapFree(GetProcessHeap(), 0, adapters);
+#endif /* IP_UDPBRDIF_FILTER */
+    return bind(fd, addr, addrlen);
+}
+
+/***********************************************************************
+ *               WS2_shim_poll          (INTERNAL)
+ *
+ * Do not wake up on interface filtered packets when the packet is for
+ * the incorrect interface.
+ */
+int WS2_shim_poll( WS_fd_set *ws_readfds, struct pollfd *pollfds, nfds_t count, int timeout )
+{
+#ifdef IP_UDPBRDIF_FILTER
+    struct timeval tv1, tv2;
+    int torig = 0;
+    int ret;
+
+    unsigned int check_iface = FALSE;
+    unsigned int noevents = FALSE;
+
+    if (ws_readfds != NULL)
+        check_iface = WS2_fd_sets_with_ifacefilter( pollfds, count );
+
+    if (timeout > 0)
+    {
+        torig = timeout;
+        gettimeofday( &tv1, 0 );
+    }
+
+    do
+    {
+        if ((ret = poll(pollfds, count, timeout)) < 0)
+            break;
+        if (check_iface != 0 && ret > 0)
+            noevents = WS2_check_ifacefilter_fds( ws_readfds, pollfds, count );
+        if (timeout < 0) continue;
+        gettimeofday( &tv2, 0 );
+
+        tv2.tv_sec  -= tv1.tv_sec;
+        tv2.tv_usec -= tv1.tv_usec;
+        if (tv2.tv_usec < 0)
+        {
+            tv2.tv_usec += 1000000;
+            tv2.tv_sec  -= 1;
+        }
+
+        timeout = torig - (tv2.tv_sec * 1000) - (tv2.tv_usec + 999) / 1000;
+        if (timeout <= 0) break;
+    } while (noevents);
+    return ret;
+#else
+    return poll(pollfds, count, timeout);
+#endif /* IP_UDPBRDIF_FILTER */
+}
+
+/***********************************************************************
+ *              WS2_ifacefilter_check_mismatch         (INTERNAL)
+ *
+ * Use a MSG_PEEK to check for packets that do not match the interface
+ * set in the interface filter.  This check is intended to occur
+ * before a select or an asynchronous read so that the packet does not
+ * cause a spurious wakeup.  So, if there is a mismatch then throw the
+ * packet away.
+ */
+int WS2_ifacefilter_check_mismatch( SOCKET sock, int fd )
+{
+#ifdef IP_UDPBRDIF_FILTER
+    unsigned int check_iface, optlen;
+    struct msghdr hdr;
+    char pktbuf[512];
+    int ret = FALSE;
+
+    memset(&hdr, 0, sizeof(struct msghdr));
+    hdr.msg_control = pktbuf;
+    hdr.msg_controllen = sizeof(pktbuf);
+    hdr.msg_flags = 0;
+
+    check_iface = 0;
+    optlen = sizeof(check_iface);
+    getsockopt(fd, IPPROTO_IP, IP_UDPBRDIF_FILTER, (void *) &check_iface, &optlen);
+    if (check_iface)
+    {
+        sem_t *lock = NULL;
+        int iface_index;
+
+        if ((iface_index = _get_sock_iface(sock)) == -1)
+            return FALSE;
+        lock = WS2_lock_sock(fd);
+        if ( recvmsg(fd, &hdr, MSG_PEEK|MSG_DONTWAIT) != -1 )
+        {
+            int packet_index = WS2_get_pktiface(&hdr);
+
+            if (packet_index != -1 && packet_index != iface_index)
+            {
+                if ( recvmsg(fd, &hdr, MSG_DONTWAIT) != -1 )
+                    ret = TRUE;
+                else
+                    WARN("Failed to throw away packet with interface mismatch! %m\n");
+            }
+        }
+        else
+            WARN("Failed to peek at message header! %m\n");
+        WS2_unlock_sock(lock);
+    }
+    return ret;
+#else
+    return FALSE;
+#endif
+}
+
-- 
1.7.1

