summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/isn/nb/cache.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/isn/nb/cache.c2746
1 files changed, 2746 insertions, 0 deletions
diff --git a/private/ntos/tdi/isn/nb/cache.c b/private/ntos/tdi/isn/nb/cache.c
new file mode 100644
index 000000000..cbd27ad67
--- /dev/null
+++ b/private/ntos/tdi/isn/nb/cache.c
@@ -0,0 +1,2746 @@
+/*++
+
+Copyright (c) 1989-1993 Microsoft Corporation
+
+Module Name:
+
+ cache.c
+
+Abstract:
+
+ This module contains the name cache routines for the Netbios
+ module of the ISN transport.
+
+Author:
+
+ Adam Barr (adamba) 20-December-1993
+
+Environment:
+
+ Kernel mode
+
+Revision History:
+
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+#ifdef RASAUTODIAL
+#include <acd.h>
+#include <acdapi.h>
+
+extern BOOLEAN fAcdLoadedG;
+extern ACD_DRIVER AcdDriverG;
+
+BOOLEAN
+NbiAttemptAutoDial(
+ IN PDEVICE pDevice,
+ IN PCONNECTION pConnection,
+ IN ULONG ulFlags,
+ IN ACD_CONNECT_CALLBACK pProc,
+ IN PREQUEST pRequest
+ );
+
+VOID
+NbiRetryTdiConnect(
+ IN BOOLEAN fSuccess,
+ IN PVOID *pArgs
+ );
+#endif // RASAUTODIAL
+
+//
+// BUGBUG: We should change to monitor add name packets better,
+// so if we get an add for a different place we attempt to determine
+// if it is real or bogus and update if possible.
+//
+
+
+NTSTATUS
+CacheFindName(
+ IN PDEVICE Device,
+ IN FIND_NAME_TYPE Type,
+ IN PUCHAR RemoteName OPTIONAL,
+ OUT PNETBIOS_CACHE * CacheName
+)
+
+/*++
+
+Routine Description:
+
+ This routine looks up a particular remote name in the
+ Netbios name cache. If it cannot find it, a find name
+ request is queued up.
+
+ THIS REQUEST IS CALLED WITH THE DEVICE LOCK HELD AND
+ RETURNS WITH IT HELD.
+
+Arguments:
+
+ Device - The netbios device.
+
+ Type - Defines the type. The effect this has is:
+ FindNameConnect - On connects we will ignore an existing
+ cache entry if it got no response before.
+ FindNameNetbiosFindName - For these we ignore an existing
+ cache entry if it is for a group name -- this is
+ because the find name wants the address of every
+ machine, not just the network list.
+ FindNameOther - Normal handling is done.
+
+ RemoteName - The name to be discovered -- will be NULL if it
+ is the broadcast address.
+
+ CacheName - Returns the cache entry that was discovered.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY p;
+ PSINGLE_LIST_ENTRY s;
+ PNETBIOS_CACHE FoundCacheName;
+ PNB_SEND_RESERVED Reserved;
+ PUCHAR RealRemoteName; // RemoteName or NetbiosBroadcastName
+
+ //
+ // First scan the netbios name cache to see if we know
+ // about this remote.
+ //
+
+ if (RemoteName) {
+ RealRemoteName = RemoteName;
+ } else {
+ RealRemoteName = NetbiosBroadcastName;
+ }
+
+ if ( FindInNetbiosCacheTable ( Device->NameCache,
+ RealRemoteName,
+ &FoundCacheName ) == STATUS_SUCCESS ) {
+
+ //
+ // If this is a netbios find name, we only can use unique
+ // names in the cache; for the group ones we need to requery
+ // because the cache only lists networks, not individual machines.
+ // For connect requests, if we find an empty cache entry we
+ // remove it and requery.
+ //
+
+ if ( FoundCacheName->Unique || (Type != FindNameNetbiosFindName) ) {
+
+ if (FoundCacheName->NetworksUsed > 0) {
+
+ *CacheName = FoundCacheName;
+ NB_DEBUG2 (CACHE, ("Found cache name <%.16s>\n", RemoteName ? RemoteName : "<broadcast>"));
+ return STATUS_SUCCESS;
+
+ } else {
+
+ if (Type != FindNameConnect) {
+
+ if (FoundCacheName->FailedOnDownWan) {
+ NB_DEBUG2 (CACHE, ("Found cache name, but down wan <%.16s>\n", RemoteName ? RemoteName : "<broadcast>"));
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ } else {
+ NB_DEBUG2 (CACHE, ("Found cache name, but no nets <%.16s>\n", RemoteName ? RemoteName : "<broadcast>"));
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ } else {
+
+ //
+ // This is a connect and the current cache entry
+ // has zero names; delete it.
+ //
+
+ RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName );
+ CTEAssert (FoundCacheName->ReferenceCount == 1);
+ if (--FoundCacheName->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Free unneeded empty cache entry %lx\n", FoundCacheName));
+ NbiFreeMemory(
+ FoundCacheName,
+ sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Free due to replacement");
+ }
+
+ }
+ }
+ }
+ }
+
+
+ //
+ // There was no suitable cache entry for this network, first see
+ // if there is one pending.
+ //
+
+ for (p = Device->WaitingFindNames.Flink;
+ p != &Device->WaitingFindNames;
+ p = p->Flink) {
+
+ Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
+
+ //
+ // For this purpose we ignore a packet if a route
+ // has been found and it was for a unique name. This
+ // is because the cache information has already been
+ // inserted for this name. Otherwise if the name has
+ // since been deleted from the cache, the request
+ // that is looking for this name will starve because
+ // FindNameTimeout will just destroy the packet.
+ //
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
+ continue;
+ }
+
+ if (RtlEqualMemory(
+ Reserved->u.SR_FN.NetbiosName,
+ RealRemoteName, 16)) {
+
+ NB_DEBUG2 (CACHE, ("Cache name already pending <%.16s>\n", RemoteName ? RemoteName : "<broadcast>"));
+
+ //
+ // There is already one pending. If it is for a group
+ // name and this is a netbios find name, we make sure
+ // the retry count is such that at least one more
+ // query will be sent, so the netbios find name
+ // buffer can be filled with the responses from this.
+ //
+
+ if ((Type == FindNameNetbiosFindName) &&
+ (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) &&
+ (Reserved->u.SR_FN.RetryCount == Device->BroadcastCount)) {
+
+ --Reserved->u.SR_FN.RetryCount;
+ }
+
+ return STATUS_PENDING;
+ }
+ }
+
+ s = NbiPopSendPacket(Device, TRUE);
+
+ if (s == NULL) {
+ NB_DEBUG (CACHE, ("Couldn't get packet to find <%.16s>\n", RemoteName ? RemoteName : "<broadcast>"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage);
+
+ //
+ // We have the packet, fill it in for this request.
+ //
+
+ CTEAssert (Reserved->SendInProgress == FALSE);
+ Reserved->SendInProgress = FALSE;
+ Reserved->Type = SEND_TYPE_FIND_NAME;
+ RtlCopyMemory (Reserved->u.SR_FN.NetbiosName, RealRemoteName, 16);
+ Reserved->u.SR_FN.StatusAndSentOnUpLine = FNStatusNoResponse; // SentOnUpLine is FALSE
+ Reserved->u.SR_FN.RetryCount = 0;
+ Reserved->u.SR_FN.NewCache = NULL;
+ Reserved->u.SR_FN.SendTime = Device->FindNameTime;
+#if !defined(_PNP_POWER)
+ Reserved->u.SR_FN.CurrentNicId = 1;
+
+ (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code
+ IPX_QUERY_MAX_TYPE_20_NIC_ID,
+ (USHORT)0,
+ &Reserved->u.SR_FN.MaximumNicId,
+ sizeof(USHORT),
+ NULL);
+
+ if (Reserved->u.SR_FN.MaximumNicId == 0) {
+ Reserved->u.SR_FN.MaximumNicId = 1; // code assumes at least one
+ }
+#endif !_PNP_POWER
+ NB_DEBUG2 (CACHE, ("Queued FIND_NAME %lx for <%.16s>\n",
+ Reserved, RemoteName ? RemoteName : "<broadcast>"));
+
+
+ InsertHeadList(
+ &Device->WaitingFindNames,
+ &Reserved->WaitLinkage);
+
+ ++Device->FindNamePacketCount;
+
+ if (!Device->FindNameTimerActive) {
+
+ Device->FindNameTimerActive = TRUE;
+ NbiReferenceDevice (Device, DREF_FN_TIMER);
+
+ CTEStartTimer(
+ &Device->FindNameTimer,
+ 1, // 1 ms, i.e. expire immediately
+ FindNameTimeout,
+ (PVOID)Device);
+ }
+
+ NbiReferenceDevice (Device, DREF_FIND_NAME);
+
+ return STATUS_PENDING;
+
+} /* CacheFindName */
+
+
+VOID
+FindNameTimeout(
+ CTEEvent * Event,
+ PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when the find name timer expires.
+ It is called every FIND_NAME_GRANULARITY milliseconds unless there
+ is nothing to do.
+
+Arguments:
+
+ Event - The event used to queue the timer.
+
+ Context - The context, which is the device pointer.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PDEVICE Device = (PDEVICE)Context;
+ PLIST_ENTRY p, q;
+ PNB_SEND_RESERVED Reserved;
+ PNDIS_PACKET Packet;
+ NB_CONNECTIONLESS UNALIGNED * Header;
+ PNETBIOS_CACHE FoundCacheName;
+ NDIS_STATUS NdisStatus;
+#if !defined(_PNP_POWER)
+ static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
+#endif !_PNP_POWER
+ NB_DEFINE_LOCK_HANDLE (LockHandle)
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+
+ ++Device->FindNameTime;
+
+ if (Device->FindNamePacketCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("FindNameTimeout exiting\n"));
+
+ Device->FindNameTimerActive = FALSE;
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ NbiDereferenceDevice (Device, DREF_FN_TIMER);
+
+ return;
+ }
+
+ //
+ // Check what is on the queue; this is set up as a
+ // loop but in fact it rarely does (under no
+ // circumstances can we send more than one packet
+ // each time this function executes).
+ //
+#if defined(_PNP_POWER)
+ while (TRUE) {
+
+ p = Device->WaitingFindNames.Flink;
+ if (p == &Device->WaitingFindNames) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ break;
+ }
+
+ Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
+
+ if (Reserved->SendInProgress) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ break;
+ }
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
+
+ //
+ // This was a find name for a unique name which got a
+ // response but was not freed at the time (because
+ // SendInProgress was still TRUE) so we free it now.
+ //
+
+ (VOID)RemoveHeadList (&Device->WaitingFindNames);
+ ExInterlockedPushEntrySList(
+ &Device->SendPacketList,
+ &Reserved->PoolLinkage,
+ &NbiGlobalPoolInterlock);
+ --Device->FindNamePacketCount;
+
+ //
+ // It is OK to do this with the lock held because
+ // it won't be the last one (we have the RIP_TIMER ref).
+ //
+
+ NbiDereferenceDevice (Device, DREF_FIND_NAME);
+ continue;
+ }
+
+ if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ break;
+ }
+
+ (VOID)RemoveHeadList (&Device->WaitingFindNames);
+
+
+ //
+ // Increment the counter and see if we have sent
+ // all the frames we need to (we will age out
+ // here if we got no response for a unique query,
+ // or if we are doing a global name or broadcast
+ // search). We also kill the query right now if
+ // we have not found anything but down wan lines
+ // to send it on.
+ //
+
+ if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) ||
+ ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) {
+
+#if DBG
+ if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) {
+ NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved));
+ } else {
+ NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved));
+ }
+#endif
+
+ //
+ // This packet is stale, clean it up and continue.
+ //
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) {
+
+ CTEAssert (Reserved->u.SR_FN.NewCache != NULL);
+
+ //
+ // If this was a group name and we have a new
+ // cache entry that we have been building for it,
+ // then insert that in the queue and use it
+ // to succeed any pending connects. Because
+ // netbios find name requests can cause cache
+ // requests for group names to be queued even
+ // if we already have on in the database, we
+ // first scan for old ones and remove them.
+ //
+
+ if ( FindInNetbiosCacheTable( Device->NameCache,
+ Reserved->u.SR_FN.NetbiosName,
+ &FoundCacheName ) == STATUS_SUCCESS ) {
+
+ NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName));
+
+ RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName );
+
+ if (--FoundCacheName->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName));
+ NbiFreeMemory(
+ FoundCacheName,
+ sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Free due to replacement");
+
+ }
+
+ }
+
+ Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp;
+
+ InsertInNetbiosCacheTable(
+ Device->NameCache,
+ Reserved->u.SR_FN.NewCache);
+
+ //
+ // Reference it for the moment since CacheHandlePending
+ // uses it after releasing the lock. CacheHandlePending
+ // will dereference it.
+ //
+
+ ++Reserved->u.SR_FN.NewCache->ReferenceCount;
+
+ //
+ // This call releases the locks
+ //
+
+ CacheHandlePending(
+ Device,
+ Reserved->u.SR_FN.NetbiosName,
+ NetbiosNameFound,
+ Reserved->u.SR_FN.NewCache
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+ } else {
+
+ CTEAssert (Reserved->u.SR_FN.NewCache == NULL);
+
+ //
+ // Allocate an empty cache entry to record the
+ // fact that we could not find this name, unless
+ // there is already an entry for this name.
+ //
+
+ if ( FindInNetbiosCacheTable( Device->NameCache,
+ Reserved->u.SR_FN.NetbiosName,
+ &FoundCacheName ) == STATUS_SUCCESS ) {
+
+
+ NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName));
+ } else {
+
+ PNETBIOS_CACHE EmptyCache;
+
+ //
+ // Nothing found.
+ //
+
+ EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry");
+ if (EmptyCache != NULL) {
+
+ RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE));
+
+ NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n",
+ EmptyCache, Reserved->u.SR_FN.NetbiosName));
+
+ RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16);
+ EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name
+ EmptyCache->ReferenceCount = 1;
+ EmptyCache->NetworksAllocated = 1;
+ EmptyCache->TimeStamp = Device->CacheTimeStamp;
+ EmptyCache->NetworksUsed = 0;
+ EmptyCache->FailedOnDownWan = (BOOLEAN)
+ !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved);
+
+ InsertInNetbiosCacheTable (
+ Device->NameCache,
+ EmptyCache);
+ }
+ }
+
+ //
+ // Fail all datagrams, etc. that were waiting for
+ // this route. This call releases the lock.
+ //
+
+ CacheHandlePending(
+ Device,
+ Reserved->u.SR_FN.NetbiosName,
+ NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ?
+ NetbiosNameNotFoundNormal :
+ NetbiosNameNotFoundWanDown,
+ NULL
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+ }
+
+ ExInterlockedPushEntrySList(
+ &Device->SendPacketList,
+ &Reserved->PoolLinkage,
+ &NbiGlobalPoolInterlock);
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+
+ --Device->FindNamePacketCount;
+ NbiDereferenceDevice (Device, DREF_FIND_NAME);
+ continue;
+ }
+
+
+
+
+ //
+ // Send the packet out again. We first set the time so
+ // it won't be sent again until the appropriate timeout.
+ //
+
+ Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout);
+
+ InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage);
+
+ CTEAssert (Reserved->Identifier == IDENTIFIER_NB);
+ CTEAssert (!Reserved->SendInProgress);
+ Reserved->SendInProgress = TRUE;
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+ //
+ // If this is the first retry, we need to initialize the packet
+ //
+ if ( Reserved->u.SR_FN.RetryCount == 1 ) {
+ //
+ // Fill in the IPX header -- the default header has the broadcast
+ // address on net 0 as the destination IPX address, which is
+ // what we want.
+ //
+
+ Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]);
+ RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER));
+ Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256;
+ Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256;
+
+ Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04);
+
+ //
+ // Now fill in the Netbios header.
+ //
+
+ RtlZeroMemory (Header->NameFrame.RoutingInfo, 32);
+ Header->NameFrame.ConnectionControlFlag = 0x00;
+ Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME;
+ Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME;
+ Header->NameFrame.NameTypeFlag = 0x00;
+
+ RtlCopyMemory(
+ Header->NameFrame.Name,
+ Reserved->u.SR_FN.NetbiosName,
+ 16);
+
+
+ }
+ //
+ // Now submit the packet to IPX.
+ //
+
+ Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]);
+
+ NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx\n", Reserved));
+
+ NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) +
+ sizeof(NB_NAME_FRAME));
+ if ((NdisStatus =
+ (*Device->Bind.SendHandler)(
+ &BroadcastTarget,
+ Packet,
+ sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME),
+ sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) {
+
+ NbiSendComplete(
+ Packet,
+ NdisStatus);
+
+ }
+
+
+ break;
+
+ }
+#else
+ while (TRUE) {
+
+ p = Device->WaitingFindNames.Flink;
+ if (p == &Device->WaitingFindNames) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ break;
+ }
+
+ Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
+
+ if (Reserved->SendInProgress) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ break;
+ }
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
+
+ //
+ // This was a find name for a unique name which got a
+ // response but was not freed at the time (because
+ // SendInProgress was still TRUE) so we free it now.
+ //
+
+ (VOID)RemoveHeadList (&Device->WaitingFindNames);
+ ExInterlockedPushEntrySList(
+ &Device->SendPacketList,
+ &Reserved->PoolLinkage,
+ &NbiGlobalPoolInterlock);
+ --Device->FindNamePacketCount;
+
+ //
+ // It is OK to do this with the lock held because
+ // it won't be the last one (we have the RIP_TIMER ref).
+ //
+
+ NbiDereferenceDevice (Device, DREF_FIND_NAME);
+ continue;
+ }
+
+ if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ break;
+ }
+
+ (VOID)RemoveHeadList (&Device->WaitingFindNames);
+
+ //
+ // Save this now, then change it if needed.
+ //
+
+ BroadcastTarget.NicId = Reserved->u.SR_FN.CurrentNicId;
+
+ if (Reserved->u.SR_FN.CurrentNicId == 1) {
+
+ //
+ // Increment the counter and see if we have sent
+ // all the frames we need to (we will age out
+ // here if we got no response for a unique query,
+ // or if we are doing a global name or broadcast
+ // search). We also kill the query right now if
+ // we have not found anything but down wan lines
+ // to send it on.
+ //
+
+ if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) ||
+ ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) {
+
+#if DBG
+ if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) {
+ NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved));
+ } else {
+ NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved));
+ }
+#endif
+
+ //
+ // This packet is stale, clean it up and continue.
+ //
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) {
+
+ CTEAssert (Reserved->u.SR_FN.NewCache != NULL);
+
+ //
+ // If this was a group name and we have a new
+ // cache entry that we have been building for it,
+ // then insert that in the queue and use it
+ // to succeed any pending connects. Because
+ // netbios find name requests can cause cache
+ // requests for group names to be queued even
+ // if we already have on in the database, we
+ // first scan for old ones and remove them.
+ //
+
+ if ( FindInNetbiosCacheTable( Device->NameCache,
+ Reserved->u.SR_FN.NetbiosName,
+ &FoundCacheName ) == STATUS_SUCCESS ) {
+
+ NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName));
+
+ RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName );
+
+ if (--FoundCacheName->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName));
+ NbiFreeMemory(
+ FoundCacheName,
+ sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Free due to replacement");
+
+ }
+ }
+
+ Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp;
+
+ InsertInNetbiosCacheTable(
+ Device->NameCache,
+ Reserved->u.SR_FN.NewCache);
+
+ //
+ // Reference it for the moment since CacheHandlePending
+ // uses it after releasing the lock. CacheHandlePending
+ // will dereference it.
+ //
+
+ ++Reserved->u.SR_FN.NewCache->ReferenceCount;
+
+ //
+ // This call releases the locks
+ //
+
+ CacheHandlePending(
+ Device,
+ Reserved->u.SR_FN.NetbiosName,
+ NetbiosNameFound,
+ Reserved->u.SR_FN.NewCache
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+ } else {
+
+ CTEAssert (Reserved->u.SR_FN.NewCache == NULL);
+
+ //
+ // Allocate an empty cache entry to record the
+ // fact that we could not find this name, unless
+ // there is already an entry for this name.
+ //
+
+ if ( FindInNetbiosCacheTable( Device->NameCache,
+ Reserved->u.SR_FN.NetbiosName,
+ &FoundCacheName ) == STATUS_SUCCESS ) {
+
+
+ NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName));
+
+ } else {
+
+ PNETBIOS_CACHE EmptyCache;
+
+ //
+ // Nothing found.
+ //
+
+ EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry");
+ if (EmptyCache != NULL) {
+
+ RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE));
+
+ NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n",
+ EmptyCache, Reserved->u.SR_FN.NetbiosName));
+
+ RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16);
+ EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name
+ EmptyCache->ReferenceCount = 1;
+ EmptyCache->NetworksAllocated = 1;
+ EmptyCache->TimeStamp = Device->CacheTimeStamp;
+ EmptyCache->NetworksUsed = 0;
+ EmptyCache->FailedOnDownWan = (BOOLEAN)
+ !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved);
+
+ InsertInNetbiosCacheTable (
+ Device->NameCache,
+ EmptyCache);
+ }
+ }
+
+ //
+ // Fail all datagrams, etc. that were waiting for
+ // this route. This call releases the lock.
+ //
+
+ CacheHandlePending(
+ Device,
+ Reserved->u.SR_FN.NetbiosName,
+ NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ?
+ NetbiosNameNotFoundNormal :
+ NetbiosNameNotFoundWanDown,
+ NULL
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+ }
+
+ ExInterlockedPushEntrySList(
+ &Device->SendPacketList,
+ &Reserved->PoolLinkage,
+ &NbiGlobalPoolInterlock);
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+ --Device->FindNamePacketCount;
+ NbiDereferenceDevice (Device, DREF_FIND_NAME);
+ continue;
+ }
+
+ }
+
+
+ //
+ // Increment the current NIC ID for next time.
+ //
+
+ if (Reserved->u.SR_FN.CurrentNicId >= Reserved->u.SR_FN.MaximumNicId) {
+ Reserved->u.SR_FN.CurrentNicId = 1;
+ } else {
+ ++Reserved->u.SR_FN.CurrentNicId;
+ }
+
+
+ //
+ // Send the packet out again. We first set the time so
+ // it won't be sent again until the appropriate timeout.
+ // If we are going to wrap around the maximum NIC ID
+ // after sending this packet we wait the full configured
+ // amount, otherwise we wait almost the minimum (but still
+ // insert ourselves at the back of the queue to be fair).
+ //
+
+ if (Reserved->u.SR_FN.CurrentNicId == 1) {
+ Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout);
+ } else {
+ Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + 2);
+ }
+ InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage);
+
+ CTEAssert (Reserved->Identifier == IDENTIFIER_NB);
+ CTEAssert (!Reserved->SendInProgress);
+ Reserved->SendInProgress = TRUE;
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+ //
+ // Fill in the IPX header -- the default header has the broadcast
+ // address on net 0 as the destination IPX address, which is
+ // what we want.
+ //
+
+ Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]);
+ RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER));
+ Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256;
+ Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256;
+
+ Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04);
+
+ //
+ // Now fill in the Netbios header.
+ //
+
+ RtlZeroMemory (Header->NameFrame.RoutingInfo, 32);
+ Header->NameFrame.ConnectionControlFlag = 0x00;
+ Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME;
+ Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME;
+ Header->NameFrame.NameTypeFlag = 0x00;
+
+ RtlCopyMemory(
+ Header->NameFrame.Name,
+ Reserved->u.SR_FN.NetbiosName,
+ 16);
+
+ //
+ // Now submit the packet to IPX.
+ //
+
+ Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]);
+
+ NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx to %d\n", Reserved, BroadcastTarget.NicId));
+
+ NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) +
+ sizeof(NB_NAME_FRAME));
+ if ((NdisStatus =
+ (*Device->Bind.SendHandler)(
+ &BroadcastTarget,
+ Packet,
+ sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME),
+ sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) {
+
+ NbiSendComplete(
+ Packet,
+ NdisStatus);
+
+ }
+
+ if (NdisStatus == STATUS_DEVICE_DOES_NOT_EXIST) {
+
+ //
+ // This send was done on a down wan line. To avoid
+ // extensive delays sending find names on systems
+ // with a lot of wan lines, we loop around immediately
+ // and do the next send. We put this packet back on
+ // the head of the list and change its SendTime so
+ // it will be resent immediately.
+ //
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+
+ NB_DEBUG2 (CACHE, ("FindNameTimeout resending %lx, wan send failed\n", Reserved));
+
+ RemoveEntryList (&Reserved->WaitLinkage);
+ InsertHeadList (&Device->WaitingFindNames, &Reserved->WaitLinkage);
+ Reserved->u.SR_FN.SendTime = Device->FindNameTime;
+
+ continue;
+
+ } else {
+
+ //
+ // We keep track of when it finds a net that isn't
+ // a down wan line so that we can tell when datagram
+ // sends should fail (otherwise we succeed them, so
+ // the browser won't think this is a down wan line).
+ //
+
+ NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE);
+ }
+
+
+ break;
+
+ }
+#endif _PNP_POWER
+
+ //
+ // Since we did something this time, we restart the timer.
+ //
+
+ CTEStartTimer(
+ &Device->FindNameTimer,
+ FIND_NAME_GRANULARITY,
+ FindNameTimeout,
+ (PVOID)Device);
+
+} /* FindNameTimeout */
+
+
+VOID
+CacheHandlePending(
+ IN PDEVICE Device,
+ IN PUCHAR RemoteName,
+ IN NETBIOS_NAME_RESULT Result,
+ IN PNETBIOS_CACHE CacheName OPTIONAL
+ IN NB_LOCK_HANDLE_PARAM(LockHandle)
+ )
+
+/*++
+
+Routine Description:
+
+ This routine cleans up pending datagrams and connects
+ that were waiting for a route to be discovered to a
+ given Netbios NAME. THIS ROUTINE IS CALLED WITH
+ DEVICE->LOCK ACQUIRED AND RETURNS WITH IT RELEASED.
+
+Arguments:
+
+ Device - The device.
+
+ RemoteName - The netbios name that was being searched for.
+
+ Result - Indicates if the name was found, or not found due
+ to no response or wan lines being down.
+
+ CacheName - If Result is NetbiosNameFound, the cache entry for this name.
+ This entry has been referenced and this routine will deref it.
+
+ LockHandle - The handle used to acquire the lock.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ LIST_ENTRY DatagramList;
+ LIST_ENTRY ConnectList;
+ LIST_ENTRY AdapterStatusList;
+ LIST_ENTRY NetbiosFindNameList;
+ PNB_SEND_RESERVED Reserved;
+ PNDIS_PACKET Packet;
+ PLIST_ENTRY p;
+ PREQUEST ConnectRequest, DatagramRequest, AdapterStatusRequest, NetbiosFindNameRequest;
+ PCONNECTION Connection;
+ PADDRESS_FILE AddressFile;
+ TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress;
+ CTELockHandle CancelLH;
+ NB_DEFINE_LOCK_HANDLE (LockHandle1)
+
+
+ InitializeListHead (&DatagramList);
+ InitializeListHead (&ConnectList);
+ InitializeListHead (&AdapterStatusList);
+ InitializeListHead (&NetbiosFindNameList);
+
+ //
+ // Put all connect requests on ConnectList. They will
+ // be continued or failed later.
+ //
+
+ p = Device->WaitingConnects.Flink;
+
+ while (p != &Device->WaitingConnects) {
+
+ ConnectRequest = LIST_ENTRY_TO_REQUEST(p);
+ Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest);
+ p = p->Flink;
+
+ if (RtlEqualMemory (Connection->RemoteName, RemoteName, 16)) {
+
+ RemoveEntryList (REQUEST_LINKAGE(ConnectRequest));
+ InsertTailList (&ConnectList, REQUEST_LINKAGE(ConnectRequest));
+
+ Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK;
+ }
+
+ }
+
+
+ //
+ // Put all the datagrams on Datagram list. They will be
+ // sent or failed later.
+ //
+
+ p = Device->WaitingDatagrams.Flink;
+
+ while (p != &Device->WaitingDatagrams) {
+
+ Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
+
+ p = p->Flink;
+
+ //
+ // Check differently based on whether we were looking for
+ // the broadcast address or not.
+ //
+
+ if (Reserved->u.SR_DG.RemoteName == (PVOID)-1) {
+ if (!RtlEqualMemory (RemoteName, NetbiosBroadcastName, 16)) {
+ continue;
+ }
+ } else {
+
+ if (!RtlEqualMemory (RemoteName, Reserved->u.SR_DG.RemoteName->NetbiosName, 16)) {
+ continue;
+ }
+ }
+
+ RemoveEntryList (&Reserved->WaitLinkage);
+ InsertTailList (&DatagramList, &Reserved->WaitLinkage);
+
+ //
+ // Reference this here with the lock held.
+ //
+
+ if (Result == NetbiosNameFound) {
+ ++CacheName->ReferenceCount;
+ }
+
+ }
+
+
+ //
+ // Put all the adapter status requests on AdapterStatus
+ // list. They will be sent or failed later.
+ //
+
+ p = Device->WaitingAdapterStatus.Flink;
+
+ while (p != &Device->WaitingAdapterStatus) {
+
+ AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p);
+
+ p = p->Flink;
+
+ RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(AdapterStatusRequest);
+
+ if (!RtlEqualMemory(
+ RemoteName,
+ RemoteAddress->NetbiosName,
+ 16)) {
+ continue;
+ }
+
+ RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest));
+ InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest));
+
+ //
+ // Reference this here with the lock held.
+ //
+
+ if (Result == NetbiosNameFound) {
+ ++CacheName->ReferenceCount;
+ }
+
+ }
+
+
+ //
+ // Put all the netbios find name requests on NetbiosFindName
+ // list. They will be completed later.
+ //
+
+ p = Device->WaitingNetbiosFindName.Flink;
+
+ while (p != &Device->WaitingNetbiosFindName) {
+
+ NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p);
+
+ p = p->Flink;
+
+ RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest);
+
+ if (!RtlEqualMemory(
+ RemoteName,
+ RemoteAddress->NetbiosName,
+ 16)) {
+ continue;
+ }
+
+ RemoveEntryList (REQUEST_LINKAGE(NetbiosFindNameRequest));
+ InsertTailList (&NetbiosFindNameList, REQUEST_LINKAGE(NetbiosFindNameRequest));
+
+ }
+
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+
+ //
+ // Now that the lock is free, process all the packets on
+ // the various lists.
+ //
+
+ for (p = ConnectList.Flink; p != &ConnectList; ) {
+
+ ConnectRequest = LIST_ENTRY_TO_REQUEST(p);
+ p = p->Flink;
+
+ Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest);
+
+ NB_GET_CANCEL_LOCK( &CancelLH );
+ NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1);
+
+ if ((Connection->State == CONNECTION_STATE_CONNECTING) &&
+ (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) {
+
+ if (Result == NetbiosNameFound) {
+
+ NB_DEBUG2 (CONNECTION, ("Found queued connect %lx on %lx\n", ConnectRequest, Connection));
+
+ //
+ // Continue with the connection sequence.
+ //
+
+ Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE;
+
+
+ if (!ConnectRequest->Cancel) {
+
+ IoSetCancelRoutine (ConnectRequest, NbiCancelConnectWaitResponse);
+
+ NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1 );
+ NB_FREE_CANCEL_LOCK ( CancelLH );
+
+ Connection->LocalTarget = CacheName->Networks[0].LocalTarget;
+ RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12);
+ NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE);
+
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
+
+ *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress;
+ RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6);
+ Connection->FindRouteRequest.Identifier = IDENTIFIER_NB;
+ Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED;
+
+ //
+ // When this completes, we will send the session init.
+ // We don't call it if the client is for network 0,
+ // instead just fake as if no route could be found
+ // and we will use the local target we got here.
+ //
+
+ if (CacheName->FirstResponse.NetworkAddress != 0) {
+
+ (*Device->Bind.FindRouteHandler)(
+ &Connection->FindRouteRequest);
+
+ } else {
+
+ NbiFindRouteComplete(
+ &Connection->FindRouteRequest,
+ FALSE);
+
+ }
+
+ } else {
+
+ NB_DEBUG2 (CONNECTION, ("Cancelling connect %lx on %lx\n", ConnectRequest, Connection));
+
+ goto AbortConnect;
+
+ //
+ // Jumps down into the else below.
+ //
+
+ }
+
+ } else {
+ BOOLEAN bAutodialAttempt = FALSE;
+
+ NB_DEBUG2 (CONNECTION, ("Timing out connect %lx on %lx\n", ConnectRequest, Connection));
+AbortConnect:
+
+ ASSERT (Connection->ConnectRequest == ConnectRequest);
+
+#ifdef RASAUTODIAL
+ if (fAcdLoadedG) {
+ CTELockHandle adirql;
+ BOOLEAN fEnabled;
+
+ //
+ // See if the automatic connection driver knows
+ // about this address before we search the
+ // network. If it does, we return STATUS_PENDING,
+ // and we will come back here via NbfRetryTdiConnect().
+ //
+ CTEGetLock(&AcdDriverG.SpinLock, &adirql);
+ fEnabled = AcdDriverG.fEnabled;
+ CTEFreeLock(&AcdDriverG.SpinLock, adirql);
+ if (fEnabled && NbiAttemptAutoDial(
+ Device,
+ Connection,
+ 0,
+ NbiRetryTdiConnect,
+ ConnectRequest))
+ {
+ NB_SYNC_FREE_LOCK(&Connection->Lock, LockHandle1);
+ NB_FREE_CANCEL_LOCK(CancelLH);
+
+ bAutodialAttempt = TRUE;
+ }
+ }
+#endif // RASAUTODIAL
+
+ if (!bAutodialAttempt) {
+ Connection->ConnectRequest = NULL;
+ Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
+
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
+
+ IoSetCancelRoutine( ConnectRequest, (PDRIVER_CANCEL)NULL );
+ NB_FREE_CANCEL_LOCK( CancelLH );
+
+ REQUEST_STATUS(ConnectRequest) = STATUS_BAD_NETWORK_PATH;
+
+ NbiCompleteRequest(ConnectRequest);
+ NbiFreeRequest (Device, ConnectRequest);
+ }
+
+ NbiDereferenceConnection (Connection, CREF_CONNECT);
+
+ }
+
+ } else {
+
+ // BUGBUG What happens to the IRP? Who completes it?
+
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
+ NB_FREE_CANCEL_LOCK( CancelLH );
+
+ }
+
+ NbiDereferenceConnection (Connection, CREF_WAIT_CACHE);
+
+ }
+
+
+ for (p = DatagramList.Flink; p != &DatagramList; ) {
+
+ Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
+ p = p->Flink;
+
+ if (Result == NetbiosNameFound) {
+
+ NB_DEBUG2 (DATAGRAM, ("Found queued datagram %lx on %lx\n", Reserved->u.SR_DG.DatagramRequest, Reserved->u.SR_DG.AddressFile));
+
+ Reserved->u.SR_DG.Cache = CacheName;
+ Reserved->u.SR_DG.CurrentNetwork = 0;
+
+ //
+ // CacheName was referenced above.
+ //
+
+ Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]);
+ if ( REQUEST_NDIS_BUFFER( Reserved->u.SR_DG.DatagramRequest )) {
+ NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Reserved->u.SR_DG.DatagramRequest));
+ }
+
+ NbiTransmitDatagram (Reserved);
+
+ } else {
+
+ //
+ // BETABUGBUG: Should we send it once as a broadcast
+ // on net 0, just in case??
+ //
+
+ AddressFile = Reserved->u.SR_DG.AddressFile;
+ DatagramRequest = Reserved->u.SR_DG.DatagramRequest;
+
+ NB_DEBUG2 (DATAGRAM, ("Timing out datagram %lx on %lx\n", DatagramRequest, AddressFile));
+
+ //
+ // If the failure was due to a down wan line indicate
+ // that, otherwise return success (so the browser won't
+ // confuse this with a down wan line).
+ //
+
+ if (Result == NetbiosNameNotFoundWanDown) {
+ REQUEST_STATUS(DatagramRequest) = STATUS_DEVICE_DOES_NOT_EXIST;
+ REQUEST_INFORMATION(DatagramRequest) = 0;
+ } else {
+ REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS;
+ }
+
+ NbiCompleteRequest(DatagramRequest);
+ NbiFreeRequest (Device, DatagramRequest);
+
+ NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM);
+
+ ExInterlockedPushEntrySList(
+ &Device->SendPacketList,
+ &Reserved->PoolLinkage,
+ &NbiGlobalPoolInterlock);
+ }
+
+ }
+
+
+ for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) {
+
+ AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p);
+ p = p->Flink;
+
+ if (Result == NetbiosNameFound) {
+
+ NB_DEBUG2 (QUERY, ("Found queued AdapterStatus %lx\n", AdapterStatusRequest));
+
+ //
+ // Continue with the AdapterStatus sequence. We put
+ // it in ActiveAdapterStatus, it will either get
+ // completed when a response is received or timed
+ // out by the long timeout.
+ //
+
+ REQUEST_STATUS(AdapterStatusRequest) = (NTSTATUS)CacheName;
+
+ //
+ // CacheName was referenced above.
+ //
+
+ REQUEST_INFORMATION (AdapterStatusRequest) = 0;
+
+ NB_INSERT_TAIL_LIST(
+ &Device->ActiveAdapterStatus,
+ REQUEST_LINKAGE (AdapterStatusRequest),
+ &Device->Lock);
+
+ NbiSendStatusQuery (AdapterStatusRequest);
+
+ } else {
+
+ NB_DEBUG2 (QUERY, ("Timing out AdapterStatus %lx\n", AdapterStatusRequest));
+
+ REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT;
+
+ NbiCompleteRequest(AdapterStatusRequest);
+ NbiFreeRequest (Device, AdapterStatusRequest);
+
+ NbiDereferenceDevice (Device, DREF_STATUS_QUERY);
+
+ }
+
+ }
+
+
+ for (p = NetbiosFindNameList.Flink; p != &NetbiosFindNameList; ) {
+
+ NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p);
+ p = p->Flink;
+
+ //
+ // In fact there is not much difference between success or
+ // failure, since in the successful case the information
+ // will already have been written to the buffer. Just
+ // complete the request with the appropriate status,
+ // which will already be stored in the request.
+ //
+
+ if (Result == NetbiosNameFound) {
+
+ if (CacheName->Unique) {
+
+ NB_DEBUG2 (QUERY, ("Found queued unique NetbiosFindName %lx\n", NetbiosFindNameRequest));
+
+ } else {
+
+ NB_DEBUG2 (QUERY, ("Found queued group NetbiosFindName %lx\n", NetbiosFindNameRequest));
+
+ }
+
+ } else {
+
+ CTEAssert (REQUEST_STATUS(NetbiosFindNameRequest) == STATUS_IO_TIMEOUT);
+ NB_DEBUG2 (QUERY, ("Timed out NetbiosFindName %lx\n", NetbiosFindNameRequest));
+
+ }
+
+ //
+ // This sets REQUEST_INFORMATION(Request) to the correct value.
+ //
+
+ NbiSetNetbiosFindNameInformation (NetbiosFindNameRequest);
+
+ NbiCompleteRequest(NetbiosFindNameRequest);
+ NbiFreeRequest (Device, NetbiosFindNameRequest);
+
+ NbiDereferenceDevice (Device, DREF_NB_FIND_NAME);
+
+ }
+
+
+ //
+ // We referenced this temporarily so we could use it in here,
+ // deref and check if we need to delete it.
+ //
+
+ if (Result == NetbiosNameFound) {
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1);
+
+ if (--CacheName->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Free newly allocated cache entry %lx\n", CacheName));
+ NbiFreeMemory(
+ CacheName,
+ sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Free in CacheHandlePending");
+
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1);
+
+ }
+
+} /* CacheHandlePending */
+
+
+VOID
+NbiProcessNameRecognized(
+ IN PIPX_LOCAL_TARGET RemoteAddress,
+ IN ULONG MacOptions,
+ IN PUCHAR PacketBuffer,
+ IN UINT PacketSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine handles NB_CMD_NAME_RECOGNIZED frames.
+
+Arguments:
+
+ RemoteAddress - The local target this packet was received from.
+
+ MacOptions - The MAC options for the underlying NDIS binding.
+
+ LookaheadBuffer - The packet data, starting at the IPX
+ header.
+
+ PacketSize - The total length of the packet, starting at the
+ IPX header.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY p;
+ PDEVICE Device = NbiDevice;
+ PNETBIOS_CACHE NameCache;
+ PREQUEST NetbiosFindNameRequest;
+ PNB_SEND_RESERVED Reserved;
+ TDI_ADDRESS_NETBIOS UNALIGNED * RemoteNetbiosAddress;
+ NB_CONNECTIONLESS UNALIGNED * Connectionless =
+ (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer;
+ NB_DEFINE_LOCK_HANDLE(LockHandle)
+
+
+#if 0
+ //
+ // BETABUGBUG: We should handle responses from network 0
+ // differently -- if they are for a group name, we should
+ // keep them around but only until we get a non-zero
+ // response from the same card.
+ //
+
+ if (*(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork) == 0) {
+ return;
+ }
+#endif
+
+
+ //
+ // We need to scan our queue of pending find name packets
+ // to see if someone is waiting for this name.
+ //
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+
+ for (p = Device->WaitingFindNames.Flink;
+ p != &Device->WaitingFindNames;
+ p = p->Flink) {
+
+ Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
+
+ //
+ // Find names which have already found unique names are
+ // "dead", waiting for FindNameTimeout to remove them,
+ // and should be ignored when scanning the list.
+ //
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
+
+ continue;
+ }
+
+ if (RtlEqualMemory (Reserved->u.SR_FN.NetbiosName, Connectionless->NameFrame.Name, 16)) {
+ break;
+ }
+ }
+
+ if (p == &Device->WaitingFindNames) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ return;
+ }
+
+
+ //
+ // Scan for any netbios find name requests on the queue, and
+ // inform them about this remote. We need to do this on every
+ // response because group names need every computer recorded,
+ // but the normal cache only includes one entry per network.
+ //
+
+ for (p = Device->WaitingNetbiosFindName.Flink;
+ p != &Device->WaitingNetbiosFindName;
+ p = p->Flink) {
+
+ NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p);
+
+ RemoteNetbiosAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest);
+
+ if (!RtlEqualMemory(
+ Connectionless->NameFrame.Name,
+ RemoteNetbiosAddress->NetbiosName,
+ 16)) {
+ continue;
+ }
+
+ //
+ // This will update the request status if needed.
+ //
+
+ NbiUpdateNetbiosFindName(
+ NetbiosFindNameRequest,
+#if defined(_PNP_POWER)
+ &RemoteAddress->NicHandle,
+#else
+ RemoteAddress->NicId,
+#endif _PNP_POWER
+ (TDI_ADDRESS_IPX UNALIGNED *)Connectionless->IpxHeader.SourceNetwork,
+ (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0));
+
+ }
+
+
+ //
+ // See what is up with this pending find name packet.
+ //
+
+ if (Reserved->u.SR_FN.NewCache == NULL) {
+
+ //
+ // This is the first response we have received, so we
+ // allocate the initial entry with room for a single
+ // entry.
+ //
+
+ NameCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry");
+ if (NameCache == NULL) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ return;
+ }
+
+ NB_DEBUG2 (CACHE, ("Alloc new cache %lx for <%.16s>, net %lx\n",
+ NameCache, Reserved->u.SR_FN.NetbiosName,
+ *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork)));
+
+ RtlCopyMemory (NameCache->NetbiosName, Connectionless->NameFrame.Name, 16);
+ NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0);
+ NameCache->ReferenceCount = 1;
+ RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12);
+ NameCache->NetworksAllocated = 1;
+ NameCache->NetworksUsed = 1;
+ NameCache->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork);
+
+ if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16)) {
+
+ NB_SET_SR_FN_STATUS (Reserved, FNStatusResponseGroup);
+ NameCache->Unique = FALSE;
+
+ } else {
+
+ NB_SET_SR_FN_STATUS(
+ Reserved,
+ NameCache->Unique ? FNStatusResponseUnique : FNStatusResponseGroup);
+
+ }
+
+ Reserved->u.SR_FN.NewCache = NameCache;
+
+ //
+ // If this packet was not routed to us and is for a group name,
+ // rather than use whatever local target it happened to come
+ // from we set it up so that it is broadcast on that net.
+ //
+
+ if ((RtlEqualMemory (RemoteAddress->MacAddress, Connectionless->IpxHeader.SourceNode, 6)) &&
+ (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup)) {
+#if defined(_PNP_POWER)
+ NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle;
+#else
+ NameCache->Networks[0].LocalTarget.NicId = RemoteAddress->NicId;
+#endif _PNP_POWER
+ RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6);
+ RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6);
+ } else {
+ NameCache->Networks[0].LocalTarget = *RemoteAddress;
+ }
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
+
+ //
+ // Complete pending requests now, since it is a unique
+ // name we have all the information we will get.
+ //
+
+ NameCache->TimeStamp = Device->CacheTimeStamp;
+
+ InsertInNetbiosCacheTable(
+ Device->NameCache,
+ NameCache);
+
+ //
+ // Reference it since CacheHandlePending uses it
+ // with the lock released. CacheHandlePending
+ // will dereference it.
+ //
+
+ ++NameCache->ReferenceCount;
+
+ //
+ // This call releases the lock.
+ //
+
+ CacheHandlePending(
+ Device,
+ Reserved->u.SR_FN.NetbiosName,
+ NetbiosNameFound,
+ NameCache
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+ } else {
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+ }
+
+ } else {
+
+ //
+ // We already have a response to this frame.
+ //
+
+ if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
+
+ //
+ // BUGBUG: Should we check that the response is also
+ // unique? Not much to do since I don't know of an
+ // equivalent to the netbeui NAME_IN_CONFLICT.
+ //
+
+ } else {
+
+ //
+ // This is a group name.
+ //
+
+ if (Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) {
+
+ //
+ // Update our information about this network if needed.
+ // This may free the existing cache and allocate a new one.
+ //
+
+ Reserved->u.SR_FN.NewCache =
+ CacheUpdateNameCache(
+ Reserved->u.SR_FN.NewCache,
+ RemoteAddress,
+ (TDI_ADDRESS_IPX UNALIGNED *)
+ Connectionless->IpxHeader.SourceNetwork,
+ FALSE);
+
+ } else {
+
+ //
+ // BUGBUG: This respondent thinks it is a unique name
+ // but we think it is group, should we do something?
+ //
+
+ }
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+ }
+
+} /* NbiProcessNameRecognized */
+
+
+PNETBIOS_CACHE
+CacheUpdateNameCache(
+ IN PNETBIOS_CACHE NameCache,
+ IN PIPX_LOCAL_TARGET RemoteAddress,
+ IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress,
+ IN BOOLEAN ModifyQueue
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to update a netbios cache entry
+ with a new network, if it is does not already contain
+ information about the network. It is called when a frame
+ is received advertising the appropriate cache entry, which
+ is either a group name or the broadcast name.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH IT HELD.
+
+Arguments:
+
+ NameCache - The name cache entry to update.
+
+ RemoteAddress - The remote address on which a frame was received.
+
+ IpxAddress - The source IPX address of the frame.
+
+ ModifyQueue - TRUE if we should update the queue which this
+ cache entry is in, if we reallocate it.
+
+Return Value:
+
+ The netbios cache entry, either the original or a reallocated one.
+
+--*/
+
+{
+
+ PDEVICE Device = NbiDevice;
+ USHORT NewNetworks;
+ PNETBIOS_CACHE NewNameCache;
+ PLIST_ENTRY OldPrevious;
+ UINT i;
+
+ //
+ // See if we already know about this network.
+ //
+
+ for (i = 0; i < NameCache->NetworksUsed; i++) {
+ if (NameCache->Networks[i].Network == SourceAddress->NetworkAddress) {
+ return NameCache;
+ }
+ }
+
+ //
+ // We need to add information about this network
+ // to the name cache entry. If we have to allocate
+ // a new one we do that.
+ //
+
+ NB_DEBUG2 (CACHE, ("Got new net %lx for <%.16s>\n",
+ SourceAddress->NetworkAddress,
+ NameCache->NetbiosName));
+
+ if (NameCache->NetworksUsed == NameCache->NetworksAllocated) {
+
+ //
+ // We double the number of entries allocated until
+ // we hit 16, then add 8 at a time.
+ //
+
+ if (NameCache->NetworksAllocated < 16) {
+ NewNetworks = NameCache->NetworksAllocated * 2;
+ } else {
+ NewNetworks = NameCache->NetworksAllocated + 8;
+ }
+
+
+ NewNameCache = NbiAllocateMemory(
+ sizeof(NETBIOS_CACHE) + ((NewNetworks-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Enlarge cache entry");
+
+ if (NewNameCache == NULL) {
+ return NameCache;
+ }
+
+ NB_DEBUG2 (CACHE, ("Expand cache %lx to %lx for <%.16s>\n",
+ NameCache, NewNameCache, NameCache->NetbiosName));
+
+ //
+ // Copy the new current data to the new one.
+ //
+
+ RtlCopyMemory(
+ NewNameCache,
+ NameCache,
+ sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)));
+
+ NewNameCache->NetworksAllocated = NewNetworks;
+ NewNameCache->ReferenceCount = 1;
+
+ if (ModifyQueue) {
+
+ //
+ // Insert at the same place as the old one. The time
+ // stamp is the same as the old one.
+ //
+
+
+ ReinsertInNetbiosCacheTable( Device->NameCache, NameCache, NewNameCache );
+
+ }
+
+ if (--NameCache->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", NameCache));
+ NbiFreeMemory(
+ NameCache,
+ sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Enlarge existing");
+
+ }
+
+ NameCache = NewNameCache;
+
+ }
+
+ NameCache->Networks[NameCache->NetworksUsed].Network =
+ SourceAddress->NetworkAddress;
+
+ //
+ // If this packet was not routed to us, then store the local
+ // target for a correct broadcast.
+ //
+
+ if (RtlEqualMemory (RemoteAddress->MacAddress, SourceAddress->NodeAddress, 6)) {
+#if defined(_PNP_POWER)
+ NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicHandle = RemoteAddress->NicHandle;
+#else
+ NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicId = RemoteAddress->NicId;
+#endif _PNP_POWER
+ RtlCopyMemory (NameCache->Networks[NameCache->NetworksUsed].LocalTarget.MacAddress, BroadcastAddress, 6);
+ } else {
+ NameCache->Networks[NameCache->NetworksUsed].LocalTarget = *RemoteAddress;
+ }
+
+ ++NameCache->NetworksUsed;
+
+ return NameCache;
+
+} /* CacheUpdateNameCache */
+
+
+VOID
+CacheUpdateFromAddName(
+ IN PIPX_LOCAL_TARGET RemoteAddress,
+ IN NB_CONNECTIONLESS UNALIGNED * Connectionless,
+ IN BOOLEAN LocalFrame
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when an add name frame is received.
+ If it is for a group name it checks if our cache entry for
+ that group name needs to be updated to include a new network;
+ for all frames it checks if our broadcast cache entry needs
+ to be updated to include a new network.
+
+Arguments:
+
+ RemoteAddress - The address the frame was received from.
+
+ Connectionless - The header of the received add name.
+
+ LocalFrame - TRUE if the frame was sent locally.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PUCHAR NetbiosName;
+ PNETBIOS_CACHE NameCache;
+ PLIST_ENTRY p;
+ PDEVICE Device = NbiDevice;
+ NB_DEFINE_LOCK_HANDLE (LockHandle)
+
+
+ NetbiosName = (PUCHAR)Connectionless->NameFrame.Name;
+
+ //
+ // First look up the broadcast name.
+ //
+ // BUGBUG: We should cache a pointer to the cache name
+ // for the broadcast entry, if there is one.
+ //
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+
+ if (!LocalFrame) {
+
+ if ( FindInNetbiosCacheTable( Device->NameCache,
+ NetbiosBroadcastName,
+ &NameCache ) == STATUS_SUCCESS ) {
+ //
+ // This will reallocate a cache entry and update the
+ // queue if necessary.
+ //
+
+ (VOID)CacheUpdateNameCache(
+ NameCache,
+ RemoteAddress,
+ (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork),
+ TRUE);
+ }
+
+ }
+
+
+ //
+ // Now see if our database needs to be updated based on this.
+ //
+
+ if ( FindInNetbiosCacheTable( Device->NameCache,
+ Connectionless->NameFrame.Name,
+ &NameCache ) == STATUS_SUCCESS ) {
+
+
+ if (!NameCache->Unique) {
+
+ if (!LocalFrame) {
+
+ //
+ // This will reallocate a cache entry and update the
+ // queue if necessary.
+ //
+
+ (VOID)CacheUpdateNameCache(
+ NameCache,
+ RemoteAddress,
+ (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork),
+ TRUE);
+
+ }
+
+ } else {
+
+ //
+ // To be safe, delete any unique names we get add
+ // names for (we will requery next time we need it).
+ // BUGBUG: Update the database instead -- but then
+ // we may not get the best route??
+ //
+
+ RemoveFromNetbiosCacheTable ( Device->NameCache, NameCache );
+
+ if (--NameCache->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Free add named cache entry %lx\n", NameCache));
+ NbiFreeMemory(
+ NameCache,
+ sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Enlarge existing");
+
+ }
+
+ }
+
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+} /* CacheUpdateFromAddName */
+
+
+VOID
+NbiProcessDeleteName(
+ IN PIPX_LOCAL_TARGET RemoteAddress,
+ IN ULONG MacOptions,
+ IN PUCHAR PacketBuffer,
+ IN UINT PacketSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine handles NB_CMD_DELETE_NAME frames.
+
+Arguments:
+
+ RemoteAddress - The local target this packet was received from.
+
+ MacOptions - The MAC options for the underlying NDIS binding.
+
+ LookaheadBuffer - The packet data, starting at the IPX
+ header.
+
+ PacketSize - The total length of the packet, starting at the
+ IPX header.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NB_CONNECTIONLESS UNALIGNED * Connectionless =
+ (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer;
+ PUCHAR NetbiosName;
+ PNETBIOS_CACHE CacheName;
+ PDEVICE Device = NbiDevice;
+ NB_DEFINE_LOCK_HANDLE (LockHandle)
+
+
+ if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) {
+ return;
+ }
+
+ //
+ // We want to update our netbios cache to reflect the
+ // fact that this name is no longer valid.
+ //
+
+ NetbiosName = (PUCHAR)Connectionless->NameFrame.Name;
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+
+ if ( FindInNetbiosCacheTable( Device->NameCache,
+ NetbiosName,
+ &CacheName ) == STATUS_SUCCESS ) {
+
+ //
+ // We don't track group names since we don't know if
+ // this is the last person that owns it. We also drop
+ // the frame if does not come from the person we think
+ // owns this name.
+ //
+
+ if ((!CacheName->Unique) ||
+ (CacheName->NetworksUsed == 0) ||
+ (!RtlEqualMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12))) {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ return;
+ }
+
+ NB_DEBUG2 (CACHE, ("Found cache name to delete <%.16s>\n", NetbiosName));
+
+ }else {
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+ return;
+ }
+
+
+ //
+ // We have a cache entry, take it out of the list. If no
+ // one else is using it, delete it; if not, they will delete
+ // it when they are done.
+ //
+
+
+ RemoveFromNetbiosCacheTable ( Device->NameCache, CacheName);
+
+ if (--CacheName->ReferenceCount == 0) {
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+ NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName));
+ NbiFreeMemory(
+ CacheName,
+ sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Name deleted");
+
+ } else {
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+ }
+
+} /* NbiProcessDeleteName */
+
+VOID
+InsertInNetbiosCacheTable(
+ IN PNETBIOS_CACHE_TABLE CacheTable,
+ IN PNETBIOS_CACHE CacheEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine inserts a new cache entry in the hash table
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+ CacheEntry - Entry to be inserted.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ USHORT HashIndex;
+
+ //
+ // Keep a threshold of how many entries do we keep in the table.
+ // If it crosses the threshold, just remove the oldest entry
+ //
+ if ( CacheTable->CurrentEntries >= CacheTable->MaxHashIndex * NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET ) {
+ PNETBIOS_CACHE OldestCacheEntry = NULL;
+ PNETBIOS_CACHE NextEntry;
+ PLIST_ENTRY p;
+
+ for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) {
+ if ( (p = CacheTable->Bucket[ HashIndex ].Blink ) != &CacheTable->Bucket[ HashIndex ] ) {
+ NextEntry = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
+
+ if ( OldestCacheEntry ) {
+ if ( NextEntry->TimeStamp < OldestCacheEntry->TimeStamp ) {
+ OldestCacheEntry = NextEntry;
+ }
+ } else {
+ OldestCacheEntry = NextEntry;
+ }
+ }
+ }
+
+ CTEAssert( OldestCacheEntry );
+
+ NB_DEBUG2 (CACHE, ("Threshold exceeded, removing oldest cache entry %lx\n", OldestCacheEntry));
+ RemoveEntryList (&OldestCacheEntry->Linkage);
+ CacheTable->CurrentEntries--;
+
+ if (--OldestCacheEntry->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Freed cache entry %lx\n", OldestCacheEntry));
+
+ NbiFreeMemory(
+ OldestCacheEntry,
+ sizeof(NETBIOS_CACHE) + ((OldestCacheEntry->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Aged out");
+
+ }
+
+ }
+ HashIndex = ( ( CacheEntry->NetbiosName[0] & 0x0f ) << 4 ) + ( CacheEntry->NetbiosName[1] & 0x0f );
+ HashIndex = HashIndex % CacheTable->MaxHashIndex;
+
+ InsertHeadList( &CacheTable->Bucket[HashIndex], &CacheEntry->Linkage );
+ CacheTable->CurrentEntries++;
+} /* InsertInNetbiosCacheTable */
+
+
+__inline
+VOID
+ReinsertInNetbiosCacheTable(
+ IN PNETBIOS_CACHE_TABLE CacheTable,
+ IN PNETBIOS_CACHE OldEntry,
+ IN PNETBIOS_CACHE NewEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine inserts a new cache entry at the same place where
+ the old entry was.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+ CacheEntry - Entry to be inserted.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLIST_ENTRY OldPrevious;
+
+ OldPrevious = OldEntry->Linkage.Blink;
+ RemoveEntryList (&OldEntry->Linkage);
+ InsertHeadList (OldPrevious, &NewEntry->Linkage);
+} /* ReinsertInNetbiosCacheTable */
+
+__inline
+VOID
+RemoveFromNetbiosCacheTable(
+ IN PNETBIOS_CACHE_TABLE CacheTable,
+ IN PNETBIOS_CACHE CacheEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes an entry from the cache table.
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+ CacheEntry - Entry to be removed.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ RemoveEntryList( &CacheEntry->Linkage );
+ CacheTable->CurrentEntries--;
+} /* RemoveFromNetbiosCacheTable */
+
+
+
+VOID
+FlushOldFromNetbiosCacheTable(
+ IN PNETBIOS_CACHE_TABLE CacheTable,
+ IN USHORT AgeLimit
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes all the old entries from the hash table.
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+ AgeLimit - All the entries older than AgeLimit will be removed.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ USHORT HashIndex;
+ PLIST_ENTRY p;
+ PNETBIOS_CACHE CacheName;
+
+ //
+ // run the hash table looking for old entries. Since new entries
+ // are stored at the head and all entries are time stamped when
+ // they are inserted, we scan backwards and stop once we find
+ // an entry which does not need to be aged.
+ // we repeat this for each bucket.
+
+ for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) {
+ for (p = CacheTable->Bucket[ HashIndex ].Blink;
+ p != &CacheTable->Bucket[ HashIndex ];
+ ) {
+
+ CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
+ p = p->Blink;
+
+ //
+ // see if any entries have been around for more than agelimit
+ //
+
+ if ((USHORT)(NbiDevice->CacheTimeStamp - CacheName->TimeStamp) >= AgeLimit ) {
+
+ RemoveEntryList (&CacheName->Linkage);
+ CacheTable->CurrentEntries--;
+
+ if (--CacheName->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Aging out name cache entry %lx\n", CacheName));
+
+ NbiFreeMemory(
+ CacheName,
+ sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Aged out");
+
+ }
+
+ } else {
+
+ break;
+
+ }
+ } // for loop
+ } // for loop
+} /* FlushOldFromNetbiosCacheTable */
+
+VOID
+FlushFailedNetbiosCacheEntries(
+ IN PNETBIOS_CACHE_TABLE CacheTable
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes all the failed entries from the hash table.
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ USHORT HashIndex;
+ PLIST_ENTRY p;
+ PNETBIOS_CACHE CacheName;
+
+ //
+ // run the hash table looking for old entries. Since new entries
+ // are stored at the head and all entries are time stamped when
+ // they are inserted, we scan backwards and stop once we find
+ // an entry which does not need to be aged.
+ // we repeat this for each bucket.
+
+ for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) {
+ for (p = CacheTable->Bucket[ HashIndex ].Blink;
+ p != &CacheTable->Bucket[ HashIndex ];
+ ) {
+
+ CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
+ p = p->Blink;
+
+ //
+ // flush all the failed cache entries.
+ // We do this when a new adapter appears, and there's a possiblity that
+ // the failed entries might succeed now on the new adapter.
+ //
+
+ if (CacheName->NetworksUsed == 0) {
+ RemoveEntryList (&CacheName->Linkage);
+ CacheTable->CurrentEntries--;
+ CTEAssert( CacheName->ReferenceCount == 1 );
+ CTEAssert( CacheName->NetworksAllocated == 1 );
+
+ NB_DEBUG2 (CACHE, ("Flushing out failed name cache entry %lx\n", CacheName));
+
+ NbiFreeMemory(
+ CacheName,
+ sizeof(NETBIOS_CACHE),
+ MEMORY_CACHE,
+ "Aged out");
+
+ }
+ } // for loop
+ } // for loop
+} /* FlushFailedNetbiosCacheEntries */
+
+VOID
+RemoveInvalidRoutesFromNetbiosCacheTable(
+ IN PNETBIOS_CACHE_TABLE CacheTable,
+ IN NIC_HANDLE UNALIGNED *InvalidNicHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes all invalid route entries from the hash table.
+ Routes become invalid when the binding is deleted in Ipx due to PnP
+ event.
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+ InvalidRouteNicId - NicId of the invalid routes.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ PLIST_ENTRY p;
+ PNETBIOS_CACHE CacheName;
+ USHORT i,j,NetworksRemoved;
+ USHORT HashIndex;
+ PDEVICE Device = NbiDevice;
+
+ //
+ // Flush all the cache entries that are using this NicId in the local
+ // target.
+ //
+
+ for ( HashIndex = 0; HashIndex < Device->NameCache->MaxHashIndex; HashIndex++) {
+ for (p = Device->NameCache->Bucket[ HashIndex ].Flink;
+ p != &Device->NameCache->Bucket[ HashIndex ];
+ ) {
+
+ CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
+ p = p->Flink;
+
+
+ //
+ // Remove each of those routes which is using this NicId.
+ // if no routes left, then flush the cache entry also.
+ // ( unique names have only one route anyways )
+ //
+ for ( i = 0, NetworksRemoved = 0; i < CacheName->NetworksUsed; i++ ) {
+ if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId == InvalidNicHandle->NicId ) {
+ CTEAssert( RtlEqualMemory( &CacheName->Networks[i].LocalTarget.NicHandle, InvalidNicHandle, sizeof(NIC_HANDLE)));
+ for ( j = i+1; j < CacheName->NetworksUsed; j++ ) {
+ CacheName->Networks[j-1] = CacheName->Networks[j];
+ }
+ NetworksRemoved++;
+ } else if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId > InvalidNicHandle->NicId ) {
+ CacheName->Networks[i].LocalTarget.NicHandle.NicId--;
+ }
+ }
+ CTEAssert( NetworksRemoved <= CacheName->NetworksUsed );
+ if ( ! ( CacheName->NetworksUsed -= NetworksRemoved ) ) {
+ RemoveEntryList (&CacheName->Linkage);
+ CacheTable->CurrentEntries--;
+
+ NB_DEBUG2 (CACHE, ("Removed cache entry %lx bcoz route(NicId %d) deleted\n", CacheName, InvalidNicHandle->NicId ));
+ if (--CacheName->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Freed name cache entry %lx\n", CacheName));
+
+ NbiFreeMemory(
+ CacheName,
+ sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Aged out");
+
+ }
+ }
+ } // for loop
+ } // for loop
+} /* RemoveInvalidRoutesFromNetbiosCacheTable */
+
+
+NTSTATUS
+FindInNetbiosCacheTable(
+ IN PNETBIOS_CACHE_TABLE CacheTable,
+ IN PUCHAR NameToBeFound,
+ OUT PNETBIOS_CACHE *CacheEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine finds a netbios name in the Hash Table and returns
+ the corresponding cache entry.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+ CacheEntry - Pointer to the netbios cache entry if found.
+
+Return Value:
+
+ STATUS_SUCCESS - if successful.
+
+ STATUS_UNSUCCESSFUL - otherwise.
+
+--*/
+
+{
+ USHORT HashIndex;
+ PLIST_ENTRY p;
+ PNETBIOS_CACHE FoundCacheName;
+
+
+ HashIndex = ( ( NameToBeFound[0] & 0x0f ) << 4 ) + ( NameToBeFound[1] & 0x0f );
+ HashIndex = HashIndex % CacheTable->MaxHashIndex;
+
+ for (p = ( CacheTable->Bucket[ HashIndex ] ).Flink;
+ p != &CacheTable->Bucket[ HashIndex ];
+ p = p->Flink) {
+
+ FoundCacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
+
+ //
+ // See if this entry is for the same name we are looking for.
+
+ if ( RtlEqualMemory (FoundCacheName->NetbiosName, NameToBeFound, 16) ) {
+ *CacheEntry = FoundCacheName;
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_UNSUCCESSFUL;
+} /* FindInNetbiosCacheTable */
+
+NTSTATUS
+CreateNetbiosCacheTable(
+ IN OUT PNETBIOS_CACHE_TABLE *NewTable,
+ IN USHORT MaxHashIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates a new hash table for netbios cache
+ and initializes it.
+
+ THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS
+ WITH THE LOCK HELD.
+
+Arguments:
+
+ NewTable - The pointer of the table to be created.
+
+ MaxHashIndex - Number of buckets in the hash table.
+
+Return Value:
+
+ STATUS_SUCCESS - if successful.
+
+ STATUS_INSUFFICIENT_RESOURCES - If cannot allocate memory.
+
+--*/
+
+{
+ USHORT i;
+
+ *NewTable = NbiAllocateMemory (sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( MaxHashIndex - 1) ,
+ MEMORY_CACHE, "Cache Table");
+
+ if ( *NewTable ) {
+ for ( i = 0; i < MaxHashIndex; i++ ) {
+ InitializeListHead(& (*NewTable)->Bucket[i] );
+ }
+
+ (*NewTable)->MaxHashIndex = MaxHashIndex;
+ (*NewTable)->CurrentEntries = 0;
+ return STATUS_SUCCESS;
+ }
+ else {
+ NB_DEBUG( CACHE, ("Cannot create Netbios Cache Table\n") );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+} /* CreateNetbiosCacheTable */
+
+
+VOID
+DestroyNetbiosCacheTable(
+ IN PNETBIOS_CACHE_TABLE CacheTable
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes all entries from the hash table.
+ and free up the hash table.
+
+Arguments:
+
+ CacheTable - The pointer of the Hash Table.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ USHORT HashIndex;
+ PLIST_ENTRY p;
+ PNETBIOS_CACHE CacheName;
+
+
+ for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) {
+ while (!IsListEmpty ( &( CacheTable->Bucket[ HashIndex ] ) ) ) {
+
+ p = RemoveHeadList ( &( CacheTable->Bucket[ HashIndex ] ));
+ CacheTable->CurrentEntries--;
+ CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
+
+ NB_DEBUG2 (CACHE, ("Free cache entry %lx\n", CacheName));
+
+ NbiFreeMemory(
+ CacheName,
+ sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Free entries");
+
+ }
+ } // for loop
+
+ CTEAssert( CacheTable->CurrentEntries == 0 );
+
+ NbiFreeMemory (CacheTable, sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( CacheTable->MaxHashIndex - 1) ,
+ MEMORY_CACHE, "Free Cache Table");
+
+} /* DestroyNetbiosCacheTable */
+
+