michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /******************************************************************* michael@0: ** udpsrc.c -- Test basic function of UDP server michael@0: ** michael@0: ** udpsrv operates on the same machine with program udpclt. michael@0: ** udpsrv is the server side of a udp sockets application. michael@0: ** udpclt is the client side of a udp sockets application. michael@0: ** michael@0: ** The test is designed to assist developers in porting/debugging michael@0: ** the UDP socket functions of NSPR. michael@0: ** michael@0: ** This test is not a stress test. michael@0: ** michael@0: ** main() starts two threads: UDP_Server() and UDP_Client(); michael@0: ** main() uses PR_JoinThread() to wait for the threads to complete. michael@0: ** michael@0: ** UDP_Server() does repeated recvfrom()s from a socket. michael@0: ** He detects an EOF condition set by UDP_Client(). For each michael@0: ** packet received by UDP_Server(), he checks its content for michael@0: ** expected content, then sends the packet back to UDP_Client(). michael@0: ** michael@0: ** UDP_Client() sends packets to UDP_Server() using sendto() michael@0: ** he recieves packets back from the server via recvfrom(). michael@0: ** After he sends enough packets containing UDP_AMOUNT_TO_WRITE michael@0: ** bytes of data, he sends an EOF message. michael@0: ** michael@0: ** The test issues a pass/fail message at end. michael@0: ** michael@0: ** Notes: michael@0: ** The variable "_debug_on" can be set to 1 to cause diagnostic michael@0: ** messages related to client/server synchronization. Useful when michael@0: ** the test hangs. michael@0: ** michael@0: ** Error messages are written to stdout. michael@0: ** michael@0: ******************************************************************** michael@0: */ michael@0: /* --- include files --- */ michael@0: #include "nspr.h" michael@0: #include "prpriv.h" michael@0: michael@0: #include "plgetopt.h" michael@0: #include "prttools.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifdef XP_PC michael@0: #define mode_t int michael@0: #endif michael@0: michael@0: #define UDP_BUF_SIZE 4096 michael@0: #define UDP_DGRAM_SIZE 128 michael@0: #define UDP_AMOUNT_TO_WRITE (PRInt32)((UDP_DGRAM_SIZE * 1000l) +1) michael@0: #define NUM_UDP_CLIENTS 1 michael@0: #define NUM_UDP_DATAGRAMS_PER_CLIENT 5 michael@0: #define UDP_SERVER_PORT 9050 michael@0: #define UDP_CLIENT_PORT 9053 michael@0: #define MY_INADDR PR_INADDR_ANY michael@0: #define PEER_INADDR PR_INADDR_LOOPBACK michael@0: michael@0: #define UDP_TIMEOUT 400000 michael@0: /* #define UDP_TIMEOUT PR_INTERVAL_NO_TIMEOUT */ michael@0: michael@0: /* --- static data --- */ michael@0: static PRIntn _debug_on = 0; michael@0: static PRBool passed = PR_TRUE; michael@0: static PRUint32 cltBytesRead = 0; michael@0: static PRUint32 srvBytesRead = 0; michael@0: static PRFileDesc *output = NULL; michael@0: michael@0: /* --- static function declarations --- */ michael@0: #define DPRINTF(arg) if (_debug_on) PR_fprintf(output, arg) michael@0: michael@0: michael@0: michael@0: /******************************************************************* michael@0: ** ListNetAddr() -- Display the Net Address on stdout michael@0: ** michael@0: ** Description: displays the component parts of a PRNetAddr struct michael@0: ** michael@0: ** Arguments: address of PRNetAddr structure to display michael@0: ** michael@0: ** Returns: void michael@0: ** michael@0: ** Notes: michael@0: ** michael@0: ******************************************************************** michael@0: */ michael@0: void ListNetAddr( char *msg, PRNetAddr *na ) michael@0: { michael@0: char mbuf[256]; michael@0: michael@0: sprintf( mbuf, "ListNetAddr: %s family: %d, port: %d, ip: %8.8X\n", michael@0: msg, na->inet.family, PR_ntohs( na->inet.port), PR_ntohl(na->inet.ip) ); michael@0: #if 0 michael@0: DPRINTF( mbuf ); michael@0: #endif michael@0: } /* --- end ListNetAddr() --- */ michael@0: michael@0: /******************************************************************** michael@0: ** UDP_Server() -- Test a UDP server application michael@0: ** michael@0: ** Description: The Server side of a UDP Client/Server application. michael@0: ** michael@0: ** Arguments: none michael@0: ** michael@0: ** Returns: void michael@0: ** michael@0: ** Notes: michael@0: ** michael@0: ** michael@0: ******************************************************************** michael@0: */ michael@0: static void PR_CALLBACK UDP_Server( void *arg ) michael@0: { michael@0: static char svrBuf[UDP_BUF_SIZE]; michael@0: PRFileDesc *svrSock; michael@0: PRInt32 rv; michael@0: PRNetAddr netaddr; michael@0: PRBool bound = PR_FALSE; michael@0: PRBool endOfInput = PR_FALSE; michael@0: PRInt32 numBytes = UDP_DGRAM_SIZE; michael@0: michael@0: DPRINTF("udpsrv: UDP_Server(): starting\n" ); michael@0: michael@0: /* --- Create the socket --- */ michael@0: DPRINTF("udpsrv: UDP_Server(): Creating UDP Socket\n" ); michael@0: svrSock = PR_NewUDPSocket(); michael@0: if ( svrSock == NULL ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Server(): PR_NewUDPSocket() returned NULL\n" ); michael@0: return; michael@0: } michael@0: michael@0: /* --- Initialize the sockaddr_in structure --- */ michael@0: memset( &netaddr, 0, sizeof( netaddr )); michael@0: netaddr.inet.family = PR_AF_INET; michael@0: netaddr.inet.port = PR_htons( UDP_SERVER_PORT ); michael@0: netaddr.inet.ip = PR_htonl( MY_INADDR ); michael@0: michael@0: /* --- Bind the socket --- */ michael@0: while ( !bound ) michael@0: { michael@0: DPRINTF("udpsrv: UDP_Server(): Binding socket\n" ); michael@0: rv = PR_Bind( svrSock, &netaddr ); michael@0: if ( rv < 0 ) michael@0: { michael@0: if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR ) michael@0: { michael@0: if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ michael@0: PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n"); michael@0: PR_Sleep( PR_MillisecondsToInterval( 2000 )); michael@0: continue; michael@0: } michael@0: else michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ michael@0: PR_Bind(): failed: %ld with error: %ld\n", michael@0: rv, PR_GetError() ); michael@0: PR_Close( svrSock ); michael@0: return; michael@0: } michael@0: } michael@0: else michael@0: bound = PR_TRUE; michael@0: } michael@0: ListNetAddr( "UDP_Server: after bind", &netaddr ); michael@0: michael@0: /* --- Recv the socket --- */ michael@0: while( !endOfInput ) michael@0: { michael@0: DPRINTF("udpsrv: UDP_Server(): RecvFrom() socket\n" ); michael@0: rv = PR_RecvFrom( svrSock, svrBuf, numBytes, 0, &netaddr, UDP_TIMEOUT ); michael@0: if ( rv == -1 ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Server(): PR_RecvFrom(): failed with error: %ld\n", michael@0: PR_GetError() ); michael@0: PR_Close( svrSock ); michael@0: return; michael@0: } michael@0: ListNetAddr( "UDP_Server after RecvFrom", &netaddr ); michael@0: michael@0: srvBytesRead += rv; michael@0: michael@0: if ( svrBuf[0] == 'E' ) michael@0: { michael@0: DPRINTF("udpsrv: UDP_Server(): EOF on input detected\n" ); michael@0: endOfInput = PR_TRUE; michael@0: } michael@0: michael@0: /* --- Send the socket --- */ michael@0: DPRINTF("udpsrv: UDP_Server(): SendTo(): socket\n" ); michael@0: rv = PR_SendTo( svrSock, svrBuf, rv, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT ); michael@0: if ( rv == -1 ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Server(): PR_SendTo(): failed with error: %ld\n", michael@0: PR_GetError() ); michael@0: PR_Close( svrSock ); michael@0: return; michael@0: } michael@0: ListNetAddr( "UDP_Server after SendTo", &netaddr ); michael@0: } michael@0: michael@0: /* --- Close the socket --- */ michael@0: DPRINTF("udpsrv: UDP_Server(): Closing socket\n" ); michael@0: rv = PR_Close( svrSock ); michael@0: if ( rv != PR_SUCCESS ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Server(): PR_Close(): failed to close socket\n" ); michael@0: return; michael@0: } michael@0: michael@0: DPRINTF("udpsrv: UDP_Server(): Normal end\n" ); michael@0: } /* --- end UDP_Server() --- */ michael@0: michael@0: michael@0: static char cltBuf[UDP_BUF_SIZE]; michael@0: static char cltBufin[UDP_BUF_SIZE]; michael@0: /******************************************************************** michael@0: ** UDP_Client() -- Test a UDP client application michael@0: ** michael@0: ** Description: michael@0: ** michael@0: ** Arguments: michael@0: ** michael@0: ** michael@0: ** Returns: michael@0: ** 0 -- Successful execution michael@0: ** 1 -- Test failed. michael@0: ** michael@0: ** Notes: michael@0: ** michael@0: ** michael@0: ******************************************************************** michael@0: */ michael@0: static void PR_CALLBACK UDP_Client( void *arg ) michael@0: { michael@0: PRFileDesc *cltSock; michael@0: PRInt32 rv; michael@0: PRBool bound = PR_FALSE; michael@0: PRNetAddr netaddr; michael@0: PRNetAddr netaddrx; michael@0: PRBool endOfInput = PR_FALSE; michael@0: PRInt32 numBytes = UDP_DGRAM_SIZE; michael@0: PRInt32 writeThisMany = UDP_AMOUNT_TO_WRITE; michael@0: int i; michael@0: michael@0: michael@0: DPRINTF("udpsrv: UDP_Client(): starting\n" ); michael@0: michael@0: /* --- Create the socket --- */ michael@0: cltSock = PR_NewUDPSocket(); michael@0: if ( cltSock == NULL ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Client(): PR_NewUDPSocket() returned NULL\n" ); michael@0: return; michael@0: } michael@0: michael@0: /* --- Initialize the sockaddr_in structure --- */ michael@0: memset( &netaddr, 0, sizeof( netaddr )); michael@0: netaddr.inet.family = PR_AF_INET; michael@0: netaddr.inet.ip = PR_htonl( MY_INADDR ); michael@0: netaddr.inet.port = PR_htons( UDP_CLIENT_PORT ); michael@0: michael@0: /* --- Initialize the write buffer --- */ michael@0: for ( i = 0; i < UDP_BUF_SIZE ; i++ ) michael@0: cltBuf[i] = i; michael@0: michael@0: /* --- Bind the socket --- */ michael@0: while ( !bound ) michael@0: { michael@0: DPRINTF("udpsrv: UDP_Client(): Binding socket\n" ); michael@0: rv = PR_Bind( cltSock, &netaddr ); michael@0: if ( rv < 0 ) michael@0: { michael@0: if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR ) michael@0: { michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Client(): PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n"); michael@0: PR_Sleep( PR_MillisecondsToInterval( 2000 )); michael@0: continue; michael@0: } michael@0: else michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Client(): PR_Bind(): failed: %ld with error: %ld\n", michael@0: rv, PR_GetError() ); michael@0: PR_Close( cltSock ); michael@0: return; michael@0: } michael@0: } michael@0: else michael@0: bound = PR_TRUE; michael@0: } michael@0: ListNetAddr( "UDP_Client after Bind", &netaddr ); michael@0: michael@0: /* --- Initialize the sockaddr_in structure --- */ michael@0: memset( &netaddr, 0, sizeof( netaddr )); michael@0: netaddr.inet.family = PR_AF_INET; michael@0: netaddr.inet.ip = PR_htonl( PEER_INADDR ); michael@0: netaddr.inet.port = PR_htons( UDP_SERVER_PORT ); michael@0: michael@0: /* --- send and receive packets until no more data left */ michael@0: while( !endOfInput ) michael@0: { michael@0: /* michael@0: ** Signal EOF in the data stream on the last packet michael@0: */ michael@0: if ( writeThisMany <= UDP_DGRAM_SIZE ) michael@0: { michael@0: DPRINTF("udpsrv: UDP_Client(): Send EOF packet\n" ); michael@0: cltBuf[0] = 'E'; michael@0: endOfInput = PR_TRUE; michael@0: } michael@0: michael@0: /* --- SendTo the socket --- */ michael@0: if ( writeThisMany > UDP_DGRAM_SIZE ) michael@0: numBytes = UDP_DGRAM_SIZE; michael@0: else michael@0: numBytes = writeThisMany; michael@0: writeThisMany -= numBytes; michael@0: { michael@0: char mbuf[256]; michael@0: sprintf( mbuf, "udpsrv: UDP_Client(): write_this_many: %d, numbytes: %d\n", michael@0: writeThisMany, numBytes ); michael@0: DPRINTF( mbuf ); michael@0: } michael@0: michael@0: DPRINTF("udpsrv: UDP_Client(): SendTo(): socket\n" ); michael@0: rv = PR_SendTo( cltSock, cltBuf, numBytes, 0, &netaddr, UDP_TIMEOUT ); michael@0: if ( rv == -1 ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) michael@0: PR_fprintf(output, michael@0: "udpsrv: UDP_Client(): PR_SendTo(): failed with error: %ld\n", michael@0: PR_GetError() ); michael@0: PR_Close( cltSock ); michael@0: return; michael@0: } michael@0: ListNetAddr( "UDP_Client after SendTo", &netaddr ); michael@0: michael@0: /* --- RecvFrom the socket --- */ michael@0: memset( cltBufin, 0, UDP_BUF_SIZE ); michael@0: DPRINTF("udpsrv: UDP_Client(): RecvFrom(): socket\n" ); michael@0: rv = PR_RecvFrom( cltSock, cltBufin, numBytes, 0, &netaddrx, UDP_TIMEOUT ); michael@0: if ( rv == -1 ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) PR_fprintf(output, michael@0: "udpsrv: UDP_Client(): PR_RecvFrom(): failed with error: %ld\n", michael@0: PR_GetError() ); michael@0: PR_Close( cltSock ); michael@0: return; michael@0: } michael@0: ListNetAddr( "UDP_Client after RecvFrom()", &netaddr ); michael@0: cltBytesRead += rv; michael@0: michael@0: /* --- verify buffer --- */ michael@0: for ( i = 0; i < rv ; i++ ) michael@0: { michael@0: if ( cltBufin[i] != i ) michael@0: { michael@0: /* --- special case, end of input --- */ michael@0: if ( endOfInput && i == 0 && cltBufin[0] == 'E' ) michael@0: continue; michael@0: passed = PR_FALSE; michael@0: if (debug_mode) PR_fprintf(output, michael@0: "udpsrv: UDP_Client(): return data mismatch\n" ); michael@0: PR_Close( cltSock ); michael@0: return; michael@0: } michael@0: } michael@0: if (debug_mode) PR_fprintf(output, "."); michael@0: } michael@0: michael@0: /* --- Close the socket --- */ michael@0: DPRINTF("udpsrv: UDP_Server(): Closing socket\n" ); michael@0: rv = PR_Close( cltSock ); michael@0: if ( rv != PR_SUCCESS ) michael@0: { michael@0: passed = PR_FALSE; michael@0: if (debug_mode) PR_fprintf(output, michael@0: "udpsrv: UDP_Client(): PR_Close(): failed to close socket\n" ); michael@0: return; michael@0: } michael@0: DPRINTF("udpsrv: UDP_Client(): ending\n" ); michael@0: } /* --- end UDP_Client() --- */ michael@0: michael@0: /******************************************************************** michael@0: ** main() -- udpsrv michael@0: ** michael@0: ** arguments: michael@0: ** michael@0: ** Returns: michael@0: ** 0 -- Successful execution michael@0: ** 1 -- Test failed. michael@0: ** michael@0: ** Description: michael@0: ** michael@0: ** Standard test case setup. michael@0: ** michael@0: ** Calls the function UDP_Server() michael@0: ** michael@0: ******************************************************************** michael@0: */ michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PRThread *srv, *clt; michael@0: /* The command line argument: -d is used to determine if the test is being run michael@0: in debug mode. The regress tool requires only one line output:PASS or FAIL. michael@0: All of the printfs associated with this test has been handled with a if (debug_mode) michael@0: test. michael@0: Usage: test_name -d -v michael@0: */ michael@0: PLOptStatus os; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "dv"); michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'd': /* debug mode */ michael@0: debug_mode = 1; michael@0: break; michael@0: case 'v': /* verbose mode */ michael@0: _debug_on = 1; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); michael@0: PR_STDIO_INIT(); michael@0: output = PR_STDERR; michael@0: michael@0: PR_SetConcurrency(4); michael@0: michael@0: /* michael@0: ** Create the Server thread michael@0: */ michael@0: DPRINTF( "udpsrv: Creating Server Thread\n" ); michael@0: srv = PR_CreateThread( PR_USER_THREAD, michael@0: UDP_Server, michael@0: (void *) 0, michael@0: PR_PRIORITY_LOW, michael@0: PR_LOCAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0 ); michael@0: if ( srv == NULL ) michael@0: { michael@0: if (debug_mode) PR_fprintf(output, "udpsrv: Cannot create server thread\n" ); michael@0: passed = PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: ** Give the Server time to Start michael@0: */ michael@0: DPRINTF( "udpsrv: Pausing to allow Server to start\n" ); michael@0: PR_Sleep( PR_MillisecondsToInterval(200) ); michael@0: michael@0: /* michael@0: ** Create the Client thread michael@0: */ michael@0: DPRINTF( "udpsrv: Creating Client Thread\n" ); michael@0: clt = PR_CreateThread( PR_USER_THREAD, michael@0: UDP_Client, michael@0: (void *) 0, michael@0: PR_PRIORITY_LOW, michael@0: PR_LOCAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0 ); michael@0: if ( clt == NULL ) michael@0: { michael@0: if (debug_mode) PR_fprintf(output, "udpsrv: Cannot create server thread\n" ); michael@0: passed = PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: DPRINTF("udpsrv: Waiting to join Server & Client Threads\n" ); michael@0: PR_JoinThread( srv ); michael@0: PR_JoinThread( clt ); michael@0: michael@0: /* michael@0: ** Evaluate test results michael@0: */ michael@0: if (debug_mode) PR_fprintf(output, "\n\nudpsrv: main(): cltBytesRead(%ld), \ michael@0: srvBytesRead(%ld), expected(%ld)\n", michael@0: cltBytesRead, srvBytesRead, UDP_AMOUNT_TO_WRITE ); michael@0: if ( cltBytesRead != srvBytesRead || cltBytesRead != UDP_AMOUNT_TO_WRITE ) michael@0: { michael@0: passed = PR_FALSE; michael@0: } michael@0: PR_Cleanup(); michael@0: if ( passed ) michael@0: return 0; michael@0: else michael@0: return 1; michael@0: } /* --- end main() --- */