[PATCH 3/6] wsdapi: Add support for sending messages via UDP multicast.

Huw Davies huw at codeweavers.com
Tue Mar 13 05:58:16 CDT 2018


On Mon, Mar 12, 2018 at 10:59:46PM +0000, Owen Rudge wrote:
> +#define SEND_ADDRESS_IPV4   "239.255.255.250"
> +#define SEND_ADDRESS_IPV6   "FF02::C"
> +#define SEND_PORT           "3702"

It seems odd to store these as strings and then resolve them.  Couldn't
they just be stored direclty as addresses?

> +
> +#define UNICAST_UDP_REPEAT     1
> +#define MULTICAST_UDP_REPEAT   2
> +#define UDP_MIN_DELAY         50
> +#define UDP_MAX_DELAY        250
> +#define UDP_UPPER_DELAY      500
> +
> +static struct addrinfo *resolve_address(const char *address, const char *port, int family, int type, int protocol)
> +{
> +    struct addrinfo hints, *result = NULL;
> +    int ret;
> +
> +    ZeroMemory(&hints, sizeof(hints));
> +
> +    hints.ai_flags = AI_PASSIVE;
> +    hints.ai_family = family;
> +    hints.ai_socktype = type;
> +    hints.ai_protocol = protocol;
> +
> +    ret = getaddrinfo(address, port, &hints, &result);
> +
> +    if (ret != 0)
> +    {
> +        WARN("Invalid address '%s' (getaddrinfo returned %d)\n", debugstr_a(address), ret);
> +        return NULL;
> +    }
> +
> +    return result;
> +}
> +
> +static void send_message(SOCKET s, char *data, int length, SOCKADDR_STORAGE *dest, int max_initial_delay, int repeat)
> +{
> +    UINT delay;
> +    int len;
> +
> +    /* Sleep for a random amount of time before sending the message */
> +    if (max_initial_delay > 0)
> +    {
> +        BCryptGenRandom(NULL, (BYTE*) &delay, sizeof(UINT), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
> +        Sleep(delay % max_initial_delay);
> +    }
> +
> +    len = (dest->ss_family == AF_INET6) ? sizeof(SOCKADDR_IN6) : sizeof(SOCKADDR_IN);
> +
> +    if (sendto(s, data, length, 0, (SOCKADDR *) dest, len) == SOCKET_ERROR)
> +        WARN("Unable to send data to socket: %d\n", WSAGetLastError());
> +
> +    if (repeat <= 0)
> +        return;
> +
> +    repeat--;
> +
> +    BCryptGenRandom(NULL, (BYTE*) &delay, sizeof(UINT), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
> +    delay = delay % (UDP_MAX_DELAY - UDP_MIN_DELAY + 1) + UDP_MIN_DELAY;
> +
> +    for (;;)
> +    {
> +        Sleep(delay);
> +
> +        if (sendto(s, data, length, 0, (SOCKADDR *) dest, len) == SOCKET_ERROR)
> +            WARN("Unable to send data to socket: %d\n", WSAGetLastError());
> +
> +        if (repeat <= 0)
> +            break;
> +
> +        repeat--;
> +
> +        delay = min(delay * 2, UDP_UPPER_DELAY);
> +    }
> +}

It should be possible to arrange this so there is only one sendto()
call, rather than the first one followed by the loop.

Are these things really supposed to go off semi-randomly?


> +
> +typedef struct sending_thread_params
> +{
> +    char *data;
> +    int length;
> +    SOCKET sock;
> +    SOCKADDR_STORAGE dest;
> +    int max_initial_delay;
> +} sending_thread_params;
> +
> +static DWORD WINAPI sending_thread(LPVOID lpParam)
> +{
> +    sending_thread_params *params = (sending_thread_params *) lpParam;
> +
> +    send_message(params->sock, params->data, params->length, &params->dest, params->max_initial_delay, MULTICAST_UDP_REPEAT);
> +    closesocket(params->sock);
> +
> +    heap_free(params->data);
> +    heap_free(params);
> +
> +    return 0;
> +}
> +
> +BOOL send_udp_multicast_of_type(char *data, int length, int max_initial_delay, ULONG family)
> +{
> +    IP_ADAPTER_ADDRESSES *adapter_addresses = NULL, *adapter_addr;
> +    sending_thread_params *send_params;
> +    struct addrinfo *multi_addr;
> +    ULONG bufferSize = 0;
> +    struct in6_addr i_addr;
> +    LPSOCKADDR sockaddr;
> +    BOOL ret = FALSE;
> +    HANDLE thread_handle;
> +    char ttl = 8;

This magic number should be a #define or constant.

> +    ULONG retval;
> +    SOCKET s;
> +
> +    /* Resolve the multicast address */
> +    if (family == AF_INET6)
> +        multi_addr = resolve_address(SEND_ADDRESS_IPV6, SEND_PORT, AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
> +    else
> +        multi_addr = resolve_address(SEND_ADDRESS_IPV4, SEND_PORT, AF_INET, SOCK_DGRAM, IPPROTO_UDP);
> +
> +    if (multi_addr == NULL)
> +        return FALSE;
> +
> +    /* Get size of buffer for adapters */
> +    retval = GetAdaptersAddresses(family, 0, NULL, NULL, &bufferSize);
> +
> +    if (retval != ERROR_BUFFER_OVERFLOW)
> +    {
> +        WARN("GetAdaptorsAddresses failed with error %08x\n", retval);
> +        goto cleanup;
> +    }
> +
> +    adapter_addresses = (IP_ADAPTER_ADDRESSES *) heap_alloc(bufferSize);
> +
> +    if (adapter_addresses == NULL)
> +    {
> +        WARN("Out of memory allocating space for adapter information\n");
> +        goto cleanup;
> +    }
> +
> +    /* Get list of adapters */
> +    retval = GetAdaptersAddresses(family, 0, NULL, adapter_addresses, &bufferSize);
> +
> +    if (retval != ERROR_SUCCESS)
> +    {
> +        WARN("GetAdaptorsAddresses failed with error %08x\n", retval);
> +        goto cleanup;
> +    }
> +
> +    for (adapter_addr = adapter_addresses; adapter_addr != NULL; adapter_addr = adapter_addr->Next)
> +    {
> +        if (adapter_addr->FirstUnicastAddress == NULL)
> +        {
> +            TRACE("No address found for adaptor '%s' (%p)\n", debugstr_a(adapter_addr->AdapterName), adapter_addr);
> +            continue;
> +        }
> +
> +        sockaddr = adapter_addr->FirstUnicastAddress->Address.lpSockaddr;
> +
> +        /* Create a socket and bind to the adapter address */
> +        s = socket(family, SOCK_DGRAM, IPPROTO_UDP);
> +
> +        if (s == INVALID_SOCKET)
> +        {
> +            WARN("Unable to create socket: %d\n", WSAGetLastError());
> +            continue;
> +        }
> +
> +        if (bind(s, sockaddr, adapter_addr->FirstUnicastAddress->Address.iSockaddrLength) == SOCKET_ERROR)
> +        {
> +            WARN("Unable to bind to socket (adaptor '%s' (%p)): %d\n", debugstr_a(adapter_addr->AdapterName), adapter_addr, WSAGetLastError());
> +            closesocket(s);
> +            continue;
> +        }
> +
> +        /* Zero out the interface */
> +        ZeroMemory(&i_addr, sizeof(i_addr));

If you just want an address full of zeros, declare it as static const and call it i_addr_zero or something.

> +        /* Set the multicast interface and TTL value */
> +        setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *) &i_addr, (family == AF_INET6) ? sizeof(struct in6_addr) : sizeof(struct in_addr));
> +        setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
> +
> +        /* Set up the thread parameters */
> +        send_params = heap_alloc(sizeof(sending_thread_params));

  send_params = heap_alloc(sizeof(*send_params));

> +
> +        send_params->data = heap_alloc(length);
> +        memcpy(send_params->data, data, length);
> +        send_params->length = length;
> +        send_params->sock = s;
> +        send_params->max_initial_delay = max_initial_delay;
> +        memcpy(&send_params->dest, multi_addr->ai_addr, (family == AF_INET6) ? sizeof(SOCKADDR_IN6) : sizeof(SOCKADDR_IN));
> +
> +        thread_handle = CreateThread(NULL, 0, sending_thread, send_params, 0, NULL);
> +
> +        if (thread_handle == NULL)
> +        {
> +            WARN("CreateThread failed (error %d)\n", GetLastError());
> +            closesocket(s);
> +
> +            heap_free(send_params->data);
> +            heap_free(send_params);
> +
> +            continue;
> +        }
> +
> +        CloseHandle(thread_handle);
> +    }
> +
> +    ret = TRUE;
> +
> +cleanup:
> +    freeaddrinfo(multi_addr);
> +    if (adapter_addresses != NULL) heap_free(adapter_addresses);
> +
> +    return ret;
> +}
> +
> +BOOL send_udp_multicast(IWSDiscoveryPublisherImpl *impl, char *data, int length, int max_initial_delay)
> +{
> +    if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV4) && (!send_udp_multicast_of_type(data, length, max_initial_delay, AF_INET))) return FALSE;
> +    if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV6) && (!send_udp_multicast_of_type(data, length, max_initial_delay, AF_INET6))) return FALSE;
> +    return TRUE;
> +}
> +
> +void terminate_networking(IWSDiscoveryPublisherImpl *impl)
> +{
> +    BOOL needsCleanup = impl->publisherStarted;
> +
> +    impl->publisherStarted = FALSE;
> +
> +    if (needsCleanup)
> +        WSACleanup();
> +}
> +
> +BOOL init_networking(IWSDiscoveryPublisherImpl *impl)
> +{
> +    WSADATA wsaData;
> +    int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
> +
> +    if (ret != 0)
> +    {
> +        WARN("WSAStartup failed with error: %d\n", ret);
> +        return FALSE;
> +    }
> +
> +    impl->publisherStarted = TRUE;
> +
> +    /* TODO: Start listening */
> +    return TRUE;
> +}
> diff --git a/dlls/wsdapi/soap.c b/dlls/wsdapi/soap.c
> index cb1ec0ea9d..cd3900007d 100644
> --- a/dlls/wsdapi/soap.c
> +++ b/dlls/wsdapi/soap.c
> @@ -27,6 +27,8 @@
>  #include "wine/debug.h"
>  #include "wine/heap.h"
>  
> +WINE_DEFAULT_DEBUG_CHANNEL(wsdapi);
> +
>  #define APP_MAX_DELAY                       500
>  
>  static BOOL write_and_send_message(IWSDiscoveryPublisherImpl *impl, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
> @@ -49,7 +51,17 @@ static BOOL write_and_send_message(IWSDiscoveryPublisherImpl *impl, WSD_SOAP_HEA
>  
>      memcpy(full_xml, xml_header, xml_header_len);
>  
> -    /* TODO: Send the message */
> +    if (remote_address == NULL)
> +    {
> +        /* Send the message via UDP multicast */
> +        if (send_udp_multicast(impl, full_xml, xml_length + xml_header_len, max_initial_delay))
> +            ret = TRUE;
> +    }
> +    else
> +    {
> +        /* TODO: Send the message via UDP unicast */
> +        FIXME("TODO: Send the message via UDP unicast\n");
> +    }
>  
>      heap_free(full_xml);
>  
> @@ -61,7 +73,7 @@ HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLON
>      const WSD_URI_LIST *xaddrs_list, const WSDXML_ELEMENT *hdr_any, const WSDXML_ELEMENT *ref_param_any,
>      const WSDXML_ELEMENT *endpoint_ref_any, const WSDXML_ELEMENT *any)
>  {
> -    HRESULT ret = E_NOTIMPL;
> +    HRESULT ret = E_OUTOFMEMORY;
>  
>      /* TODO: Populate message body */
>  
> diff --git a/dlls/wsdapi/tests/discovery.c b/dlls/wsdapi/tests/discovery.c
> index e4a672b311..d81dcdc936 100644
> --- a/dlls/wsdapi/tests/discovery.c
> +++ b/dlls/wsdapi/tests/discovery.c
> @@ -570,7 +570,7 @@ static void Publish_tests(void)
>  
>      /* Publish the service */
>      rc = IWSDiscoveryPublisher_Publish(publisher, publisherIdW, 1, 1, 1, NULL, NULL, NULL, NULL);
> -    todo_wine ok(rc == S_OK, "Publish failed: %08x\n", rc);
> +    ok(rc == S_OK, "Publish failed: %08x\n", rc);
>  
>      /* Wait up to 2 seconds for messages to be received */
>      if (WaitForMultipleObjects(msgStorage->numThreadHandles, msgStorage->threadHandles, TRUE, 2000) == WAIT_TIMEOUT)
> @@ -583,7 +583,7 @@ static void Publish_tests(void)
>      DeleteCriticalSection(&msgStorage->criticalSection);
>  
>      /* Verify we've received a message */
> -    todo_wine ok(msgStorage->messageCount >= 1, "No messages received\n");
> +    ok(msgStorage->messageCount >= 1, "No messages received\n");
>  
>      sprintf(endpointReferenceString, "<wsa:EndpointReference><wsa:Address>%s</wsa:Address></wsa:EndpointReference>", publisherId);
>  
> diff --git a/dlls/wsdapi/wsdapi_internal.h b/dlls/wsdapi/wsdapi_internal.h
> index 631dfb3d7e..417eed9145 100644
> --- a/dlls/wsdapi/wsdapi_internal.h
> +++ b/dlls/wsdapi/wsdapi_internal.h
> @@ -49,6 +49,12 @@ typedef struct IWSDiscoveryPublisherImpl {
>      BOOL                  publisherStarted;
>  } IWSDiscoveryPublisherImpl;
>  
> +/* network.c */
> +
> +BOOL init_networking(IWSDiscoveryPublisherImpl *impl);
> +void terminate_networking(IWSDiscoveryPublisherImpl *impl);
> +BOOL send_udp_multicast(IWSDiscoveryPublisherImpl *impl, char *data, int length, int max_initial_delay);
> +
>  /* soap.c */
>  
>  HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG metadata_ver, ULONGLONG instance_id,
> 

> 




More information about the wine-devel mailing list