// // // hashtbl.c // // This file contains the name code to implement the local and remote // hash tables used to store local and remote names to IP addresses // The hash table should not use more than 256 buckets since the hash // index is only calculated to one byte! #include "precomp.h" VOID DestroyHashTable(IN PHASHTABLE pHashTable); //******************* Pageable Routine Declarations **************** #ifdef ALLOC_PRAGMA #pragma CTEMakePageable(INIT, CreateHashTable) #pragma CTEMakePageable(PAGE, DestroyHashTables) #pragma CTEMakePageable(PAGE, DestroyHashTable) #endif //******************* Pageable Routine Declarations **************** //---------------------------------------------------------------------------- NTSTATUS CreateHashTable( tHASHTABLE **pHashTable, LONG lNumBuckets, enum eNbtLocation LocalRemote ) /*++ Routine Description: This routine creates a hash table uTableSize long. Arguments: Return Value: The function value is the status of the operation. --*/ { ULONG uSize; LONG i; NTSTATUS status; CTEPagedCode(); uSize = (lNumBuckets-1)*sizeof(LIST_ENTRY) + sizeof(tHASHTABLE); *pHashTable = (tHASHTABLE *) NbtAllocMem (uSize, NBT_TAG2('01')); if (*pHashTable) { // initialize all of the buckets to have null chains off of them for (i=0;i < lNumBuckets ;i++ ) { InitializeListHead(&(*pHashTable)->Bucket[i]); } (*pHashTable)->LocalRemote = LocalRemote; (*pHashTable)->lNumBuckets = lNumBuckets; status = STATUS_SUCCESS; } else { IF_DBG(NBT_DEBUG_HASHTBL) KdPrint(("Nbt.CreateHashTable: Unable to create hash table\n")); status = STATUS_INSUFFICIENT_RESOURCES; } return(status); } #ifdef _PNP_POWER_ VOID DestroyHashTable( IN PHASHTABLE pHashTable ) { LONG i, j; tNAMEADDR *pNameAddr; LIST_ENTRY *pEntry; CTEPagedCode(); if (pHashTable == NULL) { return; } /* * Go through all the buckets to see if there are any names left */ for (i = 0; i < pHashTable->lNumBuckets; i++) { while (!IsListEmpty(&(pHashTable->Bucket[i]))) { pEntry = RemoveHeadList(&(pHashTable->Bucket[i])); pNameAddr = CONTAINING_RECORD(pEntry, tNAMEADDR, Linkage); IF_DBG(NBT_DEBUG_HASHTBL) KdPrint (("netbt!DestroyHashTable: WARNING! Freeing Name: <%16.16s:%x>\n", pNameAddr->Name, pNameAddr->Name[15])); /* * Notify deferencer not to do RemoveListEntry again becaseu we already do it above. */ if (pNameAddr->Verify == REMOTE_NAME && (pNameAddr->NameTypeState & PRELOADED)) { ASSERT(pNameAddr->RefCount == 2); NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_PRELOADED, FALSE); } ASSERT(pNameAddr->RefCount == 1); pNameAddr->Linkage.Flink = pNameAddr->Linkage.Blink = NULL; NBT_DEREFERENCE_NAMEADDR(pNameAddr,((pNameAddr->Verify==LOCAL_NAME)?REF_NAME_LOCAL:REF_NAME_REMOTE),FALSE); } } CTEMemFree(pHashTable); } //---------------------------------------------------------------------------- VOID DestroyHashTables( ) /*++ Routine Description: This routine destroys a hash table and frees the entries in NumBuckets It Must be called with the NbtConfig lock held! Arguments: Return Value: The function value is the status of the operation. --*/ { CTEPagedCode(); IF_DBG(NBT_DEBUG_HASHTBL) KdPrint (("netbt!DestroyHashTables: destroying remote hash table ...")); DestroyHashTable(NbtConfig.pRemoteHashTbl); NbtConfig.pRemoteHashTbl = NULL; IF_DBG(NBT_DEBUG_HASHTBL) KdPrint (("\nnetbt!DestroyHashTables: destroying local hash table ...")); DestroyHashTable(NbtConfig.pLocalHashTbl); NbtConfig.pLocalHashTbl = NULL; IF_DBG(NBT_DEBUG_HASHTBL) KdPrint (("\n")); } #endif // _PNP_POWER_ //---------------------------------------------------------------------------- NTSTATUS NbtUpdateRemoteName( IN tDEVICECONTEXT *pDeviceContext, IN tNAMEADDR *pNameAddrNew, IN tNAMEADDR *pNameAddrDiscard, IN USHORT NameAddFlags ) { tIPADDRESS IpAddress; tIPADDRESS *pLmhSvcGroupList = NULL; tIPADDRESS *pOrigIpAddrs = NULL; ULONG AdapterIndex = 0; // by default ULONG i; ASSERT (pNameAddrNew); // // See if we need to grow the IP addrs cache for the cached name // if (pNameAddrNew->RemoteCacheLen < NbtConfig.RemoteCacheLen) { tADDRESS_ENTRY *pRemoteCache; pRemoteCache = (tADDRESS_ENTRY *)NbtAllocMem(NbtConfig.RemoteCacheLen*sizeof(tADDRESS_ENTRY),NBT_TAG2('02')); if (pRemoteCache) { CTEZeroMemory(pRemoteCache, NbtConfig.RemoteCacheLen*sizeof(tADDRESS_ENTRY)); /* * Copy data from and free the previous cache (if any) */ if (pNameAddrNew->pRemoteIpAddrs) { CTEMemCopy (pRemoteCache, pNameAddrNew->pRemoteIpAddrs, sizeof(tADDRESS_ENTRY) * pNameAddrNew->RemoteCacheLen); CTEFreeMem (pNameAddrNew->pRemoteIpAddrs) } pNameAddrNew->pRemoteIpAddrs = pRemoteCache; pNameAddrNew->RemoteCacheLen = NbtConfig.RemoteCacheLen; } else { KdPrint(("Nbt.NbtUpdateRemoteName: FAILed to expand Cache entry!\n")); } } // // If the new entry being added replaces an entry which was // either pre-loaded or set by a client, and the new entry itself // does not have that flag set, then ignore this update. // ASSERT (NAME_RESOLVED_BY_DNS > NAME_RESOLVED_BY_LMH_P); // For the check below to succeed! if (((pNameAddrNew->NameAddFlags & NAME_RESOLVED_BY_CLIENT) && !(NameAddFlags & NAME_RESOLVED_BY_CLIENT)) || ((pNameAddrNew->NameAddFlags & NAME_RESOLVED_BY_LMH_P) > (NameAddFlags & (NAME_RESOLVED_BY_LMH_P | NAME_RESOLVED_BY_DNS)))) { return (STATUS_UNSUCCESSFUL); } if (pNameAddrDiscard) { IpAddress = pNameAddrDiscard->IpAddress; pLmhSvcGroupList = pNameAddrDiscard->pLmhSvcGroupList; pNameAddrDiscard->pLmhSvcGroupList = NULL; pNameAddrNew->TimeOutCount = NbtConfig.RemoteTimeoutCount; // Reset it since we are updating it! pOrigIpAddrs = pNameAddrDiscard->pIpAddrsList; } else { IpAddress = pNameAddrNew->IpAddress; pLmhSvcGroupList = pNameAddrNew->pLmhSvcGroupList; pNameAddrNew->pLmhSvcGroupList = NULL; } if ((NameAddFlags & (NAME_RESOLVED_BY_DNS | NAME_RESOLVED_BY_CLIENT | NAME_RESOLVED_BY_IP)) && (pNameAddrNew->RemoteCacheLen)) { ASSERT (!pLmhSvcGroupList); pNameAddrNew->pRemoteIpAddrs[0].IpAddress = IpAddress; if ((pNameAddrNew->NameAddFlags & NAME_RESOLVED_BY_LMH_P) && (NameAddFlags & NAME_RESOLVED_BY_DNS)) { // // If the name was resolved by DNS, then don't overwrite the // name entry if it was pre-loaded below // pNameAddrNew->NameAddFlags |= NameAddFlags; return (STATUS_SUCCESS); } } if ((pDeviceContext) && (!IsDeviceNetbiosless(pDeviceContext)) && (pDeviceContext->AdapterNumber < pNameAddrNew->RemoteCacheLen)) { AdapterIndex = pDeviceContext->AdapterNumber; pNameAddrNew->AdapterMask |= pDeviceContext->AdapterMask; if (IpAddress) { pNameAddrNew->IpAddress = IpAddress; // in case we are copying from pNameAddrDiscard pNameAddrNew->pRemoteIpAddrs[AdapterIndex].IpAddress = IpAddress; // new addr } // // Now see if we need to update the Original IP addresses list! // if (pOrigIpAddrs) { // pOrigIpAddrs could only have been set earlier if it was obtained from pNameAddrDiscard! pNameAddrDiscard->pIpAddrsList = NULL; } else if (pOrigIpAddrs = pNameAddrNew->pIpAddrsList) { pNameAddrNew->pIpAddrsList = NULL; } if (pOrigIpAddrs) { if (pNameAddrNew->pRemoteIpAddrs[AdapterIndex].pOrigIpAddrs) { CTEFreeMem (pNameAddrNew->pRemoteIpAddrs[AdapterIndex].pOrigIpAddrs); } pNameAddrNew->pRemoteIpAddrs[AdapterIndex].pOrigIpAddrs = pOrigIpAddrs; } } if (pLmhSvcGroupList) { ASSERT(NameAddFlags == (NAME_RESOLVED_BY_LMH_P|NAME_ADD_INET_GROUP)); if (pNameAddrNew->pLmhSvcGroupList) { CTEFreeMem (pNameAddrNew->pLmhSvcGroupList); } pNameAddrNew->pLmhSvcGroupList = pLmhSvcGroupList; } pNameAddrNew->NameAddFlags |= NameAddFlags; return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS LockAndAddToHashTable( IN tHASHTABLE *pHashTable, IN PCHAR pName, IN PCHAR pScope, IN tIPADDRESS IpAddress, IN enum eNbtAddrType NameType, IN tNAMEADDR *pNameAddr, OUT tNAMEADDR **ppNameAddress, IN tDEVICECONTEXT *pDeviceContext, IN USHORT NameAddFlags ) { NTSTATUS status; CTELockHandle OldIrq; CTESpinLock (&NbtConfig.JointLock, OldIrq); status = AddToHashTable(pHashTable, pName, pScope, IpAddress, NameType, pNameAddr, ppNameAddress, pDeviceContext, NameAddFlags); CTESpinFree (&NbtConfig.JointLock, OldIrq); return (status); } //---------------------------------------------------------------------------- NTSTATUS AddToHashTable( IN tHASHTABLE *pHashTable, IN PCHAR pName, IN PCHAR pScope, IN tIPADDRESS IpAddress, IN enum eNbtAddrType NameType, IN tNAMEADDR *pNameAddr, OUT tNAMEADDR **ppNameAddress, IN tDEVICECONTEXT *pDeviceContext, IN USHORT NameAddFlags ) /*++ Routine Description: This routine adds a name to IPaddress to the hash table Called with the spin lock HELD. Arguments: Return Value: The function value is the status of the operation. --*/ { tNAMEADDR *pNameAddress; tNAMEADDR *pScopeAddr; NTSTATUS status; ULONG iIndex; CTELockHandle OldIrq; ULONG i, OldRemoteCacheLen; tNAMEADDR *pNameAddrFound; tADDRESS_ENTRY *pRemoteCache = NULL; BOOLEAN fNameIsAlreadyInCache; tIPADDRESS OldIpAddress; if (pNameAddr) { ASSERT ((pNameAddr->Verify == LOCAL_NAME) || (pNameAddr->Verify == REMOTE_NAME)); } fNameIsAlreadyInCache = (STATUS_SUCCESS == FindInHashTable(pHashTable,pName,pScope,&pNameAddrFound)); if ((fNameIsAlreadyInCache) && (pNameAddrFound->Verify == REMOTE_NAME) && !(pNameAddrFound->NameTypeState & STATE_RELEASED)) { OldIpAddress = pNameAddrFound->IpAddress; pNameAddrFound->IpAddress = IpAddress; if (!(NameAddFlags & NAME_ADD_IF_NOT_FOUND_ONLY) && ((pNameAddr) || !(pNameAddrFound->NameAddFlags & NAME_ADD_INET_GROUP))) { // // We have a valid existing name, so just update it! // status = NbtUpdateRemoteName(pDeviceContext, pNameAddrFound, pNameAddr, NameAddFlags); if (!NT_SUCCESS (status)) { // // We Failed most problably because we were not allowed to // over-write or modify the current entry for some reason. // So, reset the old IpAddress // pNameAddrFound->IpAddress = OldIpAddress; } } if (pNameAddr) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } else { ASSERT (!(NameAddFlags & NAME_ADD_INET_GROUP)); } if (ppNameAddress) { *ppNameAddress = pNameAddrFound; } // found it in the table so we're done - return pending to // differentiate from the name added case. Pending passes the // NT_SUCCESS() test as well as Success does. // return (STATUS_PENDING); } // first hash the name to an index // take the lower nibble of the first 2 characters.. mod table size iIndex = ((pName[0] & 0x0F) << 4) + (pName[1] & 0x0F); iIndex = iIndex % pHashTable->lNumBuckets; CTESpinLock(&NbtConfig,OldIrq); if (!pNameAddr) { // // Allocate memory for another hash table entry // pNameAddress = (tNAMEADDR *)NbtAllocMem(sizeof(tNAMEADDR),NBT_TAG('0')); if ((pNameAddress) && (pHashTable->LocalRemote == NBT_REMOTE) && (NbtConfig.RemoteCacheLen) && (!(pRemoteCache = (tADDRESS_ENTRY *) NbtAllocMem(NbtConfig.RemoteCacheLen*sizeof(tADDRESS_ENTRY),NBT_TAG2('03'))))) { CTEMemFree (pNameAddress); pNameAddress = NULL; } if (!pNameAddress) { CTESpinFree(&NbtConfig,OldIrq); KdPrint (("AddToHashTable: ERROR - INSUFFICIENT_RESOURCES\n")); return(STATUS_INSUFFICIENT_RESOURCES); } CTEZeroMemory(pNameAddress,sizeof(tNAMEADDR)); pNameAddress->IpAddress = IpAddress; pNameAddress->NameTypeState = (NameType == NBT_UNIQUE) ? NAMETYPE_UNIQUE : NAMETYPE_GROUP; pNameAddress->NameTypeState |= STATE_RESOLVED; CTEMemCopy (pNameAddress->Name, pName, (ULONG)NETBIOS_NAME_SIZE); // fill in the name if ((pHashTable->LocalRemote == NBT_LOCAL) || (pHashTable->LocalRemote == NBT_REMOTE_ALLOC_MEM)) { pNameAddress->Verify = LOCAL_NAME; NBT_REFERENCE_NAMEADDR (pNameAddress, REF_NAME_LOCAL); } else { ASSERT (!(NameAddFlags & NAME_ADD_INET_GROUP)); pNameAddress->Verify = REMOTE_NAME; CTEZeroMemory(pRemoteCache, NbtConfig.RemoteCacheLen*sizeof(tADDRESS_ENTRY)); pNameAddress->pRemoteIpAddrs = pRemoteCache; pNameAddress->RemoteCacheLen = NbtConfig.RemoteCacheLen; NBT_REFERENCE_NAMEADDR (pNameAddress, REF_NAME_REMOTE); NbtUpdateRemoteName(pDeviceContext, pNameAddress, NULL, NameAddFlags); } } else { // // See if we need to grow the IP addrs cache for remote names // ASSERT (!pNameAddr->pRemoteIpAddrs); if (pNameAddr->Verify == REMOTE_NAME) { NbtUpdateRemoteName(pDeviceContext, pNameAddr, NULL, NameAddFlags); } pNameAddress = pNameAddr; } pNameAddress->pTimer = NULL; pNameAddress->TimeOutCount = NbtConfig.RemoteTimeoutCount; // put on the head of the list in case the same name is in the table // twice (where the second one is waiting for its reference count to // go to zero, and will ultimately be removed, we want to find the new // name on any query of the table // InsertHeadList(&pHashTable->Bucket[iIndex],&pNameAddress->Linkage); if (pHashTable->LocalRemote == NBT_REMOTE) { NbtConfig.NumNameCached++; } // check for a scope too ( on non-local names only ) if ((pHashTable->LocalRemote != NBT_LOCAL) && (*pScope)) { // we must have a scope // see if the scope is already in the hash table and add if necessary // status = FindInHashTable(pHashTable, pScope, NULL, &pScopeAddr); if (!NT_SUCCESS(status)) { PUCHAR Scope; status = STATUS_SUCCESS; // *TODO* - this check will not adequately protect against // bad scopes passed in - i.e. we may run off into memory // and get an access violation...however converttoascii should // do the protection. For local names the scope should be // ok since NBT read it from the registry and checked it first // iIndex = 0; Scope = pScope; while (*Scope && (iIndex <= 255)) { iIndex++; Scope++; } // the whole length must be 255 or less, so the scope can only be // 255-16... if (iIndex > (255 - NETBIOS_NAME_SIZE)) { RemoveEntryList(&pNameAddress->Linkage); if (pNameAddress->pRemoteIpAddrs) { CTEMemFree ((PVOID)pNameAddress->pRemoteIpAddrs); } pNameAddress->Verify += 10; CTEMemFree(pNameAddress); CTESpinFree(&NbtConfig,OldIrq); return(STATUS_UNSUCCESSFUL); } iIndex++; // to copy the null // // the scope is a variable length string, so allocate enough // memory for the tNameAddr structure based on this string length // pScopeAddr = (tNAMEADDR *)NbtAllocMem((USHORT)(sizeof(tNAMEADDR) + iIndex - NETBIOS_NAME_SIZE),NBT_TAG('1')); if ( !pScopeAddr ) { RemoveEntryList(&pNameAddress->Linkage); if (pNameAddress->pRemoteIpAddrs) { CTEMemFree ((PVOID)pNameAddress->pRemoteIpAddrs); } pNameAddress->Verify += 10; CTEMemFree (pNameAddress); CTESpinFree(&NbtConfig,OldIrq); return STATUS_INSUFFICIENT_RESOURCES ; } CTEZeroMemory(pScopeAddr, (sizeof(tNAMEADDR)+iIndex-NETBIOS_NAME_SIZE)); // copy the scope to the name field including the Null at the end. // to the end of the name CTEMemCopy(pScopeAddr->Name,pScope,iIndex); // mark the entry as containing a scope name for cleanup later pScopeAddr->NameTypeState = NAMETYPE_SCOPE | STATE_RESOLVED; // keep the size of the name in the context value for easier name // comparisons in FindInHashTable pScopeAddr->Verify = REMOTE_NAME; NBT_REFERENCE_NAMEADDR (pScopeAddr, REF_NAME_REMOTE); NBT_REFERENCE_NAMEADDR (pScopeAddr, REF_NAME_SCOPE); pScopeAddr->ulScopeLength = iIndex; pNameAddress->pScope = pScopeAddr; // add the scope record to the hash table iIndex = ((pScopeAddr->Name[0] & 0x0F) << 4) + (pScopeAddr->Name[1] & 0x0F); iIndex = iIndex % pHashTable->lNumBuckets; InsertTailList(&pHashTable->Bucket[iIndex],&pScopeAddr->Linkage); if (pHashTable->LocalRemote == NBT_REMOTE) { NbtConfig.NumNameCached++; } } else { // the scope is already in the hash table so link the name to the // scope pNameAddress->pScope = pScopeAddr; } } else { pNameAddress->pScope = NULL; // no scope } // return the pointer to the hash table block if (ppNameAddress) { // return the pointer to the hash table block *ppNameAddress = pNameAddress; } CTESpinFree(&NbtConfig,OldIrq); return(STATUS_SUCCESS); } //---------------------------------------------------------------------------- tNAMEADDR * LockAndFindName( enum eNbtLocation Location, PCHAR pName, PCHAR pScope, ULONG *pRetNameType ) { tNAMEADDR *pNameAddr; CTELockHandle OldIrq; CTESpinLock (&NbtConfig.JointLock, OldIrq); pNameAddr = FindName(Location, pName, pScope, pRetNameType); CTESpinFree (&NbtConfig.JointLock, OldIrq); return (pNameAddr); } //---------------------------------------------------------------------------- tNAMEADDR * FindName( enum eNbtLocation Location, PCHAR pName, PCHAR pScope, ULONG *pRetNameType ) /*++ Routine Description: This routine searches the name table to find a name. The table searched depends on the Location passed in - whether it searches the local table or the network names table. The routine checks the state of the name and only returns names in the resolved state. Arguments: Return Value: The function value is the status of the operation. --*/ { tNAMEADDR *pNameAddr; NTSTATUS status; tHASHTABLE *pHashTbl; if (Location == NBT_LOCAL) { pHashTbl = pNbtGlobConfig->pLocalHashTbl; } else { pHashTbl = pNbtGlobConfig->pRemoteHashTbl; } status = FindInHashTable (pHashTbl, pName, pScope, &pNameAddr); if (!NT_SUCCESS(status)) { return(NULL); } *pRetNameType = pNameAddr->NameTypeState; // // Only return names that are in the resolved state // if (!(pNameAddr->NameTypeState & STATE_RESOLVED)) { pNameAddr = NULL; } return(pNameAddr); } //---------------------------------------------------------------------------- NTSTATUS FindInHashTable( tHASHTABLE *pHashTable, PCHAR pName, PCHAR pScope, tNAMEADDR **pNameAddress ) /*++ Routine Description: This routine checks if the name passed in matches a hash table entry. Called with the spin lock HELD. Arguments: Return Value: The function value is the status of the operation. --*/ { PLIST_ENTRY pEntry; PLIST_ENTRY pHead; tNAMEADDR *pNameAddr; int iIndex; ULONG uNameSize; PCHAR pScopeTbl; ULONG uInScopeLength = 0; // first hash the name to an index... // take the lower nibble of the first 2 characters.. mod table size // iIndex = ((pName[0] & 0x0F) << 4) + (pName[1] & 0x0F); iIndex = iIndex % pHashTable->lNumBuckets; if (pScope) { uInScopeLength = strlen (pScope); } // check if the name is already in the table // check each entry in the hash list...until the end of the list pHead = &pHashTable->Bucket[iIndex]; pEntry = pHead; while ((pEntry = pEntry->Flink) != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); if (pNameAddr->NameTypeState & NAMETYPE_SCOPE) { // scope names are treated differently since they are not // 16 bytes long... the length is stored separately. uNameSize = pNameAddr->ulScopeLength; } else { uNameSize = NETBIOS_NAME_SIZE; } // // strncmp will terminate at the first non-matching byte // or when it has matched uNameSize bytes // // Bug # 225328 -- have to use CTEMemEqu to compare all // uNameSize bytes (otherwise bad name can cause termination // due to NULL character) // if (!(pNameAddr->NameTypeState & STATE_RELEASED) && CTEMemEqu (pName, pNameAddr->Name, uNameSize)) { // now check if the scopes match. Scopes are stored differently // on the local and remote tables. // if (!pScope) { // passing in a Null scope means try to find the name without // worrying about a scope matching too... *pNameAddress = pNameAddr; return(STATUS_SUCCESS); } // // Check if Local Hash table // if (pHashTable == NbtConfig.pLocalHashTbl) { // In the local hash table case the scope is the same for all // names on the node and it is stored in the NbtConfig structure pScopeTbl = NbtConfig.pScope; uNameSize = NbtConfig.ScopeLength; } // // This is a Remote Hash table lookup // else if (pNameAddr->pScope) { pScopeTbl = &pNameAddr->pScope->Name[0]; uNameSize = pNameAddr->pScope->ulScopeLength; } // // Remote Hash table entry with NULL scope // so if passed in scope is also Null, we have a match // else if (!uInScopeLength) { *pNameAddress = pNameAddr; return(STATUS_SUCCESS); } else { // // Hash table scope length is 0 != uInScopeLength // ==> No match! // continue; } // // strncmp will terminate at the first non-matching byte // or when it has matched uNameSize bytes // if (0 == strncmp (pScope, pScopeTbl, uNameSize)) { // the scopes match so return *pNameAddress = pNameAddr; return(STATUS_SUCCESS); } } // end of matching name found } return(STATUS_UNSUCCESSFUL); }