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