2429 lines
70 KiB
C
2429 lines
70 KiB
C
/*++
|
||
|
||
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 ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,CreateNetbiosCacheTable)
|
||
#endif
|
||
|
||
#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
|
||
|
||
//
|
||
// 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)( // 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).
|
||
//
|
||
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 <%16.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;
|
||
|
||
}
|
||
|
||
//
|
||
// 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 * 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 *)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 *)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 ((Result == NetbiosNameFound) && (!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 {
|
||
BOOLEAN bAutodialAttempt = FALSE;
|
||
|
||
if (ConnectRequest->Cancel) {
|
||
NB_DEBUG2 (CONNECTION, ("Cancelling connect %lx on %lx\n", ConnectRequest, Connection));
|
||
}
|
||
else
|
||
{
|
||
NB_DEBUG2 (CONNECTION, ("Timing out connect %lx on %lx\n", ConnectRequest, Connection));
|
||
}
|
||
|
||
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 {
|
||
|
||
CTEAssert (0); // 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 {
|
||
|
||
//
|
||
// 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;
|
||
} else {
|
||
REQUEST_STATUS(DatagramRequest) = STATUS_BAD_NETWORK_PATH;
|
||
}
|
||
REQUEST_INFORMATION(DatagramRequest) = 0;
|
||
|
||
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_STATUSPTR(AdapterStatusRequest) = (PVOID)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 * RemoteNetbiosAddress;
|
||
NB_CONNECTIONLESS UNALIGNED * Connectionless =
|
||
(NB_CONNECTIONLESS UNALIGNED *)PacketBuffer;
|
||
NB_DEFINE_LOCK_HANDLE(LockHandle)
|
||
|
||
|
||
#if 0
|
||
//
|
||
// 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)
|
||
{
|
||
if ((FindInNetbiosCacheTable (Device->NameCache,
|
||
Connectionless->NameFrame.Name,
|
||
&NameCache ) == STATUS_SUCCESS) &&
|
||
(NameCache->NetworksUsed == 0))
|
||
{
|
||
//
|
||
// Update our information about this network if needed.
|
||
//
|
||
NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0);
|
||
if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16))
|
||
{
|
||
NameCache->Unique = FALSE;
|
||
}
|
||
|
||
RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12);
|
||
NameCache->NetworksUsed = 1;
|
||
NameCache->Networks[0].Network = *(UNALIGNED ULONG*)(Connectionless->IpxHeader.SourceNetwork);
|
||
|
||
//
|
||
// 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)) &&
|
||
(!NameCache->Unique))
|
||
{
|
||
NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle;
|
||
RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6);
|
||
RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6);
|
||
}
|
||
else
|
||
{
|
||
NameCache->Networks[0].LocalTarget = *RemoteAddress;
|
||
}
|
||
}
|
||
|
||
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 *)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) {
|
||
|
||
//
|
||
// 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 {
|
||
|
||
//
|
||
// Hmmm... 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.
|
||
//
|
||
|
||
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).
|
||
//
|
||
|
||
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;
|
||
|
||
if (NULL == CacheTable) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// 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 */
|
||
|
||
|