diff options
Diffstat (limited to 'security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapdefaultclient.c')
-rw-r--r-- | security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapdefaultclient.c | 2493 |
1 files changed, 2493 insertions, 0 deletions
diff --git a/security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapdefaultclient.c b/security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapdefaultclient.c new file mode 100644 index 000000000..3dc06be9a --- /dev/null +++ b/security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapdefaultclient.c @@ -0,0 +1,2493 @@ +/* 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_ldapdefaultclient.c + * + * LDAPDefaultClient Function Definitions + * + */ + +/* We can't decode the length of a message without at least this many bytes */ +#define MINIMUM_MSG_LENGTH 5 + +#include "pkix_pl_ldapdefaultclient.h" + +/* --Private-LdapDefaultClient-Message-Building-Functions---------------- */ + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_MakeBind + * DESCRIPTION: + * + * This function creates and encodes a Bind message, using the arena pointed + * to by "arena", the version number contained in "versionData", the + * LDAPBindAPI pointed to by "bindAPI", and the messageID contained in + * "msgNum", and stores a pointer to the encoded string at "pBindMsg". + * + * See pkix_pl_ldaptemplates.c for the ASN.1 description of a Bind message. + * + * This code is not used if the DefaultClient was created with a NULL pointer + * supplied for the LDAPBindAPI structure. (Bind and Unbind do not seem to be + * expected for anonymous Search requests.) + * + * PARAMETERS: + * "arena" + * The address of the PLArenaPool used in encoding the message. Must be + * non-NULL. + * "versionData" + * The Int32 containing the version number to be encoded in the Bind + * message. + * "bindAPI" + * The address of the LDAPBindAPI to be encoded in the Bind message. Must + * be non-NULL. + * "msgNum" + * The Int32 containing the MessageID to be encoded in the Bind message. + * "pBindMsg" + * The address at which the encoded Bind message will 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_MakeBind( + PLArenaPool *arena, + PKIX_Int32 versionData, + LDAPBindAPI *bindAPI, + PKIX_UInt32 msgNum, + SECItem **pBindMsg, + void *plContext) +{ + LDAPMessage msg; + char version = '\0'; + SECItem *encoded = NULL; + PKIX_UInt32 len = 0; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_MakeBind"); + PKIX_NULLCHECK_TWO(arena, pBindMsg); + + PKIX_PL_NSSCALL(LDAPDEFAULTCLIENT, PORT_Memset, + (&msg, 0, sizeof (LDAPMessage))); + + version = (char)versionData; + + msg.messageID.type = siUnsignedInteger; + msg.messageID.data = (void*)&msgNum; + msg.messageID.len = sizeof (msgNum); + + msg.protocolOp.selector = LDAP_BIND_TYPE; + + msg.protocolOp.op.bindMsg.version.type = siUnsignedInteger; + msg.protocolOp.op.bindMsg.version.data = (void *)&version; + msg.protocolOp.op.bindMsg.version.len = sizeof (char); + + /* + * XXX At present we only know how to handle anonymous requests (no + * authentication), and we are guessing how to do simple authentication. + * This section will need to be revised and extended when other + * authentication is needed. + */ + if (bindAPI->selector == SIMPLE_AUTH) { + msg.protocolOp.op.bindMsg.bindName.type = siAsciiString; + msg.protocolOp.op.bindMsg.bindName.data = + (void *)bindAPI->chooser.simple.bindName; + len = PL_strlen(bindAPI->chooser.simple.bindName); + msg.protocolOp.op.bindMsg.bindName.len = len; + + msg.protocolOp.op.bindMsg.authentication.type = siAsciiString; + msg.protocolOp.op.bindMsg.authentication.data = + (void *)bindAPI->chooser.simple.authentication; + len = PL_strlen(bindAPI->chooser.simple.authentication); + msg.protocolOp.op.bindMsg.authentication.len = len; + } + + PKIX_PL_NSSCALLRV(LDAPDEFAULTCLIENT, encoded, SEC_ASN1EncodeItem, + (arena, NULL, (void *)&msg, PKIX_PL_LDAPMessageTemplate)); + if (!encoded) { + PKIX_ERROR(PKIX_SECASN1ENCODEITEMFAILED); + } + + *pBindMsg = encoded; +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_MakeUnbind + * DESCRIPTION: + * + * This function creates and encodes a Unbind message, using the arena pointed + * to by "arena" and the messageID contained in "msgNum", and stores a pointer + * to the encoded string at "pUnbindMsg". + * + * See pkix_pl_ldaptemplates.c for the ASN.1 description of an Unbind message. + * + * This code is not used if the DefaultClient was created with a NULL pointer + * supplied for the LDAPBindAPI structure. (Bind and Unbind do not seem to be + * expected for anonymous Search requests.) + * + * PARAMETERS: + * "arena" + * The address of the PLArenaPool used in encoding the message. Must be + * non-NULL. + * "msgNum" + * The Int32 containing the MessageID to be encoded in the Unbind message. + * "pUnbindMsg" + * The address at which the encoded Unbind message will 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_MakeUnbind( + PLArenaPool *arena, + PKIX_UInt32 msgNum, + SECItem **pUnbindMsg, + void *plContext) +{ + LDAPMessage msg; + SECItem *encoded = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_MakeUnbind"); + PKIX_NULLCHECK_TWO(arena, pUnbindMsg); + + PKIX_PL_NSSCALL(LDAPDEFAULTCLIENT, PORT_Memset, + (&msg, 0, sizeof (LDAPMessage))); + + msg.messageID.type = siUnsignedInteger; + msg.messageID.data = (void*)&msgNum; + msg.messageID.len = sizeof (msgNum); + + msg.protocolOp.selector = LDAP_UNBIND_TYPE; + + msg.protocolOp.op.unbindMsg.dummy.type = siBuffer; + msg.protocolOp.op.unbindMsg.dummy.data = NULL; + msg.protocolOp.op.unbindMsg.dummy.len = 0; + + PKIX_PL_NSSCALLRV(LDAPDEFAULTCLIENT, encoded, SEC_ASN1EncodeItem, + (arena, NULL, (void *)&msg, PKIX_PL_LDAPMessageTemplate)); + if (!encoded) { + PKIX_ERROR(PKIX_SECASN1ENCODEITEMFAILED); + } + + *pUnbindMsg = encoded; +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_MakeAbandon + * DESCRIPTION: + * + * This function creates and encodes a Abandon message, using the arena pointed + * to by "arena" and the messageID contained in "msgNum", and stores a pointer + * to the encoded string at "pAbandonMsg". + * + * See pkix_pl_ldaptemplates.c for the ASN.1 description of an Abandon message. + * + * PARAMETERS: + * "arena" + * The address of the PLArenaPool used in encoding the message. Must be + * non-NULL. + * "msgNum" + * The Int32 containing the MessageID to be encoded in the Abandon message. + * "pAbandonMsg" + * The address at which the encoded Abandon message will 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_MakeAbandon( + PLArenaPool *arena, + PKIX_UInt32 msgNum, + SECItem **pAbandonMsg, + void *plContext) +{ + LDAPMessage msg; + SECItem *encoded = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_MakeAbandon"); + PKIX_NULLCHECK_TWO(arena, pAbandonMsg); + + PKIX_PL_NSSCALL(LDAPDEFAULTCLIENT, PORT_Memset, + (&msg, 0, sizeof (LDAPMessage))); + + msg.messageID.type = siUnsignedInteger; + msg.messageID.data = (void*)&msgNum; + msg.messageID.len = sizeof (msgNum); + + msg.protocolOp.selector = LDAP_ABANDONREQUEST_TYPE; + + msg.protocolOp.op.abandonRequestMsg.messageID.type = siBuffer; + msg.protocolOp.op.abandonRequestMsg.messageID.data = (void*)&msgNum; + msg.protocolOp.op.abandonRequestMsg.messageID.len = sizeof (msgNum); + + PKIX_PL_NSSCALLRV(LDAPDEFAULTCLIENT, encoded, SEC_ASN1EncodeItem, + (arena, NULL, (void *)&msg, PKIX_PL_LDAPMessageTemplate)); + if (!encoded) { + PKIX_ERROR(PKIX_SECASN1ENCODEITEMFAILED); + } + + *pAbandonMsg = encoded; +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_DecodeBindResponse + * DESCRIPTION: + * + * This function decodes the encoded data pointed to by "src", using the arena + * pointed to by "arena", storing the decoded LDAPMessage at "pBindResponse" + * and the decoding status at "pStatus". + * + * PARAMETERS: + * "arena" + * The address of the PLArenaPool to be used in decoding the message. Must + * be non-NULL. + * "src" + * The address of the SECItem containing the DER- (or BER-)encoded string. + * Must be non-NULL. + * "pBindResponse" + * The address at which the LDAPMessage is stored, if the decoding is + * successful (the returned status is SECSuccess). Must be non-NULL. + * "pStatus" + * The address at which the decoding 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: + * Returns NULL if the function succeeds. + * Returns a LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_DecodeBindResponse( + PLArenaPool *arena, + SECItem *src, + LDAPMessage *pBindResponse, + SECStatus *pStatus, + void *plContext) +{ + SECStatus rv = SECFailure; + LDAPMessage response; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_DecodeBindResponse"); + PKIX_NULLCHECK_FOUR(arena, src, pBindResponse, pStatus); + + PKIX_PL_NSSCALL + (LDAPDEFAULTCLIENT, + PORT_Memset, + (&response, 0, sizeof (LDAPMessage))); + + PKIX_PL_NSSCALLRV(LDAPDEFAULTCLIENT, rv, SEC_ASN1DecodeItem, + (arena, &response, PKIX_PL_LDAPMessageTemplate, src)); + + if (rv == SECSuccess) { + *pBindResponse = response; + } + + *pStatus = rv; + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_VerifyBindResponse + * DESCRIPTION: + * + * This function verifies that the contents of the message in the rcvbuf of + * the LdapDefaultClient object pointed to by "client", and whose length is + * provided by "buflen", is a response to a successful Bind. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "buflen" + * The value of the number of bytes in the receive buffer. + * "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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_VerifyBindResponse( + PKIX_PL_LdapDefaultClient *client, + PKIX_UInt32 bufLen, + void *plContext) +{ + SECItem decode = {siBuffer, NULL, 0}; + SECStatus rv = SECFailure; + LDAPMessage msg; + LDAPBindResponse *ldapBindResponse = NULL; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_VerifyBindResponse"); + PKIX_NULLCHECK_TWO(client, client->rcvBuf); + + decode.data = (unsigned char *)(client->rcvBuf); + decode.len = bufLen; + + PKIX_CHECK(pkix_pl_LdapDefaultClient_DecodeBindResponse + (client->arena, &decode, &msg, &rv, plContext), + PKIX_LDAPDEFAULTCLIENTDECODEBINDRESPONSEFAILED); + + if (rv == SECSuccess) { + ldapBindResponse = &msg.protocolOp.op.bindResponseMsg; + if (*(ldapBindResponse->resultCode.data) == SUCCESS) { + client->connectStatus = BOUND; + } else { + PKIX_ERROR(PKIX_BINDREJECTEDBYSERVER); + } + } else { + PKIX_ERROR(PKIX_CANTDECODEBINDRESPONSEFROMSERVER); + } + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_RecvCheckComplete + * DESCRIPTION: + * + * This function determines whether the current response in the + * LdapDefaultClient pointed to by "client" is complete, in the sense that all + * bytes required to satisfy the message length field in the encoding have been + * received. If so, the pointer to input data is updated to reflect the number + * of bytes consumed, provided by "bytesProcessed". The state machine flag + * pointed to by "pKeepGoing" is updated to indicate whether processing can + * continue without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "bytesProcessed" + * The UInt32 value of the number of bytes consumed from the current + * buffer. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_RecvCheckComplete( + PKIX_PL_LdapDefaultClient *client, + PKIX_UInt32 bytesProcessed, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_Boolean complete = PKIX_FALSE; + SECStatus rv = SECFailure; + LDAPMessageType messageType = 0; + LDAPResultCode resultCode = 0; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_RecvCheckComplete"); + PKIX_NULLCHECK_TWO(client, pKeepGoing); + + PKIX_CHECK(pkix_pl_LdapResponse_IsComplete + (client->currentResponse, &complete, plContext), + PKIX_LDAPRESPONSEISCOMPLETEFAILED); + + if (complete) { + PKIX_CHECK(pkix_pl_LdapResponse_Decode + (client->arena, client->currentResponse, &rv, plContext), + PKIX_LDAPRESPONSEDECODEFAILED); + + if (rv != SECSuccess) { + PKIX_ERROR(PKIX_CANTDECODESEARCHRESPONSEFROMSERVER); + } + + PKIX_CHECK(pkix_pl_LdapResponse_GetMessageType + (client->currentResponse, &messageType, plContext), + PKIX_LDAPRESPONSEGETMESSAGETYPEFAILED); + + if (messageType == LDAP_SEARCHRESPONSEENTRY_TYPE) { + + if (client->entriesFound == NULL) { + PKIX_CHECK(PKIX_List_Create + (&(client->entriesFound), plContext), + PKIX_LISTCREATEFAILED); + } + + PKIX_CHECK(PKIX_List_AppendItem + (client->entriesFound, + (PKIX_PL_Object *)client->currentResponse, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_DECREF(client->currentResponse); + + /* current receive buffer empty? */ + if (client->currentBytesAvailable == 0) { + client->connectStatus = RECV; + *pKeepGoing = PKIX_TRUE; + } else { + client->connectStatus = RECV_INITIAL; + client->currentInPtr = &((char *) + (client->currentInPtr))[bytesProcessed]; + *pKeepGoing = PKIX_TRUE; + } + + } else if (messageType == LDAP_SEARCHRESPONSERESULT_TYPE) { + PKIX_CHECK(pkix_pl_LdapResponse_GetResultCode + (client->currentResponse, + &resultCode, + plContext), + PKIX_LDAPRESPONSEGETRESULTCODEFAILED); + + if ((client->entriesFound == NULL) && + ((resultCode == SUCCESS) || + (resultCode == NOSUCHOBJECT))) { + PKIX_CHECK(PKIX_List_Create + (&(client->entriesFound), plContext), + PKIX_LISTCREATEFAILED); + } else if (resultCode == SUCCESS) { + PKIX_CHECK(PKIX_List_SetImmutable + (client->entriesFound, plContext), + PKIX_LISTSETIMMUTABLEFAILED); + PKIX_CHECK(PKIX_PL_HashTable_Add + (client->cachePtr, + (PKIX_PL_Object *)client->currentRequest, + (PKIX_PL_Object *)client->entriesFound, + plContext), + PKIX_HASHTABLEADDFAILED); + } else { + PKIX_ERROR(PKIX_UNEXPECTEDRESULTCODEINRESPONSE); + } + + client->connectStatus = BOUND; + *pKeepGoing = PKIX_FALSE; + PKIX_DECREF(client->currentResponse); + + } else { + PKIX_ERROR(PKIX_SEARCHRESPONSEPACKETOFUNKNOWNTYPE); + } + } else { + client->connectStatus = RECV; + *pKeepGoing = PKIX_TRUE; + } + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* --Private-LdapDefaultClient-Object-Functions------------------------- */ + +static PKIX_Error * +pkix_pl_LdapDefaultClient_InitiateRequest( + PKIX_PL_LdapClient *client, + LDAPRequestParams *requestParams, + void **pPollDesc, + PKIX_List **pResponse, + void *plContext); + +static PKIX_Error * +pkix_pl_LdapDefaultClient_ResumeRequest( + PKIX_PL_LdapClient *client, + void **pPollDesc, + PKIX_List **pResponse, + void *plContext); + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_CreateHelper + * DESCRIPTION: + * + * This function creates a new LdapDefaultClient using the Socket pointed to + * by "socket", the PRIntervalTime pointed to by "timeout", and the + * LDAPBindAPI pointed to by "bindAPI", and stores the result at "pClient". + * + * A value of zero for "timeout" means the LDAPClient will use non-blocking + * I/O. + * + * PARAMETERS: + * "socket" + * Address of the Socket to be used for the client. Must be non-NULL. + * "bindAPI" + * The address of the LDAPBindAPI containing the Bind information to be + * encoded in the Bind message. + * "pClient" + * The address at which the created LdapDefaultClient 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 LdapDefaultClient 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_LdapDefaultClient_CreateHelper( + PKIX_PL_Socket *socket, + LDAPBindAPI *bindAPI, + PKIX_PL_LdapDefaultClient **pClient, + void *plContext) +{ + PKIX_PL_HashTable *ht; + PKIX_PL_LdapDefaultClient *ldapDefaultClient = NULL; + PKIX_PL_Socket_Callback *callbackList; + PRFileDesc *fileDesc = NULL; + PLArenaPool *arena = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_CreateHelper"); + PKIX_NULLCHECK_TWO(socket, pClient); + + PKIX_CHECK(PKIX_PL_Object_Alloc + (PKIX_LDAPDEFAULTCLIENT_TYPE, + sizeof (PKIX_PL_LdapDefaultClient), + (PKIX_PL_Object **)&ldapDefaultClient, + plContext), + PKIX_COULDNOTCREATELDAPDEFAULTCLIENTOBJECT); + + ldapDefaultClient->vtable.initiateFcn = + pkix_pl_LdapDefaultClient_InitiateRequest; + ldapDefaultClient->vtable.resumeFcn = + pkix_pl_LdapDefaultClient_ResumeRequest; + + PKIX_CHECK(pkix_pl_Socket_GetPRFileDesc + (socket, &fileDesc, plContext), + PKIX_SOCKETGETPRFILEDESCFAILED); + + ldapDefaultClient->pollDesc.fd = fileDesc; + ldapDefaultClient->pollDesc.in_flags = 0; + ldapDefaultClient->pollDesc.out_flags = 0; + + ldapDefaultClient->bindAPI = bindAPI; + + PKIX_CHECK(PKIX_PL_HashTable_Create + (LDAP_CACHEBUCKETS, 0, &ht, plContext), + PKIX_HASHTABLECREATEFAILED); + + ldapDefaultClient->cachePtr = ht; + + PKIX_CHECK(pkix_pl_Socket_GetCallbackList + (socket, &callbackList, plContext), + PKIX_SOCKETGETCALLBACKLISTFAILED); + + ldapDefaultClient->callbackList = callbackList; + + PKIX_INCREF(socket); + ldapDefaultClient->clientSocket = socket; + + ldapDefaultClient->messageID = 0; + + ldapDefaultClient->bindAPI = bindAPI; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + PKIX_ERROR_FATAL(PKIX_OUTOFMEMORY); + } + ldapDefaultClient->arena = arena; + + ldapDefaultClient->sendBuf = NULL; + ldapDefaultClient->bytesToWrite = 0; + + PKIX_CHECK(PKIX_PL_Malloc + (RCVBUFSIZE, &ldapDefaultClient->rcvBuf, plContext), + PKIX_MALLOCFAILED); + ldapDefaultClient->capacity = RCVBUFSIZE; + + ldapDefaultClient->bindMsg = NULL; + ldapDefaultClient->bindMsgLen = 0; + + ldapDefaultClient->entriesFound = NULL; + ldapDefaultClient->currentRequest = NULL; + ldapDefaultClient->currentResponse = NULL; + + *pClient = ldapDefaultClient; + +cleanup: + + if (PKIX_ERROR_RECEIVED) { + PKIX_DECREF(ldapDefaultClient); + } + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: PKIX_PL_LdapDefaultClient_Create + * DESCRIPTION: + * + * This function creates a new LdapDefaultClient using the PRNetAddr pointed to + * by "sockaddr", the PRIntervalTime pointed to by "timeout", and the + * LDAPBindAPI pointed to by "bindAPI", and stores the result at "pClient". + * + * A value of zero for "timeout" means the LDAPClient will use non-blocking + * I/O. + * + * PARAMETERS: + * "sockaddr" + * Address of the PRNetAddr to be used for the socket connection. Must be + * non-NULL. + * "timeout" + * The PRIntervalTime to be used in I/O requests for this client. + * "bindAPI" + * The address of the LDAPBindAPI containing the Bind information to be + * encoded in the Bind message. + * "pClient" + * The address at which the created LdapDefaultClient 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 LdapDefaultClient 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_LdapDefaultClient_Create( + PRNetAddr *sockaddr, + PRIntervalTime timeout, + LDAPBindAPI *bindAPI, + PKIX_PL_LdapDefaultClient **pClient, + void *plContext) +{ + PRErrorCode status = 0; + PKIX_PL_Socket *socket = NULL; + PKIX_PL_LdapDefaultClient *client = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "PKIX_PL_LdapDefaultClient_Create"); + PKIX_NULLCHECK_TWO(sockaddr, pClient); + + PKIX_CHECK(pkix_pl_Socket_Create + (PKIX_FALSE, timeout, sockaddr, &status, &socket, plContext), + PKIX_SOCKETCREATEFAILED); + + PKIX_CHECK(pkix_pl_LdapDefaultClient_CreateHelper + (socket, bindAPI, &client, plContext), + PKIX_LDAPDEFAULTCLIENTCREATEHELPERFAILED); + + /* Did Socket_Create say the connection was made? */ + if (status == 0) { + if (client->bindAPI != NULL) { + client->connectStatus = CONNECTED; + } else { + client->connectStatus = BOUND; + } + } else { + client->connectStatus = CONNECT_PENDING; + } + + *pClient = client; + +cleanup: + if (PKIX_ERROR_RECEIVED) { + PKIX_DECREF(client); + } + + PKIX_DECREF(socket); + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: PKIX_PL_LdapDefaultClient_CreateByName + * DESCRIPTION: + * + * This function creates a new LdapDefaultClient using the hostname pointed to + * by "hostname", the PRIntervalTime pointed to by "timeout", and the + * LDAPBindAPI pointed to by "bindAPI", and stores the result at "pClient". + * + * A value of zero for "timeout" means the LDAPClient will use non-blocking + * I/O. + * + * PARAMETERS: + * "hostname" + * Address of the hostname to be used for the socket connection. Must be + * non-NULL. + * "timeout" + * The PRIntervalTime to be used in I/O requests for this client. + * "bindAPI" + * The address of the LDAPBindAPI containing the Bind information to be + * encoded in the Bind message. + * "pClient" + * The address at which the created LdapDefaultClient 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 LdapDefaultClient 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_LdapDefaultClient_CreateByName( + char *hostname, + PRIntervalTime timeout, + LDAPBindAPI *bindAPI, + PKIX_PL_LdapDefaultClient **pClient, + void *plContext) +{ + PRErrorCode status = 0; + PKIX_PL_Socket *socket = NULL; + PKIX_PL_LdapDefaultClient *client = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "PKIX_PL_LdapDefaultClient_CreateByName"); + PKIX_NULLCHECK_TWO(hostname, pClient); + + PKIX_CHECK(pkix_pl_Socket_CreateByName + (PKIX_FALSE, timeout, hostname, &status, &socket, plContext), + PKIX_SOCKETCREATEBYNAMEFAILED); + + PKIX_CHECK(pkix_pl_LdapDefaultClient_CreateHelper + (socket, bindAPI, &client, plContext), + PKIX_LDAPDEFAULTCLIENTCREATEHELPERFAILED); + + /* Did Socket_Create say the connection was made? */ + if (status == 0) { + if (client->bindAPI != NULL) { + client->connectStatus = CONNECTED; + } else { + client->connectStatus = BOUND; + } + } else { + client->connectStatus = CONNECT_PENDING; + } + + *pClient = client; + +cleanup: + if (PKIX_ERROR_RECEIVED) { + PKIX_DECREF(client); + } + + PKIX_DECREF(socket); + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_Destroy + * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_Destroy( + PKIX_PL_Object *object, + void *plContext) +{ + PKIX_Int32 bytesWritten = 0; + PKIX_PL_LdapDefaultClient *client = NULL; + PKIX_PL_Socket_Callback *callbackList = NULL; + SECItem *encoded = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_Destroy"); + PKIX_NULLCHECK_ONE(object); + + PKIX_CHECK(pkix_CheckType + (object, PKIX_LDAPDEFAULTCLIENT_TYPE, plContext), + PKIX_OBJECTNOTANLDAPDEFAULTCLIENT); + + client = (PKIX_PL_LdapDefaultClient *)object; + + switch (client->connectStatus) { + case CONNECT_PENDING: + break; + case CONNECTED: + case BIND_PENDING: + case BIND_RESPONSE: + case BIND_RESPONSE_PENDING: + case BOUND: + case SEND_PENDING: + case RECV: + case RECV_PENDING: + case RECV_INITIAL: + case RECV_NONINITIAL: + case ABANDON_PENDING: + if (client->bindAPI != NULL) { + PKIX_CHECK(pkix_pl_LdapDefaultClient_MakeUnbind + (client->arena, + ++(client->messageID), + &encoded, + plContext), + PKIX_LDAPDEFAULTCLIENTMAKEUNBINDFAILED); + + callbackList = + (PKIX_PL_Socket_Callback *)(client->callbackList); + PKIX_CHECK(callbackList->sendCallback + (client->clientSocket, + encoded->data, + encoded->len, + &bytesWritten, + plContext), + PKIX_SOCKETSENDFAILED); + } + break; + default: + PKIX_ERROR(PKIX_LDAPDEFAULTCLIENTINILLEGALSTATE); + } + + PKIX_DECREF(client->cachePtr); + PKIX_DECREF(client->clientSocket); + PKIX_DECREF(client->entriesFound); + PKIX_DECREF(client->currentRequest); + PKIX_DECREF(client->currentResponse); + + PKIX_CHECK(PKIX_PL_Free + (client->rcvBuf, plContext), PKIX_FREEFAILED); + + PKIX_PL_NSSCALL + (LDAPDEFAULTCLIENT, + PORT_FreeArena, + (client->arena, PR_FALSE)); + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_Hashcode + * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h) + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_Hashcode( + PKIX_PL_Object *object, + PKIX_UInt32 *pHashcode, + void *plContext) +{ + PKIX_PL_LdapDefaultClient *ldapDefaultClient = NULL; + PKIX_UInt32 tempHash = 0; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_Hashcode"); + PKIX_NULLCHECK_TWO(object, pHashcode); + + PKIX_CHECK(pkix_CheckType + (object, PKIX_LDAPDEFAULTCLIENT_TYPE, plContext), + PKIX_OBJECTNOTANLDAPDEFAULTCLIENT); + + ldapDefaultClient = (PKIX_PL_LdapDefaultClient *)object; + + PKIX_CHECK(PKIX_PL_Object_Hashcode + ((PKIX_PL_Object *)ldapDefaultClient->clientSocket, + &tempHash, + plContext), + PKIX_SOCKETHASHCODEFAILED); + + if (ldapDefaultClient->bindAPI != NULL) { + tempHash = (tempHash << 7) + + ldapDefaultClient->bindAPI->selector; + } + + *pHashcode = tempHash; + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_Equals + * (see comments for PKIX_PL_EqualsCallback in pkix_pl_system.h) + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_Equals( + PKIX_PL_Object *firstObject, + PKIX_PL_Object *secondObject, + PKIX_Int32 *pResult, + void *plContext) +{ + PKIX_PL_LdapDefaultClient *firstClientContext = NULL; + PKIX_PL_LdapDefaultClient *secondClientContext = NULL; + PKIX_Int32 compare = 0; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_Equals"); + PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult); + + *pResult = PKIX_FALSE; + + PKIX_CHECK(pkix_CheckTypes + (firstObject, + secondObject, + PKIX_LDAPDEFAULTCLIENT_TYPE, + plContext), + PKIX_OBJECTNOTANLDAPDEFAULTCLIENT); + + firstClientContext = (PKIX_PL_LdapDefaultClient *)firstObject; + secondClientContext = (PKIX_PL_LdapDefaultClient *)secondObject; + + if (firstClientContext == secondClientContext) { + *pResult = PKIX_TRUE; + goto cleanup; + } + + PKIX_CHECK(PKIX_PL_Object_Equals + ((PKIX_PL_Object *)firstClientContext->clientSocket, + (PKIX_PL_Object *)secondClientContext->clientSocket, + &compare, + plContext), + PKIX_SOCKETEQUALSFAILED); + + if (!compare) { + goto cleanup; + } + + if (PKIX_EXACTLY_ONE_NULL + (firstClientContext->bindAPI, secondClientContext->bindAPI)) { + goto cleanup; + } + + if (firstClientContext->bindAPI) { + if (firstClientContext->bindAPI->selector != + secondClientContext->bindAPI->selector) { + goto cleanup; + } + } + + *pResult = PKIX_TRUE; + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_RegisterSelf + * + * DESCRIPTION: + * Registers PKIX_PL_LDAPDEFAULTCLIENT_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_LdapDefaultClient_RegisterSelf(void *plContext) +{ + extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; + pkix_ClassTable_Entry entry; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_RegisterSelf"); + + entry.description = "LdapDefaultClient"; + entry.objCounter = 0; + entry.typeObjectSize = sizeof(PKIX_PL_LdapDefaultClient); + entry.destructor = pkix_pl_LdapDefaultClient_Destroy; + entry.equalsFunction = pkix_pl_LdapDefaultClient_Equals; + entry.hashcodeFunction = pkix_pl_LdapDefaultClient_Hashcode; + entry.toStringFunction = NULL; + entry.comparator = NULL; + entry.duplicateFunction = NULL; + + systemClasses[PKIX_LDAPDEFAULTCLIENT_TYPE] = entry; + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_GetPollDesc + * DESCRIPTION: + * + * This function retrieves the PRPollDesc from the LdapDefaultClient + * pointed to by "context" and stores the address at "pPollDesc". + * + * PARAMETERS: + * "context" + * The LdapDefaultClient whose PRPollDesc is desired. Must be non-NULL. + * "pPollDesc" + * Address where PRPollDesc will 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 Fatal Error if the function fails in an unrecoverable way. + */ +PKIX_Error * +pkix_pl_LdapDefaultClient_GetPollDesc( + PKIX_PL_LdapDefaultClient *context, + PRPollDesc **pPollDesc, + void *plContext) +{ + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_GetPollDesc"); + PKIX_NULLCHECK_TWO(context, pPollDesc); + + *pPollDesc = &(context->pollDesc); + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* --Private-Ldap-CertStore-I/O-Functions---------------------------- */ +/* + * FUNCTION: pkix_pl_LdapDefaultClient_ConnectContinue + * DESCRIPTION: + * + * This function determines whether a socket Connect initiated earlier for the + * CertStore embodied in the LdapDefaultClient "client" has completed, and + * stores in "pKeepGoing" a flag indicating whether processing can continue + * without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_ConnectContinue( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_PL_Socket_Callback *callbackList; + PRErrorCode status; + PKIX_Boolean keepGoing = PKIX_FALSE; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_ConnectContinue"); + PKIX_NULLCHECK_ONE(client); + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->connectcontinueCallback + (client->clientSocket, &status, plContext), + PKIX_SOCKETCONNECTCONTINUEFAILED); + + if (status == 0) { + if (client->bindAPI != NULL) { + client->connectStatus = CONNECTED; + } else { + client->connectStatus = BOUND; + } + keepGoing = PKIX_FALSE; + } else if (status != PR_IN_PROGRESS_ERROR) { + PKIX_ERROR(PKIX_UNEXPECTEDERRORINESTABLISHINGCONNECTION); + } + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + + *pKeepGoing = keepGoing; + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_Bind + * DESCRIPTION: + * + * This function creates and sends the LDAP-protocol Bind message for the + * CertStore embodied in the LdapDefaultClient "client", and stores in + * "pKeepGoing" a flag indicating whether processing can continue without + * further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_Bind( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + SECItem *encoded = NULL; + PKIX_Int32 bytesWritten = 0; + PKIX_PL_Socket_Callback *callbackList; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_Bind"); + PKIX_NULLCHECK_ONE(client); + + /* if we have not yet constructed the BIND message, build it now */ + if (!(client->bindMsg)) { + PKIX_CHECK(pkix_pl_LdapDefaultClient_MakeBind + (client->arena, + 3, + client->bindAPI, + client->messageID, + &encoded, + plContext), + PKIX_LDAPDEFAULTCLIENTMAKEBINDFAILED); + client->bindMsg = encoded->data; + client->bindMsgLen = encoded->len; + } + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->sendCallback + (client->clientSocket, + client->bindMsg, + client->bindMsgLen, + &bytesWritten, + plContext), + PKIX_SOCKETSENDFAILED); + + client->lastIO = PR_Now(); + + if (bytesWritten < 0) { + client->connectStatus = BIND_PENDING; + *pKeepGoing = PKIX_FALSE; + } else { + client->connectStatus = BIND_RESPONSE; + *pKeepGoing = PKIX_TRUE; + } + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_BindContinue + * DESCRIPTION: + * + * This function determines whether the LDAP-protocol Bind message for the + * CertStore embodied in the LdapDefaultClient "client" has completed, and + * stores in "pKeepGoing" a flag indicating whether processing can continue + * without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient 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_LdapDefaultClient_BindContinue( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_Int32 bytesWritten = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_BindContinue"); + PKIX_NULLCHECK_ONE(client); + + *pKeepGoing = PKIX_FALSE; + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->pollCallback + (client->clientSocket, &bytesWritten, NULL, plContext), + PKIX_SOCKETPOLLFAILED); + + /* + * If the send completed we can proceed to try for the + * response. If the send did not complete we will have + * continue to poll. + */ + if (bytesWritten >= 0) { + + client->connectStatus = BIND_RESPONSE; + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + + *pKeepGoing = PKIX_TRUE; + } + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_BindResponse + * DESCRIPTION: + * + * This function attempts to read the LDAP-protocol BindResponse message for + * the CertStore embodied in the LdapDefaultClient "client", and stores in + * "pKeepGoing" a flag indicating whether processing can continue without + * further input. + * + * If a BindResponse is received with a Result code of 0 (success), we + * continue with the connection. If a non-zero Result code is received, + * we throw an Error. Some more sophisticated handling of that condition + * might be in order in the future. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_BindResponse( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_Int32 bytesRead = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_BindResponse"); + PKIX_NULLCHECK_TWO(client, client->rcvBuf); + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->recvCallback + (client->clientSocket, + client->rcvBuf, + client->capacity, + &bytesRead, + plContext), + PKIX_SOCKETRECVFAILED); + + client->lastIO = PR_Now(); + + if (bytesRead > 0) { + PKIX_CHECK(pkix_pl_LdapDefaultClient_VerifyBindResponse + (client, bytesRead, plContext), + PKIX_LDAPDEFAULTCLIENTVERIFYBINDRESPONSEFAILED); + /* + * XXX What should we do if failure? At present if + * VerifyBindResponse throws an Error, we do too. + */ + client->connectStatus = BOUND; + } else { + client->connectStatus = BIND_RESPONSE_PENDING; + } + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + + *pKeepGoing = PKIX_TRUE; + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_BindResponseContinue + * DESCRIPTION: + * + * This function determines whether the LDAP-protocol BindResponse message for + * the CertStore embodied in the LdapDefaultClient "client" has completed, and + * stores in "pKeepGoing" a flag indicating whether processing can continue + * without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_BindResponseContinue( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_Int32 bytesRead = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_BindResponseContinue"); + PKIX_NULLCHECK_ONE(client); + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->pollCallback + (client->clientSocket, NULL, &bytesRead, plContext), + PKIX_SOCKETPOLLFAILED); + + if (bytesRead > 0) { + PKIX_CHECK(pkix_pl_LdapDefaultClient_VerifyBindResponse + (client, bytesRead, plContext), + PKIX_LDAPDEFAULTCLIENTVERIFYBINDRESPONSEFAILED); + client->connectStatus = BOUND; + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + + *pKeepGoing = PKIX_TRUE; + } else { + *pKeepGoing = PKIX_FALSE; + } + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_Send + * DESCRIPTION: + * + * This function creates and sends an LDAP-protocol message for the + * CertStore embodied in the LdapDefaultClient "client", and stores in + * "pKeepGoing" a flag indicating whether processing can continue without + * further input, and at "pBytesTransferred" the number of bytes sent. + * + * If "pBytesTransferred" is zero, it indicates that non-blocking I/O is in use + * and that transmission has not completed. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * Must be non-NULL. + * "pBytesTransferred" + * The address at which the number of bytes sent is 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_Send( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + PKIX_UInt32 *pBytesTransferred, + void *plContext) +{ + PKIX_Int32 bytesWritten = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_Send"); + PKIX_NULLCHECK_THREE(client, pKeepGoing, pBytesTransferred); + + *pKeepGoing = PKIX_FALSE; + + /* Do we have anything waiting to go? */ + if (client->sendBuf) { + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->sendCallback + (client->clientSocket, + client->sendBuf, + client->bytesToWrite, + &bytesWritten, + plContext), + PKIX_SOCKETSENDFAILED); + + client->lastIO = PR_Now(); + + /* + * If the send completed we can proceed to try for the + * response. If the send did not complete we will have + * to poll for completion later. + */ + if (bytesWritten >= 0) { + client->sendBuf = NULL; + client->connectStatus = RECV; + *pKeepGoing = PKIX_TRUE; + + } else { + *pKeepGoing = PKIX_FALSE; + client->connectStatus = SEND_PENDING; + } + + } + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + + *pBytesTransferred = bytesWritten; + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_SendContinue + * DESCRIPTION: + * + * This function determines whether the sending of the LDAP-protocol message + * for the CertStore embodied in the LdapDefaultClient "client" has completed, + * and stores in "pKeepGoing" a flag indicating whether processing can continue + * without further input, and at "pBytesTransferred" the number of bytes sent. + * + * If "pBytesTransferred" is zero, it indicates that non-blocking I/O is in use + * and that transmission has not completed. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * Must be non-NULL. + * "pBytesTransferred" + * The address at which the number of bytes sent is 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_SendContinue( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + PKIX_UInt32 *pBytesTransferred, + void *plContext) +{ + PKIX_Int32 bytesWritten = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_SendContinue"); + PKIX_NULLCHECK_THREE(client, pKeepGoing, pBytesTransferred); + + *pKeepGoing = PKIX_FALSE; + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->pollCallback + (client->clientSocket, &bytesWritten, NULL, plContext), + PKIX_SOCKETPOLLFAILED); + + /* + * If the send completed we can proceed to try for the + * response. If the send did not complete we will have + * continue to poll. + */ + if (bytesWritten >= 0) { + client->sendBuf = NULL; + client->connectStatus = RECV; + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + + *pKeepGoing = PKIX_TRUE; + } + + *pBytesTransferred = bytesWritten; + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_Recv + * DESCRIPTION: + * + * This function receives an LDAP-protocol message for the CertStore embodied + * in the LdapDefaultClient "client", and stores in "pKeepGoing" a flag + * indicating whether processing can continue without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_Recv( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_Int32 bytesRead = 0; + PKIX_UInt32 bytesToRead = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_Recv"); + PKIX_NULLCHECK_THREE(client, pKeepGoing, client->rcvBuf); + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + /* + * If we attempt to fill our buffer with every read, we increase + * the risk of an ugly situation: one or two bytes of a new message + * left over at the end of processing one message. With such a + * fragment, we can't decode a byte count and so won't know how much + * space to allocate for the next LdapResponse. We try to avoid that + * case by reading just enough to complete the current message, unless + * there will be at least MINIMUM_MSG_LENGTH bytes left over. + */ + if (client->currentResponse) { + PKIX_CHECK(pkix_pl_LdapResponse_GetCapacity + (client->currentResponse, &bytesToRead, plContext), + PKIX_LDAPRESPONSEGETCAPACITYFAILED); + if ((bytesToRead > client->capacity) || + ((bytesToRead + MINIMUM_MSG_LENGTH) < client->capacity)) { + bytesToRead = client->capacity; + } + } else { + bytesToRead = client->capacity; + } + + client->currentBytesAvailable = 0; + + PKIX_CHECK(callbackList->recvCallback + (client->clientSocket, + (void *)client->rcvBuf, + bytesToRead, + &bytesRead, + plContext), + PKIX_SOCKETRECVFAILED); + + client->currentInPtr = client->rcvBuf; + client->lastIO = PR_Now(); + + if (bytesRead > 0) { + client->currentBytesAvailable = bytesRead; + client->connectStatus = RECV_INITIAL; + *pKeepGoing = PKIX_TRUE; + } else { + client->connectStatus = RECV_PENDING; + *pKeepGoing = PKIX_FALSE; + } + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_RecvContinue + * DESCRIPTION: + * + * This function determines whether the receiving of the LDAP-protocol message + * for the CertStore embodied in the LdapDefaultClient "client" has completed, + * and stores in "pKeepGoing" a flag indicating whether processing can continue + * without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_RecvContinue( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_Int32 bytesRead = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_RecvContinue"); + PKIX_NULLCHECK_TWO(client, pKeepGoing); + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->pollCallback + (client->clientSocket, NULL, &bytesRead, plContext), + PKIX_SOCKETPOLLFAILED); + + if (bytesRead > 0) { + client->currentBytesAvailable += bytesRead; + client->connectStatus = RECV_INITIAL; + *pKeepGoing = PKIX_TRUE; + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + } else { + *pKeepGoing = PKIX_FALSE; + } + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_AbandonContinue + * DESCRIPTION: + * + * This function determines whether the abandon-message request of the + * LDAP-protocol message for the CertStore embodied in the LdapDefaultClient + * "client" has completed, and stores in "pKeepGoing" a flag indicating whether + * processing can continue without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_AbandonContinue( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + PKIX_Int32 bytesWritten = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_AbandonContinue"); + PKIX_NULLCHECK_TWO(client, pKeepGoing); + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + + PKIX_CHECK(callbackList->pollCallback + (client->clientSocket, &bytesWritten, NULL, plContext), + PKIX_SOCKETPOLLFAILED); + + if (bytesWritten > 0) { + client->connectStatus = BOUND; + *pKeepGoing = PKIX_TRUE; + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)client, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + } else { + *pKeepGoing = PKIX_FALSE; + } + +cleanup: + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_RecvInitial + * DESCRIPTION: + * + * This function processes the contents of the first buffer of a received + * LDAP-protocol message for the CertStore embodied in the LdapDefaultClient + * "client", and stores in "pKeepGoing" a flag indicating whether processing can + * continue without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_RecvInitial( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + unsigned char *msgBuf = NULL; + unsigned char *to = NULL; + unsigned char *from = NULL; + PKIX_UInt32 dataIndex = 0; + PKIX_UInt32 messageIdLen = 0; + PKIX_UInt32 messageLength = 0; + PKIX_UInt32 sizeofLength = 0; + PKIX_UInt32 bytesProcessed = 0; + unsigned char messageChar = 0; + LDAPMessageType messageType = 0; + PKIX_Int32 bytesRead = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_RecvInitial"); + PKIX_NULLCHECK_TWO(client, pKeepGoing); + + /* + * Is there an LDAPResponse in progress? I.e., have we + * already processed the tag and length at the beginning of + * the message? + */ + if (client->currentResponse) { + client->connectStatus = RECV_NONINITIAL; + *pKeepGoing = PKIX_TRUE; + goto cleanup; + } + msgBuf = client->currentInPtr; + + /* Do we have enough of the message to decode the message length? */ + if (client->currentBytesAvailable < MINIMUM_MSG_LENGTH) { + /* + * No! Move these few bytes to the beginning of rcvBuf + * and hang another read. + */ + + to = (unsigned char *)client->rcvBuf; + from = client->currentInPtr; + for (dataIndex = 0; + dataIndex < client->currentBytesAvailable; + dataIndex++) { + *to++ = *from++; + } + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + PKIX_CHECK(callbackList->recvCallback + (client->clientSocket, + (void *)to, + client->capacity - client->currentBytesAvailable, + &bytesRead, + plContext), + PKIX_SOCKETRECVFAILED); + + client->currentInPtr = client->rcvBuf; + client->lastIO = PR_Now(); + + if (bytesRead <= 0) { + client->connectStatus = RECV_PENDING; + *pKeepGoing = PKIX_FALSE; + goto cleanup; + } else { + client->currentBytesAvailable += bytesRead; + } + } + + /* + * We have to determine whether the response is an entry, with + * application-specific tag LDAP_SEARCHRESPONSEENTRY_TYPE, or a + * resultCode, with application tag LDAP_SEARCHRESPONSERESULT_TYPE. + * First, we have to figure out where to look for the tag. + */ + + /* Is the message length short form (one octet) or long form? */ + if ((msgBuf[1] & 0x80) != 0) { + sizeofLength = msgBuf[1] & 0x7F; + for (dataIndex = 0; dataIndex < sizeofLength; dataIndex++) { + messageLength = + (messageLength << 8) + msgBuf[dataIndex + 2]; + } + } else { + messageLength = msgBuf[1]; + } + + /* How many bytes did the messageID require? */ + messageIdLen = msgBuf[dataIndex + 3]; + + messageChar = msgBuf[dataIndex + messageIdLen + 4]; + + /* Are we looking at an Entry message or a ResultCode message? */ + if ((SEC_ASN1_CONSTRUCTED | SEC_ASN1_APPLICATION | + LDAP_SEARCHRESPONSEENTRY_TYPE) == messageChar) { + + messageType = LDAP_SEARCHRESPONSEENTRY_TYPE; + + } else if ((SEC_ASN1_CONSTRUCTED | SEC_ASN1_APPLICATION | + LDAP_SEARCHRESPONSERESULT_TYPE) == messageChar) { + + messageType = LDAP_SEARCHRESPONSERESULT_TYPE; + + } else { + + PKIX_ERROR(PKIX_SEARCHRESPONSEPACKETOFUNKNOWNTYPE); + + } + + /* + * messageLength is the length from (tag, length, value). + * We have to allocate space for the tag and length bits too. + */ + PKIX_CHECK(pkix_pl_LdapResponse_Create + (messageType, + messageLength + dataIndex + 2, + client->currentBytesAvailable, + msgBuf, + &bytesProcessed, + &(client->currentResponse), + plContext), + PKIX_LDAPRESPONSECREATEFAILED); + + client->currentBytesAvailable -= bytesProcessed; + + PKIX_CHECK(pkix_pl_LdapDefaultClient_RecvCheckComplete + (client, bytesProcessed, pKeepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTRECVCHECKCOMPLETEFAILED); + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_RecvNonInitial + * DESCRIPTION: + * + * This function processes the contents of buffers, after the first, of a + * received LDAP-protocol message for the CertStore embodied in the + * LdapDefaultClient "client", and stores in "pKeepGoing" a flag indicating + * whether processing can continue without further input. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pKeepGoing" + * The address at which the Boolean state machine flag is stored to + * indicate whether processing can continue without further input. + * 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_RecvNonInitial( + PKIX_PL_LdapDefaultClient *client, + PKIX_Boolean *pKeepGoing, + void *plContext) +{ + + PKIX_UInt32 bytesProcessed = 0; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_RecvNonInitial"); + PKIX_NULLCHECK_TWO(client, pKeepGoing); + + PKIX_CHECK(pkix_pl_LdapResponse_Append + (client->currentResponse, + client->currentBytesAvailable, + client->currentInPtr, + &bytesProcessed, + plContext), + PKIX_LDAPRESPONSEAPPENDFAILED); + + client->currentBytesAvailable -= bytesProcessed; + + PKIX_CHECK(pkix_pl_LdapDefaultClient_RecvCheckComplete + (client, bytesProcessed, pKeepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTRECVCHECKCOMPLETEFAILED); + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_Dispatch + * DESCRIPTION: + * + * This function is the state machine dispatcher for the CertStore embodied in + * the LdapDefaultClient pointed to by "client". Results are returned by + * changes to various fields in the context. + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_Dispatch( + PKIX_PL_LdapDefaultClient *client, + void *plContext) +{ + PKIX_UInt32 bytesTransferred = 0; + PKIX_Boolean keepGoing = PKIX_TRUE; + + PKIX_ENTER(LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_Dispatch"); + PKIX_NULLCHECK_ONE(client); + + while (keepGoing) { + switch (client->connectStatus) { + case CONNECT_PENDING: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_ConnectContinue + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTCONNECTCONTINUEFAILED); + break; + case CONNECTED: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_Bind + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTBINDFAILED); + break; + case BIND_PENDING: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_BindContinue + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTBINDCONTINUEFAILED); + break; + case BIND_RESPONSE: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_BindResponse + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTBINDRESPONSEFAILED); + break; + case BIND_RESPONSE_PENDING: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_BindResponseContinue + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTBINDRESPONSECONTINUEFAILED); + break; + case BOUND: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_Send + (client, &keepGoing, &bytesTransferred, plContext), + PKIX_LDAPDEFAULTCLIENTSENDFAILED); + break; + case SEND_PENDING: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_SendContinue + (client, &keepGoing, &bytesTransferred, plContext), + PKIX_LDAPDEFAULTCLIENTSENDCONTINUEFAILED); + break; + case RECV: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_Recv + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTRECVFAILED); + break; + case RECV_PENDING: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_RecvContinue + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTRECVCONTINUEFAILED); + break; + case RECV_INITIAL: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_RecvInitial + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTRECVINITIALFAILED); + break; + case RECV_NONINITIAL: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_RecvNonInitial + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTRECVNONINITIALFAILED); + break; + case ABANDON_PENDING: + PKIX_CHECK + (pkix_pl_LdapDefaultClient_AbandonContinue + (client, &keepGoing, plContext), + PKIX_LDAPDEFAULTCLIENTABANDONCONTINUEFAILED); + break; + default: + PKIX_ERROR(PKIX_LDAPCERTSTOREINILLEGALSTATE); + } + } + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_MakeAndFilter + * DESCRIPTION: + * + * This function allocates space from the arena pointed to by "arena" to + * construct a filter that will match components of the X500Name pointed to by + * XXX... + * + * PARAMETERS: + * "arena" + * The address of the PLArenaPool used in creating the filter. Must be + * non-NULL. + * "nameComponent" + * The address of a NULL-terminated list of LDAPNameComponents + * Must be non-NULL. + * "pFilter" + * The address at which the result is stored. + * "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 CertStore Error if the function fails in a non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_MakeAndFilter( + PLArenaPool *arena, + LDAPNameComponent **nameComponents, + LDAPFilter **pFilter, + void *plContext) +{ + LDAPFilter **setOfFilter; + LDAPFilter *andFilter = NULL; + LDAPFilter *currentFilter = NULL; + PKIX_UInt32 componentsPresent = 0; + void *v = NULL; + unsigned char *component = NULL; + LDAPNameComponent **componentP = NULL; + + PKIX_ENTER(CERTSTORE, "pkix_pl_LdapDefaultClient_MakeAndFilter"); + PKIX_NULLCHECK_THREE(arena, nameComponents, pFilter); + + /* count how many components we were provided */ + for (componentP = nameComponents, componentsPresent = 0; + *(componentP++) != NULL; + componentsPresent++) {} + + /* Space for (componentsPresent + 1) pointers to LDAPFilter */ + PKIX_PL_NSSCALLRV(CERTSTORE, v, PORT_ArenaZAlloc, + (arena, (componentsPresent + 1)*sizeof(LDAPFilter *))); + setOfFilter = (LDAPFilter **)v; + + /* Space for AndFilter and <componentsPresent> EqualFilters */ + PKIX_PL_NSSCALLRV(CERTSTORE, v, PORT_ArenaZNewArray, + (arena, LDAPFilter, componentsPresent + 1)); + setOfFilter[0] = (LDAPFilter *)v; + + /* Claim the first array element for the ANDFilter */ + andFilter = setOfFilter[0]; + + /* Set ANDFilter to point to the first EqualFilter pointer */ + andFilter->selector = LDAP_ANDFILTER_TYPE; + andFilter->filter.andFilter.filters = setOfFilter; + + currentFilter = andFilter + 1; + + for (componentP = nameComponents, componentsPresent = 0; + *(componentP) != NULL; componentP++) { + setOfFilter[componentsPresent++] = currentFilter; + currentFilter->selector = LDAP_EQUALFILTER_TYPE; + component = (*componentP)->attrType; + currentFilter->filter.equalFilter.attrType.data = component; + currentFilter->filter.equalFilter.attrType.len = + PL_strlen((const char *)component); + component = (*componentP)->attrValue; + currentFilter->filter.equalFilter.attrValue.data = component; + currentFilter->filter.equalFilter.attrValue.len = + PL_strlen((const char *)component); + currentFilter++; + } + + setOfFilter[componentsPresent] = NULL; + + *pFilter = andFilter; + + PKIX_RETURN(CERTSTORE); + +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_InitiateRequest + * DESCRIPTION: + * + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "requestParams" + * The address of an LdapClientParams object. Must be non-NULL. + * "pPollDesc" + * The location where the address of the PRPollDesc is stored, if the + * client returns with I/O pending. + * "pResponse" + * The address where the List of LDAPResponses, or NULL for an + * unfinished request, is 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_InitiateRequest( + PKIX_PL_LdapClient *genericClient, + LDAPRequestParams *requestParams, + void **pPollDesc, + PKIX_List **pResponse, + void *plContext) +{ + PKIX_List *searchResponseList = NULL; + SECItem *encoded = NULL; + LDAPFilter *filter = NULL; + PKIX_PL_LdapDefaultClient *client = 0; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, + "pkix_pl_LdapDefaultClient_InitiateRequest"); + PKIX_NULLCHECK_FOUR(genericClient, requestParams, pPollDesc, pResponse); + + PKIX_CHECK(pkix_CheckType + ((PKIX_PL_Object *)genericClient, + PKIX_LDAPDEFAULTCLIENT_TYPE, + plContext), + PKIX_GENERICCLIENTNOTANLDAPDEFAULTCLIENT); + + client = (PKIX_PL_LdapDefaultClient *)genericClient; + + PKIX_CHECK(pkix_pl_LdapDefaultClient_MakeAndFilter + (client->arena, requestParams->nc, &filter, plContext), + PKIX_LDAPDEFAULTCLIENTMAKEANDFILTERFAILED); + + PKIX_CHECK(pkix_pl_LdapRequest_Create + (client->arena, + client->messageID++, + requestParams->baseObject, + requestParams->scope, + requestParams->derefAliases, + requestParams->sizeLimit, + requestParams->timeLimit, + PKIX_FALSE, /* attrs only */ + filter, + requestParams->attributes, + &client->currentRequest, + plContext), + PKIX_LDAPREQUESTCREATEFAILED); + + /* check hashtable for matching request */ + PKIX_CHECK(PKIX_PL_HashTable_Lookup + (client->cachePtr, + (PKIX_PL_Object *)(client->currentRequest), + (PKIX_PL_Object **)&searchResponseList, + plContext), + PKIX_HASHTABLELOOKUPFAILED); + + if (searchResponseList != NULL) { + *pPollDesc = NULL; + *pResponse = searchResponseList; + PKIX_DECREF(client->currentRequest); + goto cleanup; + } + + /* It wasn't cached. We'll have to actually send it. */ + + PKIX_CHECK(pkix_pl_LdapRequest_GetEncoded + (client->currentRequest, &encoded, plContext), + PKIX_LDAPREQUESTGETENCODEDFAILED); + + client->sendBuf = encoded->data; + client->bytesToWrite = encoded->len; + + PKIX_CHECK(pkix_pl_LdapDefaultClient_Dispatch(client, plContext), + PKIX_LDAPDEFAULTCLIENTDISPATCHFAILED); + + /* + * It's not enough that we may be done with a particular read. + * We're still processing the transaction until we've gotten the + * SearchResponseResult message and returned to the BOUND state. + * Otherwise we must still have a read pending, and must hold off + * on returning results. + */ + if ((client->connectStatus == BOUND) && + (client->entriesFound != NULL)) { + *pPollDesc = NULL; + *pResponse = client->entriesFound; + client->entriesFound = NULL; + PKIX_DECREF(client->currentRequest); + } else { + *pPollDesc = &client->pollDesc; + *pResponse = NULL; + } + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); + +} + +/* + * FUNCTION: pkix_pl_LdapDefaultClient_ResumeRequest + * DESCRIPTION: + * + * + * PARAMETERS: + * "client" + * The address of the LdapDefaultClient object. Must be non-NULL. + * "pPollDesc" + * The location where the address of the PRPollDesc is stored, if the + * client returns with I/O pending. + * "pResponse" + * The address where the List of LDAPResponses, or NULL for an + * unfinished request, is 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 LdapDefaultClient Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_pl_LdapDefaultClient_ResumeRequest( + PKIX_PL_LdapClient *genericClient, + void **pPollDesc, + PKIX_List **pResponse, + void *plContext) +{ + PKIX_PL_LdapDefaultClient *client = 0; + + PKIX_ENTER + (LDAPDEFAULTCLIENT, "pkix_pl_LdapDefaultClient_ResumeRequest"); + PKIX_NULLCHECK_THREE(genericClient, pPollDesc, pResponse); + + PKIX_CHECK(pkix_CheckType + ((PKIX_PL_Object *)genericClient, + PKIX_LDAPDEFAULTCLIENT_TYPE, + plContext), + PKIX_GENERICCLIENTNOTANLDAPDEFAULTCLIENT); + + client = (PKIX_PL_LdapDefaultClient *)genericClient; + + PKIX_CHECK(pkix_pl_LdapDefaultClient_Dispatch(client, plContext), + PKIX_LDAPDEFAULTCLIENTDISPATCHFAILED); + + /* + * It's not enough that we may be done with a particular read. + * We're still processing the transaction until we've gotten the + * SearchResponseResult message and returned to the BOUND state. + * Otherwise we must still have a read pending, and must hold off + * on returning results. + */ + if ((client->connectStatus == BOUND) && + (client->entriesFound != NULL)) { + *pPollDesc = NULL; + *pResponse = client->entriesFound; + client->entriesFound = NULL; + PKIX_DECREF(client->currentRequest); + } else { + *pPollDesc = &client->pollDesc; + *pResponse = NULL; + } + +cleanup: + + PKIX_RETURN(LDAPDEFAULTCLIENT); + +} + +/* --Public-LdapDefaultClient-Functions----------------------------------- */ + +/* + * FUNCTION: PKIX_PL_LdapDefaultClient_AbandonRequest + * DESCRIPTION: + * + * This function creates and sends an LDAP-protocol "Abandon" message to the + * server connected to the LdapDefaultClient pointed to by "client". + * + * PARAMETERS: + * "client" + * The LdapDefaultClient whose connection is to be abandoned. 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 Fatal Error if the function fails in an unrecoverable way. + */ +PKIX_Error * +PKIX_PL_LdapDefaultClient_AbandonRequest( + PKIX_PL_LdapDefaultClient *client, + void *plContext) +{ + PKIX_Int32 bytesWritten = 0; + PKIX_PL_Socket_Callback *callbackList = NULL; + SECItem *encoded = NULL; + + PKIX_ENTER(CERTSTORE, "PKIX_PL_LdapDefaultClient_AbandonRequest"); + PKIX_NULLCHECK_ONE(client); + + if (client->connectStatus == RECV_PENDING) { + PKIX_CHECK(pkix_pl_LdapDefaultClient_MakeAbandon + (client->arena, + (client->messageID) - 1, + &encoded, + plContext), + PKIX_LDAPDEFAULTCLIENTMAKEABANDONFAILED); + + callbackList = (PKIX_PL_Socket_Callback *)(client->callbackList); + PKIX_CHECK(callbackList->sendCallback + (client->clientSocket, + encoded->data, + encoded->len, + &bytesWritten, + plContext), + PKIX_SOCKETSENDFAILED); + + if (bytesWritten < 0) { + client->connectStatus = ABANDON_PENDING; + } else { + client->connectStatus = BOUND; + } + } + + PKIX_DECREF(client->entriesFound); + PKIX_DECREF(client->currentRequest); + PKIX_DECREF(client->currentResponse); + +cleanup: + + PKIX_DECREF(client); + + PKIX_RETURN(CERTSTORE); +} |