From fa97b85923b8fd163906108a1944f88169e17e35 Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Tue, 11 Oct 2011 08:19:46 -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 7a64e0e..0635e5e 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -65,6 +65,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; @@ -1525,8 +1545,151 @@ static test_setup 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. */ @@ -1571,6 +1734,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 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 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 DWORD WINAPI do_getservbyname( void *param ) -- 1.7.1