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(a)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
>
>