[PATCH 1/4] wsdapi: Add support for listening for UDP multicast broadcasts.

Huw Davies huw at codeweavers.com
Thu Jun 7 02:44:13 CDT 2018


On Wed, Jun 06, 2018 at 09:58:45PM +0100, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge at codeweavers.com>
> ---
>  dlls/wsdapi/network.c         | 278
> +++++++++++++++++++++++++++++++++++++++++-
>  dlls/wsdapi/wsdapi_internal.h |   4 +
>  2 files changed, 281 insertions(+), 1 deletion(-)
> 
> 

> diff --git a/dlls/wsdapi/network.c b/dlls/wsdapi/network.c
> index f86b3b4a3e..3e7509bfa3 100644
> --- a/dlls/wsdapi/network.c
> +++ b/dlls/wsdapi/network.c
> @@ -231,11 +231,278 @@ BOOL send_udp_multicast(IWSDiscoveryPublisherImpl *impl, char *data, int length,
>      return TRUE;
>  }
>  
> +static int join_multicast_group(SOCKET s, SOCKADDR_STORAGE *group, SOCKADDR_STORAGE *iface)
> +{
> +    int level, optname, optlen;
> +    struct ipv6_mreq mreqv6;
> +    struct ip_mreq mreqv4;
> +    char *optval;
> +
> +    if (iface->ss_family == AF_INET6)
> +    {
> +        level = IPPROTO_IPV6;
> +        optname = IPV6_ADD_MEMBERSHIP;
> +        optval = (char *)&mreqv6;
> +        optlen = sizeof(mreqv6);
> +
> +        mreqv6.ipv6mr_multiaddr = ((SOCKADDR_IN6 *)group)->sin6_addr;
> +        mreqv6.ipv6mr_interface = ((SOCKADDR_IN6 *)iface)->sin6_scope_id;
> +    }
> +    else
> +    {
> +        level = IPPROTO_IP;
> +        optname = IP_ADD_MEMBERSHIP;
> +        optval = (char *)&mreqv4;
> +        optlen = sizeof(mreqv4);
> +
> +        mreqv4.imr_multiaddr.s_addr = ((SOCKADDR_IN *)group)->sin_addr.s_addr;
> +        mreqv4.imr_interface.s_addr = ((SOCKADDR_IN *)iface)->sin_addr.s_addr;
> +    }
> +
> +    return setsockopt(s, level, optname, optval, optlen);
> +}
> +
> +static int set_send_interface(SOCKET s, SOCKADDR_STORAGE *iface)
> +{
> +    int level, optname, optlen;
> +    char *optval = NULL;
> +
> +    if (iface->ss_family == AF_INET6)
> +    {
> +        level = IPPROTO_IPV6;
> +        optname = IPV6_MULTICAST_IF;
> +        optval = (char *) &((SOCKADDR_IN6 *)iface)->sin6_scope_id;
> +        optlen = sizeof(((SOCKADDR_IN6 *)iface)->sin6_scope_id);
> +    }
> +    else
> +    {
> +        level = IPPROTO_IP;
> +        optname = IP_MULTICAST_IF;
> +        optval = (char *) &((SOCKADDR_IN *)iface)->sin_addr.s_addr;
> +        optlen = sizeof(((SOCKADDR_IN *)iface)->sin_addr.s_addr);
> +    }
> +
> +    return setsockopt(s, level, optname, optval, optlen);
> +}
> +
> +typedef struct listener_thread_params
> +{
> +    IWSDiscoveryPublisherImpl *impl;
> +    SOCKET listening_socket;
> +    BOOL ipv6;
> +} listener_thread_params;
> +
> +#define RECEIVE_BUFFER_SIZE        65536
> +
> +static DWORD WINAPI listening_thread(LPVOID params)
> +{
> +    listener_thread_params *parameter = (listener_thread_params *)params;
> +    int bytes_received, address_len, err;
> +    SOCKADDR_STORAGE source_addr;
> +    char *buffer;
> +
> +    buffer = heap_alloc(RECEIVE_BUFFER_SIZE);
> +    address_len = parameter->ipv6 ? sizeof(SOCKADDR_IN6) : sizeof(SOCKADDR_IN);
> +
> +    while (parameter->impl->publisherStarted)
> +    {
> +        bytes_received = recvfrom(parameter->listening_socket, buffer, RECEIVE_BUFFER_SIZE, 0,
> +            (LPSOCKADDR) &source_addr, &address_len);
> +
> +        if (bytes_received == SOCKET_ERROR)
> +        {
> +            err = WSAGetLastError();
> +
> +            if (err != WSAETIMEDOUT)
> +            {
> +                WARN("Received error when trying to read from socket: %d. Stopping listener.\n", err);
> +                return 0;
> +            }
> +        }
> +        else
> +        {
> +            /* TODO: Process received message */
> +        }
> +    }
> +
> +    /* The publisher has been stopped */
> +    closesocket(parameter->listening_socket);
> +
> +    heap_free(buffer);
> +    heap_free(parameter);
> +
> +    return 0;
> +}
> +
> +static void start_listening(IWSDiscoveryPublisherImpl *impl, SOCKADDR_STORAGE *bind_address)

