From a330d73055def5559e94285a2dede7ff66b88876 Mon Sep 17 00:00:00 2001
From: Erich Hoover <ehoover@mines.edu>
Date: Sat, 18 Sep 2010 20:28:11 -0600
Subject: ws2_32/tests: Add UDP broadcast tests.

---
 dlls/ws2_32/tests/sock.c |  277 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 277 insertions(+), 0 deletions(-)

diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 16c6fcb..70fd899 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -59,6 +59,26 @@ static PCSTR  (WINAPI  *pInetNtop)(INT,LPVOID,LPSTR,ULONG) = 0;
 
 /**************** Structs and typedefs ***************/
 
+/* UDP select thread information */
+struct udp_select {
+    SOCKET socket;
+    int responses;
+};
+
+/* UDP IOCP thread information */
+struct udp_iocp {
+    HANDLE iocp;
+    SOCKET socket;
+    int responses;
+};
+
+/* IOCP key for retrieving buffer information */
+struct iocp_key {
+    SOCKET socket;
+    int num_buffers;
+    WSABUF *buffers;
+};
+
 typedef struct thread_info
 {
     HANDLE thread;
@@ -1181,8 +1201,151 @@ static test_setup tests [NUM_TESTS] =
     }
 };
 
+/* Perform a timed "select" (100 ms) and read from a socket,
+ * does not block so if the "select" times out then this fails.
+ */
+int select_recv(SOCKET sock, char *buffer, int len)
+{
+    struct timeval tv = {0, 100000};
+    unsigned long enable = 1;
+    fd_set fdset;
+    int result;
+
+    FD_ZERO(&fdset);
+    FD_SET(sock, &fdset);
+    ioctlsocket(sock, FIONBIO, &enable);
+    result = select(sock+1, &fdset, NULL, NULL, &tv);
+    if (result < 0)
+        return -1;
+    else if (result > 0 && FD_ISSET(sock, &fdset))
+    {
+        result = recv(sock, buffer, len, 0);
+        return result;
+    }
+    return -2;
+}
+
+/* Perform a timed "IOCP" operation (100 ms) and read from a socket,
+ * does not block so if the "select" times out then this fails.
+ */
+int iocp_recv(HANDLE iocp, char *buffer, unsigned int len)
+{
+    struct iocp_key *key = NULL;
+    LPOVERLAPPED o = NULL;
+    unsigned int bytes, i;
+    int result;
+
+    result = GetQueuedCompletionStatus(iocp, &bytes, (PULONG_PTR) &key, &o, 100);
+    if (result && bytes > 0)
+    {
+        for (i=0; i < key->num_buffers; i++)
+        {
+            if (key->buffers[i].buf[0] != '\0')
+            {
+                memcpy(buffer, key->buffers[i].buf, bytes);
+                key->buffers[i].buf[0] = '\0';
+            }
+        }
+
+        return bytes;
+    }
+    return -1;
+}
+
+/* Loop through waiting for UDP datagrams (using "select")
+ * and count the number successfully received.
+ */
+static void WINAPI udp_select_thread( struct udp_select *udpsel )
+{
+    int tries = 0;
+
+    while (tries < 10)
+    {
+        char buffer[100];
+        int ret;
+
+        buffer[0] = '\0';
+        ret = select_recv(udpsel->socket, buffer, sizeof(buffer));
+        if (ret == sizeof(struct sockaddr_in))
+        {
+            struct sockaddr_in *addr = (struct sockaddr_in *) buffer;
+
+            if (ntohl(addr->sin_addr.s_addr) == INADDR_LOOPBACK)
+                (udpsel->responses)++;
+        }
+        tries++;
+    }
+}
+
+/* Loop through waiting for UDP datagrams (using IOCP)
+ * and count the number successfully received.
+ */
+static void WINAPI udp_iocp_thread( struct udp_iocp *udpiocp )
+{
+    int tries = 0;
+
+    while (tries < 10)
+    {
+        char buffer[100];
+        int ret;
+
+        buffer[0] = '\0';
+        ret = iocp_recv(udpiocp->iocp, buffer, sizeof(buffer));
+        if (ret == sizeof(struct sockaddr_in))
+        {
+            struct sockaddr_in *addr = (struct sockaddr_in *) buffer;
+
+            if (ntohl(addr->sin_addr.s_addr) == INADDR_LOOPBACK)
+                (udpiocp->responses)++;
+        }
+        tries++;
+    }
+}
+
+void start_iocp_recv( struct iocp_key *iocp_key, OVERLAPPED *o, int buffer_size, int count, int use_readfile )
+{
+    unsigned int flags = 0;
+    int i, ret;
+
+    iocp_key->num_buffers = count;
+    iocp_key->buffers = HeapAlloc(GetProcessHeap(), 0, count*sizeof(WSABUF));
+    for(i = 0; i < count; i++)
+    {
+        iocp_key->buffers[i].len = buffer_size;
+        iocp_key->buffers[i].buf = HeapAlloc(GetProcessHeap(), 0, buffer_size);
+        iocp_key->buffers[i].buf[0] = '\0';
+        if (use_readfile)
+        {
+            ret = ReadFile ((HANDLE)iocp_key->socket, iocp_key->buffers[i].buf, iocp_key->buffers[i].len, NULL ,o); 
+            ok ( ret == 0 && WSAGetLastError() == ERROR_IO_PENDING, "UDP: failed to start IOCP read!\n" );
+        }
+        else
+        {
+            ret = WSARecv(iocp_key->socket, &(iocp_key->buffers[i]), 1, NULL, &flags, o, NULL);
+            ok ( ret == SOCKET_ERROR && WSAGetLastError() == ERROR_IO_PENDING, "UDP: failed to start IOCP read!\n" );
+        }
+    }
+}
+
+void cleanup_iocp_recv( struct iocp_key *iocp_key )
+{
+    int i;
+
+    for(i = 0; i < iocp_key->num_buffers; i++)
+        HeapFree(GetProcessHeap(), 0, iocp_key->buffers[i].buf);
+    HeapFree(GetProcessHeap(), 0, iocp_key->buffers);
+}
+
 static void test_UDP(void)
 {
+    SOCKET broadcast_sock, write_sock, read_sock;
+    struct udp_iocp udpiocp = {0, 0, 0};
+    struct udp_select udpsel = {0, 0};
+    HANDLE thread_handle, iocp;
+    struct sockaddr_in addr;
+    int run_num;
+    DWORD id;
+
     /* This function tests UDP sendto() and recvfrom(). UDP is unreliable, so it is
        possible that this test fails due to dropped packets. */
 
@@ -1227,6 +1390,120 @@ static void test_UDP(void)
         ok ( n_recv == sizeof(buf), "UDP: recvfrom() received wrong amount of data or socket error: %d\n", n_recv );
         ok ( memcmp ( &peer[0].peer.sin_port, buf, sizeof(peer[0].addr.sin_port) ) == 0, "UDP: port numbers do not match\n" );
     }
