From: "Erich E. Hoover" Subject: [PATCH 1/1] ws2_32: Permit broadcast packets on interface-bound sockets for systems with IP_BOUND_IF. Message-Id: Date: Mon, 16 Jul 2012 14:51:41 -0600 This patch fixes the issue where interface-bound sockets cannot receive broadcast packets (Bug #7929) for systems supporting the IP_BOUND_IF socket option (Mac OS X 10.6+ and Solaris). The patch was kindly compiled and tested with Command & Conquer Generals: Zero Hour by the folks over at the Wineskin forums. This patch has a minor revision against the original "deferred" version in that address reuse code is now relocated in order to make everything a little more readable and a missing word in a couple FIXME messages are now properly there. From c2f48ed29996b2ab8ebd13d3abe95bfa5f3042cc Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Mon, 16 Jul 2012 14:43:28 -0600 Subject: ws2_32: Permit broadcast packets on interface-bound sockets for systems with IP_BOUND_IF. --- dlls/ws2_32/socket.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 105 insertions(+), 0 deletions(-) diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 212733b..9783852 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -2107,6 +2107,105 @@ static int WINAPI WS2_WSARecvMsg( SOCKET s, LPWSAMSG msg, LPDWORD lpNumberOfByte } /*********************************************************************** + * interface_bind (INTERNAL) + * + * Take bind() calls on any name corresponding to a local network adapter and restrict the given socket to + * operating only on the specified interface. This restriction consists of two components: + * 1) An outgoing packet restriction suggesting the egress interface for all packets. + * 2) An incoming packet restriction dropping packets not meant for the interface. + * + * If the function succeeds in placing these restrictions (returns TRUE) then the name for the bind() may + * safely be changed to INADDR_ANY, permitting the transmission and receipt of broadcast packets on the + * socket. This behavior is only relevant to UDP sockets and is needed for applications that expect to be able + * to receive broadcast packets on a socket that is bound to a specific network interface. + */ +static BOOL interface_bind( SOCKET s, int fd, struct sockaddr *addr ) +{ + struct sockaddr_in *in_sock = (struct sockaddr_in *) addr; + PIP_ADAPTER_INFO adapters = NULL, adapter; + unsigned int sock_type = 0, optlen; + BOOL ret = FALSE; + DWORD adap_size; + int enable = 1; + + 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 network adapter list, " + "receiving broadcast packets will not work on socket %04lx.\n", s); + goto cleanup; + } + adapters = HeapAlloc(GetProcessHeap(), 0, adap_size); + if (adapters == NULL) + { + FIXME("Failed to allocate the network adapter list, " + "receiving broadcast packets will not work on socket %04lx.\n", s); + goto cleanup; + } + /* Obtain the IPv4 adapter list */ + if (GetAdaptersInfo(adapters, &adap_size) != NO_ERROR) + { + FIXME("Failed to obtain the network adapter list, " + "receiving broadcast packets will not work on socket %04lx.\n", s); + 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) + { +#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) + { + ERR("Setting the socket option IP_BOUND_IF failed, " + "receiving broadcast packets will not work on socket %04lx.\n", s); + goto cleanup; + } + + TRACE("Socket %04lx bound to interface index %d\n", s, adapter->Index); + ret = TRUE; +#else + /* No known way to bind to an interface on this platform */ + FIXME("Broadcast packets on interface-bound sockets is not currently supported on this platform, " + "receiving broadcast packets will not work on socket %04lx.\n", s); +#endif + break; + } + } + + if (!ret) + goto cleanup; + + /* Will soon be switching to INADDR_ANY: permit address reuse */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) != 0) + { + ret = FALSE; + ERR("Failed to enable address reuse socket option, " + "receiving broadcast packets will not work on socket %04lx.\n", s); + } + +cleanup: + HeapFree(GetProcessHeap(), 0, adapters); + return ret; +} + +/*********************************************************************** * bind (WS2_32.2) */ int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen) @@ -2157,6 +2256,12 @@ int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen) "INADDR_ANY instead.\n"); in4->sin_addr.s_addr = htonl(WS_INADDR_ANY); } + else if (interface_bind(s, fd, &uaddr.addr)) + { + /* Bound to a specific interface, change the binding + * address so that broadcast packets work properly */ + in4->sin_addr.s_addr = htonl(WS_INADDR_ANY); + } } if (bind(fd, &uaddr.addr, uaddrlen) < 0) { -- 1.7.5.4