This should probably return an indication of success (even if it's not
possible for the caller to do something sensible with the result).

> +{
> +    SOCKADDR_STORAGE multicast_addr, bind_addr, interface_addr;
> +    listener_thread_params *parameter = NULL;
> +    const DWORD receive_timeout = 5000;
> +    const UINT reuse_addr = 1;
> +    HANDLE thread_handle;
> +    int address_length;
> +    SOCKET s = 0;
> +
> +    TRACE("(%p, %p) family %d\n", impl, bind_address, bind_address->ss_family);
> +
> +    /* Populate the multicast address */
> +    ZeroMemory(&multicast_addr, sizeof(SOCKADDR_STORAGE));
> +
> +    if (bind_address->ss_family == AF_INET)
> +    {
> +        SOCKADDR_IN *sockaddr4 = (SOCKADDR_IN *)&multicast_addr;
> +
> +        sockaddr4->sin_port = htons(SEND_PORT);
> +        sockaddr4->sin_addr.S_un.S_addr = htonl(SEND_ADDRESS_IPV4);
> +        address_length = sizeof(SOCKADDR_IN);
> +    }
> +    else
> +    {
> +        SOCKADDR_IN6 *sockaddr6 = (SOCKADDR_IN6 *)&multicast_addr;
> +
> +        sockaddr6->sin6_port = htons(SEND_PORT);
> +        memcpy(&sockaddr6->sin6_addr, &send_address_ipv6, sizeof(send_address_ipv6));
> +        address_length = sizeof(SOCKADDR_IN6);
> +    }
> +
> +    /* Update the port for the binding address */
> +    memcpy(&bind_addr, bind_address, address_length);
> +    ((SOCKADDR_IN *)&bind_addr)->sin_port = htons(SEND_PORT);
> +
> +    /* Update the port for the interface address */
> +    memcpy(&interface_addr, bind_address, address_length);
> +    ((SOCKADDR_IN *)&interface_addr)->sin_port = htons(0);
> +
> +    /* Create the socket */
> +    s = socket(bind_address->ss_family, SOCK_DGRAM, IPPROTO_UDP);
> +
> +    if (s == INVALID_SOCKET)
> +    {
> +        WARN("socket() failed (error %d)\n", WSAGetLastError());
> +        goto cleanup;
> +    }
> +
> +    /* Ensure the socket can be reused */
> +    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_addr, sizeof(reuse_addr)) == SOCKET_ERROR)
> +    {
> +        WARN("setsockopt(SO_REUSEADDR) failed (error %d)\n", WSAGetLastError());
> +        goto cleanup;
> +    }
> +
> +    /* Bind the socket to the local interface so we can receive data */
> +    if (bind(s, (struct sockaddr *)&bind_addr, address_length) == SOCKET_ERROR)
> +    {
> +        WARN("bind() failed (error %d)\n", WSAGetLastError());
> +        goto cleanup;
> +    }
> +
> +    /* Join the multicast group */
> +    if (join_multicast_group(s, &multicast_addr, &interface_addr) == SOCKET_ERROR)
> +    {
> +        WARN("Unable to join multicast group (error %d)\n", WSAGetLastError());
> +        goto cleanup;
> +    }
> +
> +    /* Set the outgoing interface */
> +    if (set_send_interface(s, &interface_addr) == SOCKET_ERROR)
> +    {
> +        WARN("Unable to set outgoing interface (error %d)\n", WSAGetLastError());
> +        goto cleanup;
> +    }
> +
> +    /* Set a 5-second receive timeout */
> +    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&receive_timeout, sizeof(receive_timeout)) == SOCKET_ERROR)
> +    {
> +        WARN("setsockopt(SO_RCVTIME0) failed (error %d)\n", WSAGetLastError());
> +        goto cleanup;
> +    }
> +
> +    /* Allocate memory for thread parameters */
> +    parameter = heap_alloc(sizeof(listener_thread_params));
> +
> +    parameter->impl = impl;
> +    parameter->listening_socket = s;
> +    parameter->ipv6 = (bind_address->ss_family == AF_INET6);
> +
> +    thread_handle = CreateThread(NULL, 0, listening_thread, parameter, 0, NULL);
> +
> +    if (thread_handle == NULL)
> +    {
> +        WARN("CreateThread failed (error %d)\n", GetLastError());
> +        goto cleanup;
> +    }
> +
> +    impl->thread_handles[impl->num_thread_handles] = thread_handle;
> +    impl->num_thread_handles++;
> +
> +    return;
> +
> +cleanup:
> +    closesocket(s);
> +    heap_free(parameter);
> +}
> +
> +static BOOL start_listening_on_all_addresses(IWSDiscoveryPublisherImpl *impl, ULONG family)
> +{
> +    IP_ADAPTER_ADDRESSES *adapter_addresses = NULL, *adapter_address;
> +    ULONG bufferSize = 0;
> +    ULONG ret;
> +
> +    ret = GetAdaptersAddresses(family, 0, NULL, NULL, &bufferSize); /* family should be AF_INET or AF_INET6 */
> +
> +    if (ret != ERROR_BUFFER_OVERFLOW)
> +    {
> +        WARN("GetAdaptorsAddresses failed with error %08x\n", ret);
> +        return FALSE;
> +    }
> +
> +    /* Get size of buffer for adapters */
> +    adapter_addresses = (IP_ADAPTER_ADDRESSES *)heap_alloc(bufferSize);
> +
> +    if (adapter_addresses == NULL)
> +    {
> +        WARN("Out of memory allocating space for adapter information\n");
> +        return FALSE;
> +    }
> +
> +    /* Get list of adapters */
> +    ret = GetAdaptersAddresses(family, 0, NULL, adapter_addresses, &bufferSize);
> +
> +    if (ret != ERROR_SUCCESS)
> +    {
> +        WARN("GetAdaptorsAddresses failed with error %08x\n", ret);
> +        goto cleanup;
> +    }
> +
> +    for (adapter_address = adapter_addresses; adapter_address != NULL; adapter_address = adapter_address->Next)
> +    {
> +        if (impl->num_thread_handles >= MAX_WSD_THREADS)
> +        {
> +            WARN("Exceeded maximum number of supported listener threads; too many network interfaces.");
> +            goto cleanup;
> +        }
> +
> +        if (adapter_address->FirstUnicastAddress == NULL)
> +        {
> +            TRACE("No address found for adaptor '%s' (%p)\n", adapter_address->AdapterName, adapter_address);
> +            continue;
> +        }
> +
> +        start_listening(impl, (SOCKADDR_STORAGE *)adapter_address->FirstUnicastAddress->Address.lpSockaddr);
> +    }
> +
> +cleanup:
> +    heap_free(adapter_addresses);
> +    return ret == ERROR_SUCCESS;
> +}
> +
>  void terminate_networking(IWSDiscoveryPublisherImpl *impl)
>  {
>      BOOL needsCleanup = impl->publisherStarted;
>  
>      impl->publisherStarted = FALSE;
> +    WaitForMultipleObjects(impl->num_thread_handles, impl->thread_handles, TRUE, INFINITE);
>  
>      if (needsCleanup)
>          WSACleanup();
> @@ -254,6 +521,15 @@ BOOL init_networking(IWSDiscoveryPublisherImpl *impl)
>  
>      impl->publisherStarted = TRUE;
>  
> -    /* TODO: Start listening */
> +    if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV4) && (!start_listening_on_all_addresses(impl, AF_INET)))
> +        goto cleanup;
> +
> +    if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV6) && (!start_listening_on_all_addresses(impl, AF_INET6)))
> +        goto cleanup;
> +
>      return TRUE;
> +
> +cleanup:
> +    terminate_networking(impl);
> +    return FALSE;
>  }
> diff --git a/dlls/wsdapi/wsdapi_internal.h b/dlls/wsdapi/wsdapi_internal.h
> index 45d54c2ef1..f88822d5ef 100644
> --- a/dlls/wsdapi/wsdapi_internal.h
> +++ b/dlls/wsdapi/wsdapi_internal.h
> @@ -40,6 +40,8 @@ struct notificationSink
>      IWSDiscoveryPublisherNotify *notificationSink;
>  };
>  
> +#define MAX_WSD_THREADS        20
> +
>  typedef struct IWSDiscoveryPublisherImpl {
>      IWSDiscoveryPublisher IWSDiscoveryPublisher_iface;
>      LONG                  ref;
> @@ -47,6 +49,8 @@ typedef struct IWSDiscoveryPublisherImpl {
>      DWORD                 addressFamily;
>      struct list           notificationSinks;
>      BOOL                  publisherStarted;
> +    HANDLE                thread_handles[MAX_WSD_THREADS];
> +    int                   num_thread_handles;
>  } IWSDiscoveryPublisherImpl;
>  
>  /* network.c */
> 

> 




More information about the wine-devel mailing list