+
+    for ( i = 1; i < NUM_UDP_PEERS; i++ )
+        closesocket ( peer[i].s );
+
+    read_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+    write_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+    broadcast_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+    udpsel.responses = 0;
+    if (read_sock != INVALID_SOCKET && write_sock != INVALID_SOCKET
+        && broadcast_sock != INVALID_SOCKET)
+    {
+        unsigned int enable = 1;
+        int ret;
+
+        /* setup the necessary sockets */
+        addr.sin_family         = AF_INET;
+        addr.sin_addr.s_addr    = htonl ( INADDR_LOOPBACK );
+        addr.sin_port           = htons ( 2000 );
+        do_bind ( read_sock, (struct sockaddr *) &addr, sizeof(addr) );
+        ret = setsockopt ( read_sock, SOL_SOCKET, SO_BROADCAST, (LPVOID) &enable, sizeof (enable) );
+        ok ( ret == 0, "UDP: failed to set broadcast flag!\n" );
+        ret = setsockopt ( broadcast_sock, SOL_SOCKET, SO_BROADCAST, (LPVOID) &enable, sizeof (enable) );
+        ok ( ret == 0, "UDP: failed to set broadcast flag!\n" );
+
+        /* test the "select" operation on UDP sockets */
+        udpsel.socket = read_sock;
+        thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) &udp_select_thread, &udpsel, 0, &id );
+
+        /* send test packets to the thread waiting on "select" */
+        memcpy ( buf, &addr, sizeof(addr) );
+        ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+        addr.sin_addr.s_addr    = htonl ( INADDR_BROADCAST );
+        ret = sendto ( broadcast_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        addr.sin_addr.s_addr    = htonl ( INADDR_LOOPBACK );
+        if (ret == -1 && WSAGetLastError() == WSAENETUNREACH)
+            skip ( "A connected network interface is required to run this test.\n" );
+        else
+            ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+        ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+        /* send a test packet <out> on the "receive" interface (which is valid) */
+        ret = sendto ( read_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+
+        /* wait for the thread to exit and check the replies */
+        WaitForSingleObject ( thread_handle, TEST_TIMEOUT * 1000 );
+        ok ( udpsel.responses == 3, "UDP: expected three replies (received %d).\n", udpsel.responses);
+
+        /* test IOCP with both WSARecv and ReadFile */
+        for (run_num = 0; run_num <= 1; run_num++)
+        {
+            /* create an IOCP handle and associate it with our read socket */
+            iocp = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,0,3);
+            if (iocp)
+            {
+                struct iocp_key iocp_key;
+                OVERLAPPED o;
+
+                /* reset the read socket (make sure IOCP runs are separate) */
+                closesocket ( read_sock );
+                read_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+                do_bind ( read_sock, (struct sockaddr *) &addr, sizeof(addr) );
+
+                /* initialize the IO Completion port */
+                iocp_key.socket = read_sock;
+                CreateIoCompletionPort ( (HANDLE)read_sock, iocp, (ULONG_PTR) &iocp_key, 1 );
+                memset ( &o, 0, sizeof(OVERLAPPED) );
+                o.hEvent = CreateEvent ( NULL, TRUE, TRUE, NULL );
+                udpiocp.responses = 0;
+                start_iocp_recv ( &iocp_key, &o, sizeof(addr), 10, run_num );
+
+                /* test the "select" operation on UDP sockets */
+                udpiocp.iocp = iocp;
+                udpiocp.socket = read_sock;
+                thread_handle = CreateThread ( NULL, 0, (LPTHREAD_START_ROUTINE) &udp_iocp_thread, &udpiocp, 0, &id );
+
+                /* send test packets to the thread waiting on the IO Completion */
+                memcpy ( buf, &addr, sizeof(addr) );
+                ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+                addr.sin_addr.s_addr    = htonl ( INADDR_BROADCAST );
+                ret = sendto ( broadcast_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                addr.sin_addr.s_addr    = htonl ( INADDR_LOOPBACK );
+                if (ret == -1 && WSAGetLastError() == WSAENETUNREACH)
+                    skip ( "A connected network interface is required to run this test.\n" );
+                else
+                    ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+                ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+                /* send a test packet <out> on the "receive" interface (which is valid) */
+                ret = sendto ( read_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+
+                /* wait for the thread to exit, check the replies, cleanup the buffers */
+                WaitForSingleObject ( thread_handle, TEST_TIMEOUT * 1000 );
+                ok ( udpiocp.responses == 3, "UDP: expected three replies (received %d).\n", udpiocp.responses);
+                cleanup_iocp_recv ( &iocp_key );
+
+                CloseHandle(iocp);
+            }
+            else
+                skip ( "Several tests skipped, failed to create IO completion port!\n" );
+        }
+    }
+    else
+        skip ( "Several tests skipped, failed to create necessary sockets!\n" );
+    /* cleanup socket select test */
+    if (read_sock != INVALID_SOCKET);
+        closesocket ( read_sock );
+    if (write_sock != INVALID_SOCKET);
+        closesocket ( write_sock );
+    if (broadcast_sock != INVALID_SOCKET);
+        closesocket ( broadcast_sock );
 }
 
 static void WINAPI do_getservbyname( HANDLE *starttest )
-- 
1.7.0.4

