andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: /* andre@0: * pkix_pl_socket.c andre@0: * andre@0: * Socket Function Definitions andre@0: * andre@0: */ andre@0: andre@0: /* andre@0: * If Socket Tracing is active, messages sent and received will be andre@0: * timestamped and dumped (to stdout) in standard hex-dump format. E.g., andre@0: * andre@0: * 1116612359156140: andre@0: * 28F0: 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00 Hello, world!. andre@0: * andre@0: * The timestamp is not formatted to be meaningful except as an increasing andre@0: * value of seconds.microseconds, which is good enough to correlate two andre@0: * sides of a message exchange and to figure durations. andre@0: * andre@0: * Code to perform Socket tracing will be compiled in if PKIX_SOCKETTRACE andre@0: * is defined, but that doesn't mean socket tracing is active. Tracing also andre@0: * requires that the Boolean socketTraceFlag is set to PKIX_TRUE. That is andre@0: * the default value, but it can be overridden by using the debugger to andre@0: * change its value -- allowing tracing to be turned on and off at various andre@0: * breakpoints -- or by setting the environment variable SOCKETTRACE. A andre@0: * value of 1 sets socketTraceFlag to PKIX_TRUE (tracing on), and any other andre@0: * value sets socketTraceFlag to PKIX_FALSE (tracing off). The environment andre@0: * value is checked during system initialization. andre@0: */ andre@0: #ifndef BUILD_OPT andre@0: #define PKIX_SOCKETTRACE 1 andre@0: #endif andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: #define PKIX_SOCKETTRACE 1 andre@0: #endif andre@0: andre@0: #include "pkix_pl_socket.h" andre@0: andre@0: /* --Private-Socket-Functions---------------------------------- */ andre@0: andre@0: #ifdef PKIX_SOCKETTRACE andre@0: static PKIX_Boolean socketTraceFlag = PKIX_FALSE; andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_socket_timestamp andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions prints to stdout the time of day, as obtained from the andre@0: * system function gettimeofday, as seconds.microseconds. Its resolution andre@0: * is whatever the system call provides. andre@0: * andre@0: * PARAMETERS: andre@0: * none andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static void pkix_pl_socket_timestamp() { andre@0: PRInt64 prTime; andre@0: prTime = PR_Now(); andre@0: printf("%lld:\n", prTime); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_socket_hexDigit andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions prints to stdout the byte "byteVal" as two hex digits. andre@0: * andre@0: * PARAMETERS: andre@0: * "byteVal" andre@0: * The value to be printed. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static void pkix_pl_socket_hexDigit(char byteVal) { andre@0: int n = 0; andre@0: char cHi = '\0'; andre@0: char cLow = '\0'; andre@0: n = ((byteVal >> 4) & 0xf); andre@0: if (n > 9) { andre@0: cHi = (char) ((n - 10) + 'A'); andre@0: } else { andre@0: cHi = (char) (n + '0'); andre@0: } andre@0: n = byteVal & 0xf; andre@0: if (n > 9) { andre@0: cLow = (char) ((n - 10) + 'A'); andre@0: } else { andre@0: cLow = (char) (n + '0'); andre@0: } andre@0: (void) printf("%c%c", cHi, cLow); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_socket_linePrefix andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions prints to stdout the address provided by "addr" as four andre@0: * hexadecimal digits followed by a colon and a space. andre@0: * andre@0: * PARAMETERS: andre@0: * "addr" andre@0: * The address to be printed andre@0: * none andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static void pkix_pl_socket_linePrefix(PKIX_UInt32 addr) { andre@0: pkix_pl_socket_hexDigit((char)((addr >> 8) & 0xff)); andre@0: pkix_pl_socket_hexDigit((char)(addr & 0xff)); andre@0: (void) printf(": "); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_socket_traceLine andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions prints to stdout the sixteen bytes beginning at the andre@0: * address pointed to by "ptr". The bytes are printed as sixteen pairs andre@0: * of hexadecimal characters followed by an ascii interpretation, in which andre@0: * characters from 0x20 to 0x7d are shown as their ascii equivalents, and andre@0: * other values are represented as periods. andre@0: * andre@0: * PARAMETERS: andre@0: * "ptr" andre@0: * The address of the first of the bytes to be printed andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static void pkix_pl_socket_traceLine(char *ptr) { andre@0: PKIX_UInt32 i = 0; andre@0: pkix_pl_socket_linePrefix((PKIX_UInt32)ptr); andre@0: for (i = 0; i < 16; i++) { andre@0: printf(" "); andre@0: pkix_pl_socket_hexDigit(ptr[i]); andre@0: if (i == 7) { andre@0: printf(" "); andre@0: } andre@0: } andre@0: printf(" "); andre@0: for (i = 0; i < 16; i++) { andre@0: if ((ptr[i] < ' ') || (ptr[i] > '}')) { andre@0: printf("."); andre@0: } else { andre@0: printf("%c", ptr[i]); andre@0: } andre@0: } andre@0: printf("\n"); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_socket_tracePartialLine andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions prints to stdout the number of bytes given by "nBytes", andre@0: * beginning at the address pointed to by "ptr". The bytes are printed as andre@0: * pairs of hexadecimal characters followed by an ascii interpretation, in andre@0: * which characters from 0x20 to 0x7d are shown as their ascii equivalents, andre@0: * and other values are represented as periods. andre@0: * andre@0: * PARAMETERS: andre@0: * "ptr" andre@0: * The address of the first of the bytes to be printed andre@0: * "nBytes" andre@0: * The Int32 value giving the number of bytes to be printed. If "nBytes" andre@0: * is greater than sixteen, the results will be unattractive. andre@0: * none andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static void pkix_pl_socket_tracePartialLine(char *ptr, PKIX_UInt32 nBytes) { andre@0: PKIX_UInt32 i = 0; andre@0: if (nBytes > 0) { andre@0: pkix_pl_socket_linePrefix((PKIX_UInt32)ptr); andre@0: } andre@0: for (i = 0; i < nBytes; i++) { andre@0: printf(" "); andre@0: pkix_pl_socket_hexDigit(ptr[i]); andre@0: if (i == 7) { andre@0: printf(" "); andre@0: } andre@0: } andre@0: for (i = nBytes; i < 16; i++) { andre@0: printf(" "); andre@0: if (i == 7) { andre@0: printf(" "); andre@0: } andre@0: } andre@0: printf(" "); andre@0: for (i = 0; i < nBytes; i++) { andre@0: if ((ptr[i] < ' ') || (ptr[i] > '}')) { andre@0: printf("."); andre@0: } else { andre@0: printf("%c", ptr[i]); andre@0: } andre@0: } andre@0: printf("\n"); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_socket_tracebuff andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions prints to stdout the number of bytes given by "nBytes", andre@0: * beginning with the byte pointed to by "buf". The output is preceded by andre@0: * a timestamp, and each group of sixteen (and a remainder, if any) is andre@0: * preceded by its address. The contents are shown in hexadecimal and as andre@0: * ascii characters. If "nBytes" is zero, the timestamp and starting andre@0: * address are displayed. andre@0: * andre@0: * PARAMETERS: andre@0: * "buf" andre@0: * The starting address of the bytes to be printed andre@0: * "nBytes" andre@0: * The number of bytes to be printed andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: void pkix_pl_socket_tracebuff(void *buf, PKIX_UInt32 nBytes) { andre@0: PKIX_UInt32 bytesRemaining = nBytes; andre@0: PKIX_UInt32 offset = 0; andre@0: char *bufptr = (char *)buf; andre@0: andre@0: if (socketTraceFlag == PKIX_FALSE) return; andre@0: andre@0: pkix_pl_socket_timestamp(); andre@0: /* andre@0: * Special case: if called with length of zero, just do address andre@0: */ andre@0: if (nBytes == 0) { andre@0: pkix_pl_socket_linePrefix((PKIX_UInt32)buf); andre@0: printf("\n"); andre@0: } else { andre@0: while (bytesRemaining >= 16) { andre@0: pkix_pl_socket_traceLine(&bufptr[offset]); andre@0: bytesRemaining -= 16; andre@0: offset += 16; andre@0: } andre@0: pkix_pl_socket_tracePartialLine andre@0: (&bufptr[offset], bytesRemaining); andre@0: } andre@0: } andre@0: andre@0: #endif andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_SetNonBlocking andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions sets the socket represented by the PRFileDesc "fileDesc" andre@0: * to nonblocking mode. andre@0: * andre@0: * PARAMETERS: andre@0: * "fileDesc" andre@0: * The address of the PRFileDesc whose I/O mode is to be set andre@0: * non-blocking. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_SetNonBlocking( andre@0: PRFileDesc *fileDesc, andre@0: void *plContext) andre@0: { andre@0: PRStatus rv = PR_FAILURE; andre@0: PRSocketOptionData sockOptionData; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_SetNonBlocking"); andre@0: PKIX_NULLCHECK_ONE(fileDesc); andre@0: andre@0: sockOptionData.option = PR_SockOpt_Nonblocking; andre@0: sockOptionData.value.non_blocking = PR_TRUE; andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rv, fileDesc->methods->setsocketoption, andre@0: (fileDesc, &sockOptionData)); andre@0: andre@0: if (rv != PR_SUCCESS) { andre@0: PKIX_ERROR(PKIX_UNABLETOSETSOCKETTONONBLOCKING); andre@0: } andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_CreateClient andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions creates a client socket for the PKIX_PL_Socket pointed to andre@0: * by "socket". If "socket" was created with a timeout value of zero, the andre@0: * client socket is set to use nonblocking I/O. andre@0: * andre@0: * PARAMETERS: andre@0: * "socket" andre@0: * The address of the Socket for which a client socket is to be andre@0: * created. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_CreateClient( andre@0: PKIX_PL_Socket *socket, andre@0: void *plContext) andre@0: { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: PRErrorCode errorcode = 0; andre@0: #endif andre@0: PRFileDesc *mySock = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_CreateClient"); andre@0: PKIX_NULLCHECK_ONE(socket); andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, mySock, PR_NewTCPSocket, ()); andre@0: if (!mySock) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: errorcode = PR_GetError(); andre@0: printf andre@0: ("pkix_pl_Socket_CreateClient: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRNEWTCPSOCKETFAILED); andre@0: } andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf("Created socket, PRFileDesc @ %#X\n", mySock); andre@0: #endif andre@0: andre@0: socket->clientSock = mySock; andre@0: socket->status = SOCKET_UNCONNECTED; andre@0: if (socket->timeout == 0) { andre@0: PKIX_CHECK(pkix_pl_Socket_SetNonBlocking(mySock, plContext), andre@0: PKIX_SOCKETSETNONBLOCKINGFAILED); andre@0: } andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_CreateServer andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions creates a server socket for the PKIX_PL_Socket pointed to andre@0: * by "socket". If "socket" was created with a timeout value of zero, the andre@0: * server socket is set to use nonblocking I/O. andre@0: * andre@0: * Warning: there seems to be a problem with operating a server socket in andre@0: * non-blocking mode. If the server calls Recv prior to a corresponding andre@0: * Send, the message may be lost. andre@0: * andre@0: * PARAMETERS: andre@0: * "socket" andre@0: * The address of the Socket for which a server socket is to be andre@0: * created. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_CreateServer( andre@0: PKIX_PL_Socket *socket, andre@0: void *plContext) andre@0: { andre@0: /* #ifdef PKIX_SOCKETDEBUG */ andre@0: PRErrorCode errorcode = 0; andre@0: /* #endif */ andre@0: PRStatus rv = PR_FAILURE; andre@0: PRFileDesc *serverSock = NULL; andre@0: PRSocketOptionData sockOptionData; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_CreateServer"); andre@0: PKIX_NULLCHECK_ONE(socket); andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, serverSock, PR_NewTCPSocket, ()); andre@0: if (!serverSock) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: errorcode = PR_GetError(); andre@0: printf andre@0: ("pkix_pl_Socket_CreateServer: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRNEWTCPSOCKETFAILED); andre@0: } andre@0: andre@0: socket->serverSock = serverSock; andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf("Created socket, PRFileDesc @ %#X\n", serverSock); andre@0: #endif andre@0: andre@0: if (socket->timeout == 0) { andre@0: PKIX_CHECK(pkix_pl_Socket_SetNonBlocking(serverSock, plContext), andre@0: PKIX_SOCKETSETNONBLOCKINGFAILED); andre@0: } andre@0: andre@0: sockOptionData.option = PR_SockOpt_Reuseaddr; andre@0: sockOptionData.value.reuse_addr = PR_TRUE; andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rv, serverSock->methods->setsocketoption, andre@0: (serverSock, &sockOptionData)); andre@0: andre@0: if (rv != PR_SUCCESS) { andre@0: PKIX_ERROR(PKIX_UNABLETOSETSOCKETTONONBLOCKING); andre@0: } andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rv, PR_Bind, (serverSock, socket->netAddr)); andre@0: andre@0: if (rv == PR_FAILURE) { andre@0: /* #ifdef PKIX_SOCKETDEBUG */ andre@0: errorcode = PR_GetError(); andre@0: printf andre@0: ("pkix_pl_Socket_CreateServer: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: /* #endif */ andre@0: PKIX_ERROR(PKIX_PRBINDFAILED); andre@0: } andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf("Successful bind!\n"); andre@0: #endif andre@0: andre@0: socket->status = SOCKET_BOUND; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Connect andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions performs the connect function for the client socket andre@0: * specified in "socket", storing the status at "pStatus". andre@0: * andre@0: * PARAMETERS: andre@0: * "socket" andre@0: * The address of the Socket for which a connect is to be performed. andre@0: * Must be non-NULL. andre@0: * "pStatus" andre@0: * The address at which the connection status is stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Connect( andre@0: PKIX_PL_Socket *socket, andre@0: PRErrorCode *pStatus, andre@0: void *plContext) andre@0: { andre@0: PRStatus rv = PR_FAILURE; andre@0: PRErrorCode errorcode = 0; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Connect"); andre@0: PKIX_NULLCHECK_TWO(socket, socket->clientSock); andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rv, PR_Connect, andre@0: (socket->clientSock, socket->netAddr, socket->timeout)); andre@0: andre@0: if (rv == PR_FAILURE) { andre@0: errorcode = PR_GetError(); andre@0: *pStatus = errorcode; andre@0: if (errorcode == PR_IN_PROGRESS_ERROR) { andre@0: socket->status = SOCKET_CONNECTPENDING; andre@0: goto cleanup; andre@0: } else { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf andre@0: ("pkix_pl_Socket_Connect: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRCONNECTFAILED); andre@0: } andre@0: } andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf("Successful connect!\n"); andre@0: #endif andre@0: andre@0: *pStatus = 0; andre@0: socket->status = SOCKET_CONNECTED; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_ConnectContinue andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions continues the connect function for the client socket andre@0: * specified in "socket", storing the status at "pStatus". It is expected that andre@0: * the non-blocking connect has returned PR_IN_PROGRESS_ERROR. andre@0: * andre@0: * PARAMETERS: andre@0: * "socket" andre@0: * The address of the Socket for which a connect is to be continued. andre@0: * Must be non-NULL. andre@0: * "pStatus" andre@0: * The address at which the connection status is stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_ConnectContinue( andre@0: PKIX_PL_Socket *socket, andre@0: PRErrorCode *pStatus, andre@0: void *plContext) andre@0: { andre@0: PRStatus rv = PR_FAILURE; andre@0: PRErrorCode errorcode = 0; andre@0: PRPollDesc pollDesc; andre@0: PRInt32 numEvents = 0; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_ConnectContinue"); andre@0: PKIX_NULLCHECK_TWO(socket, socket->clientSock); andre@0: andre@0: pollDesc.fd = socket->clientSock; andre@0: pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; andre@0: pollDesc.out_flags = 0; andre@0: PKIX_PL_NSSCALLRV(SOCKET, numEvents, PR_Poll, (&pollDesc, 1, 0)); andre@0: if (numEvents < 0) { andre@0: PKIX_ERROR(PKIX_PRPOLLFAILED); andre@0: } andre@0: andre@0: if (numEvents == 0) { andre@0: *pStatus = PR_IN_PROGRESS_ERROR; andre@0: goto cleanup; andre@0: } andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rv, PR_ConnectContinue, andre@0: (socket->clientSock, pollDesc.out_flags)); andre@0: andre@0: /* andre@0: * PR_ConnectContinue sometimes lies. It returns PR_SUCCESS andre@0: * even though the connection is not yet ready. But its deceit andre@0: * is betrayed by the contents of out_flags! andre@0: */ andre@0: if ((rv == PR_SUCCESS) && (pollDesc.out_flags == PR_POLL_ERR)) { andre@0: *pStatus = PR_IN_PROGRESS_ERROR; andre@0: goto cleanup; andre@0: } andre@0: andre@0: if (rv == PR_FAILURE) { andre@0: errorcode = PR_GetError(); andre@0: *pStatus = errorcode; andre@0: if (errorcode == PR_IN_PROGRESS_ERROR) { andre@0: goto cleanup; andre@0: } else { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf andre@0: ("pkix_pl_Socket_ConnectContinue: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRCONNECTCONTINUEFAILED); andre@0: } andre@0: } andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf("Successful connect!\n"); andre@0: #endif andre@0: andre@0: *pStatus = 0; andre@0: socket->status = SOCKET_CONNECTED; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Destroy andre@0: * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Destroy( andre@0: PKIX_PL_Object *object, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Socket *socket = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Destroy"); andre@0: PKIX_NULLCHECK_ONE(object); andre@0: andre@0: PKIX_CHECK(pkix_CheckType andre@0: (object, PKIX_SOCKET_TYPE, plContext), andre@0: PKIX_OBJECTNOTANSOCKET); andre@0: andre@0: socket = (PKIX_PL_Socket *)object; andre@0: andre@0: if (socket->isServer) { andre@0: if (socket->serverSock) { andre@0: PR_Close(socket->serverSock); andre@0: } andre@0: } else { andre@0: if (socket->clientSock) { andre@0: PR_Close(socket->clientSock); andre@0: } andre@0: } andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Hashcode andre@0: * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h) andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Hashcode( andre@0: PKIX_PL_Object *object, andre@0: PKIX_UInt32 *pHashcode, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Socket *socket = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Hashcode"); andre@0: PKIX_NULLCHECK_TWO(object, pHashcode); andre@0: andre@0: PKIX_CHECK(pkix_CheckType(object, PKIX_SOCKET_TYPE, plContext), andre@0: PKIX_OBJECTNOTSOCKET); andre@0: andre@0: socket = (PKIX_PL_Socket *)object; andre@0: andre@0: *pHashcode = (((socket->timeout << 3) + andre@0: (socket->netAddr->inet.family << 3)) + andre@0: (*((PKIX_UInt32 *)&(socket->netAddr->inet.ip)))) + andre@0: socket->netAddr->inet.port; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Equals andre@0: * (see comments for PKIX_PL_EqualsCallback in pkix_pl_system.h) andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Equals( andre@0: PKIX_PL_Object *firstObject, andre@0: PKIX_PL_Object *secondObject, andre@0: PKIX_Int32 *pResult, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Socket *firstSocket = NULL; andre@0: PKIX_PL_Socket *secondSocket = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Equals"); andre@0: PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult); andre@0: andre@0: *pResult = PKIX_FALSE; andre@0: andre@0: PKIX_CHECK(pkix_CheckTypes andre@0: (firstObject, secondObject, PKIX_SOCKET_TYPE, plContext), andre@0: PKIX_OBJECTNOTSOCKET); andre@0: andre@0: firstSocket = (PKIX_PL_Socket *)firstObject; andre@0: secondSocket = (PKIX_PL_Socket *)secondObject; andre@0: andre@0: if (firstSocket->timeout != secondSocket->timeout) { andre@0: goto cleanup; andre@0: } andre@0: andre@0: if (firstSocket->netAddr == secondSocket->netAddr) { andre@0: *pResult = PKIX_TRUE; andre@0: goto cleanup; andre@0: } andre@0: andre@0: if ((firstSocket->netAddr->inet.family != andre@0: secondSocket->netAddr->inet.family) || andre@0: (*((PKIX_UInt32 *)&(firstSocket->netAddr->inet.ip)) != andre@0: *((PKIX_UInt32 *)&(secondSocket->netAddr->inet.ip))) || andre@0: (firstSocket->netAddr->inet.port != andre@0: secondSocket->netAddr->inet.port)) { andre@0: andre@0: goto cleanup; andre@0: andre@0: } andre@0: andre@0: *pResult = PKIX_TRUE; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_RegisterSelf andre@0: * andre@0: * DESCRIPTION: andre@0: * Registers PKIX_PL_SOCKET_TYPE and its related andre@0: * functions with systemClasses[] andre@0: * andre@0: * THREAD SAFETY: andre@0: * Not Thread Safe - for performance and complexity reasons andre@0: * andre@0: * Since this function is only called by PKIX_PL_Initialize, which should andre@0: * only be called once, it is acceptable that this function is not andre@0: * thread-safe. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_Socket_RegisterSelf(void *plContext) andre@0: { andre@0: extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; andre@0: pkix_ClassTable_Entry entry; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_RegisterSelf"); andre@0: andre@0: entry.description = "Socket"; andre@0: entry.objCounter = 0; andre@0: entry.typeObjectSize = sizeof(PKIX_PL_Socket); andre@0: entry.destructor = pkix_pl_Socket_Destroy; andre@0: entry.equalsFunction = pkix_pl_Socket_Equals; andre@0: entry.hashcodeFunction = pkix_pl_Socket_Hashcode; andre@0: entry.toStringFunction = NULL; andre@0: entry.comparator = NULL; andre@0: entry.duplicateFunction = NULL; andre@0: andre@0: systemClasses[PKIX_SOCKET_TYPE] = entry; andre@0: andre@0: #ifdef PKIX_SOCKETTRACE andre@0: { andre@0: char *val = NULL; andre@0: val = PR_GetEnv("SOCKETTRACE"); andre@0: /* Is SOCKETTRACE set in the environment? */ andre@0: if ((val != NULL) && (*val != '\0')) { andre@0: socketTraceFlag = andre@0: ((*val == '1')?PKIX_TRUE:PKIX_FALSE); andre@0: } andre@0: } andre@0: #endif andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* --Public-Socket-Functions----------------------------------- */ andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Listen andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions establishes a listening queue for the server Socket andre@0: * pointed to by "socket". andre@0: * andre@0: * PARAMETERS: andre@0: * "socket" andre@0: * The address of the server socket for which the queue is to be andre@0: * established. Must be non-NULL. andre@0: * "backlog" andre@0: * The UInt32 value of the length of the queue to be established. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Listen( andre@0: PKIX_PL_Socket *socket, andre@0: PKIX_UInt32 backlog, andre@0: void *plContext) andre@0: { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: PRErrorCode errorcode = 0; andre@0: #endif andre@0: PRStatus rv = PR_FAILURE; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Listen"); andre@0: PKIX_NULLCHECK_TWO(socket, socket->serverSock); andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rv, PR_Listen, andre@0: (socket->serverSock, (PRIntn)backlog)); andre@0: andre@0: if (rv == PR_FAILURE) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: errorcode = PR_GetError(); andre@0: printf andre@0: ("pkix_pl_Socket_Listen: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRLISTENFAILED); andre@0: } andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf("Successful listen!\n"); andre@0: #endif andre@0: andre@0: socket->status = SOCKET_LISTENING; andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Shutdown andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions performs the shutdown of any connections controlled by the andre@0: * socket pointed to by "socket". andre@0: * andre@0: * PARAMETERS: andre@0: * "socket" andre@0: * The address of the socket to be shut down. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Shutdown( andre@0: PKIX_PL_Socket *socket, andre@0: void *plContext) andre@0: { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: PRErrorCode errorcode = 0; andre@0: #endif andre@0: PRStatus rv = PR_FAILURE; andre@0: PRFileDesc *fileDesc = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Shutdown"); andre@0: PKIX_NULLCHECK_ONE(socket); andre@0: andre@0: fileDesc = andre@0: (socket->isServer)?(socket->serverSock):(socket->clientSock); andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rv, PR_Shutdown, andre@0: (fileDesc, PR_SHUTDOWN_BOTH)); andre@0: andre@0: if (rv == PR_FAILURE) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: errorcode = PR_GetError(); andre@0: printf andre@0: ("pkix_pl_Socket_Shutdown: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRSHUTDOWNFAILED); andre@0: } andre@0: socket->status = SOCKET_SHUTDOWN; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Send andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions sends a message using the socket pointed to by "sendSock", andre@0: * from the buffer pointed to by "buf", of the number of bytes given by andre@0: * "bytesToWrite", storing the number of bytes actually written at andre@0: * "pBytesWritten". If "socket" is in non-blocking mode, the send operation andre@0: * may store -1 at "pBytesWritten" and the write is not complete until a andre@0: * corresponding pkix_pl_Poll call has indicated its completion by returning andre@0: * a non-negative value for bytes written. andre@0: * andre@0: * PARAMETERS: andre@0: * "sendSock" andre@0: * The address of the Socket on which the message is to be sent. Must andre@0: * be non-NULL. andre@0: * "buf" andre@0: * The address of the data to be sent. Must be non-NULL. andre@0: * "bytesToWrite"" andre@0: * The UInt32 value indicating the number of bytes to write. andre@0: * "pBytesWritten" andre@0: * The address at which the Int32 value indicating the number of bytes andre@0: * actually written is to be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Send( andre@0: PKIX_PL_Socket *sendSock, andre@0: void *buf, andre@0: PKIX_UInt32 bytesToWrite, andre@0: PKIX_Int32 *pBytesWritten, andre@0: void *plContext) andre@0: { andre@0: PRInt32 bytesWritten = 0; andre@0: PRErrorCode errorcode = 0; andre@0: PRFileDesc *fd = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Send"); andre@0: PKIX_NULLCHECK_TWO(buf, pBytesWritten); andre@0: andre@0: fd = sendSock->clientSock; andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, bytesWritten, PR_Send, andre@0: (fd, buf, (PRInt32)bytesToWrite, 0, sendSock->timeout)); andre@0: andre@0: if (bytesWritten >= 0) { andre@0: if (sendSock->status == SOCKET_SENDRCVPENDING) { andre@0: sendSock->status = SOCKET_RCVPENDING; andre@0: } else { andre@0: sendSock->status = SOCKET_CONNECTED; andre@0: } andre@0: #ifdef PKIX_SOCKETTRACE andre@0: pkix_pl_socket_tracebuff(buf, bytesWritten); andre@0: #endif andre@0: } else { andre@0: errorcode = PR_GetError(); andre@0: if (errorcode != PR_WOULD_BLOCK_ERROR) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf andre@0: ("pkix_pl_Socket_Send: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRSENDFAILED); andre@0: } andre@0: andre@0: sendSock->writeBuf = buf; andre@0: sendSock->writeBufSize = bytesToWrite; andre@0: if (sendSock->status == SOCKET_RCVPENDING) { andre@0: sendSock->status = SOCKET_SENDRCVPENDING; andre@0: } else { andre@0: sendSock->status = SOCKET_SENDPENDING; andre@0: } andre@0: } andre@0: andre@0: *pBytesWritten = (PKIX_Int32)bytesWritten; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Recv andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions receives a message on the socket pointed to by "rcvSock", andre@0: * into the buffer pointed to by "buf", of capacity given by "capacity", andre@0: * storing the number of bytes actually received at "pBytesRead". If "socket" andre@0: * is in non-blocking mode, the receive operation may store -1 at andre@0: * "pBytesWritten". In that case the write is not complete until a andre@0: * corresponding pkix_pl_Poll call has indicated its completion by returning andre@0: * a non-negative value for bytes read. andre@0: * andre@0: * PARAMETERS: andre@0: * "rcvSock" andre@0: * The address of the Socket on which the message is to be received. andre@0: * Must be non-NULL. andre@0: * "buf" andre@0: * The address of the buffer into which the message is to be received. andre@0: * Must be non-NULL. andre@0: * "capacity" andre@0: * The UInt32 value of the size of the buffer; that is, the maximum andre@0: * number of bytes that can be received. andre@0: * "pBytesRead" andre@0: * The address at which is stored the Int32 value of the number of bytes andre@0: * actually received. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Recv( andre@0: PKIX_PL_Socket *rcvSock, andre@0: void *buf, andre@0: PKIX_UInt32 capacity, andre@0: PKIX_Int32 *pBytesRead, andre@0: void *plContext) andre@0: { andre@0: PRErrorCode errorcode = 0; andre@0: PRInt32 bytesRead = 0; andre@0: PRFileDesc *fd = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Recv"); andre@0: PKIX_NULLCHECK_THREE(rcvSock, buf, pBytesRead); andre@0: andre@0: fd = rcvSock->clientSock; andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, bytesRead, PR_Recv, andre@0: (fd, buf, (PRInt32)capacity, 0, rcvSock->timeout)); andre@0: andre@0: if (bytesRead > 0) { andre@0: if (rcvSock->status == SOCKET_SENDRCVPENDING) { andre@0: rcvSock->status = SOCKET_SENDPENDING; andre@0: } else { andre@0: rcvSock->status = SOCKET_CONNECTED; andre@0: } andre@0: #ifdef PKIX_SOCKETTRACE andre@0: pkix_pl_socket_tracebuff(buf, bytesRead); andre@0: #endif andre@0: } else if (bytesRead == 0) { andre@0: PKIX_ERROR(PKIX_PRRECVREPORTSNETWORKCONNECTIONCLOSED); andre@0: } else { andre@0: errorcode = PR_GetError(); andre@0: if (errorcode != PR_WOULD_BLOCK_ERROR) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf andre@0: ("pkix_pl_Socket_Recv: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRRECVFAILED); andre@0: } andre@0: rcvSock->readBuf = buf; andre@0: rcvSock->readBufSize = capacity; andre@0: if (rcvSock->status == SOCKET_SENDPENDING) { andre@0: rcvSock->status = SOCKET_SENDRCVPENDING; andre@0: } else { andre@0: rcvSock->status = SOCKET_RCVPENDING; andre@0: } andre@0: andre@0: } andre@0: andre@0: *pBytesRead = (PKIX_Int32)bytesRead; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Poll andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions checks for completion of an earlier Send or Recv on the andre@0: * socket pointed to by "sock", storing in "pBytesWritten" the number of bytes andre@0: * written by a completed Send and in "pBytesRead" the number of bytes andre@0: * received in a completed Recv. A value of -1 returned indicates the andre@0: * operation has still not completed. A NULL pointer may be supplied for andre@0: * "pBytesWritten" to avoid checking for completion of a Send. A NULL pointer andre@0: * may be supplied for "pBytesRead" to avoid checking for completion of a Recv. andre@0: * andre@0: * PARAMETERS: andre@0: * "sock" andre@0: * The address of the socket for which completions are to be checked. andre@0: * "pBytesWritten" andre@0: * The address at which the number of bytes written is to be stored, if andre@0: * a pending Send has completed. If NULL, Sends are not checked. andre@0: * "pBytesRead" andre@0: * The address at which the number of bytes read is to be stored, if andre@0: * a pending Recv has completed. If NULL, Recvs are not checked. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Poll( andre@0: PKIX_PL_Socket *sock, andre@0: PKIX_Int32 *pBytesWritten, andre@0: PKIX_Int32 *pBytesRead, andre@0: void *plContext) andre@0: { andre@0: PRPollDesc pollDesc; andre@0: PRInt32 numEvents = 0; andre@0: PKIX_Int32 bytesRead = 0; andre@0: PKIX_Int32 bytesWritten = 0; andre@0: PRErrorCode errorcode = 0; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Poll"); andre@0: PKIX_NULLCHECK_ONE(sock); andre@0: andre@0: pollDesc.fd = sock->clientSock; andre@0: pollDesc.in_flags = 0; andre@0: pollDesc.out_flags = 0; andre@0: andre@0: if ((pBytesWritten) && andre@0: ((sock->status == SOCKET_SENDPENDING) || andre@0: (sock->status == SOCKET_SENDRCVPENDING))) { andre@0: pollDesc.in_flags = PR_POLL_WRITE; andre@0: } andre@0: andre@0: if ((pBytesRead) && andre@0: ((sock->status == SOCKET_RCVPENDING) || andre@0: (sock->status == SOCKET_SENDRCVPENDING))) { andre@0: pollDesc.in_flags |= PR_POLL_READ; andre@0: } andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, numEvents, PR_Poll, (&pollDesc, 1, 0)); andre@0: andre@0: if (numEvents < 0) { andre@0: PKIX_ERROR(PKIX_PRPOLLFAILED); andre@0: } else if (numEvents > 0) { andre@0: if (pollDesc.out_flags & PR_POLL_WRITE) { andre@0: PKIX_CHECK(pkix_pl_Socket_Send andre@0: (sock, andre@0: sock->writeBuf, andre@0: sock->writeBufSize, andre@0: &bytesWritten, andre@0: plContext), andre@0: PKIX_SOCKETSENDFAILED); andre@0: *pBytesWritten = (PKIX_Int32)bytesWritten; andre@0: if (bytesWritten >= 0) { andre@0: sock->writeBuf = NULL; andre@0: sock->writeBufSize = 0; andre@0: } andre@0: } andre@0: andre@0: if (pollDesc.out_flags & PR_POLL_READ) { andre@0: PKIX_CHECK(pkix_pl_Socket_Recv andre@0: (sock, andre@0: sock->readBuf, andre@0: sock->readBufSize, andre@0: &bytesRead, andre@0: plContext), andre@0: PKIX_SOCKETRECVFAILED); andre@0: *pBytesRead = (PKIX_Int32)bytesRead; andre@0: if (bytesRead >= 0) { andre@0: sock->readBuf = NULL; andre@0: sock->readBufSize = 0; andre@0: } andre@0: } andre@0: } else if (numEvents == 0) { andre@0: errorcode = PR_GetError(); andre@0: if (errorcode != PR_WOULD_BLOCK_ERROR) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf andre@0: ("pkix_pl_Socket_Poll: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRPOLLFAILED); andre@0: } andre@0: if (pBytesWritten) { andre@0: *pBytesWritten = 0; andre@0: } andre@0: if (pBytesRead) { andre@0: *pBytesRead = 0; andre@0: } andre@0: } andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Accept andre@0: * DESCRIPTION: andre@0: * andre@0: * This functions accepts a client connection for the server Socket pointed andre@0: * to by "serverSocket", creating a new Socket and storing the result at andre@0: * "pRendezvousSocket". If "serverSocket" is in non-blocking mode, this andre@0: * function will return NULL if there is no client connection to accept. andre@0: * Otherwise this function will block until a connection is available. andre@0: * When a client connection is available the new Socket will have the same andre@0: * blocking/non-blocking property as "serverSocket". andre@0: * andre@0: * PARAMETERS: andre@0: * "serverSocket" andre@0: * The address of the Socket for which a client connection is to be andre@0: * accepted. Must be non-NULL. andre@0: * "pRendezvousSocket" andre@0: * The address at which the created Socket is stored, when a client andre@0: * connection is available, or at which NULL is stored, if no connection andre@0: * is available for a non-blocking "serverSocket". Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * none andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_Socket_Accept( andre@0: PKIX_PL_Socket *serverSocket, andre@0: PKIX_PL_Socket **pRendezvousSocket, andre@0: void *plContext) andre@0: { andre@0: PRErrorCode errorcode = 0; andre@0: PRFileDesc *rendezvousSock = NULL; andre@0: PRNetAddr *clientAddr = NULL; andre@0: PKIX_PL_Socket *newSocket = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Accept"); andre@0: PKIX_NULLCHECK_TWO(serverSocket, pRendezvousSocket); andre@0: andre@0: PKIX_PL_NSSCALLRV(SOCKET, rendezvousSock, PR_Accept, andre@0: (serverSocket->serverSock, clientAddr, serverSocket->timeout)); andre@0: andre@0: if (!rendezvousSock) { andre@0: errorcode = PR_GetError(); andre@0: if (errorcode != PR_WOULD_BLOCK_ERROR) { andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf andre@0: ("pkix_pl_Socket_Accept: %s\n", andre@0: PR_ErrorToString(errorcode, PR_LANGUAGE_EN)); andre@0: #endif andre@0: PKIX_ERROR(PKIX_PRACCEPTFAILED); andre@0: } andre@0: serverSocket->status = SOCKET_ACCEPTPENDING; andre@0: *pRendezvousSocket = NULL; andre@0: goto cleanup; andre@0: andre@0: } andre@0: andre@0: #ifdef PKIX_SOCKETDEBUG andre@0: printf("Successful accept!\n"); andre@0: #endif andre@0: andre@0: PKIX_CHECK(PKIX_PL_Object_Alloc andre@0: (PKIX_SOCKET_TYPE, andre@0: sizeof (PKIX_PL_Socket), andre@0: (PKIX_PL_Object **)&newSocket, andre@0: plContext), andre@0: PKIX_COULDNOTCREATESOCKETOBJECT); andre@0: andre@0: newSocket->isServer = PKIX_FALSE; andre@0: newSocket->timeout = serverSocket->timeout; andre@0: newSocket->clientSock = rendezvousSock; andre@0: newSocket->serverSock = NULL; andre@0: newSocket->netAddr = NULL; andre@0: newSocket->status = SOCKET_CONNECTED; andre@0: newSocket->callbackList.shutdownCallback = pkix_pl_Socket_Shutdown; andre@0: newSocket->callbackList.listenCallback = pkix_pl_Socket_Listen; andre@0: newSocket->callbackList.acceptCallback = pkix_pl_Socket_Accept; andre@0: newSocket->callbackList.connectcontinueCallback = andre@0: pkix_pl_Socket_ConnectContinue; andre@0: newSocket->callbackList.sendCallback = pkix_pl_Socket_Send; andre@0: newSocket->callbackList.recvCallback = pkix_pl_Socket_Recv; andre@0: newSocket->callbackList.pollCallback = pkix_pl_Socket_Poll; andre@0: andre@0: if (serverSocket->timeout == 0) { andre@0: PKIX_CHECK(pkix_pl_Socket_SetNonBlocking andre@0: (rendezvousSock, plContext), andre@0: PKIX_SOCKETSETNONBLOCKINGFAILED); andre@0: } andre@0: andre@0: *pRendezvousSocket = newSocket; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_Create andre@0: * DESCRIPTION: andre@0: * andre@0: * This function creates a new Socket, setting it to be a server or a client andre@0: * according to the value of "isServer", setting its timeout value from andre@0: * "timeout" and server address from "netAddr", and stores the created Socket andre@0: * at "pSocket". andre@0: * andre@0: * PARAMETERS: andre@0: * "isServer" andre@0: * The Boolean value indicating if PKIX_TRUE, that a server socket (using andre@0: * Bind, Listen, and Accept) is to be created, or if PKIX_FALSE, that a andre@0: * client socket (using Connect) is to be created. andre@0: * "timeout" andre@0: * A PRTimeInterval value to be used for I/O waits for this socket. If andre@0: * zero, non-blocking I/O is to be used. andre@0: * "netAddr" andre@0: * The PRNetAddr to be used for the Bind function, if this is a server andre@0: * socket, or for the Connect, if this is a client socket. andre@0: * "pSocket" andre@0: * The address at which the Socket is to be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a Socket Error if the function fails in andre@0: * a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_Socket_Create( andre@0: PKIX_Boolean isServer, andre@0: PRIntervalTime timeout, andre@0: PRNetAddr *netAddr, andre@0: PRErrorCode *status, andre@0: PKIX_PL_Socket **pSocket, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Socket *socket = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_Create"); andre@0: PKIX_NULLCHECK_ONE(pSocket); andre@0: andre@0: PKIX_CHECK(PKIX_PL_Object_Alloc andre@0: (PKIX_SOCKET_TYPE, andre@0: sizeof (PKIX_PL_Socket), andre@0: (PKIX_PL_Object **)&socket, andre@0: plContext), andre@0: PKIX_COULDNOTCREATESOCKETOBJECT); andre@0: andre@0: socket->isServer = isServer; andre@0: socket->timeout = timeout; andre@0: socket->clientSock = NULL; andre@0: socket->serverSock = NULL; andre@0: socket->netAddr = netAddr; andre@0: andre@0: socket->callbackList.listenCallback = pkix_pl_Socket_Listen; andre@0: socket->callbackList.acceptCallback = pkix_pl_Socket_Accept; andre@0: socket->callbackList.connectcontinueCallback = andre@0: pkix_pl_Socket_ConnectContinue; andre@0: socket->callbackList.sendCallback = pkix_pl_Socket_Send; andre@0: socket->callbackList.recvCallback = pkix_pl_Socket_Recv; andre@0: socket->callbackList.pollCallback = pkix_pl_Socket_Poll; andre@0: socket->callbackList.shutdownCallback = pkix_pl_Socket_Shutdown; andre@0: andre@0: if (isServer) { andre@0: PKIX_CHECK(pkix_pl_Socket_CreateServer(socket, plContext), andre@0: PKIX_SOCKETCREATESERVERFAILED); andre@0: *status = 0; andre@0: } else { andre@0: socket->timeout = timeout; andre@0: PKIX_CHECK(pkix_pl_Socket_CreateClient(socket, plContext), andre@0: PKIX_SOCKETCREATECLIENTFAILED); andre@0: PKIX_CHECK(pkix_pl_Socket_Connect(socket, status, plContext), andre@0: PKIX_SOCKETCONNECTFAILED); andre@0: } andre@0: andre@0: *pSocket = socket; andre@0: andre@0: cleanup: andre@0: if (PKIX_ERROR_RECEIVED) { andre@0: PKIX_DECREF(socket); andre@0: } andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_CreateByName andre@0: * DESCRIPTION: andre@0: * andre@0: * This function creates a new Socket, setting it to be a server or a client andre@0: * according to the value of "isServer", setting its timeout value from andre@0: * "timeout" and server address and port number from "serverName", and stores andre@0: * the status at "pStatus" and the created Socket at "pSocket". andre@0: * andre@0: * If isServer is PKIX_TRUE, it is attempted to create the socket with an ip andre@0: * address of PR_INADDR_ANY. andre@0: * andre@0: * PARAMETERS: andre@0: * "isServer" andre@0: * The Boolean value indicating if PKIX_TRUE, that a server socket (using andre@0: * Bind, Listen, and Accept) is to be created, or if PKIX_FALSE, that a andre@0: * client socket (using Connect) is to be created. andre@0: * "timeout" andre@0: * A PRTimeInterval value to be used for I/O waits for this socket. If andre@0: * zero, non-blocking I/O is to be used. andre@0: * "serverName" andre@0: * Address of a character string consisting of the server's domain name andre@0: * followed by a colon and a port number for the desired socket. andre@0: * "pStatus" andre@0: * Address at which the PRErrorCode resulting from the create is andre@0: * stored. Must be non-NULL. andre@0: * "pSocket" andre@0: * The address at which the Socket is to be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a Socket Error if the function fails in andre@0: * a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_Socket_CreateByName( andre@0: PKIX_Boolean isServer, andre@0: PRIntervalTime timeout, andre@0: char *serverName, andre@0: PRErrorCode *pStatus, andre@0: PKIX_PL_Socket **pSocket, andre@0: void *plContext) andre@0: { andre@0: PRNetAddr netAddr; andre@0: PKIX_PL_Socket *socket = NULL; andre@0: char *sepPtr = NULL; andre@0: PRHostEnt hostent; andre@0: PRIntn hostenum; andre@0: PRStatus prstatus = PR_FAILURE; andre@0: char buf[PR_NETDB_BUF_SIZE]; andre@0: PRUint16 portNum = 0; andre@0: char *localCopyName = NULL; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_CreateByName"); andre@0: PKIX_NULLCHECK_TWO(serverName, pSocket); andre@0: andre@0: localCopyName = PL_strdup(serverName); andre@0: andre@0: sepPtr = strchr(localCopyName, ':'); andre@0: /* First strip off the portnum, if present, from the end of the name */ andre@0: if (sepPtr) { andre@0: *sepPtr++ = '\0'; andre@0: portNum = (PRUint16)atoi(sepPtr); andre@0: } else { andre@0: portNum = (PRUint16)LDAP_PORT; andre@0: } andre@0: andre@0: prstatus = PR_GetHostByName(localCopyName, buf, sizeof(buf), &hostent); andre@0: andre@0: if ((prstatus != PR_SUCCESS) || (hostent.h_length != 4)) { andre@0: /* andre@0: * The hostname may be a fully-qualified name. Try using just andre@0: * the leftmost component in our lookup. andre@0: */ andre@0: sepPtr = strchr(localCopyName, '.'); andre@0: if (sepPtr) { andre@0: *sepPtr++ = '\0'; andre@0: } andre@0: prstatus = PR_GetHostByName andre@0: (localCopyName, buf, sizeof(buf), &hostent); andre@0: andre@0: if ((prstatus != PR_SUCCESS) || (hostent.h_length != 4)) { andre@0: PKIX_ERROR andre@0: (PKIX_PRGETHOSTBYNAMEREJECTSHOSTNAMEARGUMENT); andre@0: } andre@0: } andre@0: andre@0: netAddr.inet.family = PR_AF_INET; andre@0: netAddr.inet.port = PR_htons(portNum); andre@0: andre@0: if (isServer) { andre@0: andre@0: netAddr.inet.ip = PR_INADDR_ANY; andre@0: andre@0: } else { andre@0: andre@0: hostenum = PR_EnumerateHostEnt(0, &hostent, portNum, &netAddr); andre@0: if (hostenum == -1) { andre@0: PKIX_ERROR(PKIX_PRENUMERATEHOSTENTFAILED); andre@0: } andre@0: } andre@0: andre@0: PKIX_CHECK(PKIX_PL_Object_Alloc andre@0: (PKIX_SOCKET_TYPE, andre@0: sizeof (PKIX_PL_Socket), andre@0: (PKIX_PL_Object **)&socket, andre@0: plContext), andre@0: PKIX_COULDNOTCREATESOCKETOBJECT); andre@0: andre@0: socket->isServer = isServer; andre@0: socket->timeout = timeout; andre@0: socket->clientSock = NULL; andre@0: socket->serverSock = NULL; andre@0: socket->netAddr = &netAddr; andre@0: andre@0: socket->callbackList.listenCallback = pkix_pl_Socket_Listen; andre@0: socket->callbackList.acceptCallback = pkix_pl_Socket_Accept; andre@0: socket->callbackList.connectcontinueCallback = andre@0: pkix_pl_Socket_ConnectContinue; andre@0: socket->callbackList.sendCallback = pkix_pl_Socket_Send; andre@0: socket->callbackList.recvCallback = pkix_pl_Socket_Recv; andre@0: socket->callbackList.pollCallback = pkix_pl_Socket_Poll; andre@0: socket->callbackList.shutdownCallback = pkix_pl_Socket_Shutdown; andre@0: andre@0: if (isServer) { andre@0: PKIX_CHECK(pkix_pl_Socket_CreateServer(socket, plContext), andre@0: PKIX_SOCKETCREATESERVERFAILED); andre@0: *pStatus = 0; andre@0: } else { andre@0: PKIX_CHECK(pkix_pl_Socket_CreateClient(socket, plContext), andre@0: PKIX_SOCKETCREATECLIENTFAILED); andre@0: PKIX_CHECK(pkix_pl_Socket_Connect(socket, pStatus, plContext), andre@0: PKIX_SOCKETCONNECTFAILED); andre@0: } andre@0: andre@0: *pSocket = socket; andre@0: andre@0: cleanup: andre@0: PL_strfree(localCopyName); andre@0: andre@0: if (PKIX_ERROR_RECEIVED) { andre@0: PKIX_DECREF(socket); andre@0: } andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_CreateByHostAndPort andre@0: * DESCRIPTION: andre@0: * andre@0: * This function creates a new Socket, setting it to be a server or a client andre@0: * according to the value of "isServer", setting its timeout value from andre@0: * "timeout", host from "hostname", and port number from "portNum", and stores andre@0: * the status at "pStatus" and the created Socket at "pSocket". andre@0: * andre@0: * If isServer is PKIX_TRUE, it is attempted to create the socket with an ip andre@0: * address of PR_INADDR_ANY. andre@0: * andre@0: * PARAMETERS: andre@0: * "isServer" andre@0: * The Boolean value indicating if PKIX_TRUE, that a server socket (using andre@0: * Bind, Listen, and Accept) is to be created, or if PKIX_FALSE, that a andre@0: * client socket (using Connect) is to be created. andre@0: * "timeout" andre@0: * A PRTimeInterval value to be used for I/O waits for this socket. If andre@0: * zero, non-blocking I/O is to be used. andre@0: * "hostname" andre@0: * Address of a character string consisting of the server's domain name. andre@0: * "portNum" andre@0: * UInt16 value of the port number for the desired socket. andre@0: * "pStatus" andre@0: * Address at which the PRErrorCode resulting from the create is andre@0: * stored. Must be non-NULL. andre@0: * "pSocket" andre@0: * The address at which the Socket is to be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a Socket Error if the function fails in andre@0: * a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_Socket_CreateByHostAndPort( andre@0: PKIX_Boolean isServer, andre@0: PRIntervalTime timeout, andre@0: char *hostname, andre@0: PRUint16 portnum, andre@0: PRErrorCode *pStatus, andre@0: PKIX_PL_Socket **pSocket, andre@0: void *plContext) andre@0: { andre@0: PRNetAddr netAddr; andre@0: PKIX_PL_Socket *socket = NULL; andre@0: char *sepPtr = NULL; andre@0: PRHostEnt hostent; andre@0: PRIntn hostenum; andre@0: PRStatus prstatus = PR_FAILURE; andre@0: char buf[PR_NETDB_BUF_SIZE]; andre@0: andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_CreateByHostAndPort"); andre@0: PKIX_NULLCHECK_THREE(hostname, pStatus, pSocket); andre@0: andre@0: andre@0: prstatus = PR_GetHostByName(hostname, buf, sizeof(buf), &hostent); andre@0: andre@0: if ((prstatus != PR_SUCCESS) || (hostent.h_length != 4)) { andre@0: /* andre@0: * The hostname may be a fully-qualified name. Try using just andre@0: * the leftmost component in our lookup. andre@0: */ andre@0: sepPtr = strchr(hostname, '.'); andre@0: if (sepPtr) { andre@0: *sepPtr++ = '\0'; andre@0: } andre@0: prstatus = PR_GetHostByName(hostname, buf, sizeof(buf), &hostent); andre@0: andre@0: if ((prstatus != PR_SUCCESS) || (hostent.h_length != 4)) { andre@0: PKIX_ERROR andre@0: (PKIX_PRGETHOSTBYNAMEREJECTSHOSTNAMEARGUMENT); andre@0: } andre@0: } andre@0: andre@0: netAddr.inet.family = PR_AF_INET; andre@0: netAddr.inet.port = PR_htons(portnum); andre@0: andre@0: if (isServer) { andre@0: andre@0: netAddr.inet.ip = PR_INADDR_ANY; andre@0: andre@0: } else { andre@0: andre@0: hostenum = PR_EnumerateHostEnt(0, &hostent, portnum, &netAddr); andre@0: if (hostenum == -1) { andre@0: PKIX_ERROR(PKIX_PRENUMERATEHOSTENTFAILED); andre@0: } andre@0: } andre@0: andre@0: PKIX_CHECK(PKIX_PL_Object_Alloc andre@0: (PKIX_SOCKET_TYPE, andre@0: sizeof (PKIX_PL_Socket), andre@0: (PKIX_PL_Object **)&socket, andre@0: plContext), andre@0: PKIX_COULDNOTCREATESOCKETOBJECT); andre@0: andre@0: socket->isServer = isServer; andre@0: socket->timeout = timeout; andre@0: socket->clientSock = NULL; andre@0: socket->serverSock = NULL; andre@0: socket->netAddr = &netAddr; andre@0: andre@0: socket->callbackList.listenCallback = pkix_pl_Socket_Listen; andre@0: socket->callbackList.acceptCallback = pkix_pl_Socket_Accept; andre@0: socket->callbackList.connectcontinueCallback = andre@0: pkix_pl_Socket_ConnectContinue; andre@0: socket->callbackList.sendCallback = pkix_pl_Socket_Send; andre@0: socket->callbackList.recvCallback = pkix_pl_Socket_Recv; andre@0: socket->callbackList.pollCallback = pkix_pl_Socket_Poll; andre@0: socket->callbackList.shutdownCallback = pkix_pl_Socket_Shutdown; andre@0: andre@0: if (isServer) { andre@0: PKIX_CHECK(pkix_pl_Socket_CreateServer(socket, plContext), andre@0: PKIX_SOCKETCREATESERVERFAILED); andre@0: *pStatus = 0; andre@0: } else { andre@0: PKIX_CHECK(pkix_pl_Socket_CreateClient(socket, plContext), andre@0: PKIX_SOCKETCREATECLIENTFAILED); andre@0: PKIX_CHECK(pkix_pl_Socket_Connect(socket, pStatus, plContext), andre@0: PKIX_SOCKETCONNECTFAILED); andre@0: } andre@0: andre@0: *pSocket = socket; andre@0: andre@0: cleanup: andre@0: if (PKIX_ERROR_RECEIVED) { andre@0: PKIX_DECREF(socket); andre@0: } andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_GetCallbackList andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_Socket_GetCallbackList( andre@0: PKIX_PL_Socket *socket, andre@0: PKIX_PL_Socket_Callback **pCallbackList, andre@0: void *plContext) andre@0: { andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_GetCallbackList"); andre@0: PKIX_NULLCHECK_TWO(socket, pCallbackList); andre@0: andre@0: *pCallbackList = &(socket->callbackList); andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_Socket_GetPRFileDesc andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_Socket_GetPRFileDesc( andre@0: PKIX_PL_Socket *socket, andre@0: PRFileDesc **pDesc, andre@0: void *plContext) andre@0: { andre@0: PKIX_ENTER(SOCKET, "pkix_pl_Socket_GetPRFileDesc"); andre@0: PKIX_NULLCHECK_TWO(socket, pDesc); andre@0: andre@0: *pDesc = socket->clientSock; andre@0: andre@0: PKIX_RETURN(SOCKET); andre@0: }