From 8fb178f61d6bd0de12279b20cc7914d8c703b7e3 Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Tue, 11 Oct 2011 08:20:30 -0600 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 | 34 ++-- dlls/ws2_32/socket_private.h | 40 +++ dlls/ws2_32/socket_shim.c | 544 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 605 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 7a1947f..b8d9d72 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 @@ -1769,7 +1771,7 @@ static int WS2_send( int fd, struct ws2_async *wsa ) hdr.msg_flags = 0; #endif - ret = sendmsg(fd, &hdr, wsa->flags); + ret = WS2_shim_sendmsg(HANDLE2SOCKET(wsa->hSocket), fd, &hdr, wsa->flags); if (ret >= 0) { n = ret; @@ -1941,7 +1943,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 ); } @@ -2152,7 +2154,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); @@ -2243,7 +2245,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); @@ -3736,7 +3738,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) { @@ -3951,7 +3953,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 */ @@ -5778,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) @@ -5789,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..55cd60a --- /dev/null +++ b/dlls/ws2_32/socket_private.h @@ -0,0 +1,40 @@ +/* + * 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 ); + +/* 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..e520f83 --- /dev/null +++ b/dlls/ws2_32/socket_shim.c @@ -0,0 +1,544 @@ +/* + * 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 +#include +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NET_IF_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#include +#ifdef HAVE_SYS_ERRNO_H +#include +#endif + +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#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_IFACEFILTER IP_PKTINFO +# endif +#endif + +WINE_DEFAULT_DEBUG_CHANNEL(winsock); + +/* Set and retrieve the interface for UDP interface filtered sockets */ +#ifdef IP_IFACEFILTER +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_IFACEFILTER 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_IFACEFILTER, (void *) &check_iface, &optlen); + if (check_iface) + sets++; + } + } + return sets; +} + +/*********************************************************************** + * 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. + */ +static int WS2_ifacefilter_check_mismatch( SOCKET sock, int fd ) +{ + 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_IFACEFILTER, (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; +} + +/*********************************************************************** + * 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_IFACEFILTER */ + +/*********************************************************************** + * 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_IFACEFILTER + 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_IFACEFILTER, (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_IFACEFILTER */ +} + +/*********************************************************************** + * 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_IFACEFILTER + 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_IFACEFILTER, (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_IFACEFILTER is defined incorrectly." +#endif + } + else + buf = NULL; + + hdr->msg_control = buf; + hdr->msg_controllen = buflen; + hdr->msg_flags = 0; +#endif /* IP_IFACEFILTER */ + 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_IFACEFILTER + 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_IFACEFILTER, &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_IFACEFILTER */ + 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_IFACEFILTER + 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_IFACEFILTER */ +} + -- 1.7.1