From 8d1e9aa141c18295b5bd3afdc58fe2618451a297 Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Sat, 8 May 2010 16:13:45 -0600 Subject: ws2_32: Ensure select does not wake up on packets with an interface mismatch. --- dlls/ws2_32/socket.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 123 insertions(+), 0 deletions(-) diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 144ec7a..8647c8c 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1426,6 +1426,52 @@ struct in_pktinfo *WS2_get_pktinfo(struct msghdr *hdr) #endif /*********************************************************************** + * WS2_pktinfo_check_mismatch (INTERNAL) + * + * Use a MSG_PEEK to check for packets that do not match the interface + * set in the IP_PKTINFO 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. + */ +#ifdef USE_IP_PKTINFO_FILTER +static int WS2_pktinfo_check_mismatch( SOCKET sock, int fd ) +{ + unsigned int check_iface, optlen; + struct msghdr hdr; + char pktbuf[512]; + + 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_PKTINFO, (void *) &check_iface, &optlen); + if (check_iface) + { + if ( recvmsg(fd, &hdr, MSG_PEEK|MSG_DONTWAIT) != -1 ) + { + struct in_pktinfo *pktinfo = WS2_get_pktinfo(&hdr); + int iface_index = _get_sock_iface(sock); + + if (pktinfo && pktinfo->ipi_ifindex != iface_index) + { + if ( recvmsg(fd, &hdr, MSG_DONTWAIT) != -1 ) + return TRUE; + else + WARN("Failed to throw away packet with interface mismatch! %m\n"); + } + } + else + WARN("Failed to peek at message header! %m\n"); + } + return FALSE; +} +#endif + +/*********************************************************************** * WS2_recv (INTERNAL) * * Workhorse for both synchronous and asynchronous recv() operations. @@ -3144,6 +3190,69 @@ static struct pollfd *fd_sets_to_poll( const WS_fd_set *readfds, const WS_fd_set return fds; } +/* Check to see if any of the read sockets have IP_PKTINFO 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). + */ +#ifdef USE_IP_PKTINFO_FILTER +static int fd_sets_with_pktinfo( 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_PKTINFO, (void *) &check_iface, &optlen); + if (check_iface) + sets++; + } + } + return sets; +} +#endif + +/* A poll succeeded, if any of the sockets have IP_PKTINFO enabled + * then peek the data from that socket and throw the packet away + * if there is an interface mismatch. + */ +#ifdef USE_IP_PKTINFO_FILTER +static int check_pktinfo_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_pktinfo_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 + /* release the file descriptor obtained in fd_sets_to_poll */ /* must be called with the original fd_set arrays, before calling get_poll_results */ static void release_poll_fds( const WS_fd_set *readfds, const WS_fd_set *writefds, @@ -3211,6 +3320,10 @@ int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds, WS_fd_set *ws_writefds, WS_fd_set *ws_exceptfds, const struct WS_timeval* ws_timeout) { +#ifdef USE_IP_PKTINFO_FILTER + unsigned int check_iface = 0; + unsigned int noevents = FALSE; +#endif struct pollfd *pollfds; struct timeval tv1, tv2; int torig = 0; @@ -3225,6 +3338,10 @@ int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds, return SOCKET_ERROR; } +#ifdef USE_IP_PKTINFO_FILTER + check_iface = fd_sets_with_pktinfo( pollfds, count ); +#endif + if (ws_timeout) { torig = (ws_timeout->tv_sec * 1000) + (ws_timeout->tv_usec + 999) / 1000; @@ -3234,7 +3351,13 @@ int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds, while ((ret = poll( pollfds, count, timeout )) < 0) { +#ifdef USE_IP_PKTINFO_FILTER + if (check_iface != 0 && ret > 0) + noevents = check_pktinfo_fds( ws_readfds, pollfds, count ); + if (errno == EINTR || noevents) +#else if (errno == EINTR) +#endif { if (!ws_timeout) continue; gettimeofday( &tv2, 0 ); -- 1.7.0.4