static DWORD _VmDirRidSyncThr( PVOID pArg ) { DWORD dwError = 0; BOOLEAN bInLock = FALSE; PVDIR_THREAD_INFO pThrInfo = (PVDIR_THREAD_INFO)pArg; PVMDIR_SID_GEN_STACK_NODE pSidGenStackNode = NULL; VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "_VmDirRidSyc thr started" ); while (1) { if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) { goto cleanup; } VMDIR_SAFE_FREE_MEMORY(pSidGenStackNode); while (VmDirPopTSStack(gSidGenState.pStack, (PVOID*)&pSidGenStackNode) == 0 && pSidGenStackNode != NULL) { (VOID)VmDirSyncRIDSeqToDB( pSidGenStackNode->pszDomainDn, pSidGenStackNode->dwDomainRidSequence); if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) { // // Any pending updates will be performed by VmDirVmAclShutdown. // goto cleanup; } VMDIR_SAFE_FREE_MEMORY(pSidGenStackNode); } VMDIR_LOCK_MUTEX(bInLock, pThrInfo->mutexUsed); VmDirConditionTimedWait( pThrInfo->conditionUsed, pThrInfo->mutexUsed, 3 * 1000); // time wait 3 seconds // ignore error VMDIR_UNLOCK_MUTEX(bInLock, pThrInfo->mutexUsed); } cleanup: VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "_VmDirRidSyc thr stopped (%d)", dwError ); VMDIR_SAFE_FREE_MEMORY(pSidGenStackNode); return dwError; }
DWORD VmDirIndexCfgMap( PLW_HASHMAP* ppIndexCfgMap ) { DWORD dwError = 0; VDIR_SERVER_STATE vmdirState = VmDirdState(); if (!ppIndexCfgMap) { dwError = VMDIR_ERROR_INVALID_PARAMETER; BAIL_ON_VMDIR_ERROR(dwError); } if (vmdirState != VMDIRD_STATE_STARTUP) { dwError = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR(dwError); } *ppIndexCfgMap = gVdirIndexGlobals.pIndexCfgMap; error: return dwError; }
/* * Waits until there's data from the worker thread to read (or until the * server's shutdown). */ static DWORD _VmDirPagedSearchCacheWaitAndRead_inlock( PVDIR_PAGED_SEARCH_RECORD pSearchRecord, PVDIR_PAGED_SEARCH_ENTRY_LIST *ppEntryList ) { DWORD dwError = 0; PVDIR_PAGED_SEARCH_ENTRY_LIST pEntryList = NULL; while (TRUE) { dwError = dequePopLeft(pSearchRecord->pQueue, (PVOID*)&pEntryList); if (dwError == 0) { break; } else if (pSearchRecord->bProcessingCompleted) { // // Nothing in the queue and processing's complete so we must have // read all the data. // pSearchRecord->bSearchCompleted = TRUE; dwError = 0; break; } else { (VOID)VmDirConditionTimedWait( pSearchRecord->pDataAvailable, pSearchRecord->mutex, VMDIR_PSCACHE_READ_TIMEOUT); } if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) { BAIL_WITH_VMDIR_ERROR(dwError, VMDIR_ERROR_UNAVAILABLE); } } *ppEntryList = pEntryList; pSearchRecord->tLastClientRead = time(NULL); cleanup: return dwError; error: goto cleanup; }
/* * Thread to handle db checkpoint and log file aging. * 1. it sleep for x (configurable) minutes. * 2. check how long ago was lost check point * 2.1 if longer than y (configurable) minutes then check point * */ static DWORD VmDirBdbCheckpointThrFun( PVOID pArg ) { DWORD dwError = 0; int iCFGChkpointIntervalinMIN = 0; int iCFGCheckpointSizeinKB = 0; int iCFGLogAgingIntervalInMIN = 0; time_t tLastLogAge = time(NULL); PVDIR_THREAD_INFO pThrInfo = (PVDIR_THREAD_INFO)pArg; //TODO, make this configurable (or even changeable during online) iCFGChkpointIntervalinMIN = 3; iCFGCheckpointSizeinKB = 100; iCFGLogAgingIntervalInMIN = 360; while (1) { struct timespec ts = {0}; BOOLEAN bInLock = FALSE; ts.tv_sec = time(NULL) + (iCFGChkpointIntervalinMIN * 60); ts.tv_nsec = 0; VMDIR_LOCK_MUTEX(bInLock, pThrInfo->mutexUsed); dwError = VmDirConditionTimedWait( pThrInfo->conditionUsed, pThrInfo->mutexUsed, (iCFGChkpointIntervalinMIN * 60)*1000); VMDIR_UNLOCK_MUTEX(bInLock, pThrInfo->mutexUsed); if (VmDirdState() == VMDIR_SHUTDOWN) { break; } // ignore dwError == ETIMEOUT check as rare case and benign checkpoint dwError = gVdirBdbGlobals.bdbEnv->txn_checkpoint( gVdirBdbGlobals.bdbEnv, iCFGCheckpointSizeinKB, iCFGChkpointIntervalinMIN , 0); BAIL_ON_VMDIR_ERROR(dwError); VmDirLog( LDAP_DEBUG_TRACE, "Bdb: checkpoint" ); if (ts.tv_sec - tLastLogAge > (iCFGLogAgingIntervalInMIN * 60)) { dwError = bdbLogfileAging(); BAIL_ON_VMDIR_ERROR(dwError); tLastLogAge = ts.tv_sec; } } cleanup: // TODO: return dwError ? return 0; error: raise(SIGTERM); goto cleanup; }
static DWORD vmdirConnAccept( Sockbuf_IO* pSockbuf_IO, DWORD dwPort, BOOLEAN bIsLdaps ) { ber_socket_t newsockfd = -1; int retVal = LDAP_SUCCESS; ber_socket_t ip4_fd = -1; ber_socket_t ip6_fd = -1; ber_socket_t max_fd = -1; VMDIR_THREAD threadId; BOOLEAN bInLock = FALSE; int iLocalLogMask = 0; PVDIR_CONNECTION_CTX pConnCtx = NULL; fd_set event_fd_set; fd_set poll_fd_set; struct timeval timeout = {0}; // Wait for ***1st*** replication cycle to be over. if (gVmdirServerGlobals.serverId == 0) // instance has not been initialized { VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "Connection accept thread: Have NOT yet started listening on LDAP port (%u)," " waiting for the 1st replication cycle to be over.", dwPort); VMDIR_LOCK_MUTEX(bInLock, gVmdirGlobals.replCycleDoneMutex); // wait till 1st replication cycle is over if (VmDirConditionWait( gVmdirGlobals.replCycleDoneCondition, gVmdirGlobals.replCycleDoneMutex ) != 0) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "Connection accept thread: VmDirConditionWait failed." ); retVal = LDAP_OPERATIONS_ERROR; goto cleanup; } // also wake up the other (normal LDAP port/SSL LDAP port listner) LDAP connection accept thread, // waiting on 1st replication cycle to be over // BUGBUG Does not handle spurious wake up VmDirConditionSignal(gVmdirGlobals.replCycleDoneCondition); VMDIR_UNLOCK_MUTEX(bInLock, gVmdirGlobals.replCycleDoneMutex); if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) // Asked to shutdown before we started accepting { goto cleanup; } VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Connection accept thread: listening on LDAP port (%u).", dwPort); } iLocalLogMask = VmDirLogGetMask(); ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &iLocalLogMask); SetupLdapPort(dwPort, &ip4_fd, &ip6_fd); if (ip4_fd < 0 && ip6_fd < 0) { VmDirSleep(1000); goto cleanup; } FD_ZERO(&event_fd_set); if (ip4_fd >= 0) { FD_SET (ip4_fd, &event_fd_set); if (ip4_fd > max_fd) { max_fd = ip4_fd; } } if (ip6_fd >= 0) { FD_SET (ip6_fd, &event_fd_set); if (ip6_fd > max_fd) { max_fd = ip6_fd; } } retVal = VmDirSyncCounterIncrement(gVmdirGlobals.pPortListenSyncCounter); if (retVal != 0 ) { VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "%s: VmDirSyncCounterIncrement(gVmdirGlobals.pPortListenSyncCounter) returned error", __func__); BAIL_ON_VMDIR_ERROR(retVal); } while (TRUE) { if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) { goto cleanup; } poll_fd_set = event_fd_set; timeout.tv_sec = 3; timeout.tv_usec = 0; retVal = select ((int)max_fd+1, &poll_fd_set, NULL, NULL, &timeout); if (retVal < 0 ) { #ifdef _WIN32 errno = WSAGetLastError(); #endif VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s: select() (port %d) call failed: %d.", __func__, dwPort, errno); VmDirSleep( 1000 ); continue; } else if (retVal == 0) { //VMDIR_LOG_INFO( LDAP_DEBUG_CONNS, "%s: select() timeout (port %d)", __func__, dwPort); continue; } if (ip4_fd >= 0 && FD_ISSET(ip4_fd, &poll_fd_set)) { newsockfd = accept(ip4_fd, (struct sockaddr *) NULL, NULL); } else if (ip6_fd >= 0 && FD_ISSET(ip6_fd, &poll_fd_set)) { newsockfd = accept(ip6_fd, (struct sockaddr *) NULL, NULL); } else { VMDIR_LOG_INFO( LDAP_DEBUG_CONNS, "%s: select() returned with no data (port %d), return: %d", __func__, dwPort, retVal); continue; } if (newsockfd < 0) { #ifdef _WIN32 errno = WSAGetLastError(); #endif if (errno != EAGAIN && errno != EWOULDBLOCK ) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s: accept() (port %d) failed with errno: %d.", __func__, dwPort, errno ); } continue; } if ( _VmDirFlowCtrlThrEnter() == TRUE ) { tcp_close(newsockfd); newsockfd = -1; VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "Maxmimum number of concurrent LDAP threads reached. Blocking new connection" ); continue; } retVal = VmDirAllocateMemory( sizeof(VDIR_CONNECTION_CTX), (PVOID*)&pConnCtx); BAIL_ON_VMDIR_ERROR(retVal); pConnCtx->sockFd = newsockfd; newsockfd = -1; pConnCtx->pSockbuf_IO = pSockbuf_IO; retVal = VmDirCreateThread(&threadId, TRUE, ProcessAConnection, (PVOID)pConnCtx); if (retVal != 0) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s: VmDirCreateThread() (port) failed with errno: %d", __func__, dwPort, errno ); tcp_close(pConnCtx->sockFd); _VmDirFlowCtrlThrExit(); VMDIR_SAFE_FREE_MEMORY(pConnCtx); continue; } else { pConnCtx = NULL; //thread take ownership on pConnCtx VmDirFreeVmDirThread(&threadId); } } cleanup: VMDIR_UNLOCK_MUTEX(bInLock, gVmdirGlobals.replCycleDoneMutex); if (ip4_fd >= 0) { tcp_close(ip4_fd); } if (ip6_fd >= 0) { tcp_close(ip6_fd); } if (newsockfd >= 0) { tcp_close(newsockfd); } #ifndef _WIN32 raise(SIGTERM); #endif VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "%s: Connection accept thread: stop (port %d)", __func__, dwPort); return retVal; error: goto cleanup; }
static int BindListenOnPort( #ifndef _WIN32 sa_family_t addr_type, size_t addr_size, #else short addr_type, int addr_size, #endif void *pServ_addr, ber_socket_t *pSockfd ) { #define LDAP_PORT_LISTEN_BACKLOG 128 int optname = 0; int retVal = LDAP_SUCCESS; int retValBind = 0; PSTR pszLocalErrMsg = NULL; int on = 1; #ifdef _WIN32 DWORD sTimeout = 0; int reTries = 0; #else struct timeval sTimeout = {0}; #endif *pSockfd = -1; *pSockfd = socket(addr_type, SOCK_STREAM, 0); if (*pSockfd < 0) { #ifdef _WIN32 errno = WSAGetLastError(); #endif retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "%s: socket() call failed with errno: %d", __func__, errno ); } #ifdef _WIN32 optname = SO_EXCLUSIVEADDRUSE; #else optname = SO_REUSEADDR; #endif if (setsockopt(*pSockfd, SOL_SOCKET, optname, (const char *)(&on), sizeof(on)) < 0) { #ifdef _WIN32 errno = WSAGetLastError(); #endif retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "%s: setsockopt() call failed with errno: %d", __func__, errno ); } on = 1; // turn on TCP_NODELAY below if (setsockopt(*pSockfd, IPPROTO_TCP, TCP_NODELAY, (const char *)(&on), sizeof(on) ) < 0) { #ifdef _WIN32 errno = WSAGetLastError(); #endif retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "%s: setsockopt() TCP_NODELAY call failed with errno: %d", __func__, errno ); } if (addr_type == AF_INET6) { #ifdef _WIN32 if (setsockopt(*pSockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)(&on), sizeof(on) ) < 0) { errno = WSAGetLastError(); #else if (setsockopt(*pSockfd, SOL_IPV6, IPV6_V6ONLY, (const char *)(&on), sizeof(on) ) < 0) { #endif retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "%s: setsockopt() IPV6_V6ONLY call failed with errno: %d", __func__, errno ); } } if (gVmdirGlobals.dwLdapRecvTimeoutSec > 0) { #ifdef _WIN32 sTimeout = gVmdirGlobals.dwLdapRecvTimeoutSec*1000; #else sTimeout.tv_sec = gVmdirGlobals.dwLdapRecvTimeoutSec; sTimeout.tv_usec = 0; #endif if (setsockopt(*pSockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &sTimeout, sizeof(sTimeout)) < 0) { #ifdef _WIN32 errno = WSAGetLastError(); #endif retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "%s: setsockopt() SO_RCVTIMEO failed, errno: %d", __func__, errno ); } } retValBind = bind(*pSockfd, (struct sockaddr *) pServ_addr, addr_size); #ifdef _WIN32 // Add retry logic per PR 1347783 reTries = 0; while (retValBind != 0 && reTries < MAX_NUM_OF_BIND_PORT_RETRIES) { errno = WSAGetLastError(); if (errno != WSAEADDRINUSE) { break; } reTries++; VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "%s: bind() call failed with errno: %d, re-trying (%d)", __func__, errno, reTries); VmDirSleep(1000); retValBind = bind(*pSockfd, (struct sockaddr *) pServ_addr, addr_size); } #endif if (retValBind != 0) { retVal = LDAP_OPERATIONS_ERROR; //need to free socket ... BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "%s: bind() call failed with errno: %d", __func__, errno ); } if (listen(*pSockfd, LDAP_PORT_LISTEN_BACKLOG) != 0) { #ifdef _WIN32 errno = WSAGetLastError(); #endif retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "%s: listen() call failed with errno: %d", __func__, errno ); } cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (*pSockfd >= 0) { tcp_close(*pSockfd); *pSockfd = -1; } VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, VDIR_SAFE_STRING(pszLocalErrMsg)); goto cleanup; } /* * We own pConnection and delete it when done. */ static DWORD ProcessAConnection( PVOID pArg ) { VDIR_CONNECTION *pConn = NULL; int retVal = LDAP_SUCCESS; ber_tag_t tag = LBER_ERROR; ber_len_t len = 0; BerElement * ber = NULL; ber_int_t msgid = -1; PVDIR_OPERATION pOperation = NULL; int reTries = 0; BOOLEAN bDownOpThrCount = FALSE; PVDIR_CONNECTION_CTX pConnCtx = NULL; // increment operation thread counter retVal = VmDirSyncCounterIncrement(gVmdirGlobals.pOperationThrSyncCounter); BAIL_ON_VMDIR_ERROR(retVal); bDownOpThrCount = TRUE; pConnCtx = (PVDIR_CONNECTION_CTX)pArg; assert(pConnCtx); retVal = NewConnection(pConnCtx->sockFd, &pConn, pConnCtx->pSockbuf_IO); if (retVal != LDAP_SUCCESS) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s: NewConnection [%d] failed with error: %d", __func__, pConnCtx->sockFd, retVal); goto error; } while (TRUE) { if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) { goto cleanup; } ber = ber_alloc(); assert( ber != NULL); /* An LDAP request message looks like: * LDAPMessage ::= SEQUENCE { * messageID MessageID, * protocolOp CHOICE { * bindRequest BindRequest, * unbindRequest UnbindRequest, * searchRequest SearchRequest, * ... }, * controls [0] Controls OPTIONAL } */ // reset retry count reTries = 0; // Read complete LDAP request message (tag, length, and real message). while( reTries < MAX_NUM_OF_SOCK_READ_RETRIES ) { if ((tag = ber_get_next( pConn->sb, &len, ber )) == LDAP_TAG_MESSAGE ) { break; } #ifdef _WIN32 // in ber_get_next (liblber) call, sock_errset() call WSASetLastError() errno = WSAGetLastError(); if ( errno == EWOULDBLOCK || errno == EAGAIN || errno == WSAETIMEDOUT) #else if ( errno == EWOULDBLOCK || errno == EAGAIN) #endif { if (gVmdirGlobals.dwLdapRecvTimeoutSec > 0 && ber->ber_len == 0) { VMDIR_LOG_INFO( LDAP_DEBUG_CONNS, "%s: disconnecting peer (%s), idle > %d seconds", __func__, pConn->szClientIP, gVmdirGlobals.dwLdapRecvTimeoutSec); retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR( retVal ); } //This may occur when not all data have recieved - set to EAGAIN/EWOULDBLOCK by ber_get_next, // and in such case ber->ber_len > 0; if (reTries > 0 && reTries % 5 == 0) { VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "%s: ber_get_next() failed with errno = %d, peer (%s), re-trying (%d)", __func__, errno , pConn->szClientIP, reTries); } VmDirSleep(200); reTries++; continue; } // Unexpected error case. if (errno == 0) { VMDIR_LOG_INFO( LDAP_DEBUG_CONNS, "%s: ber_get_next() peer (%s) disconnected", __func__, pConn->szClientIP); } else { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s: ber_get_next() call failed with errno = %d peer (%s)", __func__, errno, pConn->szClientIP); } retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR( retVal ); } // Read LDAP request messageID (tag, length (not returned since it is implicit/integer), and messageID value) if ( (tag = ber_get_int( ber, &msgid )) != LDAP_TAG_MSGID ) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ProcessAConnection: ber_get_int() call failed." ); retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR( retVal ); } // Read protocolOp (tag) and length of the LDAP operation message, and leave the pointer at the beginning // of the LDAP operation message (to be parsed by PerformXYZ methods). if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ProcessAConnection: ber_peek_tag() call failed." ); retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR( retVal ); } retVal = VmDirNewOperation(ber, msgid, tag, pConn, &pOperation); if (retVal) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ProcessAConnection: NewOperation() call failed." ); retVal = LDAP_OPERATIONS_ERROR; } BAIL_ON_VMDIR_ERROR( retVal ); // // If this is a multi-stage operation don't overwrite the start time if it's already set. // pConn->SuperLogRec.iStartTime = pConn->SuperLogRec.iStartTime ? pConn->SuperLogRec.iStartTime : VmDirGetTimeInMilliSec(); switch (tag) { case LDAP_REQ_BIND: retVal = VmDirPerformBind(pOperation); if (retVal != LDAP_SASL_BIND_IN_PROGRESS) { _VmDirCollectBindSuperLog(pConn, pOperation); // ignore error } break; case LDAP_REQ_ADD: retVal = VmDirPerformAdd(pOperation); break; case LDAP_REQ_SEARCH: retVal = VmDirPerformSearch(pOperation); break; case LDAP_REQ_UNBIND: retVal = VmDirPerformUnbind(pOperation); break; case LDAP_REQ_MODIFY: retVal = VmDirPerformModify(pOperation); break; case LDAP_REQ_DELETE: retVal = VmDirPerformDelete(pOperation); break; case LDAP_REQ_MODDN: case LDAP_REQ_COMPARE: case LDAP_REQ_ABANDON: case LDAP_REQ_EXTENDED: VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "ProcessAConnection: Operation is not yet implemented.." ); pOperation->ldapResult.errCode = retVal = LDAP_UNWILLING_TO_PERFORM; // ignore following VmDirAllocateStringA error. VmDirAllocateStringA( "Operation is not yet implemented.", &pOperation->ldapResult.pszErrMsg); VmDirSendLdapResult( pOperation ); break; default: pOperation->ldapResult.errCode = LDAP_PROTOCOL_ERROR; retVal = LDAP_NOTICE_OF_DISCONNECT; break; } pConn->SuperLogRec.iEndTime = VmDirGetTimeInMilliSec(); VmDirOPStatisticUpdate(tag, pConn->SuperLogRec.iEndTime - pConn->SuperLogRec.iStartTime); if (tag != LDAP_REQ_BIND) { VmDirLogOperation(gVmdirGlobals.pLogger, tag, pConn, pOperation->ldapResult.errCode); _VmDirScrubSuperLogContent(tag, &pConn->SuperLogRec); } VmDirFreeOperation(pOperation); pOperation = NULL; ber_free( ber, 1); ber = NULL; if (retVal == LDAP_NOTICE_OF_DISCONNECT) // returned as a result of protocol parsing error. { // RFC 4511, section 4.1.1: If the server receives an LDAPMessage from the client in which the LDAPMessage // SEQUENCE tag cannot be recognized, the messageID cannot be parsed, the tag of the protocolOp is not // recognized as a request, or the encoding structures or lengths of data fields are found to be incorrect, // then the server **SHOULD** return the Notice of Disconnection, with the resultCode // set to protocolError, and **MUST** immediately terminate the LDAP session as described in Section 5.3. goto cleanup; } } cleanup: if (retVal == LDAP_NOTICE_OF_DISCONNECT) { // Optionally send Notice of Disconnection with rs->err. } if (ber != NULL) { ber_free( ber, 1 ); } VmDirDeleteConnection(&pConn); VMDIR_SAFE_FREE_MEMORY(pConnCtx); VmDirFreeOperation(pOperation); if (bDownOpThrCount) { VmDirSyncCounterDecrement(gVmdirGlobals.pOperationThrSyncCounter); } _VmDirFlowCtrlThrExit(); // TODO: should we return dwError ? return 0; error: goto cleanup; }
static int VmDirProcessCandidateList( VDIR_OPERATION * pOperation ) { int retVal = LDAP_SUCCESS; int i = 0; VDIR_CANDIDATES * cl = pOperation->request.searchReq.filter->candidates; VDIR_ENTRY srEntry = {0}; VDIR_ENTRY * pSrEntry = NULL; int numSentEntries = 0; BOOLEAN bExternalSearch = FALSE; BOOLEAN bInternalSearch = FALSE; BOOLEAN bStoreRsltInMem = FALSE; BOOLEAN bPageResultsCtrl = FALSE; DWORD dwPageSize = 0; ENTRYID lastEID = 0; /* * If the page size is greater than or equal to the sizeLimit value, * the server should ignore the control as the request can be satisfied in a single page. */ if (pOperation->showPagedResultsCtrl && (pOperation->request.searchReq.sizeLimit == 0 || pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize < (DWORD)pOperation->request.searchReq.sizeLimit)) { VmDirLog( LDAP_DEBUG_TRACE, "showPagedResultsCtrl applies to this query." ); bPageResultsCtrl = TRUE; dwPageSize = pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize; lastEID = atoll(pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie); pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie[0] = '\0'; VmDirSortCandidateList(cl); // sort candidate list if not yet sorted } bExternalSearch = pOperation->opType == VDIR_OPERATION_TYPE_EXTERNAL; bInternalSearch = pOperation->opType == VDIR_OPERATION_TYPE_INTERNAL; bStoreRsltInMem = pOperation->request.searchReq.bStoreRsltInMem; if (cl && cl->size > 0) { if (bInternalSearch || bStoreRsltInMem) { //TODO, we should have a hard limit on the cl->size we handle VmDirFreeEntryArrayContent(&pOperation->internalSearchEntryArray); retVal = VmDirAllocateMemory( sizeof(VDIR_ENTRY) * cl->size, (PVOID*)&pOperation->internalSearchEntryArray.pEntry); BAIL_ON_VMDIR_ERROR(retVal); } for (i = 0, numSentEntries = 0; (i < cl->size) && (pOperation->request.searchReq.sizeLimit == 0 /* unlimited */ || numSentEntries < pOperation->request.searchReq.sizeLimit); i++) { if (bExternalSearch && VmDirdState() == VMDIRD_STATE_SHUTDOWN && pOperation->syncReqCtrl == NULL) { retVal = LDAP_UNAVAILABLE; // stop all external search ops, except replication pull goto cleanup; } if (!gVmdirGlobals.bPagedSearchReadAhead) { //skip entries we sent before in sorted cl->eIds. if (bPageResultsCtrl && cl->eIds[i] <= lastEID) { continue; } } pSrEntry = bInternalSearch || bStoreRsltInMem ? (pOperation->internalSearchEntryArray.pEntry + pOperation->internalSearchEntryArray.iSize) : &srEntry; retVal = pOperation->pBEIF->pfnBEIdToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, cl->eIds[i], pSrEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { // Ignore BdbEIdToEntry errors. VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "ProcessCandiateList BEIdToEntry EID(%u), error (%u)", cl->eIds[i], retVal); continue; } if (CheckIfEntryPassesFilter(pOperation, pSrEntry, pOperation->request.searchReq.filter) == FILTER_RES_TRUE) { BOOLEAN bSendEntry = TRUE; CHAR sha1Digest[SHA_DIGEST_LENGTH] = {0}; retVal = VmDirBuildComputedAttribute( pOperation, pSrEntry ); BAIL_ON_VMDIR_ERROR( retVal ); if (pOperation->digestCtrl) { retVal = VmDirEntrySHA1Digest(pSrEntry, sha1Digest); BAIL_ON_VMDIR_ERROR(retVal); if (memcmp(sha1Digest, pOperation->digestCtrl->value.digestCtrlVal.sha1Digest, SHA_DIGEST_LENGTH) == 0) { bSendEntry = FALSE; VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL,"%s digest match %s", __FUNCTION__, pSrEntry->dn.lberbv.bv_val); } else { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL,"%s digest mismatch %s", __FUNCTION__, pSrEntry->dn.lberbv.bv_val); } } if (bSendEntry) { retVal = VmDirSendSearchEntry(pOperation, pSrEntry); if (retVal == VMDIR_ERROR_INSUFFICIENT_ACCESS) { VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "Access deny on search entry result [%s,%d] (bindedDN-%s) (targetDn-%s)\n", __FILE__, __LINE__, pOperation->conn->AccessInfo.pszBindedDn, pSrEntry->dn.lberbv.bv_val); // make sure search continues retVal = 0; } BAIL_ON_VMDIR_ERROR( retVal ); if (pSrEntry->bSearchEntrySent) { numSentEntries++; if (bInternalSearch || bStoreRsltInMem) { pOperation->internalSearchEntryArray.iSize++; pSrEntry = NULL; // EntryArray takes over *pSrEntry content if (pOperation->internalSearchEntryArray.iSize > gVmdirServerGlobals.dwMaxInternalSearchLimit) { BAIL_WITH_VMDIR_ERROR(retVal, VMDIR_ERROR_INTERNAL_SEARCH_LIMIT); } } } } } //We have sent one page size of entries, so we can break here if (bPageResultsCtrl && numSentEntries == dwPageSize) { retVal = SetPagedSearchCookie(pOperation, cl->eIds[i], i); BAIL_ON_VMDIR_ERROR(retVal); break; } VmDirFreeEntryContent( pSrEntry ); pSrEntry = NULL; // Reset to NULL so that DeleteEntry is no-op. } VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "(%d) candiates processed and (%d) entries sent", cl->size, numSentEntries); } if ( pOperation->request.searchReq.sizeLimit && numSentEntries < pOperation->request.searchReq.sizeLimit && pOperation->pBECtx->iPartialCandidates) { retVal = LDAP_UNWILLING_TO_PERFORM; VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "ProcessCandiateList may return none or paritial requested entries with sizelimit %d", pOperation->request.searchReq.sizeLimit); } #ifndef REPLICATION_V2 retVal = VmDirUpdateSyncDoneCtl( pOperation, numSentEntries); BAIL_ON_VMDIR_ERROR(retVal); #endif cleanup: pOperation->dwSentEntries = numSentEntries; VmDirFreeEntryContent(pSrEntry); return retVal; error: VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "ProcessCandiateList failed. (%u)", retVal); goto cleanup; }
DWORD VmDirIndexingThreadFun( PVOID pArg ) { DWORD dwError = 0; BOOLEAN bInLock = FALSE; BOOLEAN bResume = FALSE; VDIR_SERVER_STATE vmdirState = VMDIRD_STATE_UNDEFINED; PVDIR_INDEXING_TASK pTask = NULL; VmDirDropThreadPriority(DEFAULT_THREAD_PRIORITY_DELTA); resume: while (1) { vmdirState = VmDirdState(); if (vmdirState == VMDIRD_STATE_SHUTDOWN) { break; } else if (vmdirState != VMDIRD_STATE_NORMAL) { VmDirSleep(1000); continue; } VMDIR_LOCK_MUTEX(bInLock, gVdirIndexGlobals.mutex); if (!bResume) { PVDIR_INDEX_UPD pIndexUpd = gVdirIndexGlobals.pIndexUpd; // record current progress dwError = VmDirIndexingTaskRecordProgress(pTask, pIndexUpd); BAIL_ON_VMDIR_ERROR(dwError); // apply index updates dwError = VmDirIndexUpdApply(pIndexUpd); BAIL_ON_VMDIR_ERROR(dwError); VmDirIndexUpdFree(pIndexUpd); gVdirIndexGlobals.pIndexUpd = NULL; // compute new task VmDirFreeIndexingTask(pTask); dwError = VmDirIndexingTaskCompute(&pTask); BAIL_ON_VMDIR_ERROR(dwError); } if (VmDirIndexingTaskIsNoop(pTask)) { dwError = VmDirConditionWait( gVdirIndexGlobals.cond, gVdirIndexGlobals.mutex); BAIL_ON_VMDIR_ERROR(dwError); continue; } VMDIR_UNLOCK_MUTEX(bInLock, gVdirIndexGlobals.mutex); dwError = VmDirIndexingTaskPopulateIndices(pTask); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirIndexingTaskValidateScopes(pTask); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirIndexingTaskDeleteIndices(pTask); BAIL_ON_VMDIR_ERROR(dwError); bResume = FALSE; } cleanup: VMDIR_UNLOCK_MUTEX(bInLock, gVdirIndexGlobals.mutex); VmDirFreeIndexingTask(pTask); return dwError; error: if (dwError == ERROR_INVALID_STATE) { bResume = TRUE; goto resume; } else { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s failed, error (%d)", __FUNCTION__, dwError ); } goto cleanup; }
/* * This thread waits on gVdirAttrIndexGlobals.condition and spawns worker thread * to perform indexing in the back ground. */ static DWORD vdirIndexingThrFun( PVOID pArg ) { DWORD dwError = 0; BOOLEAN bInLock = FALSE; PVDIR_CFG_ATTR_INDEX_DESC* ppIndexDesc = NULL; PVDIR_THREAD_INFO pThrInfo = (PVDIR_THREAD_INFO)pArg; while (1) { DWORD dwCnt = 0; DWORD dwSize = 0; VMDIR_THREAD tid = {0}; PVDIR_ATTR_INDEX_INSTANCE pCache = NULL; memset(&tid, 0, sizeof(tid)); // wait till new indices are created VMDIR_LOCK_MUTEX(bInLock, gVdirAttrIndexGlobals.mutex); dwError = VmDirConditionWait( pThrInfo->conditionUsed, pThrInfo->mutexUsed); BAIL_ON_VMDIR_ERROR(dwError); // get new cache we want to enable pCache = gVdirAttrIndexGlobals.pNewCache; VMDIR_UNLOCK_MUTEX(bInLock, gVdirAttrIndexGlobals.mutex); if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) { break; } if (!pCache) { continue; } // get number of indices in building status for (dwCnt = 0, dwSize = 0; dwCnt < pCache->usNumIndex; dwCnt++) { if (pCache->pSortName[dwCnt].status == VDIR_CFG_ATTR_INDEX_BUILDING) { dwSize++; } } if (dwSize == 0) { continue; } dwError = VmDirAllocateMemory( sizeof(PVDIR_CFG_ATTR_INDEX_DESC) * (dwSize + 1), // +1 for NULL ending (PVOID)&ppIndexDesc); BAIL_ON_VMDIR_ERROR(dwError); // fill ppIndexDesc with building index // NOTE, ppIndexDesc does NOT own them; pCache does. for (dwCnt = 0, dwSize = 0; dwCnt < pCache->usNumIndex; dwCnt++) { if (pCache->pSortName[dwCnt].status == VDIR_CFG_ATTR_INDEX_BUILDING) { ppIndexDesc[dwSize] = &pCache->pSortName[dwCnt]; dwSize++; } } // TODO, detach thr now, need to join to safely finish bdb io... dwError = VmDirCreateThread( &tid, TRUE, vdirIndexingWorkerThrFun, (PVOID)ppIndexDesc); BAIL_ON_VMDIR_ERROR(dwError); // worker takes over ppIndexDesc. ppIndexDesc = NULL; VmDirFreeVmDirThread(&tid); } cleanup: VMDIR_UNLOCK_MUTEX(bInLock, gVdirAttrIndexGlobals.mutex); // TODO: should be dwError? return 0; error: VMDIR_SAFE_FREE_MEMORY(ppIndexDesc); //TODO, should we stop vmdird? VmDirLog( LDAP_DEBUG_ANY, "VmdirIndexingThr: error stop" ); goto cleanup; }
/* VmDirInternalDeleteEntry: Interface that can be used "internally" by the server code. One of the main differences between * this function and MLDelete is that this function does not send back an LDAP result to the client. * * Return: VmDir level error code. Also, pOperation->ldapResult content is set. */ int VmDirInternalDeleteEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; int deadLockRetries = 0; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; BOOLEAN leafNode = FALSE; DeleteReq * delReq = &(pOperation->request.deleteReq); ModifyReq * modReq = &(pOperation->request.modifyReq); BOOLEAN bIsDomainObject = FALSE; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; assert(pOperation && pOperation->pBECtx->pBE); if (VmDirdState() == VMDIRD_STATE_READ_ONLY) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Server in read-only mode" ); } // Normalize DN retVal = VmDirNormalizeDN( &(delReq->dn), pOperation->pSchemaCtx ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx)) ); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Execute pre modify apply Delete plugin logic retVal = VmDirExecutePreModApplyDeletePlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyDelete plugin failed - (%u)", retVal ); } retVal = VmDirNormalizeMods( pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // make sure VDIR_BACKEND_CTX has usn change number by now if ( pOperation->pBECtx->wTxnUSN <= 0 ) { retVal = VMDIR_ERROR_NO_USN; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BECtx.wTxnUSN not set"); } // BUGBUG, need to protect some system entries such as schema,domain....etc? // ************************************************************************************ // transaction retry loop begin. make sure all function within are retry agnostic. // ************************************************************************************ txnretry: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); bHasTxn = FALSE; } deadLockRetries++; if (deadLockRetries > MAX_DEADLOCK_RETRIES) { retVal = VMDIR_ERROR_LOCK_DEADLOCK; BAIL_ON_VMDIR_ERROR( retVal ); } else { if (pEntry) { VmDirFreeEntryContent(pEntry); memset(pEntry, 0, sizeof(VDIR_ENTRY)); pEntry = NULL; } retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_WRITE); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &(delReq->dn), &entry, VDIR_BACKEND_ENTRY_LOCK_WRITE); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "(%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } pEntry = &entry; // Parse Parent DN retVal = VmDirGetParentDN( &pEntry->dn, &pEntry->pdn ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Get ParentDn failed - (%u)", retVal ); // get parent entry if (pEntry->pdn.lberbv.bv_val) { PVDIR_ENTRY pParentEntry = NULL; retVal = VmDirAllocateMemory(sizeof(*pEntry), (PVOID)&pParentEntry); BAIL_ON_VMDIR_ERROR(retVal); retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &pEntry->pdn, pParentEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { VmDirFreeEntryContent(pParentEntry); VMDIR_SAFE_FREE_MEMORY(pParentEntry); switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. case VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) not found, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) lookup failed, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); } } pEntry->pParentEntry = pParentEntry; // pEntry takes over pParentEntry pParentEntry = NULL; } // SJ-TBD: Once ACLs are enabled, following check should go in ACLs logic. if (VmDirIsInternalEntry( pEntry ) || VmDirIsProtectedEntry(pEntry)) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "An internal entry (%s) can NOT be deleted.", pEntry->dn.lberbv_val ); } // only when there is parent Entry, ACL check is done if (pEntry->pParentEntry) { retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry->pParentEntry, VMDIR_RIGHT_DS_DELETE_CHILD); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)(%s)", retVal, VMDIR_ACCESS_DENIED_ERROR_MSG); } // Make sure it is a leaf node retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, pEntry->eId, &leafNode); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEChkIsLeafEntry failed, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); if (leafNode == FALSE) { retVal = VMDIR_ERROR_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Delete of a non-leaf node is not allowed." ); } // Retrieve to determine whether it is domain object earlier // before attribute modifications // ('bIsDomainObject' is needed for a domain object deletion) retVal = VmDirIsDomainObjectWithEntry(pEntry, &bIsDomainObject); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirIsDomainObjectWithEntry failed - (%u)", retVal ); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Generate mods to delete attributes that need not be present in a DELETED entry // Note: in case of executing the deadlock while loop multiple times, same attribute Delete mod be added // multiple times in the modReq, which is expected to work correctly. retVal = GenerateDeleteAttrsMods( pOperation, pEntry ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); // Generate new meta-data for the attributes being updated if ((retVal = VmDirGenerateModsNewMetaData( pOperation, modReq->mods, pEntry->eId )) != 0) { switch (retVal) { case VMDIR_ERROR_LOCK_DEADLOCK: goto txnretry; // Possible retry. BUGBUG, is modReq->mods in above call good for retry? default: BAIL_ON_VMDIR_ERROR( retVal ); } } } // Normalize attribute values in mods retVal = VmDirNormalizeMods( pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // Apply modify operations to the current entry in the DB. retVal = VmDirApplyModsToEntryStruct( pOperation->pSchemaCtx, modReq, pEntry, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // Update DBs // Update Entry retVal = pOperation->pBEIF->pfnBEEntryDelete( pOperation->pBECtx, modReq->mods, pEntry ); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } retVal = DeleteRefAttributesValue(pOperation, &(pEntry->dn)); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_LOCK_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } // Use normalized DN value if (bIsDomainObject) { retVal = VmDirInternalRemoveOrgConfig(pOperation, BERVAL_NORM_VAL(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Update domain list entry failed." ); } retVal = pOperation->pBEIF->pfnBETxnCommit( pOperation->pBECtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = FALSE; } // ************************************************************************************ // transaction retry loop end. // ************************************************************************************ VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Delete Entry (%s)", VDIR_SAFE_STRING(pEntry->dn.lberbv_val)); // Post delete entry // TODO, make it into a separate file deletePlugin.c // clean lockout cache record if exists VdirLockoutCacheRemoveRec(pEntry->dn.bvnorm_val); cleanup: if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { if (retVal == LDAP_SUCCESS) { int iPostCommitPluginRtn = 0; // Execute post Delete commit plugin logic iPostCommitPluginRtn = VmDirExecutePostDeleteCommitPlugins(pOperation, pEntry, retVal); if ( iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode // pass through ) { VmDirLog( LDAP_DEBUG_ANY, "InternalDeleteEntry: VdirExecutePostDeleteCommitPlugins - code(%d)", iPostCommitPluginRtn); } } // In case of replication, modReq is owned by the Replication thread/logic DeleteMods ( modReq ); } VmDirFreeEntryContent ( &entry ); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); } VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), retVal, pszLocalErrMsg); goto cleanup; }
int main( int argc, char * argv[]) { DWORD dwError = 0; const char * logFileName = NULL; const char * pszBootstrapSchemaFile = NULL; const char * pszStateDir = VMDIR_DB_DIR VMDIR_PATH_SEP; BOOLEAN bEnableSysLog = FALSE; BOOLEAN bConsoleMode = FALSE; int iLocalLogMask = 0; BOOLEAN bVmDirInit = FALSE; BOOLEAN bShutdownKDCService = FALSE; BOOLEAN bWaitTimeOut = FALSE; dwError = VmDirSrvUpdateConfig(); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirParseArgs( argc, argv, &pszBootstrapSchemaFile, &iLocalLogMask, &logFileName, &bEnableSysLog, &bConsoleMode); if(dwError != ERROR_SUCCESS) { ShowUsage( argv[0] ); BAIL_ON_VMDIR_ERROR(dwError); } dwError = VmDirAllocateStringA( pszBootstrapSchemaFile, &gVmdirGlobals.pszBootStrapSchemaFile); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirAllocateStringA(pszStateDir, &gVmdirGlobals.pszBDBHome); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirLogInitialize( logFileName, bEnableSysLog, "vmdird", VMDIR_LOG_INFO, iLocalLogMask); BAIL_ON_VMDIR_ERROR(dwError); VmDirdStateSet(VMDIRD_STATE_STARTUP); VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmdird: starting..."); VmDirBlockSelectedSignals(); dwError = VmDirSetEnvironment(); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirInit(); BAIL_ON_VMDIR_ERROR(dwError); bVmDirInit = TRUE; VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmkdcd: starting..."); if (VmDirdGetTargetState() != VMDIRD_STATE_RESTORE) { // Normal server startup route dwError = VmKdcServiceStartup(); BAIL_ON_VMDIR_ERROR(dwError); bShutdownKDCService = TRUE; VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmkdcd: running..."); dwError = VmDirNotifyLikewiseServiceManager(); BAIL_ON_VMDIR_ERROR(dwError); VmDirdStateSet( VmDirdGetTargetState() ); VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmdird: running... state (%d)", VmDirdState()); // main thread waits on signals dwError = VmDirHandleSignals(); BAIL_ON_VMDIR_ERROR(dwError); } VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmdird: exiting..." ); cleanup: if ( bShutdownKDCService ) { VmKdcServiceShutdown(); VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmkdcd: stop" ); } if ( bVmDirInit ) { VmDirdStateSet(VMDIRD_STATE_SHUTDOWN); VmDirShutdown(&bWaitTimeOut); if (bWaitTimeOut) { VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmdird: stop" ); goto done; } VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Lotus Vmdird: stop" ); } VmDirLogTerminate(); VmDirSrvFreeConfig(); done: return dwError; error: goto cleanup; }
/* VmDirInternalDeleteEntry: Interface that can be used "internally" by the server code. One of the main differences between * this function and MLDelete is that this function does not send back an LDAP result to the client. * * Return: VmDir level error code. Also, pOperation->ldapResult content is set. */ int VmDirInternalDeleteEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; BOOLEAN leafNode = FALSE; DeleteReq* delReq = &(pOperation->request.deleteReq); ModifyReq* modReq = &(pOperation->request.modifyReq); BOOLEAN bIsDomainObject = FALSE; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; PVDIR_OPERATION_ML_METRIC pMLMetrics = NULL; extern DWORD VmDirDeleteRaftPreCommit(PVDIR_SCHEMA_CTX, EntryId, char *, PVDIR_OPERATION); assert(pOperation && pOperation->pBECtx->pBE); pMLMetrics = &pOperation->MLMetrics; VMDIR_COLLECT_TIME(pMLMetrics->iMLStartTime); if (VmDirdState() == VMDIRD_STATE_READ_ONLY) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Server in read-only mode"); } // make sure we have minimum DN length if (delReq->dn.lberbv_len < 3) { retVal = VMDIR_ERROR_INVALID_REQUEST; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Invalid DN length - (%u)", delReq->dn.lberbv_len); } // Normalize DN retVal = VmDirNormalizeDN(&(delReq->dn), pOperation->pSchemaCtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx))); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginStartTime); retVal = pOperation->pBEIF->pfnBETxnBegin(pOperation->pBECtx, VDIR_BACKEND_TXN_WRITE, &bHasTxn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginEndTime); if (bHasTxn) { retVal = VmDirValidateOp(pOperation, __func__); BAIL_ON_VMDIR_ERROR(retVal); } // Execute pre modify apply Delete plugin logic VMDIR_COLLECT_TIME(pMLMetrics->iPrePluginsStartTime); retVal = VmDirExecutePreModApplyDeletePlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyDelete plugin failed - (%u)", retVal); VMDIR_COLLECT_TIME(pMLMetrics->iPrePlugunsEndTim); retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // BUGBUG, need to protect some system entries such as schema,domain....etc? // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &(delReq->dn), &entry, VDIR_BACKEND_ENTRY_LOCK_WRITE); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "(%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); pEntry = &entry; // Parse Parent DN retVal = VmDirGetParentDN(&pEntry->dn, &pEntry->pdn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Get ParentDn failed - (%u)", retVal); // get parent entry if (pEntry->pdn.lberbv.bv_val) { PVDIR_ENTRY pParentEntry = NULL; retVal = VmDirAllocateMemory(sizeof(*pEntry), (PVOID)&pParentEntry); BAIL_ON_VMDIR_ERROR(retVal); retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &pEntry->pdn, pParentEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { VmDirFreeEntryContent(pParentEntry); VMDIR_SAFE_FREE_MEMORY(pParentEntry); switch (retVal) { case VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) not found, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) lookup failed, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } pEntry->pParentEntry = pParentEntry; // pEntry takes over pParentEntry pParentEntry = NULL; } // // The delete will succeed if the caller either has the explicit right // to delete this object or if they have the right to delete children // of this object's parent. // retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry, VMDIR_RIGHT_DS_DELETE_OBJECT); if (retVal != ERROR_SUCCESS && pEntry->pParentEntry) { retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry->pParentEntry, VMDIR_RIGHT_DS_DELETE_CHILD); } BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)(%s)", retVal, VMDIR_ACCESS_DENIED_ERROR_MSG); // Make sure it is a leaf node retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, pEntry->eId, &leafNode); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEChkIsLeafEntry failed, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); if (leafNode == FALSE) { retVal = VMDIR_ERROR_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Delete of a non-leaf node is not allowed."); } // Retrieve to determine whether it is domain object earlier // before attribute modifications // ('bIsDomainObject' is needed for a domain object deletion) retVal = VmDirIsDomainObjectWithEntry(pEntry, &bIsDomainObject); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirIsDomainObjectWithEntry failed - (%u)", retVal); retVal = GenerateDeleteAttrsMods(pOperation, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); // Normalize attribute values in mods retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Apply modify operations to the current entry in the DB. retVal = VmDirApplyModsToEntryStruct(pOperation->pSchemaCtx, modReq, pEntry, NULL, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Update Entry retVal = pOperation->pBEIF->pfnBEEntryDelete(pOperation->pBECtx, modReq->mods, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); retVal = DeleteRefAttributesValue(pOperation, &(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DeleteRefAttributesValue (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); // Use normalized DN value if (bIsDomainObject) { retVal = VmDirInternalRemoveOrgConfig(pOperation, BERVAL_NORM_VAL(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Update domain list entry failed."); } if (pOperation->bNoRaftLog == FALSE) { retVal = VmDirDeleteRaftPreCommit( pOperation->pSchemaCtx, pEntry->eId, BERVAL_NORM_VAL(pEntry->dn), pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirDeleteRaftPreCommit error (%u)", retVal); } if (bHasTxn) { VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitStartTime); retVal = pOperation->pBEIF->pfnBETxnCommit(pOperation->pBECtx); bHasTxn = FALSE; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit logIndex %llu (%u)(%s)", pOperation->logIndex, retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitEndTime); } if (!pOperation->bSuppressLogInfo) { VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Delete Entry (%s) logIndex %llu", VDIR_SAFE_STRING(pEntry->dn.lberbv_val), pOperation->logIndex); } // Post delete entry // TODO, make it into a separate file deletePlugin.c // clean lockout cache record if exists VdirLockoutCacheRemoveRec(pEntry->dn.bvnorm_val); cleanup: if (retVal == 0) { int iPostCommitPluginRtn = 0; VMDIR_COLLECT_TIME(pMLMetrics->iPostPluginsStartTime); // Execute post Delete commit plugin logic iPostCommitPluginRtn = VmDirExecutePostDeleteCommitPlugins(pOperation, pEntry, retVal); if (iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode) // pass through { VMDIR_LOG_ERROR( LDAP_DEBUG_ANY, "InternalDeleteEntry: VdirExecutePostDeleteCommitPlugins - code(%d)", iPostCommitPluginRtn); } VMDIR_COLLECT_TIME(pMLMetrics->iPostPlugunsEndTime); } // collect metrics VMDIR_COLLECT_TIME(pMLMetrics->iMLEndTime); VmDirInternalMetricsUpdate(pOperation); VmDirInternalMetricsLogInefficientOp(pOperation); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // In case of replication, modReq is owned by the Replication thread/logic DeleteMods(modReq); } VmDirFreeEntryContent(&entry); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort(pOperation->pBECtx); } VMDIR_SET_LDAP_RESULT_ERROR(&pOperation->ldapResult, retVal, pszLocalErrMsg); goto cleanup; }
/* VmDirInternalModifyEntry: Interface that can be used "internally" by the server code, e.g. to modify schema, indices, * config etc. entries in the BDB store. One of the main differences between this function and MLModify is that * this function does not send back an LDAP result to the client. * * Return: VmDir level error code. Also, pOperation->ldapResult content is set. */ int VmDirInternalModifyEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; ModifyReq* modReq = NULL; ENTRYID entryId = 0; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; PVDIR_OPERATION_ML_METRIC pMLMetrics = NULL; assert(pOperation && pOperation->pBEIF); pMLMetrics = &pOperation->MLMetrics; VMDIR_COLLECT_TIME(pMLMetrics->iMLStartTime); if (VmDirdState() == VMDIRD_STATE_READ_ONLY) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Server in read-only mode"); } modReq = &(pOperation->request.modifyReq); // make sure we have minimum DN length if (modReq->dn.lberbv_len < 3) { retVal = VMDIR_ERROR_INVALID_REQUEST; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Invalid DN length - (%u)", modReq->dn.lberbv_len); } // Normalize DN retVal = VmDirNormalizeDN(&(modReq->dn), pOperation->pSchemaCtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx))); // Acquire schema modification mutex retVal = VmDirSchemaModMutexAcquire(pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Failed to lock schema mod mutex", retVal); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Generate mods based on MODN request retVal = VmDirGenerateRenameAttrsMods(pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); } VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginStartTime); retVal = pOperation->pBEIF->pfnBETxnBegin(pOperation->pBECtx, VDIR_BACKEND_TXN_WRITE, &bHasTxn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginEndTime); if (bHasTxn) { retVal = VmDirValidateOp(pOperation, __func__); BAIL_ON_VMDIR_ERROR(retVal); } // Execute pre modify plugin logic VMDIR_COLLECT_TIME(pMLMetrics->iPrePluginsStartTime); retVal = VmDirExecutePreModApplyModifyPlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyModify plugin failed - (%u)", retVal); VMDIR_COLLECT_TIME(pMLMetrics->iPrePlugunsEndTim); // Normalize attribute values in mods retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntryId(pOperation->pBECtx, &(modReq->dn), &entryId); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryModify (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); pEntry = &entry; retVal = VmDirModifyEntryCoreLogic( pOperation, &pOperation->request.modifyReq, entryId, pOperation->bNoRaftLog, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "CoreLogicModifyEntry failed. (%u)", retVal); if (bHasTxn) { VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitStartTime); retVal = pOperation->pBEIF->pfnBETxnCommit(pOperation->pBECtx); bHasTxn = FALSE; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit logIndex %llu (%u)(%s)", pOperation->logIndex, retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitEndTime); } if (!pOperation->bSuppressLogInfo) { VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Modify Entry (%s) blob size %d logIndex %llu", VDIR_SAFE_STRING(pEntry->dn.lberbv_val), pEntry->encodedSize, pOperation->logIndex); } cleanup: if (retVal == 0) { int iPostCommitPluginRtn = 0; VMDIR_COLLECT_TIME(pMLMetrics->iPostPluginsStartTime); // Execute post modify plugin logic iPostCommitPluginRtn = VmDirExecutePostModifyCommitPlugins(pOperation, &entry, retVal); if (iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode) // pass through { VMDIR_LOG_ERROR( LDAP_DEBUG_ANY, "InternalModifyEntry: VdirExecutePostModifyCommitPlugins - code(%d)", iPostCommitPluginRtn); } VMDIR_COLLECT_TIME(pMLMetrics->iPostPlugunsEndTime); } // Release schema modification mutex (VOID)VmDirSchemaModMutexRelease(pOperation); // collect metrics VMDIR_COLLECT_TIME(pMLMetrics->iMLEndTime); VmDirInternalMetricsUpdate(pOperation); VmDirInternalMetricsLogInefficientOp(pOperation); VmDirFreeEntryContent(&entry); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort(pOperation->pBECtx); } VMDIR_SET_LDAP_RESULT_ERROR(&pOperation->ldapResult, retVal, pszLocalErrMsg); goto cleanup; }
static DWORD _VmDirDCConnThreadFun( PVOID pArg ) { DWORD dwError = 0; DWORD dwSleepTimeSec = 0; DWORD dwThrStartTime = time(NULL); PVMDIR_DC_CONNECTION pDCConn = (PVMDIR_DC_CONNECTION)pArg; pDCConn->connState = DC_CONNECTION_STATE_CONNECTING; VMDIR_LOG_VERBOSE(VMDIR_LOG_MASK_ALL, "%s user (%s) connecting to (%s) started", __FUNCTION__, VDIR_SAFE_STRING(pDCConn->creds.pszUPN), VDIR_SAFE_STRING(pDCConn->pszRemoteDCHostName)); while (TRUE) { if (!_VmDirHasCredInfo(&pDCConn->creds)) { BAIL_WITH_VMDIR_ERROR(dwError, VMDIR_ERROR_USER_INVALID_CREDENTIAL); } dwError = _VmDirConnectToDC(pDCConn); if (dwError == 0) { VMDIR_LOG_VERBOSE(VMDIR_LOG_MASK_ALL, "%s user (%s) connected to (%s) done", __FUNCTION__, VDIR_SAFE_STRING(pDCConn->creds.pszUPN), VDIR_SAFE_STRING(pDCConn->pszRemoteDCHostName)); // have a live connection, transfer ownership back to owner pDCConn->connState = DC_CONNECTION_STATE_CONNECTED; goto cleanup; } if (pDCConn->connType == DC_CONNECTION_TYPE_BASIC) { goto error; // no retry, bail } if (dwError == VMDIR_ERROR_USER_INVALID_CREDENTIAL) { dwSleepTimeSec = MAX_DC_CONNECT_SLEEP_TIME_SEC; } else if (dwError == VMDIR_ERROR_SERVER_DOWN || dwError == VMDIR_ERROR_NETWORK_TIMEOUT) { dwSleepTimeSec = UNIT_DC_CONNECT_SLEEP_TIME_SEC * pDCConn->dwConsecutiveFailAttempt; } else { goto error; } if (dwSleepTimeSec >= MAX_DC_CONNECT_SLEEP_TIME_SEC) { dwSleepTimeSec = MAX_DC_CONNECT_SLEEP_TIME_SEC; } VMDIR_LOG_WARNING(VMDIR_LOG_MASK_ALL, "%s URGENT %s (%s) connection to (%s) failed (%d) times, last error (%d), sleep (%d)", __FUNCTION__, _VmDirDCConnType(pDCConn->connType), VDIR_SAFE_STRING(pDCConn->creds.pszUPN), VDIR_SAFE_STRING(pDCConn->pszRemoteDCHostName), pDCConn->dwConsecutiveFailAttempt, pDCConn->dwlastFailedError, dwSleepTimeSec); if (time(NULL) - dwThrStartTime >= MAX_DC_CONNECT_DURATION_TIME_SEC) { // bail to allow deleted RA cache clean up BAIL_WITH_VMDIR_ERROR(dwError, pDCConn->dwlastFailedError); } while (dwSleepTimeSec) { VmDirSleep(1000); // pause 1 second dwSleepTimeSec--; if (VmDirdState() == VMDIRD_STATE_SHUTDOWN) { goto cleanup; } } } cleanup: VmDirFreeConnCredContent(&pDCConn->creds); return dwError; error: pDCConn->connState = DC_CONNECTION_STATE_FAILED; VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "%s user (%s) connect to (%s) failed (%d), connection state set to failed", __FUNCTION__, VDIR_SAFE_STRING(pDCConn->creds.pszUPN), VDIR_SAFE_STRING(pDCConn->pszRemoteDCHostName), dwError); goto cleanup; }
static int ProcessCandidateList( VDIR_OPERATION * pOperation ) { int retVal = LDAP_SUCCESS; int i = 0; VDIR_CANDIDATES * cl = pOperation->request.searchReq.filter->candidates; VDIR_ENTRY srEntry = {0}; VDIR_ENTRY * pSrEntry = NULL; int numSentEntries = 0; BOOLEAN bInternalSearch = FALSE; BOOLEAN bPageResultsCtrl = FALSE; DWORD dwPageSize = 0; ENTRYID lastEID = 0; /* * If the page size is greater than or equal to the sizeLimit value, * the server should ignore the control as the request can be satisfied in a single page. */ if (pOperation->showPagedResultsCtrl && (pOperation->request.searchReq.sizeLimit == 0 || pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize < (DWORD)pOperation->request.searchReq.sizeLimit)) { VmDirLog( LDAP_DEBUG_TRACE, "showPagedResultsCtrl applies to this query." ); bPageResultsCtrl = TRUE; dwPageSize = pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize; lastEID = atoi(pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie); pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie[0] = '\0'; } if (cl && cl->size > 0) { if (pOperation->opType == VDIR_OPERATION_TYPE_INTERNAL) { //TODO, we should have a hard limit on the cl->size we handle bInternalSearch = TRUE; VmDirFreeEntryArrayContent(&pOperation->internalSearchEntryArray); retVal = VmDirAllocateMemory( sizeof(VDIR_ENTRY) * cl->size, (PVOID*)&pOperation->internalSearchEntryArray.pEntry); BAIL_ON_VMDIR_ERROR(retVal); } for (i = 0, numSentEntries = 0; (i < cl->size) && VmDirdState() != VMDIRD_STATE_SHUTDOWN && (pOperation->request.searchReq.sizeLimit == 0 /* unlimited */ || numSentEntries < pOperation->request.searchReq.sizeLimit); i++) { //skip entries we sent before if (bPageResultsCtrl && lastEID > 0) { if (cl->eIds[i] == lastEID) { lastEID = 0; } continue; } VMDIR_LOG_DEBUG( LDAP_DEBUG_FILTER, "ProcessCandidateList EID(%u)", cl->eIds[i]); pSrEntry = bInternalSearch ? (pOperation->internalSearchEntryArray.pEntry + pOperation->internalSearchEntryArray.iSize) : &srEntry; retVal = pOperation->pBEIF->pfnBEIdToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, cl->eIds[i], pSrEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal == 0) { if (CheckIfEntryPassesFilter( pOperation, pSrEntry, pOperation->request.searchReq.filter) == FILTER_RES_TRUE) { retVal = VmDirBuildComputedAttribute( pOperation, pSrEntry ); BAIL_ON_VMDIR_ERROR( retVal ); if (bInternalSearch) { pOperation->internalSearchEntryArray.iSize++; pSrEntry = NULL; // EntryArray takes over *pSrEntry content } else { retVal = VmDirSendSearchEntry( pOperation, pSrEntry ); if (retVal == VMDIR_ERROR_INSUFFICIENT_ACCESS) { VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "Access deny on search entry result [%s,%d] (bindedDN-%s) (targetDn-%s)\n", __FILE__, __LINE__, pOperation->conn->AccessInfo.pszBindedDn, pSrEntry->dn.lberbv.bv_val); // make sure search continues retVal = 0; } BAIL_ON_VMDIR_ERROR( retVal ); if (pSrEntry->bSearchEntrySent) { numSentEntries++; } } } //We have sent one page size of entries, so we can break here if (bPageResultsCtrl && numSentEntries == dwPageSize){ retVal = VmDirStringPrintFA( pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie, VMDIR_MAX_I64_ASCII_STR_LEN, "%u", pSrEntry->eId); BAIL_ON_VMDIR_ERROR( retVal ); break; } VmDirFreeEntryContent( pSrEntry ); pSrEntry = NULL; // Reset to NULL so that DeleteEntry is no-op. } else { // Ignore BdbEIdToEntry errors. VMDIR_LOG_WARNING( VMDIR_LOG_MASK_ALL, "ProcessCandiateList BEIdToEntry EID(%u), error (%u)", cl->eIds[i], retVal); retVal = 0; } } VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "(%d) candiates processed and (%d) entries sent", cl->size, numSentEntries); } if ( pOperation->request.searchReq.sizeLimit && numSentEntries < pOperation->request.searchReq.sizeLimit && pOperation->pBECtx->iPartialCandidates) { retVal = LDAP_UNWILLING_TO_PERFORM; VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "ProcessCandiateList may return none or paritial requested entries with sizelimit %d", pOperation->request.searchReq.sizeLimit); } cleanup: pOperation->dwSentEntries = numSentEntries; VmDirFreeEntryContent( pSrEntry ); return retVal; error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ProcessCandiateList failed. (%u)", retVal); goto cleanup; }