static int IsAttrInReplScope( VDIR_OPERATION * op, char * attrType, char * attrMetaData, BOOLEAN * inScope, PSTR* ppszErrorMsg ) { int retVal = LDAP_SUCCESS; PLW_HASHTABLE_NODE pNode = NULL; char origInvocationId[VMDIR_GUID_STR_LEN]; USN origUsn = VmDirStringToLA( VmDirStringRChrA( attrMetaData, ':' ) + 1, NULL, 10 ); int rc = 0; PSTR pszLocalErrorMsg = NULL; *inScope = FALSE; // attrMetaData format is: <local USN>:<version no>:<originating server ID>:<originating time>:<originating USN> VmDirStringNCpyA( origInvocationId, VMDIR_GUID_STR_LEN, VmDirStringChrA( VmDirStringChrA( attrMetaData, ':' ) + 1, ':') + 1, VMDIR_GUID_STR_LEN - 1); origInvocationId[VMDIR_GUID_STR_LEN - 1] = '\0'; // Skip the attribute: // - if the originating server for the current state is same as the requesting server or if it is one of those // attributes that have "local" scope only. E.g. sending ATTR_LAST_LOCAL_USN_PROCESSED and // ATTR_UP_TO_DATE_VECTOR, causes continuous back-forth replication of Replication Agreements and Server // entries between various servers. assert( op->syncReqCtrl != NULL ); if ((attrType != NULL && (VmDirStringCompareA( attrType, ATTR_LAST_LOCAL_USN_PROCESSED, FALSE) == 0 || VmDirStringCompareA( attrType, ATTR_UP_TO_DATE_VECTOR, FALSE) == 0 || VmDirStringCompareA( attrType, VDIR_ATTRIBUTE_SEQUENCE_RID, FALSE) == 0))) { // Reset metaData value so that we don't send local only attribute back. attrMetaData[0] = '\0'; *inScope = FALSE; goto cleanup; } else if ( attrType != NULL && (VmDirStringCompareA( attrType, ATTR_USN_CHANGED, FALSE) == 0)) { ; // always send uSNChanged. (PR 1573117) } else if (VmDirStringCompareA( origInvocationId, op->syncReqCtrl->value.syncReqCtrlVal.reqInvocationId.lberbv.bv_val,TRUE ) == 0) { // Change is originated from the requesting server. // Reset metaData value so that we don't send metaData as well as this attribute back. attrMetaData[0] = '\0'; *inScope = FALSE; goto cleanup; } else { rc = LwRtlHashTableFindKey( op->syncDoneCtrl->value.syncDoneCtrlVal.htUtdVector, &pNode, origInvocationId ); rc = LwNtStatusToWin32Error(rc); if (rc != 0 && rc != ERROR_NOT_FOUND) { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "IsAttrInReplScope: LwRtlHashTableFindKey failed for origInvocationId: %s", origInvocationId ); retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "LwRtlHashTableFindKey failed."); } if (pNode == NULL) // Attribute is to be sent in the result entry. { UptoDateVectorEntry * utdVectorEntry = NULL; VDIR_BERVALUE bvServerId = VDIR_BERVALUE_INIT; if (VmDirAllocateMemory( sizeof( UptoDateVectorEntry ), (PVOID *)&utdVectorEntry) != 0) { retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "IsAttrInReplScope: BervalContentDup failed."); } bvServerId.lberbv.bv_val = origInvocationId; bvServerId.lberbv.bv_len = VmDirStringLenA( origInvocationId ); if (VmDirBervalContentDup( &bvServerId, &utdVectorEntry->invocationId ) != 0) { retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "IsAttrInReplScope: BervalContentDup failed."); } utdVectorEntry->currMaxOrigUsnProcessed = origUsn; LwRtlHashTableResizeAndInsert( op->syncDoneCtrl->value.syncDoneCtrlVal.htUtdVector, &utdVectorEntry->Node, &pNode); assert( pNode == NULL ); // assert the key of added node is unique } else { UptoDateVectorEntry * utdVectorEntry = NULL; utdVectorEntry = (UptoDateVectorEntry *)LW_STRUCT_FROM_FIELD(pNode, UptoDateVectorEntry, Node); if (origUsn > utdVectorEntry->reqLastOrigUsnProcessed ) { // Attribute is to be sent in the result entry. // Update if origUsn of this attribute is > the current highest if (origUsn > utdVectorEntry->currMaxOrigUsnProcessed ) { utdVectorEntry->currMaxOrigUsnProcessed = origUsn; } } else { VMDIR_LOG_VERBOSE( LDAP_DEBUG_REPL_ATTR, "IsAttrInReplScope: Attribute: %s, metaData: %s, replication scope = FALSE", attrType, attrMetaData ); // Reset metaData value so that we don't send metaData for this attribute back. attrMetaData[0] = '\0'; *inScope = FALSE; goto cleanup; } } } VMDIR_LOG_INFO( LDAP_DEBUG_REPL_ATTR, "IsAttrInReplScope: Attribute: %s, metaData: %s, replication scope = TRUE", attrType, attrMetaData ); *inScope = TRUE; cleanup: if (ppszErrorMsg) { *ppszErrorMsg = pszLocalErrorMsg; } else { VMDIR_SAFE_FREE_MEMORY(pszLocalErrorMsg); } return( retVal ); error: goto cleanup; }
static int ParseSyncRequestControlVal( VDIR_OPERATION * op, BerValue * controlValue, // Input: control value encoded as ber SyncRequestControlValue * syncReqCtrlVal, // Output VDIR_LDAP_RESULT * lr // Output ) { int retVal = LDAP_SUCCESS; ber_tag_t tag = LBER_ERROR; ber_len_t len = 0; UptoDateVectorEntry * utdVectorEntry = NULL; BerElementBuffer berbuf; BerElement * ber = (BerElement *)&berbuf; PSTR pszLocalErrorMsg = NULL; VDIR_BACKEND_CTX backendCtx = {0}; USN maxPartnerVisibleUSN = 0; VMDIR_LOG_DEBUG( LDAP_DEBUG_TRACE, "ParseSyncRequestControlVal: Begin." ); ber_init2( ber, controlValue, LBER_USE_DER ); /* http://www.rfc-editor.org/rfc/rfc4533.txt * * syncCookie ::= OCTET STRING * * syncRequestValue ::= SEQUENCE { * mode ENUMERATED { * -- 0 unused * refreshOnly (1), * -- 2 reserved * refreshAndPersist (3) * }, * cookie syncCookie OPTIONAL, * reloadHint BOOLEAN DEFAULT FALSE * } */ if (ber_scanf( ber, "{i", &(syncReqCtrlVal->mode) ) == LBER_ERROR) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ParseSyncRequestControlVal: ber_scanf failed while parsing the sync request " "control mode" ); lr->errCode = LDAP_PROTOCOL_ERROR; retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Error in reading sync request control mode from PDU."); } syncReqCtrlVal->bvLastLocalUsnProcessed.lberbv.bv_val = ""; syncReqCtrlVal->intLastLocalUsnProcessed = 0; if (VmDirAllocateMemory( sizeof( VDIR_LDAP_CONTROL ), (PVOID *)&op->syncDoneCtrl) != 0) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ParseSyncRequestControlVal: VmDirAllocateMemory failed " ); lr->errCode = retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "ParseSyncRequestControlVal: VmDirAllocateMemory failed."); } op->syncDoneCtrl->type = LDAP_CONTROL_SYNC_DONE; if (LwRtlCreateHashTable( &op->syncDoneCtrl->value.syncDoneCtrlVal.htUtdVector, UtdVectorEntryGetKey, LwRtlHashDigestPstr, LwRtlHashEqualPstr, NULL, VMDIR_UTD_VECTOR_HASH_TABLE_SIZE ) != 0) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "UpdateSyncDoneUtdVectorEntry: LwRtlCreateHashTable failed" ); lr->errCode = retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "UpdateSyncDoneUtdVectorEntry: LwRtlCreateHashTable failed"); } tag = ber_peek_tag( ber, &len ); if (tag == LBER_SEQUENCE) { // syncCookie /* syncCookie ::= SEQUENCE { * reqServerId LDAPString, * lastLocalUsnProcessed INTEGER (0 .. maxInt), * utdVector UptoDateVectorEntryList } * * UptoDateVectorEntryList ::= SEQUENCE OF uptoDateVectorEntry UptoDateVectorEntry * * UptoDateVectorEntry ::= SEQUENCE { * serverId LDAPString, * lastOrigUsnProcessed INTEGER (0 .. maxInt) } */ // {lastLocalUsnProcessed{{<serverid1><lastOrigUsnProcessed1>}{<serverid2><lastOrigUsnProcessed2>}...}} if (ber_scanf( ber, "{mmm}", &syncReqCtrlVal->reqInvocationId.lberbv, &syncReqCtrlVal->bvLastLocalUsnProcessed.lberbv, &syncReqCtrlVal->bvUtdVector.lberbv ) == LBER_ERROR ) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ParseSyncRequestControlVal: ber_scanf failed while parsing " "lastLocalUsnProcessed in the sync request control value" ); lr->errCode = LDAP_PROTOCOL_ERROR; retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Error in reading lastLocalUsnProcessed in the sync request control value"); } VMDIR_LOG_DEBUG( LDAP_DEBUG_REPL, "ParseSyncRequestControlVal: ServerId: %s, lastLocalUsnProcessed: %s, utdVector: %s", syncReqCtrlVal->reqInvocationId.lberbv.bv_val, syncReqCtrlVal->bvLastLocalUsnProcessed.lberbv.bv_val, syncReqCtrlVal->bvUtdVector.lberbv.bv_val ); syncReqCtrlVal->intLastLocalUsnProcessed = op->syncDoneCtrl->value.syncDoneCtrlVal.intLastLocalUsnProcessed = VmDirStringToLA( syncReqCtrlVal->bvLastLocalUsnProcessed.lberbv.bv_val, NULL, 10 ); { char * nextServerIdStr = NULL; char * nextOrigUsnStr = NULL; nextServerIdStr = syncReqCtrlVal->bvUtdVector.lberbv.bv_val; while( nextServerIdStr != NULL && nextServerIdStr[0] != '\0') { PLW_HASHTABLE_NODE pNode = NULL; if (VmDirAllocateMemory( sizeof(UptoDateVectorEntry), (PVOID *)&utdVectorEntry ) != 0) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ParseSyncRequestControlVal: VmDirAllocateMemory failed " ); lr->errCode = retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "ParseSyncRequestControlVal: VmDirAllocateMemory failed"); } nextOrigUsnStr = VmDirStringChrA( nextServerIdStr, ':'); *nextOrigUsnStr = '\0'; nextOrigUsnStr++; utdVectorEntry->invocationId.lberbv.bv_val = nextServerIdStr; utdVectorEntry->invocationId.lberbv.bv_len = VmDirStringLenA( nextServerIdStr ); nextServerIdStr = VmDirStringChrA( nextOrigUsnStr, ','); *nextServerIdStr = '\0'; nextServerIdStr++; utdVectorEntry->currMaxOrigUsnProcessed = utdVectorEntry->reqLastOrigUsnProcessed = atol( nextOrigUsnStr ); LwRtlHashTableResizeAndInsert( op->syncDoneCtrl->value.syncDoneCtrlVal.htUtdVector, &utdVectorEntry->Node, &pNode); assert( pNode == NULL ); // assert the key of added node is unique. } } tag = ber_peek_tag( ber, &len ); } if (tag == LBER_BOOLEAN) { ber_int_t reloadHint; if (ber_scanf( ber, "b", &reloadHint) == LBER_ERROR) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ParseSyncRequestControlVal: Error in reading reloadHint from the PDU" ); lr->errCode = LDAP_PROTOCOL_ERROR; retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Error in reading reloadHint from the PDU."); } if (reloadHint) { syncReqCtrlVal->reloadHint = TRUE; } } if ( ber_scanf( ber, "}") == LBER_ERROR ) // End of control value { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ParseSyncRequestControlVal: ber_scanf failed while parsing the end of " "sync request control value." ); lr->errCode = LDAP_PROTOCOL_ERROR; retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Decoding error while parsing the end of sync request control value."); } backendCtx.pBE = VmDirBackendSelect(""); maxPartnerVisibleUSN = backendCtx.pBE->pfnBEGetLeastOutstandingUSN( &backendCtx, FALSE ) - 1; if (syncReqCtrlVal->intLastLocalUsnProcessed > maxPartnerVisibleUSN) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ParseSyncRequestControlVal: ServerId %s has processed my USN (%u), my max USN is (%u).", syncReqCtrlVal->reqInvocationId.lberbv.bv_val, syncReqCtrlVal->intLastLocalUsnProcessed, maxPartnerVisibleUSN ); lr->errCode = LDAP_UNWILLING_TO_PERFORM; retVal = LDAP_NOTICE_OF_DISCONNECT; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Partner is ahead of my changes."); } cleanup: // Even in the error case, syncDoneCtrl should be freed during operation delete. VMDIR_LOG_DEBUG( LDAP_DEBUG_TRACE, "ParseSyncRequestControlVal: End." ); VmDirBackendCtxContentFree( &backendCtx ); VMDIR_SAFE_FREE_MEMORY(pszLocalErrorMsg); return retVal; error: VMDIR_APPEND_ERROR_MSG(lr->pszErrMsg, pszLocalErrorMsg); goto cleanup; }
/* * Test whether the origUsn is in scope so that attribute, attr-meta-data or * attr-value-meta-data be sent back to the replicaiton consumer based on whether * the origUsn for that invocationId has been processed already by the consumer * TODO: Needed Refractoring */ DWORD VmDirIsUsnInScope( PVDIR_OPERATION pOperation, PCSTR pAttrName, PCSTR pszOrigInvocationId, USN origUsn, USN localUSN, USN priorSentUSNCreated, PBOOLEAN pbIsUsnInScope ) { DWORD dwError = 0; PLW_HASHTABLE_NODE pNode = NULL; PSTR pszLocalErrorMsg = NULL; UptoDateVectorEntry *pUtdVectorEntry = NULL; UptoDateVectorEntry *pNewUtdVectorEntry = NULL; int retVal = 0; if (!pOperation || !pszOrigInvocationId || !pbIsUsnInScope || !pOperation->syncReqCtrl || !pOperation->syncDoneCtrl) { BAIL_WITH_VMDIR_ERROR(dwError, VMDIR_ERROR_INVALID_PARAMETER); } *pbIsUsnInScope = FALSE; //Originating server for the current state is same as the requesting server if (VmDirStringCompareA( pszOrigInvocationId, pOperation->syncReqCtrl->value.syncReqCtrlVal.reqInvocationId.lberbv.bv_val, TRUE) == 0) { *pbIsUsnInScope = FALSE; goto cleanup; } retVal = LwRtlHashTableFindKey( pOperation->syncDoneCtrl->value.syncDoneCtrlVal.htUtdVector, &pNode, pszOrigInvocationId); retVal = LwNtStatusToWin32Error(retVal); if (retVal != 0 && retVal != ERROR_NOT_FOUND) { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "%s: LwRtlHashTableFindKey failed for origInvocationId: %s", __FUNCTION__, pszOrigInvocationId); dwError = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG(dwError, (pszLocalErrorMsg), "LwRtlHashTableFindKey failed."); } if (pNode == NULL) { VDIR_BERVALUE bvServerId = VDIR_BERVALUE_INIT; dwError = VmDirAllocateMemory(sizeof(UptoDateVectorEntry), (PVOID *)&pNewUtdVectorEntry); BAIL_ON_VMDIR_ERROR(dwError); bvServerId.lberbv.bv_val = (PSTR)pszOrigInvocationId; bvServerId.lberbv.bv_len = VmDirStringLenA(pszOrigInvocationId); dwError = VmDirBervalContentDup(&bvServerId, &pNewUtdVectorEntry->invocationId); BAIL_ON_VMDIR_ERROR(dwError); pNewUtdVectorEntry->currMaxOrigUsnProcessed = origUsn; LwRtlHashTableResizeAndInsert( pOperation->syncDoneCtrl->value.syncDoneCtrlVal.htUtdVector, &pNewUtdVectorEntry->Node, &pNode); // assert the key of added node is unique assert(pNode == NULL); pNewUtdVectorEntry = NULL; *pbIsUsnInScope = TRUE; goto cleanup; } pUtdVectorEntry = (UptoDateVectorEntry *)LW_STRUCT_FROM_FIELD(pNode, UptoDateVectorEntry, Node); if (origUsn > pUtdVectorEntry->reqLastOrigUsnProcessed ) { // attribute or the valueMetaData item in scope if origUsn valueMetaData is > the current highest if (origUsn > pUtdVectorEntry->currMaxOrigUsnProcessed ) { pUtdVectorEntry->currMaxOrigUsnProcessed = origUsn; } // Note, this handles ADD->MODIFY case but not multiple MODIFYs scenario. // However, it is fine as consumer should be able to handle redundant feed from supplier. // The key point here is to NOT send ATTR_USN_CREATED, so we can derive correct sync_state in WriteSyncStateControl. if (localUSN > priorSentUSNCreated) { *pbIsUsnInScope = TRUE; if (priorSentUSNCreated > 0) { VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "%s new usn %llu after prior usncreated %llu attr %s", __FUNCTION__, origUsn, priorSentUSNCreated, VDIR_SAFE_STRING(pAttrName)); } } else { VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "%s (add->modify) race condition avoided. skip prior usncreated %llu attr %s", __FUNCTION__, priorSentUSNCreated, VDIR_SAFE_STRING(pAttrName)); } goto cleanup; } VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "%s: (not in scope) attr name: %s orig invo: %s utdUsn: %"PRId64" usn: %"PRId64, __FUNCTION__, VDIR_SAFE_STRING(pAttrName), VDIR_SAFE_STRING(pszOrigInvocationId), pUtdVectorEntry->reqLastOrigUsnProcessed, origUsn); cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrorMsg); return dwError; error: VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL, "failed, error (%d)", dwError); if (pNewUtdVectorEntry) { VmDirFreeBervalContent(&pNewUtdVectorEntry->invocationId); } VMDIR_SAFE_FREE_MEMORY(pNewUtdVectorEntry); goto cleanup; }
static DWORD VmDirPagedSearchCacheNodeAllocate( PVDIR_PAGED_SEARCH_RECORD *ppSearchRec, PVDIR_OPERATION pOperation, DWORD dwCandidatesProcessed ) { DWORD dwError = 0; PVDIR_PAGED_SEARCH_RECORD pSearchRecord = NULL; char szGuidStr[VMDIR_GUID_STR_LEN] = {0}; uuid_t guid = {0}; VDIR_BERVALUE strFilter = VDIR_BERVALUE_INIT; dwError = VmDirAllocateMemory(sizeof(*pSearchRecord), (PVOID)&pSearchRecord); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirUuidGenerate(&guid); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirUuidToStringLower(&guid, szGuidStr, sizeof(szGuidStr)); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirAllocateStringA(szGuidStr, &pSearchRecord->pszGuid); BAIL_ON_VMDIR_ERROR(dwError); // // Capture the original search filter and mark it as undeletable. Because // the ava strings aren't allocated separately but are part of the request // we have to specifically clone them. // dwError = FilterToStrFilter(pOperation->request.searchReq.filter, &strFilter); BAIL_ON_VMDIR_ERROR(dwError); dwError = StrFilterToFilter(strFilter.lberbv.bv_val, &pSearchRecord->pFilter); BAIL_ON_VMDIR_ERROR(dwError); pSearchRecord->pTotalCandidates = pOperation->request.searchReq.filter->candidates; pOperation->request.searchReq.filter->candidates = NULL; pSearchRecord->dwPageSize = pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.pageSize; // // dwCandidatesProcessed is the count of entries of pFilter->candidates // that we went through building up the first page of results. We add // one because we want to start our next search past the point of where // we've already been. // pSearchRecord->dwCandidatesProcessed = dwCandidatesProcessed + 1; dwError = dequeCreate(&pSearchRecord->pQueue); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirAllocateMutex(&pSearchRecord->mutex); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirAllocateCondition(&pSearchRecord->pDataAvailable); BAIL_ON_VMDIR_ERROR(dwError); dwError = VmDirPagedSearchCreateThread(pSearchRecord); BAIL_ON_VMDIR_ERROR(dwError); LwRtlHashTableResizeAndInsert( gPagedSearchCache.pHashTbl, &pSearchRecord->Node, NULL); pSearchRecord->dwRefCount = 1; pSearchRecord->tLastClientRead = time(NULL); *ppSearchRec = pSearchRecord; cleanup: VmDirFreeBervalContent(&strFilter); return dwError; error: VmDirPagedSearchCacheRecordFree(pSearchRecord); goto cleanup; }