/* * test_socket.c * * Test Socket Type * * Copyright 2004-2005 Sun Microsystems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */ #include "testutil.h" #include "testutil_nss.h" #include "pkix_pl_common.h" #define LDAP_PORT 389 static void *plContext = NULL; typedef enum { SERVER_LISTENING, SERVER_RECV1, SERVER_POLL1, SERVER_SEND2, SERVER_POLL2, SERVER_RECV3, SERVER_POLL3, SERVER_SEND4, SERVER_POLL4, SERVER_DONE, SERVER_FAILED } SERVER_STATE; typedef enum { CLIENT_WAITFORCONNECT, CLIENT_SEND1, CLIENT_POLL1, CLIENT_RECV2, CLIENT_POLL2, CLIENT_SEND3, CLIENT_POLL3, CLIENT_RECV4, CLIENT_POLL4, CLIENT_DONE, CLIENT_FAILED } CLIENT_STATE; SERVER_STATE serverState; CLIENT_STATE clientState; PKIX_PL_Socket *sSock = NULL; PKIX_PL_Socket *cSock = NULL; PKIX_PL_Socket *rendezvousSock = NULL; PKIX_PL_Socket_Callback *sCallbackList; PKIX_PL_Socket_Callback *cCallbackList; PKIX_PL_Socket_Callback *rvCallbackList; PRNetAddr serverNetAddr; PRNetAddr clientNetAddr; PRIntn backlog = 0; PRIntervalTime timeout = 0; char *sendBuf1 = "Hello, world!"; char *sendBuf2 = "Ack"; char *sendBuf3 = "What do you mean, \"Ack\"?"; char *sendBuf4 = "What do you mean, \"What do you mean, \'Ack\'?\"?"; char rcvBuf1[100]; char rcvBuf2[100]; static void printUsage(char *testname) { char *fmt = "USAGE: %s [-arenas] server:port\n"; printf(fmt, testname); } /* Functional tests for Socket public functions */ static void do_other_work(void) { /* while waiting for nonblocking I/O to complete */ (void)PR_Sleep(2 * 60); } static PKIX_Boolean server() { PKIX_Int32 bytesRead = 0; PKIX_Int32 bytesWritten = 0; PKIX_Boolean keepGoing = PKIX_FALSE; PKIX_TEST_STD_VARS(); switch (serverState) { case SERVER_LISTENING: subTest("SERVER_LISTENING"); PKIX_TEST_EXPECT_NO_ERROR(sCallbackList->acceptCallback(sSock, &rendezvousSock, plContext)); if (rendezvousSock) { PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_GetCallbackList(rendezvousSock, &rvCallbackList, plContext)); serverState = SERVER_RECV1; } break; case SERVER_RECV1: subTest("SERVER_RECV1"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->recvCallback(rendezvousSock, rcvBuf1, sizeof(rcvBuf1), &bytesRead, plContext)); if (bytesRead > 0) { /* confirm that rcvBuf1 = sendBuf1 */ if ((bytesRead != (PRInt32)PL_strlen(sendBuf1) + 1) || (strncmp(sendBuf1, rcvBuf1, bytesRead) != 0)) { testError("Receive buffer mismatch\n"); } serverState = SERVER_SEND2; keepGoing = PKIX_TRUE; } else { serverState = SERVER_POLL1; } break; case SERVER_POLL1: subTest("SERVER_POLL1"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->pollCallback(rendezvousSock, NULL, &bytesRead, plContext)); if (bytesRead > 0) { /* confirm that rcvBuf1 = sendBuf1 */ if ((bytesRead != (PRInt32)PL_strlen(sendBuf1) + 1) || (strncmp(sendBuf1, rcvBuf1, bytesRead) != 0)) { testError("Receive buffer mismatch\n"); } serverState = SERVER_SEND2; keepGoing = PKIX_TRUE; } break; case SERVER_SEND2: subTest("SERVER_SEND2"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->sendCallback(rendezvousSock, sendBuf2, strlen(sendBuf2) + 1, &bytesWritten, plContext)); if (bytesWritten > 0) { serverState = SERVER_RECV3; } else { serverState = SERVER_POLL2; } break; case SERVER_POLL2: subTest("SERVER_POLL2"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->pollCallback(rendezvousSock, &bytesWritten, NULL, plContext)); if (bytesWritten > 0) { serverState = SERVER_RECV3; } break; case SERVER_RECV3: subTest("SERVER_RECV3"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->recvCallback(rendezvousSock, rcvBuf1, sizeof(rcvBuf1), &bytesRead, plContext)); if (bytesRead > 0) { serverState = SERVER_SEND4; keepGoing = PKIX_TRUE; } else { serverState = SERVER_POLL3; } break; case SERVER_POLL3: subTest("SERVER_POLL3"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->pollCallback(rendezvousSock, NULL, &bytesRead, plContext)); if (bytesRead > 0) { serverState = SERVER_SEND4; keepGoing = PKIX_TRUE; } break; case SERVER_SEND4: subTest("SERVER_SEND4"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->sendCallback(rendezvousSock, sendBuf4, strlen(sendBuf4) + 1, &bytesWritten, plContext)); if (bytesWritten > 0) { PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->shutdownCallback(rendezvousSock, plContext)); PKIX_TEST_DECREF_BC(sSock); PKIX_TEST_DECREF_BC(rendezvousSock); serverState = SERVER_DONE; } else { serverState = SERVER_POLL4; } break; case SERVER_POLL4: subTest("SERVER_POLL4"); PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->pollCallback(rendezvousSock, &bytesWritten, NULL, plContext)); if (bytesWritten > 0) { PKIX_TEST_EXPECT_NO_ERROR(rvCallbackList->shutdownCallback(rendezvousSock, plContext)); PKIX_TEST_DECREF_BC(sSock); PKIX_TEST_DECREF_BC(rendezvousSock); serverState = SERVER_DONE; } break; case SERVER_DONE: default: subTest("SERVER_DONE"); break; } cleanup: PKIX_TEST_RETURN(); return (keepGoing); } static PKIX_Boolean client() { PKIX_Boolean keepGoing = PKIX_FALSE; PKIX_Int32 bytesRead = 0; PKIX_Int32 bytesWritten = 0; PRErrorCode cStat = 0; /* At 2 seconds each cycle, this should suffice! */ PKIX_UInt32 giveUpCount = 10; PKIX_TEST_STD_VARS(); switch (clientState) { case CLIENT_WAITFORCONNECT: subTest("CLIENT_WAITFORCONNECT"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->connectcontinueCallback(cSock, &cStat, plContext)); if (cStat == 0) { clientState = CLIENT_SEND1; keepGoing = PKIX_TRUE; } else { clientState = CLIENT_WAITFORCONNECT; if (--giveUpCount == 0) { testError("Client unable to connect"); } } break; case CLIENT_SEND1: subTest("CLIENT_SEND1"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->sendCallback(cSock, sendBuf1, strlen(sendBuf1) + 1, &bytesWritten, plContext)); if (bytesWritten > 0) { clientState = CLIENT_RECV2; } else { clientState = CLIENT_POLL1; } break; case CLIENT_POLL1: subTest("CLIENT_POLL1"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->pollCallback(cSock, &bytesWritten, NULL, plContext)); if (bytesWritten > 0) { clientState = CLIENT_RECV2; } else { clientState = CLIENT_POLL1; } break; case CLIENT_RECV2: subTest("CLIENT_RECV2"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->recvCallback(cSock, rcvBuf2, sizeof(rcvBuf2), &bytesRead, plContext)); if (bytesRead > 0) { /* confirm that rcvBuf2 = sendBuf2 */ if ((bytesRead != (PRInt32)PL_strlen(sendBuf2) + 1) || (strncmp(sendBuf2, rcvBuf2, bytesRead) != 0)) { testError("Receive buffer mismatch\n"); } clientState = CLIENT_SEND3; keepGoing = PKIX_TRUE; } else { clientState = CLIENT_POLL2; } break; case CLIENT_POLL2: subTest("CLIENT_POLL2"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->pollCallback(cSock, NULL, &bytesRead, plContext)); if (bytesRead > 0) { /* confirm that rcvBuf2 = sendBuf2 */ if ((bytesRead != (PRInt32)PL_strlen(sendBuf2) + 1) || (strncmp(sendBuf2, rcvBuf2, bytesRead) != 0)) { testError("Receive buffer mismatch\n"); } clientState = CLIENT_SEND3; } else { clientState = CLIENT_POLL2; } break; case CLIENT_SEND3: subTest("CLIENT_SEND3"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->sendCallback(cSock, sendBuf3, strlen(sendBuf3) + 1, &bytesWritten, plContext)); if (bytesWritten > 0) { clientState = CLIENT_RECV4; } else { clientState = CLIENT_POLL3; } break; case CLIENT_POLL3: subTest("CLIENT_POLL3"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->pollCallback(cSock, &bytesWritten, NULL, plContext)); if (bytesWritten > 0) { clientState = CLIENT_RECV4; } else { clientState = CLIENT_POLL3; } break; case CLIENT_RECV4: subTest("CLIENT_RECV4"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->recvCallback(cSock, rcvBuf2, sizeof(rcvBuf2), &bytesRead, plContext)); if (bytesRead > 0) { PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->shutdownCallback(cSock, plContext)); PKIX_TEST_DECREF_BC(cSock); clientState = CLIENT_DONE; } else { clientState = CLIENT_POLL4; } break; case CLIENT_POLL4: subTest("CLIENT_POLL4"); clientState = CLIENT_FAILED; PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->pollCallback(cSock, NULL, &bytesRead, plContext)); if (bytesRead > 0) { PKIX_TEST_EXPECT_NO_ERROR(cCallbackList->shutdownCallback(cSock, plContext)); PKIX_TEST_DECREF_BC(cSock); clientState = CLIENT_DONE; } else { clientState = CLIENT_POLL4; } break; case CLIENT_DONE: default: subTest("CLIENT_DONE"); break; } cleanup: PKIX_TEST_RETURN(); return (keepGoing); } static void dispatcher() { PKIX_Boolean keepGoing = PKIX_FALSE; PKIX_TEST_STD_VARS(); do { if (serverState < SERVER_DONE) { do { keepGoing = server(); } while (keepGoing == PKIX_TRUE); } if (clientState < CLIENT_DONE) { do { keepGoing = client(); } while (keepGoing == PKIX_TRUE); } do_other_work(); } while ((serverState < SERVER_DONE) || (clientState < CLIENT_DONE)); PKIX_TEST_RETURN(); } int test_socket(int argc, char *argv[]) { int j = 0; PKIX_UInt32 actualMinorVersion; char buf[PR_NETDB_BUF_SIZE]; char *serverName = NULL; char *sepPtr = NULL; PRHostEnt hostent; PRUint16 portNum = 0; PRStatus prstatus = PR_FAILURE; PRErrorCode cStat = 0; void *ipaddr = NULL; PKIX_Error *bindError = NULL; PRIntn hostenum; PKIX_TEST_STD_VARS(); startTests("Socket"); PKIX_TEST_EXPECT_NO_ERROR( PKIX_PL_NssContext_Create(0, PKIX_FALSE, NULL, &plContext)); if (argc != (j + 2)) { printUsage(argv[0]); pkixTestErrorMsg = "Missing command line argument."; goto cleanup; } serverName = argv[j + 1]; subTest("Using pkix_pl_Socket_CreateByName"); PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_CreateByName(PKIX_TRUE, timeout, serverName, &cStat, &sSock, plContext)); PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_GetCallbackList(sSock, &sCallbackList, plContext)); PKIX_TEST_EXPECT_NO_ERROR(sCallbackList->listenCallback(sSock, backlog, plContext)); serverState = SERVER_LISTENING; PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_CreateByName(PKIX_FALSE, timeout, serverName, &cStat, &cSock, plContext)); PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_GetCallbackList(cSock, &cCallbackList, plContext)); if ((timeout == 0) && (cStat == PR_IN_PROGRESS_ERROR)) { clientState = CLIENT_WAITFORCONNECT; } else { clientState = CLIENT_SEND1; } dispatcher(); subTest("Using pkix_pl_Socket_Create"); sepPtr = strchr(serverName, ':'); /* First strip off the portnum, if present, from the end of the name */ if (sepPtr) { *sepPtr++ = '\0'; portNum = (PRUint16)atoi(sepPtr); } else { portNum = (PRUint16)LDAP_PORT; } /* * The hostname may be a fully-qualified name. Just * use the leftmost component in our lookup. */ sepPtr = strchr(serverName, '.'); if (sepPtr) { *sepPtr++ = '\0'; } prstatus = PR_GetHostByName(serverName, buf, sizeof(buf), &hostent); if ((prstatus != PR_SUCCESS) || (hostent.h_length != 4)) { printUsage(argv[0]); pkixTestErrorMsg = "PR_GetHostByName rejects command line argument."; goto cleanup; } serverNetAddr.inet.family = PR_AF_INET; serverNetAddr.inet.port = PR_htons(portNum); serverNetAddr.inet.ip = PR_INADDR_ANY; hostenum = PR_EnumerateHostEnt(0, &hostent, portNum, &clientNetAddr); if (hostenum == -1) { pkixTestErrorMsg = "PR_EnumerateHostEnt failed."; goto cleanup; } backlog = 5; /* timeout = PR_INTERVAL_NO_TIMEOUT; */ /* timeout = 0; nonblocking */ timeout = 0; bindError = pkix_pl_Socket_Create(PKIX_TRUE, timeout, &serverNetAddr, &cStat, &sSock, plContext); /* If PR_Bind can't handle INADDR_ANY, try it with the real name */ if (bindError) { PKIX_TEST_DECREF_BC(bindError); serverNetAddr.inet.ip = PR_htonl(*(PRUint32 *)ipaddr); PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_Create(PKIX_TRUE, timeout, &serverNetAddr, &cStat, &sSock, plContext)); } PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_GetCallbackList(sSock, &sCallbackList, plContext)); PKIX_TEST_EXPECT_NO_ERROR(sCallbackList->listenCallback(sSock, backlog, plContext)); serverState = SERVER_LISTENING; PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_Create(PKIX_FALSE, timeout, &clientNetAddr, &cStat, &cSock, plContext)); PKIX_TEST_EXPECT_NO_ERROR(pkix_pl_Socket_GetCallbackList(cSock, &cCallbackList, plContext)); if ((timeout == 0) && (cStat == PR_IN_PROGRESS_ERROR)) { clientState = CLIENT_WAITFORCONNECT; } else { clientState = CLIENT_SEND1; } dispatcher(); cleanup: PKIX_TEST_DECREF_AC(sSock); PKIX_TEST_DECREF_AC(cSock); PKIX_TEST_DECREF_AC(rendezvousSock); PKIX_TEST_RETURN(); endTests("Socket"); return (0); }