/*++ 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 #include 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; PSLIST_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 : "")); return STATUS_SUCCESS; } else { if (Type != FindNameConnect) { if (FoundCacheName->FailedOnDownWan) { NB_DEBUG2 (CACHE, ("Found cache name, but down wan <%.16s>\n", RemoteName ? RemoteName : "")); return STATUS_DEVICE_DOES_NOT_EXIST; } else { NB_DEBUG2 (CACHE, ("Found cache name, but no nets <%.16s>\n", RemoteName ? RemoteName : "")); 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 : "")); // // 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 : "")); 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 : "")); 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 */