// Copyright (c) 1996-1999 Microsoft Corporation //+------------------------------------------------------------------------- // // Microsoft Windows // // File: idt_ldap.cxx // // Contents: Intra-domain table based on LDAP. // // Classes: // // Functions: // // // // History: 18-Nov-96 BillMo Created. // // Notes: // // Codework: // //-------------------------------------------------------------------------- #include "pch.cxx" #pragma hdrstop #include "trksvr.hxx" CLdapOMTAddModify::CLdapOMTAddModify( const CDomainRelativeObjId & ldKey, const CDomainRelativeObjId & ldNew, const CDomainRelativeObjId & ldBirth, const ULONG & seqRefresh, BYTE bFlags, int mod_op ) : _lsmClass(s_objectClass, s_linkTrackOMTEntry, 0), _lbmCurrentLocation( s_currentLocation, reinterpret_cast(&ldNew), sizeof(ldNew), mod_op ), _lbmBirthLocation( s_birthLocation, reinterpret_cast(&ldBirth), sizeof(ldBirth), mod_op ), _ltvRefresh( seqRefresh ), _lsmRefresh( s_timeRefresh, _ltvRefresh, mod_op ), _lbmFlags( s_oMTIndxGuid, reinterpret_cast(&bFlags), sizeof(BYTE), mod_op ) { int i = 0; if (mod_op == LDAP_MOD_ADD) { _mods[i++] = &_lsmClass._mod; _mods[i++] = &_lbmFlags._mod; } _mods[i++] = &_lbmCurrentLocation._mod; if (ldKey != ldBirth) _mods[i++] = &_lbmBirthLocation._mod; _mods[i++] = &_lsmRefresh._mod; _mods[i++] = NULL; TrkAssert(i <= sizeof(_mods)/sizeof(_mods[0])); } void CIntraDomainTable::Initialize( CTrkSvrConfiguration *pTrkSvrConfiguration, CQuotaTable* pqtable ) { _fInitializeCalled = TRUE; _pqtable = pqtable; _pTrkSvrConfiguration = pTrkSvrConfiguration; _QuotaReported.Initialize(); } void CIntraDomainTable::UnInitialize() { if (_fInitializeCalled) { } _fInitializeCalled = FALSE; } //+---------------------------------------------------------------------------- // // CIntraDomainTable::Add // // Add an entry to the move table. Return TRUE if the entry was added, but // didn't already exist. Return FALSE if the entry already exists. If // there's an error, raise an exception. // //+---------------------------------------------------------------------------- BOOL CIntraDomainTable::Add(const CDomainRelativeObjId &ldKey, const CDomainRelativeObjId &ldNew, const CDomainRelativeObjId &ldBirth, BOOL *pfQuotaExceeded OPTIONAL ) { int err; BYTE bFlags = QFLAG_UNCOUNTED; CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey); // The following constructor sets up the mods array for // the ldap_add_s call that we're about to do. CLdapOMTAddModify lam( ldKey, // Key ldNew, // New droid ldBirth, // Birth droid // Current sequence number _pRefreshSequenceStorage->GetSequenceNumber(), bFlags, // Flags, will be pointed to // (so can't e.g. use a #define value) LDAP_MOD_ADD); // Add this element // Return FALSE here means that we pretend to the caller that the entry // already exists. We don't want to raise an exception here. Our caller // always tries to add the entry, if the add fails because the entry // already exists, the caller will then try to modify the entry. If the // quota has been exceeded, we don't want to add more entries, but we // still want entries already in the table to be modifiable. So instead of // raising an exception, we really want the caller to try to modify the // entry instead. if(_pqtable->IsMoveQuotaExceeded()) { if( NULL != pfQuotaExceeded ) *pfQuotaExceeded = TRUE; if( !_QuotaReported.IsSet() ) { _QuotaReported.Set(); TrkReportEvent( EVENT_TRK_SERVICE_MOVE_QUOTA_EXCEEDED, EVENTLOG_WARNING_TYPE, TRKREPORT_LAST_PARAM ); } return FALSE; } else { _QuotaReported.Clear(); } err = ldap_add_s(Ldap(), dnKey, lam._mods); if (err == LDAP_SUCCESS) { { LDAPMod* mods[2]; BYTE bFlags = QFLAG_UNCOUNTED; CLdapBinaryMod lbm(s_oMTIndxGuid, reinterpret_cast(&bFlags), sizeof(BYTE), LDAP_MOD_REPLACE); mods[0] = &lbm._mod; mods[1] = NULL; err = ldap_modify_s(Ldap(), dnKey, mods); } _pqtable->IncrementMoveCountCache(); return(TRUE); } else if (err == LDAP_ALREADY_EXISTS) { return(FALSE); } else { TrkRaiseWin32Error(LdapMapErrorToWin32(err)); return(FALSE); } } // TRUE if found and deleted, FALSE if not found, exception on other errors BOOL CIntraDomainTable::Delete(const CDomainRelativeObjId &ldKey) { int err; CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey); BOOL fFound; fFound = _pqtable->UpdateFlags(Ldap(), dnKey, QFLAG_DELETED); if( fFound ) _pqtable->DecrementMoveCountCache(); return fFound; } // TRUE if entry exists and modified, FALSE if not existent, exception otherwise BOOL CIntraDomainTable::Modify(const CDomainRelativeObjId &ldKey, const CDomainRelativeObjId &ldNew, const CDomainRelativeObjId &ldBirth ) { int err; CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey); CLdapOMTAddModify lam( ldKey, ldNew, ldBirth, _pRefreshSequenceStorage->GetSequenceNumber(), 0, // Only used with LDAP_MOD_ADD LDAP_MOD_REPLACE); err = ldap_modify_s(Ldap(), dnKey, lam._mods); if (err == LDAP_SUCCESS) { return(TRUE); } else if (err == LDAP_NO_SUCH_OBJECT) { return(FALSE); } else { TrkRaiseWin32Error(LdapMapErrorToWin32(err)); return(FALSE); } } // must leave outputs unchanged if returning FALSE BOOL CIntraDomainTable::Query(const CDomainRelativeObjId &ldKey, CDomainRelativeObjId *pldNew, CDomainRelativeObjId *pldBirth, BOOL *pfDeleted OPTIONAL, BOOL *pfCounted OPTIONAL ) { int err; TCHAR *aptszAttrs[] = { const_cast(s_currentLocation), const_cast(s_birthLocation), const_cast(s_oMTIndxGuid), NULL }; LDAPMessage * pRes = NULL; CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey); BOOL fFound = FALSE; __try { err = ldap_search_s( Ldap(), dnKey, LDAP_SCOPE_BASE, TEXT("(objectclass=*)"), aptszAttrs, 0, // attribute types and values are wanted &pRes ); if (err == LDAP_SUCCESS) { // found it, lets get the attributes out if (ldap_count_entries(Ldap(), pRes) == 1) { LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes); if (pEntry == NULL) { TrkRaiseWin32Error(LdapMapErrorToWin32(Ldap()->ld_errno)); } fFound = Query( Ldap(), pEntry, ldKey, pldNew, pldBirth, pfDeleted, pfCounted ); } } else if (err != LDAP_NO_SUCH_OBJECT) { TrkRaiseWin32Error(LdapMapErrorToWin32(err)); } } __finally { if (NULL != pRes) ldap_msgfree(pRes); } return(fFound); } BOOL CIntraDomainTable::Query( LDAP* pLdap, LDAPMessage *pEntry, const CDomainRelativeObjId ldKey, CDomainRelativeObjId *pldNew, CDomainRelativeObjId *pldBirth, BOOL *pfDeleted OPTIONAL, BOOL *pfCounted OPTIONAL ) { BOOL fFound = FALSE; struct berval **ppbvCurrentLocation = NULL; struct berval **ppbvBirthLocation = NULL; struct berval **ppbvQuotaFlags = NULL; BYTE bQuotaFlags = 0; if( NULL != pfDeleted ) *pfDeleted = FALSE; __try { ppbvBirthLocation = ldap_get_values_len(pLdap, pEntry, const_cast(s_birthLocation) ); if (NULL != ppbvBirthLocation && sizeof(*pldBirth) > (*ppbvBirthLocation)->bv_len) { TrkLog((TRKDBG_ERROR, TEXT("Couldn't get current location for %s"), (const TCHAR*)CDebugString(ldKey) )); TrkRaiseWin32Error( LdapMapErrorToWin32(pLdap->ld_errno) ); } ppbvCurrentLocation = ldap_get_values_len(pLdap, pEntry, const_cast(s_currentLocation) ); if (NULL == ppbvCurrentLocation || sizeof(*pldNew) > (*ppbvCurrentLocation)->bv_len) { TrkLog((TRKDBG_ERROR, TEXT("Couldn't get current location for %s"), (const TCHAR*)CDebugString(ldKey) )); TrkRaiseWin32Error( LdapMapErrorToWin32(pLdap->ld_errno) ); } ppbvQuotaFlags = ldap_get_values_len(pLdap, pEntry, const_cast(s_oMTIndxGuid) ); if (NULL != ppbvQuotaFlags && sizeof(BYTE) <= (*ppbvQuotaFlags)->bv_len) { bQuotaFlags = *(BYTE*)(*ppbvQuotaFlags)->bv_val; } if( NULL != pfCounted ) { if( bQuotaFlags & QFLAG_UNCOUNTED ) *pfCounted = FALSE; else *pfCounted = TRUE; } if( bQuotaFlags & QFLAG_DELETED ) { if( NULL != pfDeleted ) *pfDeleted = TRUE; TrkLog(( TRKDBG_IDT, TEXT("IdtQuery: Entry marked deleted will be ignored (0x%x): %s"), bQuotaFlags, (const TCHAR*)CDebugString(ldKey) )); } else { *pldNew = *reinterpret_cast( (*ppbvCurrentLocation)->bv_val ); if (NULL != ppbvBirthLocation) *pldBirth = *reinterpret_cast( (*ppbvBirthLocation)->bv_val ); else *pldBirth = ldKey; fFound = TRUE; } } __finally { if (NULL != ppbvCurrentLocation) ldap_value_free_len(ppbvCurrentLocation); if (NULL != ppbvBirthLocation) ldap_value_free_len(ppbvBirthLocation); if (NULL != ppbvQuotaFlags) ldap_value_free_len(ppbvQuotaFlags); } return( fFound ); } //+---------------------------------------------------------------------------- // // CIntraDomainTable::Touch // // Update the refresh attribute for an entry in the move table. // Return TRUE if the entry exists and was touched, FALSE if it // doesn't exist, and raise an exception if there's an error. // //+---------------------------------------------------------------------------- // BUGBUG: if we ever move to per-user quotas, // check ownership of entry being touched. BOOL CIntraDomainTable::Touch( const CDomainRelativeObjId &ldKey ) { BOOL fReturn = FALSE; int err; CLdapTimeValue ltvRefresh( _pRefreshSequenceStorage->GetSequenceNumber()); CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_REPLACE ); CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey); TCHAR ** pptszRefresh = NULL; LDAPMessage * pEntry = NULL; LDAPMessage* pRes = NULL; LDAPMod * mods[2]; CObjId objZero; __try { // // if the birth id is zero, then it is invalid and not worth doing a refresh // if (ldKey.GetObjId() == objZero) { __leave; } // // Check to see if the object already has a recent sequence number. // TCHAR* rgptszAttrs[2]; rgptszAttrs[0] = const_cast(s_timeRefresh); rgptszAttrs[1] = NULL; err = ldap_search_s(Ldap(), dnKey, LDAP_SCOPE_BASE, TEXT("(ObjectClass=*)"), rgptszAttrs, 0, &pRes); if (err == LDAP_SUCCESS) { // The search call worked, but did we find an object? if( 1 == ldap_count_entries(Ldap(), pRes) ) { // The object already exists pEntry = ldap_first_entry(Ldap(), pRes); if( NULL != pEntry ) { // Get the refresh counter pptszRefresh = ldap_get_values( Ldap(), pEntry, const_cast(s_timeRefresh) ); if( NULL != pptszRefresh ) { SequenceNumber seqRefresh = 0; if( 1 == _stscanf( *pptszRefresh, TEXT("%d"), &seqRefresh )) { // Is the refresh counter already set to a recent value? // We'll consider it recent enough if it's within half of the // refresh cycle (15 days) // First, how long is the GC timer in seconds? LONG lGCTimerInSeconds = _pTrkSvrConfiguration->GetGCPeriod() // 30 days in seconds / _pTrkSvrConfiguration->GetGCDivisor(); // => 1 day in seconds // Next, how many ticks is half the period? LONG lWindow = _pTrkSvrConfiguration->GetGCPeriod() // 30 days (in seconds) / 2 // => 15 days (in seconds) / lGCTimerInSeconds; // => 15 TrkLog(( TRKDBG_WARNING, TEXT("Window = %d"), lWindow )); if( seqRefresh + lWindow >= _pRefreshSequenceStorage->GetSequenceNumber() ) { TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("Not touching %s with %d, seq %d already set"), (const TCHAR*)CDebugString(ldKey), _pRefreshSequenceStorage->GetSequenceNumber(), seqRefresh )); __leave; } } } } } } else if (err == LDAP_NO_SUCH_OBJECT) { TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("Touch: object %s not found"), (const TCHAR*) CDebugString(ldKey) )); __leave; } // // Set the correct sequence number // mods[0] = &lsmRefresh._mod; mods[1] = NULL; err = ldap_modify_s(Ldap(), dnKey, mods); if (err == LDAP_SUCCESS) { TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("Touch: object %s touched"), (const TCHAR*) CDebugString(ldKey) )); fReturn = TRUE; __leave; } else if (err == LDAP_NO_SUCH_OBJECT) { TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("Touch: object %s not found"), (const TCHAR*) CDebugString(ldKey) )); __leave; } else if (err == LDAP_NO_SUCH_ATTRIBUTE) { TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR, TEXT("Touch: object %s attribute not found"), (const TCHAR*) CDebugString(ldKey) )); // deal with old server data CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_ADD ); mods[0] = &lsmRefresh._mod; err = ldap_modify_s(Ldap(), dnKey, mods); } if (err != LDAP_SUCCESS) { TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("Touch: object %s --> exceptional error"), (const TCHAR*) CDebugString(ldKey) )); __leave; } } __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception during IDT::Touch (%08x)"), GetExceptionCode() )); } if (pptszRefresh != NULL) ldap_value_free(pptszRefresh); if(pRes != NULL) ldap_msgfree(pRes); return( fReturn ); } ULONG CIntraDomainTable::GarbageCollect( SequenceNumber seqCurrent, SequenceNumber seqOldestToKeep, const BOOL * pfAbort ) { CLdapIdtKeyDn dn(GetBaseDn()); TCHAR * apszAttrs[3]; GC_ENUM_CONTEXT EnumContext; apszAttrs[0] = const_cast(s_Cn); apszAttrs[1] = const_cast(s_timeRefresh); apszAttrs[2] = 0; TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("GC-ing move table (%d/%d)"), seqCurrent, seqOldestToKeep )); memset( &EnumContext, 0, sizeof(EnumContext) ); EnumContext.seqOldestToKeep = seqOldestToKeep; EnumContext.seqCurrent = seqCurrent; EnumContext.pfAbort = pfAbort; EnumContext.dwRepetitiveTaskDelay = _pTrkSvrConfiguration->GetRepetitiveTaskDelay(); EnumContext.pqtable = _pqtable; if (!LdapEnumerate( Ldap(), dn, LDAP_SCOPE_ONELEVEL, TEXT("(objectClass=*)"), apszAttrs, GcEnumerateCallback, &EnumContext )) { TrkRaiseException(TRK_E_SERVICE_STOPPING); } TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("GC-ed %d entries from the move table"), EnumContext.cEntries )); _pqtable->OnMoveTableGcComplete( EnumContext.cEntries ); return EnumContext.cEntries; } #if DBG void CIntraDomainTable::PurgeAll() { int err; TCHAR *apszAttrs[2] = { TEXT("cn"), NULL }; LDAPMessage * pRes; TCHAR tszObjectMoveTable[MAX_PATH+1]; __try { _tcscpy(tszObjectMoveTable, s_ObjectMoveTableRDN); _tcscat(tszObjectMoveTable, GetBaseDn()); err = ldap_search_s( Ldap(), tszObjectMoveTable, LDAP_SCOPE_ONELEVEL, TEXT("(objectclass=*)"), apszAttrs, 0, // attribute types and values are wanted &pRes ); if (err == LDAP_SUCCESS) { // found it, lets get the attributes out int cEntries = ldap_count_entries(Ldap(), pRes); LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes); if (pEntry != NULL) { do { TCHAR * ptszDn = ldap_get_dn(Ldap(), pEntry); int errd = ldap_delete_s(Ldap(),ptszDn); TrkLog((TRKDBG_ERROR, TEXT("Purged %s status=%d"), ptszDn, errd)); ldap_memfree(ptszDn); } while ( pEntry = ldap_next_entry(Ldap(), pEntry)); } } } __finally { if (err == LDAP_SUCCESS) { ldap_msgfree(pRes); } } } #endif void CDomainRelativeObjId::FillLdapIdtKeyBuffer(TCHAR * const pchCN, DWORD cch) const { TCHAR *pchBuf = pchCN; _tcscpy(pchBuf, TEXT("CN=")); pchBuf = pchBuf + 3; _volume.Stringize(pchBuf); _object.Stringize(pchBuf); TrkAssert(pchBuf <= pchCN+cch); } void CDomainRelativeObjId::ReadLdapIdtKeyBuffer(const TCHAR * pchCN ) { const TCHAR *pchBuf; Init(); if( 0 == _tcsncmp( pchCN, TEXT("CN="), 3 )) { pchBuf = &pchCN[3]; if( !_volume.Unstringize(pchBuf) || !_object.Unstringize(pchBuf) ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't unstringize droid from %s"), pchCN )); Init(); } } } void CDomainRelativeObjId::InitFromLdapBuffer(char * pVolumeId, int cbVolumeId, char * pObjId, int cbObjId) { DWORD iBuf = 0; if (cbVolumeId != sizeof(_volume) || cbObjId != sizeof(_object)) { TrkRaiseException(TRK_E_CORRUPT_IDT); } memcpy(&_volume, pVolumeId, sizeof(_volume)); memcpy(&_object, pObjId, sizeof(_object)); }