//+---------------------------------------------------------------------------- // // Copyright (C) 2001, Microsoft Corporation // // File: DfsSiteCache.cxx // // Contents: implements the base DFS Site hash class // // // History: July. 10 2001, Author: Rohanp // //----------------------------------------------------------------------------- #include "dfsgeneric.hxx" #include "dfsinit.hxx" #include "dsgetdc.h" #include "lm.h" #include #include "DfsSiteCache.hxx" #include #include "DfsSiteCache.tmh" void * AllocateSitecacheData(ULONG Size ) { PVOID RetValue = NULL; if (Size) { RetValue = (PVOID) new BYTE[Size]; if(RetValue != NULL) { RtlZeroMemory( RetValue, Size ); } } return RetValue; } DFSSTATUS DfsSiteNameCache::DfsInitializeSiteCache(void) { DFSSTATUS Status = ERROR_SUCCESS; SHASH_FUNCTABLE FunctionTable; ZeroMemory(&FunctionTable, sizeof(FunctionTable)); FunctionTable.CompareFunc = CompareSiteIpAddress; FunctionTable.HashFunc = DfsHashIpAddress; FunctionTable.AllocFunc = AllocateSitecacheData; FunctionTable.FreeFunc = DfsDeallocateHashData; FunctionTable.AllocHashEntryFunc = AllocateSitecacheData; FunctionTable.FreeHashEntryFunc = DeallocateSiteCacheData; Status = ShashInitHashTable(&pSiteTable, &FunctionTable); Status = RtlNtStatusToDosError(Status); return Status; } DfsSiteNameCache * DfsSiteNameCache::CreateSiteHashTable(DFSSTATUS * pStatus) { DfsSiteNameCache * pSiteTable = NULL; DFSSTATUS Status = ERROR_SUCCESS; pSiteTable = new DfsSiteNameCache(&Status); if(pSiteTable == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; } else { Status = pSiteTable->DfsInitializeSiteCache(); if(Status != ERROR_SUCCESS) { delete pSiteTable; pSiteTable = NULL; } } *pStatus = Status; return pSiteTable; } DFSSTATUS DfsSiteNameCache::CreateSiteData( IN DWORD IpAddress, IN DfsSite *pNewSite, OUT PDFSSITE_DATA *ppSiteData ) { PDFSSITE_DATA SiteStructure = NULL; DFSSTATUS Status = ERROR_SUCCESS; *ppSiteData = NULL; do { SiteStructure = (PDFSSITE_DATA) AllocateSitecacheData(sizeof( DFSSITE_DATA )); if (SiteStructure == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } RtlZeroMemory(SiteStructure, sizeof(DFSSITE_DATA)); SiteStructure->Header.RefCount = 1; SiteStructure->Header.pvKey = (PVOID) (ULONG_PTR)IpAddress; SiteStructure->Header.pData = (PVOID)SiteStructure; SiteStructure->FirstAccessTime = GetTickCount(); SiteStructure->InMruList = FALSE; SiteStructure->Accessed = SITE_ACCESSED; SiteStructure->IpAddress = IpAddress; // // Now attach a referenced ClientSite. // pNewSite->AcquireReference(); SiteStructure->ClientSite = pNewSite; *ppSiteData = SiteStructure; } while (FALSE); // // We haven't added this to the table yet. // So, just free the whole thing in case of error. // This will release the DfsSite as well, if it's initialized. // if (Status != ERROR_SUCCESS && SiteStructure != NULL) { DeallocateSiteCacheData( SiteStructure ); *ppSiteData = NULL; } return Status; } DFSSTATUS DfsSiteNameCache::InsertInMruList(PDFSSITE_DATA SiteStructure) { DFSSTATUS Status = ERROR_SUCCESS; EnterCriticalSection(&m_Lock); SiteStructure->InMruList = TRUE; InsertHeadList( &MruList, &SiteStructure->ListEntry ) ; LeaveCriticalSection(&m_Lock); return Status; } DFSSTATUS DfsSiteNameCache::RemoveFromMruList(PDFSSITE_DATA pSiteStructure) { DFSSTATUS Status = ERROR_SUCCESS; EnterCriticalSection(&m_Lock); if(pSiteStructure->InMruList == TRUE) { RemoveEntryList( &pSiteStructure->ListEntry ) ; pSiteStructure->InMruList = FALSE ; ReleaseSiteCacheData(pSiteStructure); } LeaveCriticalSection(&m_Lock); return Status; } DFSSTATUS DfsSiteNameCache::MoveToFrontOfMruList(PDFSSITE_DATA pSiteStructure) { DFSSTATUS Status = ERROR_SUCCESS; EnterCriticalSection(&m_Lock); if(pSiteStructure->InMruList == TRUE) { RemoveEntryList( &pSiteStructure->ListEntry ) ; InsertHeadList( &MruList, &pSiteStructure->ListEntry ) ; InterlockedExchange(&pSiteStructure->Accessed, SITE_ACCESSED); } LeaveCriticalSection(&m_Lock); return Status; } DFSSTATUS DfsSiteNameCache::StoreSiteInCache( DWORD IpAddress, DfsSite *pSite, BOOLEAN IsRefresh) { PDFSSITE_DATA SiteStructure = NULL; NTSTATUS NtStatus = STATUS_SUCCESS; DFSSTATUS Status = ERROR_SUCCESS; LONG CurrentSiteEntries = 0; //assume success first. Also, if an existing entry is inthe cache, and we are //just replacing it (IsRefresh == TRUE), then don't bump the count. if(IsRefresh == FALSE) { CurrentSiteEntries = InterlockedIncrement( &DfsServerGlobalData.NumClientDfsSiteEntries ); if(CurrentSiteEntries > DfsServerGlobalData.NumClientSiteEntriesAllowed) { InterlockedDecrement( &DfsServerGlobalData.NumClientDfsSiteEntries ); Status = ERROR_REMOTE_SESSION_LIMIT_EXCEEDED; return Status; } } Status = CreateSiteData( IpAddress, pSite, &SiteStructure ); if (Status == ERROR_SUCCESS) { if(IsRefresh) { SiteStructure->Accessed = SITE_NOTACCESSED; } NtStatus = SHashInsertKey(pSiteTable, SiteStructure, (void *) (ULONG_PTR)IpAddress, SHASH_REPLACE_IFFOUND); if(NtStatus == STATUS_SUCCESS) { if(InsertInMruList(SiteStructure) != ERROR_SUCCESS) { InterlockedDecrement(&SiteStructure->Header.RefCount); } // for stats DFS_TRACE_LOW( SITE, "Added Site %ws to IPCache (IP %x)\n", pSite->SiteNameString(), IpAddress); } else { DeallocateSiteCacheData( SiteStructure ); Status = RtlNtStatusToDosError(NtStatus); } } if((NtStatus != STATUS_SUCCESS) && (IsRefresh == FALSE)) { InterlockedDecrement( &DfsServerGlobalData.NumClientDfsSiteEntries ); } return Status; } PSHASH_HEADER DfsSiteNameCache::LookupIpInHash(DWORD IpAddress) { DFSSTATUS Status = ERROR_SUCCESS; PSHASH_HEADER pHeader = NULL; PDFSSITE_DATA pSiteStructure = NULL; pHeader = SHashLookupKeyEx(pSiteTable, (void *) (ULONG_PTR) IpAddress); if(pHeader != NULL) { pSiteStructure = (PDFSSITE_DATA) pHeader; MoveToFrontOfMruList(pSiteStructure); } return pHeader; } void DfsSiteNameCache:: ReleaseSiteCacheData(PDFSSITE_DATA pData) { SHashReleaseReference(pSiteTable, (PSHASH_HEADER) pData ); } BOOLEAN DfsSiteNameCache::IsTimeToRefresh(PDFSSITE_DATA pSiteData) { DWORD TimeNow = 0; TimeNow = GetTickCount(); if ((TimeNow > pSiteData->FirstAccessTime) && (TimeNow - pSiteData->FirstAccessTime) > DfsServerGlobalData.SiteSupportRefreshInterval) { return TRUE; } if ((TimeNow < pSiteData->FirstAccessTime) && ((TimeNow - 0) + (0xFFFFFFFF - pSiteData->FirstAccessTime) > DfsServerGlobalData.SiteSupportRefreshInterval)) { return TRUE; } return FALSE; } DFSSTATUS DfsSiteNameCache::ResetSiteData(PDFSSITE_DATA pExistingData) { DFSSTATUS Status = ERROR_SUCCESS; DfsSite *pClientSite = NULL; unsigned char *IpAddr = (unsigned char *) &(pExistingData->IpAddress); DFS_TRACE_LOW( SITE, "ResetSiteData: old Site %ws, IP %d:%d:%d:%d\n", pExistingData->ClientSite->SiteNameString(), IpAddr[0], IpAddr[1], IpAddr[2], IpAddr[3]); // // Find a new DfsSite for it. This will do the DsGetSiteName calls // again. // DfsIpToDfsSite((char *) (ULONG_PTR)&pExistingData->IpAddress, 4, AF_INET, &pClientSite); ASSERT( pClientSite != NULL ); Status = StoreSiteInCache( pExistingData->IpAddress, pClientSite, TRUE); // // Release the reference we got (above in DfsIpToDfsSite). // The cache has already taken the reference it needs in CreateSiteData. // pClientSite->ReleaseReference(); DFS_TRACE_LOW( SITE, "ResetSiteData: new Site %ws, IP %d:%d:%d:%d\n", pExistingData->ClientSite->SiteNameString(), IpAddr[0], IpAddr[1], IpAddr[2], IpAddr[3]); return Status; } DFSSTATUS DfsSiteNameCache::DeleteSiteData(PDFSSITE_DATA pSiteStructure) { DFSSTATUS Status = ERROR_SUCCESS; SHashMarkForDeletion(pSiteTable, (PSHASH_HEADER) pSiteStructure); return Status; } DFSSTATUS DfsSiteNameCache::RemoveAndDeleteSiteData(PDFSSITE_DATA pExistingData) { DFSSTATUS Status = ERROR_SUCCESS; unsigned char *IpAddr = (unsigned char *) &(pExistingData->IpAddress); DFS_TRACE_LOW( SITE, "RemoveAndDeleteSiteData: old Site %ws, IP %d:%d:%d:%d\n", pExistingData->ClientSite->SiteNameString(), IpAddr[0], IpAddr[1], IpAddr[2], IpAddr[3]); Status = RemoveFromMruList(pExistingData); if(Status == STATUS_SUCCESS) { Status = DeleteSiteData(pExistingData); } if(Status == ERROR_SUCCESS) { InterlockedDecrement( &DfsServerGlobalData.NumClientDfsSiteEntries ); } return Status; } DFSSTATUS DfsSiteNameCache::RefreshSiteData(void) { DFSSTATUS Status = ERROR_SUCCESS; BOOLEAN TimeToRefresh = FALSE; BOOLEAN WasAccessed = FALSE; LPWSTR *SiteNamesArray = NULL; PDFSSITE_DATA pExistingData = NULL; SHASH_ITERATOR Iter; pExistingData = (PDFSSITE_DATA) SHashStartEnumerate(&Iter, pSiteTable); while ((pExistingData != NULL) && (!DfsIsShuttingDown())) { WasAccessed = ResetSiteAccessMask(pExistingData); if(WasAccessed) { TimeToRefresh = IsTimeToRefresh(pExistingData); if(TimeToRefresh == TRUE) { Status = RemoveFromMruList(pExistingData); if(Status == STATUS_SUCCESS) { Status = ResetSiteData(pExistingData); if(Status != STATUS_SUCCESS) { if(InsertInMruList(pExistingData) == ERROR_SUCCESS) { InterlockedIncrement(&pExistingData->Header.RefCount); } } } } } else { RemoveAndDeleteSiteData(pExistingData); } pExistingData = (PDFSSITE_DATA) SHashNextEnumerate(&Iter, pSiteTable); } SHashFinishEnumerate(&Iter, pSiteTable); return Status; } ULONG DfsHashIpAddress( void * pDfsAddress) { ULONG BucketNo = 0; CHAR *pBuffer = (char *) &pDfsAddress; CHAR *pBufferEnd = &pBuffer[4]; ULONG Ch = 0; BucketNo = 0; while (pBuffer != pBufferEnd) { Ch = *pBuffer & 0xff; BucketNo *= 131; BucketNo += Ch; pBuffer++; } return BucketNo; } int CompareSiteIpAddress(void* pvKey1, void* pvKey2) { int CompVal = 0; ULONG_PTR IpAddress1 = (ULONG_PTR) pvKey1; ULONG_PTR IpAddress2 = (ULONG_PTR) pvKey2; if(IpAddress1 < IpAddress2) { CompVal = -1; } else if (IpAddress1 > IpAddress2) { CompVal = 1; } return CompVal; } VOID DeallocateSiteCacheData(PVOID pPointer ) { PDFSSITE_DATA pSiteStructure = (PDFSSITE_DATA)pPointer; if(pPointer) { if (pSiteStructure->ClientSite != NULL) { pSiteStructure->ClientSite->ReleaseReference(); pSiteStructure->ClientSite = NULL; } delete [] (PBYTE)pPointer; } }