On Tue, Oct 04, 2011 at 10:51:26AM -0600, Erich Hoover wrote:
Real Name: Erich Hoover
Description: I know it's been quite a while, but I'm back with another solution for Bug #7929. This patch uses SO_BINDTODEVICE in order to bind a socket to a specific interface while still allowing broadcast packets (a Linux-only feature). While this feature normally requires root privileges, it can also be enabled by giving wine-preloader the CAP_NET_RAW capability. Should insufficient permissions be available, the patch outputs an appropriate error message notifying the user that broadcast packets will not function properly.
Frankly, no sane distributor will ever do this.
Ciao, Marcus
Changelog: ws2_32: Patch to selectively bind to interfaces while still allowing broadcast packets.
From f4c904b9b21da7664eff33be918da71795db01af Mon Sep 17 00:00:00 2001 From: Erich Hoover ehoover@mines.edu Date: Tue, 4 Oct 2011 10:43:06 -0600 Subject: ws2_32: Selectively bind UDP sockets to interfaces while still allowing broadcast packets.
dlls/ws2_32/socket.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 94 insertions(+), 0 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index f0313fc..174ec15 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -2101,6 +2101,94 @@ static int WINAPI WS2_WSARecvMsg( SOCKET s, LPWSAMSG msg, LPDWORD lpNumberOfByte }
/***********************************************************************
interface_bind (INTERNAL)
- Bind the given socket exclusively to a specific interface.
- */
+static int interface_bind( 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;
- int ret = FALSE;
- DWORD adap_size;
- if (in_sock->sin_addr.s_addr == htonl(WS_INADDR_ANY))
- {
/* Not binding to specific interface, use default route */
goto cleanup;
- }
- optlen = sizeof(sock_type);
- if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &optlen) == -1
|| sock_type != SOCK_DGRAM)
- {
/* Not a UDP socket, interface-specific bind is irrelevant. */
goto cleanup;
- }
- /* Obtain the size of the IPv4 adapter list and allocate structure memory */
- if (GetAdaptersInfo(NULL, &adap_size) != ERROR_BUFFER_OVERFLOW)
- {
ERR("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)
- {
ERR("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)
- {
ERR("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)
{
+#ifdef SO_BINDTODEVICE
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
lstrcpynA(ifr.ifr_name, adapter->AdapterName, sizeof(ifr.ifr_name));
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr)) == 0)
{
/* Success! All packets sent on this socket will go through the
* specified interface. */
TRACE("Bound to interface '%s' (corresponds to local address %s).\n",
adapter->AdapterName, inet_ntoa(in_sock->sin_addr));
ret = TRUE;
break;
}
/* If we are unable to bind to a specific interface then notify the
* user that broadcast packets will not work for this socket. The
* user can correct this by giving CAP_NET_RAW access to the
* wine-preloader binary. */
ERR("SO_BINDTODEVICE failed (requires CAP_NET_RAW), "
"broadcast packets will not work on this socket.\n");
+#else /* SO_BINDTODEVICE */
WARN("SO_BINDTODEVICE is unsupported on this platform, "
"broadcast packets will not work on this socket.\n");
+#endif /* SO_BINDTODEVICE */
break;
}
- }
+cleanup:
- HeapFree(GetProcessHeap(), 0, adapters);
- return ret;
+}
+/***********************************************************************
bind (WS2_32.2)
*/ int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen) @@ -2151,6 +2239,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(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.1
On Tue, Oct 4, 2011 at 3:30 PM, Marcus Meissner marcus@jet.franken.de wrote:
On Tue, Oct 04, 2011 at 10:51:26AM -0600, Erich Hoover wrote:
... While this feature normally requires root privileges, it can also be enabled by giving wine-preloader the CAP_NET_RAW capability. ...
Frankly, no sane distributor will ever do this.
I wouldn't expect them to. If a user wants this feature then they'd need to enable it themselves, just like for ICMP or for raw socket creation.
Erich Hoover ehoover@mines.edu