//+------------------------------------------------------------------------- // // Copyright (C) 1997, Microsoft Corporation. // // File: ipsup.c // // Contents: Support routines for managing DFS_IP_INFO entries and // DFS_IP_PENDING_INFO entries // // Functions: DfsInitIp - Initialize the hash table for DFS_IP_INFO lookup // DfsLookupIpInfo - Lookup a DFS_IP_INFO // DfsAllocateIpInfo - Allocate a DFS_IP_INFO // DfsInsertIpInfo - Put a DFS_IP_INFO into the table // DfsDeleteIpInfo - Remove a DFS_IP_INFO from the table // DfsReleaseIpInfo - Stop using a DFS_IP_INFO // // DfsFsctrlCreateIpInfo - Load an IpInfo table entry // DfsFsctrlDeleteIpInfo - Remove an IpInfo table entry // // History: 16 Dec 1997 Jharper Created // //-------------------------------------------------------------------------- #include "dfsprocs.h" #include "attach.h" #include "ipsup.h" #include "fsctrl.h" #include "dfslpc.h" #include "registry.h" #include "regkeys.h" #define Dbg 0x1000 // // Manifest constants // #define IP_DEFAULT_HASH_SIZE 16 // default size of hash table #define IP_DEFAULT_NUMBER_ENTRIES 250 // default max # of entries #define IP_DEFAULT_TIMEOUT (60 * 60 * 24) // default time entry can live (in sec) NTSTATUS DfsInitIpInfoHashTable( IN ULONG cHash, OUT PIP_HASH_TABLE *ppHashTable ); NTSTATUS DfsAllocateIpInfo( IN PDFS_IPADDRESS pDfsIpAddress, IN PUNICODE_STRING pSiteName, OUT PDFS_IP_INFO *ppIpInfo ); VOID DfsInsertIpInfo( IN PDFS_IPADDRESS pDfsIpAddress, IN PDFS_IP_INFO pIpInfo ); VOID DfsDeleteIpInfo( PDFS_IP_INFO pIpInfo ); ULONG DfsHashIpAddress( IN PDFS_IPADDRESS pDfsIpAddress, IN DWORD HashMask ); PDFS_IP_INFO DfsLookupIpInfo( IN PIP_HASH_TABLE pHashTable, IN PDFS_IPADDRESS pDfsIpAddress ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, DfsInitIp) #pragma alloc_text(PAGE, DfsUninitIp) #pragma alloc_text(PAGE, DfsInitIpInfoHashTable) #pragma alloc_text(PAGE, DfsAllocateIpInfo) #pragma alloc_text(PAGE, DfsLookupIpInfo) #pragma alloc_text(PAGE, DfsInsertIpInfo) #pragma alloc_text(PAGE, DfsDeleteIpInfo) #pragma alloc_text(PAGE, DfsReleaseIpInfo) #pragma alloc_text(PAGE, DfsHashIpAddress) #pragma alloc_text(PAGE, DfsFsctrlCreateIpInfo) #pragma alloc_text(PAGE, DfsFsctrlDeleteIpInfo) #endif #ifdef DBG VOID DfsDumpIpTable(void); #endif //+------------------------------------------------------------------------- // // Function: DfsInitIpHashTable - Initialize the DFS_IP_INFO lookup hash table // // Synopsis: This function initializes data structures which are // used for looking up a DFS_IP_INFO associated with some IP address // // Arguments: [cHash] -- Size of the hash table to be allocated. Must be // a power of two. If zero, a default size is used. // // Returns: NTSTATUS -- STATUS_SUCCESS, unless memory allocation // fails. // // Note: The hash buckets are initialized to zero, then later // initialized to a list head when used. This is a debugging // aid to determine if some hash buckets are never used. // //-------------------------------------------------------------------------- NTSTATUS DfsInitIpHashTable( ULONG cHash, ULONG cEntries, PIP_HASH_TABLE *ppHashTable) { PIP_HASH_TABLE pHashTable; ULONG cbHashTable; ULONG Timeout; NTSTATUS status; PBYTE pData; if (cHash == 0) { cHash = IP_DEFAULT_HASH_SIZE; } ASSERT ((cHash & (cHash-1)) == 0); // Assure cHash is a power of two if (cEntries == 0) { cEntries = IP_DEFAULT_NUMBER_ENTRIES; } cbHashTable = sizeof(IP_HASH_TABLE) + (cHash-1) * sizeof(LIST_ENTRY); pHashTable = ExAllocatePoolWithTag(NonPagedPool, cbHashTable, ' sfD'); if (pHashTable == NULL) { return STATUS_NO_MEMORY; } pHashTable->NodeTypeCode = DFS_NTC_IP_HASH; pHashTable->NodeByteSize = (NODE_BYTE_SIZE) cbHashTable; pHashTable->MaxEntries = cEntries; pHashTable->EntryCount = 0; InitializeListHead(&pHashTable->LRUChain); pHashTable->HashMask = (cHash-1); ExInitializeFastMutex( &pHashTable->HashListMutex ); RtlZeroMemory(&pHashTable->HashBuckets[0], cHash * sizeof(LIST_ENTRY)); // // If there is a timeout override in the registry, get it // Timeout = IP_DEFAULT_TIMEOUT; status = KRegSetRoot(wszRegDfsDriver); if (NT_SUCCESS(status)) { status = KRegGetValue( L"", wszIpCacheTimeout, (PVOID ) &pData); KRegCloseRoot(); if (NT_SUCCESS(status)) { Timeout = *((ULONG*)pData); ExFreePool(pData); } } pHashTable->Timeout.QuadPart = UInt32x32To64( Timeout, 10 * 1000 * 1000 ); *ppHashTable = pHashTable; return(STATUS_SUCCESS); } NTSTATUS DfsInitIp( ULONG cHash, ULONG cEntries) { NTSTATUS status; status = DfsInitIpHashTable( cHash, cEntries, &DfsData.IpHashTable ); return status; } VOID DfsUninitIp( VOID ) { ExFreePool (DfsData.IpHashTable); } //+------------------------------------------------------------------------- // // Function: DfsLookupIpInfo - Lookup a DFS_IP_INFO in the hash table // // Synopsis: This function will lookup a DFS_IP_INFO. // It will increment the UseCount on the DFS_IP_INFO. // // Arguments: [pDfsIpAddress] -- Ip address being looked up. // // Returns: PVOID -- pointer to the DFS_IP_INFO found, or NULL if none // //-------------------------------------------------------------------------- PDFS_IP_INFO DfsLookupIpInfo( PIP_HASH_TABLE pHashTable, PDFS_IPADDRESS pDfsIpAddress) { PLIST_ENTRY pListHead, pLink; PDFS_IP_INFO pIpInfo; ExAcquireFastMutex( &pHashTable->HashListMutex); pListHead = &pHashTable->HashBuckets[DfsHashIpAddress(pDfsIpAddress,pHashTable->HashMask)]; if ((pListHead->Flink == NULL) || // list not initialized (pListHead->Flink == pListHead)) { // list empty ExReleaseFastMutex( &pHashTable->HashListMutex ); return NULL; } for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) { pIpInfo = CONTAINING_RECORD(pLink, DFS_IP_INFO, HashChain); if (pDfsIpAddress->IpFamily == pIpInfo->IpAddress.IpFamily && pDfsIpAddress->IpLen == pIpInfo->IpAddress.IpLen && RtlCompareMemory( pDfsIpAddress->IpData, pIpInfo->IpAddress.IpData, pDfsIpAddress->IpLen) == pDfsIpAddress->IpLen ) { RemoveEntryList(&pIpInfo->LRUChain); InsertHeadList(&pHashTable->LRUChain, &pIpInfo->LRUChain); pIpInfo->UseCount++; ExReleaseFastMutex( &pHashTable->HashListMutex ); return pIpInfo; } } ExReleaseFastMutex( &pHashTable->HashListMutex); return NULL; } //+------------------------------------------------------------------------- // // Function: DfsInsertIpInfo - Inserts a DFS_IP_INFO into the hash table // // Synopsis: This function associates a DFS_IP_INFO with an Ip address. This // involves removing any existing entry, and adding the new. // // Arguments: [pDfsIpAddress] -- Pointer to the corresponding IpAddress, used // as the hash key. // [pIpInfo] -- Pointer to the DFS_IP_INFO to be inserted. // // Returns: -nothing- // //-------------------------------------------------------------------------- VOID DfsInsertIpInfo( PDFS_IPADDRESS pDfsIpAddress, PDFS_IP_INFO pIpInfo) { PIP_HASH_TABLE pHashTable = (PIP_HASH_TABLE) DfsData.IpHashTable; PLIST_ENTRY pListHead; PDFS_IP_INFO pExistingIpInfo; PDFS_IP_INFO pTailIpInfo; LARGE_INTEGER now; pExistingIpInfo = DfsLookupIpInfo(pHashTable, &pIpInfo->IpAddress); // // Put the new one in // ExAcquireFastMutex( &pHashTable->HashListMutex); pListHead = &pHashTable->HashBuckets[DfsHashIpAddress(pDfsIpAddress,pHashTable->HashMask)]; if (pListHead->Flink == NULL) { InitializeListHead(pListHead); } KeQuerySystemTime(&now); pIpInfo->Timeout.QuadPart = now.QuadPart + pHashTable->Timeout.QuadPart; InsertHeadList(pListHead, &pIpInfo->HashChain); InsertHeadList(&pHashTable->LRUChain, &pIpInfo->LRUChain); pHashTable->EntryCount++; // // If adding this entry causes the number of entries to exceed the maximum, // then remove entries from the tail of the LRU list. // pListHead = &pHashTable->LRUChain; if (pHashTable->EntryCount > pHashTable->MaxEntries && pListHead->Blink != pListHead) { pTailIpInfo = CONTAINING_RECORD(pListHead->Blink, DFS_IP_INFO, LRUChain); if (pTailIpInfo != pIpInfo) { pTailIpInfo->Flags |= IP_INFO_DELETE_PENDING; RemoveEntryList(&pTailIpInfo->HashChain); RemoveEntryList(&pTailIpInfo->LRUChain); if (pTailIpInfo->UseCount == 0) { ExFreePool(pTailIpInfo); } pHashTable->EntryCount--; } } ExReleaseFastMutex( &pHashTable->HashListMutex ); if (pExistingIpInfo != NULL) { DfsDeleteIpInfo( pExistingIpInfo); DfsReleaseIpInfo( pExistingIpInfo); } DebugTrace(0, Dbg, "Added pIpInfo %08lx ", pIpInfo); DebugTrace(0, Dbg, "For Site %wZ ", &pIpInfo->SiteName); } //+------------------------------------------------------------------------- // // Function: DfsDeleteIpInfo - Delete a DFS_IP_INFO from the lookup hash table // // Synopsis: This function Deletes a DFS_IP_INFO from the hash table. // // Arguments: [pIpInfo] -- Pointer to the DFS_IP_INFO to delete // // Returns: -nothing- // //-------------------------------------------------------------------------- VOID DfsDeleteIpInfo( PDFS_IP_INFO pIpInfo) { PIP_HASH_TABLE pHashTable = (PIP_HASH_TABLE) DfsData.IpHashTable; ExAcquireFastMutex( &pHashTable->HashListMutex); pIpInfo->Flags |= IP_INFO_DELETE_PENDING; RemoveEntryList(&pIpInfo->HashChain); RemoveEntryList(&pIpInfo->LRUChain); pHashTable->EntryCount--; ExReleaseFastMutex( &pHashTable->HashListMutex); DebugTrace(0, Dbg, "deleted pIpInfo %08lx ", pIpInfo); DebugTrace(0, Dbg, "For site %wZ ", &pIpInfo->SiteName); } //+---------------------------------------------------------------------------- // // Function: DfsAllocateIpInfo - Allocate a DFS_IP_INFO // // Synopsis: This function allocates a contiguous DFS_IP_INFO struct. The // strings are stored in the allocated buffer after the DFS_IP_INFO // structure. // // Arguments: [pSiteName] -- The site name // [pDfsIpAddress -- The Ip address of the client // [ppIpInfo] -- On successful return, has pointer to newly allocated // DFS_IP_INFO. // // Returns: [STATUS_SUCCESS] -- Successfully allocated DFS_IP_INFO // // [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory condition // //----------------------------------------------------------------------------- NTSTATUS DfsAllocateIpInfo( PDFS_IPADDRESS pDfsIpAddress, PUNICODE_STRING pSiteName, PDFS_IP_INFO *ppIpInfo) { NTSTATUS status; PDFS_IP_INFO pIpInfo; ULONG Size; ULONG i; LPWSTR pwCh; PUNICODE_STRING pustr; DebugTrace(0, Dbg, "DfsAllocateIpInfo(%wZ)\n", pSiteName); // // Size the buffer - include storage for the unicode string after the // DFS_IP_INFO structure. // Size = sizeof(DFS_IP_INFO) + pSiteName->Length; pIpInfo = (PDFS_IP_INFO) ExAllocatePoolWithTag( PagedPool, Size, ' sfD' ); if (pIpInfo != NULL) { RtlZeroMemory( pIpInfo, Size ); pIpInfo->NodeTypeCode = DFS_NTC_IP_INFO; pIpInfo->NodeByteSize = (USHORT)Size; pIpInfo->IpAddress = *pDfsIpAddress; pwCh = (LPWSTR) &pIpInfo[1]; pustr = &pIpInfo->SiteName; pustr->Length = pustr->MaximumLength = pSiteName->Length; pustr->Buffer = pwCh; RtlCopyMemory(pwCh, pSiteName->Buffer, pSiteName->Length); pwCh += pustr->Length / sizeof(WCHAR); *ppIpInfo = pIpInfo; status = STATUS_SUCCESS; DebugTrace(0, Dbg, "DfsAllocateIpInfo pIpInfo = %d\n", pIpInfo); } else { status = STATUS_INSUFFICIENT_RESOURCES; } return( status ); } //+---------------------------------------------------------------------------- // // Function: DfsReleaseIpInfo // // Synopsis: Decrements UseCount of and possibly frees a DFS_IP_INFO // // Arguments: [pIpInfo] -- The DFS_IP_INFO to release // // Returns: Nothing // //----------------------------------------------------------------------------- VOID DfsReleaseIpInfo( PDFS_IP_INFO pIpInfo) { PIP_HASH_TABLE pHashTable = (PIP_HASH_TABLE) DfsData.IpHashTable; if (pIpInfo == NULL) { return; } // // There's a potential race with DfsDeleteIpInfo/DfsInsertIpInfo w.r.t. // DELETE_PENDING and the test below of DELETE_PENDING, so we still have // to acquire the Mutex to safely test the DELETE_PENDING bit. // ExAcquireFastMutex( &pHashTable->HashListMutex); pIpInfo->UseCount--; if ((pIpInfo->Flags & IP_INFO_DELETE_PENDING) != 0 && pIpInfo->UseCount == 0) { ExFreePool(pIpInfo); } ExReleaseFastMutex( &pHashTable->HashListMutex); } //+---------------------------------------------------------------------------- // // Function: DfsHashIpAddress // // Synopsis: Generates a hash 0-N // // Arguments: [pDfsIpAddress] -- Ip address to hash // // Returns: Nothing // //----------------------------------------------------------------------------- ULONG DfsHashIpAddress( PDFS_IPADDRESS pDfsAddress, DWORD HashMask) { ULONG BucketNo = 0; CHAR *pBuffer = pDfsAddress->IpData; CHAR *pBufferEnd = &pBuffer[pDfsAddress->IpLen]; ULONG Ch; BucketNo = 0; while (pBuffer != pBufferEnd) { Ch = *pBuffer & 0xff; BucketNo *= 131; BucketNo += Ch; pBuffer++; } BucketNo = BucketNo & HashMask; return BucketNo; } //+------------------------------------------------------------------------- // // Function: DfsFsctrlCreateIpInfo, public // // Synopsis: // // Arguments: // // Returns: // //-------------------------------------------------------------------------- NTSTATUS DfsFsctrlCreateIpInfo( PIRP Irp, PVOID InputBuffer, ULONG InputBufferLength) { NTSTATUS status = STATUS_SUCCESS; PDFS_CREATE_IP_INFO_ARG arg; PDFS_IP_INFO pIpInfo; ULONG i; DebugTrace(+1, Dbg, "DfsFsctrlCreateIpInfo()\n", 0); STD_FSCTRL_PROLOGUE(DfsFsctrlCreateIpInfo, TRUE, FALSE); if (InputBufferLength < sizeof(DFS_CREATE_IP_INFO_ARG)) { status = STATUS_INVALID_PARAMETER; goto exit_with_status; } // // unmarshal the arguments... // arg = (PDFS_CREATE_IP_INFO_ARG) InputBuffer; OFFSET_TO_POINTER(arg->SiteName.Buffer, arg); if (!UNICODESTRING_IS_VALID(arg->SiteName, InputBuffer, InputBufferLength) || arg->IpAddress.IpLen > sizeof(arg->IpAddress.IpData) ) { status = STATUS_INVALID_PARAMETER; goto exit_with_status; } status = DfsAllocateIpInfo( &arg->IpAddress, &arg->SiteName, &pIpInfo); if (NT_SUCCESS(status)) { DfsInsertIpInfo( &arg->IpAddress, pIpInfo); } exit_with_status: DfsCompleteRequest( Irp, status ); DebugTrace(-1, Dbg, "DfsFsctrlCreateIpInfo: Exit -> %08lx\n", ULongToPtr( status ) ); return status; } //+------------------------------------------------------------------------- // // Function: DfsFsctrlDeleteIpInfo, public // // Synopsis: // // Arguments: // // Returns: // //-------------------------------------------------------------------------- NTSTATUS DfsFsctrlDeleteIpInfo( PIRP Irp, PVOID InputBuffer, ULONG InputBufferLength) { NTSTATUS status = STATUS_SUCCESS; PDFS_DELETE_IP_INFO_ARG arg; PDFS_IP_INFO pIpInfo; PIP_HASH_TABLE pHashTable = DfsData.IpHashTable; DebugTrace(+1, Dbg, "DfsFsctrlDeleteIpInfo()\n", 0); STD_FSCTRL_PROLOGUE(DfsFsctrlDeleteIpInfo, TRUE, FALSE); if (InputBufferLength < sizeof(DFS_DELETE_IP_INFO_ARG)) { status = STATUS_INVALID_PARAMETER; goto exit_with_status; } // // unmarshal the arguments... // arg = (PDFS_DELETE_IP_INFO_ARG) InputBuffer; if ( arg->IpAddress.IpLen > sizeof(arg->IpAddress.IpData)) { status = STATUS_INVALID_PARAMETER; goto exit_with_status; } pIpInfo = DfsLookupIpInfo( pHashTable, &arg->IpAddress); // // The DfsLookupIpInfo() call bumped the usecount, so we're sure pIpInfo // won't become invalid as we're using it. // if (pIpInfo != NULL) { // // Removes from the table, but doesn't free the memory // DfsDeleteIpInfo( pIpInfo); // // This will decrement the usecount, and if it goes to zero, frees the memory // DfsReleaseIpInfo( pIpInfo); } exit_with_status: DfsCompleteRequest( Irp, status ); DebugTrace(-1, Dbg, "DfsFsctrlDeleteIpInfo: Exit -> %08lx\n", ULongToPtr( status ) ); return status; } PDFS_IP_INFO DfsLookupSiteByIpaddress( PDFS_IPADDRESS pDfsIpAddress, BOOLEAN UseForce) { PDFS_IP_INFO pIpInfo; LARGE_INTEGER now; NTSTATUS status; PIP_HASH_TABLE pHashTable = DfsData.IpHashTable; if (pDfsIpAddress == NULL) { return NULL; } KeQuerySystemTime(&now); pIpInfo = DfsLookupIpInfo(pHashTable, pDfsIpAddress); if (pIpInfo == NULL || now.QuadPart > pIpInfo->Timeout.QuadPart) { // // Entry is not in cache, or is old // if (pIpInfo != NULL) { // Old entry - try for a new one if (UseForce == TRUE) { ExAcquireFastMutex( &pHashTable->HashListMutex); pIpInfo->Timeout.QuadPart = now.QuadPart + UInt32x32To64( 10 * 60, 10 * 1000 * 1000); ExReleaseFastMutex( &pHashTable->HashListMutex); DfsLpcIpRequest(pDfsIpAddress); pIpInfo = DfsLookupIpInfo(pHashTable, pDfsIpAddress); } } else { if (UseForce == TRUE) { DfsLpcIpRequest(pDfsIpAddress); pIpInfo = DfsLookupIpInfo(pHashTable, pDfsIpAddress); } } } return pIpInfo; } #ifdef DBG VOID DfsDumpIpTable(void) { PLIST_ENTRY pListHead, pLink; PDFS_IP_INFO pIpInfo; PIP_HASH_TABLE pHashTable = DfsData.IpHashTable; ULONG i, j; DbgPrint("%d entries total\n", pHashTable->EntryCount); for (i = 0; i <= pHashTable->HashMask; i++) { pListHead = &pHashTable->HashBuckets[i]; if ((pListHead->Flink == NULL) || // list not initialized (pListHead->Flink == pListHead)) { // list empty continue; } for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) { pIpInfo = CONTAINING_RECORD(pLink, DFS_IP_INFO, HashChain); DbgPrint("B:%02d Ip:0x%x N:%wZ C=%d\n", i, *((ULONG *)&pIpInfo->IpAddress.IpData), &pIpInfo->SiteName, pIpInfo->UseCount); } } } #endif