/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: Inbound.c Abstract: This file implements the inbound name service pdu handling. It handles name queries from the network and Registration responses from the network. Author: Jim Stewart (Jimst) 10-2-92 Revision History: --*/ #include "precomp.h" #include "ctemacro.h" #include "inbound.tmh" NTSTATUS DecodeNodeStatusResponse( IN tNAMEHDR UNALIGNED *pNameHdr, IN ULONG Length, IN PUCHAR pName, IN ULONG lNameSize, IN tIPADDRESS SrcIpAddress ); NTSTATUS SendNodeStatusResponse( IN tNAMEHDR UNALIGNED *pInNameHdr, IN ULONG Length, IN PUCHAR pName, IN ULONG lNameSize, IN tIPADDRESS SrcIpAddress, IN USHORT SrcPort, IN tDEVICECONTEXT *pDeviceContext ); NTSTATUS UpdateNameState( IN tADDSTRUCT UNALIGNED *pAddrStruct, IN tNAMEADDR *pNameAddr, IN ULONG Length, #ifdef MULTIPLE_WINS IN PULONG pContextFlags, #endif IN tDEVICECONTEXT *pDeviceContext, IN BOOLEAN SrcIsNameServer, IN tDGRAM_SEND_TRACKING *Context, IN CTELockHandle OldIrq1 ); NTSTATUS ChkIfValidRsp( IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNumBytes, IN tNAMEADDR *pNameAddr ); NTSTATUS ChooseBestIpAddress( IN tADDSTRUCT UNALIGNED *pAddrStruct, IN ULONG Len, IN tDEVICECONTEXT *pDeviceContext, OUT tDGRAM_SEND_TRACKING *pTracker, OUT tIPADDRESS *pIpAddress, IN BOOLEAN fReturnAddrList ); NTSTATUS GetNbFlags( IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNameSize, IN LONG lNumBytes, OUT USHORT *pRegType ); VOID PrintHexString( IN tNAMEHDR UNALIGNED *pNameHdr, IN ULONG lNumBytes ); ULONG MakeList( IN tDEVICECONTEXT *pDeviceContext, IN ULONG CountAddrs, IN tADDSTRUCT UNALIGNED *pAddrStruct, IN tIPADDRESS *pAddrArray, IN ULONG SizeOfAddrArray, IN BOOLEAN IsSubnetMatch ); BOOL IsBrowserName( IN PCHAR pName ); #if DBG #define KdPrintHexString(pHdr,NumBytes) \ PrintHexString(pHdr,NumBytes) #else #define KdPrintHexString(pHdr,NumBytes) #endif //---------------------------------------------------------------------------- BOOLEAN IsRemoteAddress( IN tNAMEADDR *pNameAddr, IN tIPADDRESS IpAddress ) { ULONG i; for (i=0; iRemoteCacheLen; i++) { if (pNameAddr->pRemoteIpAddrs[i].IpAddress == IpAddress) { return TRUE; } } return FALSE; } //---------------------------------------------------------------------------- NTSTATUS QueryFromNet( IN tDEVICECONTEXT *pDeviceContext, IN PVOID pSrcAddress, IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNumBytes, IN USHORT OpCodeFlags, IN BOOLEAN fBroadcast ) /*++ Routine Description: This routine handles both name query requests and responses. For Queries it checks if the name is registered on this node. If this node is a proxy it then forwards a name query onto the Name Server, adding the name to the remote proxy cache... Arguments: Return Value: NTSTATUS - success or not - failure means no response to net --*/ { NTSTATUS status; LONG lNameSize; CHAR pName[NETBIOS_NAME_SIZE]; PUCHAR pScope; tNAMEADDR *pNameAddr; tTIMERQENTRY *pTimer; COMPLETIONCLIENT pClientCompletion; #ifdef MULTIPLE_WINS tDGRAM_SEND_TRACKING *Context; ULONG AddrStructLength; USHORT RdLength; // The length field in the packet #else PVOID Context; #endif PTRANSPORT_ADDRESS pSourceAddress; tIPADDRESS SrcAddress; CTELockHandle OldIrq1; tQUERYRESP UNALIGNED *pQuery; USHORT SrcPort; tIPADDRESS IpAddr; tADDSTRUCT UNALIGNED *pAddrs; tIPADDRESS *pFailedAddresses; ULONG i, j, CountAddrs, InterfaceContext; tQUERY_ADDRS *pQueryAddrs = NULL; LONG MinimumBytes; pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); SrcPort = ntohs(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port); #ifdef VXD // // is this a response from a DNS server? if yes then handle it // appropriately // if (SrcPort == NBT_DNSSERVER_UDP_PORT) { USHORT TransactionId; TransactionId = ntohs(pNameHdr->TransactId); if ( TransactionId >= DIRECT_DNS_NAME_QUERY_BASE ) { ProcessDnsResponseDirect( pDeviceContext, pSrcAddress, pNameHdr, lNumBytes, OpCodeFlags ); } else { ProcessDnsResponse( pDeviceContext, pSrcAddress, pNameHdr, lNumBytes, OpCodeFlags ); } return(STATUS_DATA_NOT_ACCEPTED); } #endif // // check the pdu size for errors - be sure the name is long enough for // the scope on this machine. // if (lNumBytes < (NBT_MINIMUM_QUERY + NbtConfig.ScopeLength - 1)) { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Name Query PDU TOO short = %X,Src= %X\n",lNumBytes,SrcAddress)); NbtTrace(NBT_TRACE_NAMESRV, ("Discard name query: Query PDU too short %x, Src=%!ipaddr!", lNumBytes, SrcAddress)); return(STATUS_DATA_NOT_ACCEPTED); } // get the name out of the network pdu and pass to routine to check // local table *TODO* this assumes just one name in the Query response... status = ConvertToAscii ((PCHAR) &pNameHdr->NameRR, (lNumBytes-1) - FIELD_OFFSET(tNAMEHDR,NameRR), // -1 for QUEST_STATUS pName, &pScope, &lNameSize); if (!NT_SUCCESS(status)) { // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.QueryFromNet: WARNING!!! Rejecting Request -- ConvertToAscii FAILed\n")); NbtTrace(NBT_TRACE_NAMESRV, ("Discard name query: Src=%!ipaddr! ConvertToAscii returns %!status!", SrcAddress, status)); return(STATUS_DATA_NOT_ACCEPTED); } // check if this is a request or a response pdu // // *** RESPONSE *** // if (OpCodeFlags & OP_RESPONSE) { if (!(OpCodeFlags & FL_AUTHORITY)) { // *** Redirect Response from Wins *** // // This is a redirect response telling us to go to another // name server, which we do not support, so just return // *TODO* // NbtTrace(NBT_TRACE_NAMESRV, ("Ignore redirecting response from %!ipaddr!", SrcAddress)); return(STATUS_DATA_NOT_ACCEPTED); } // // check if this is a node status request, since is looks very similar // to a name query, except that the NBSTAT field is 0x21 instead of // 0x20 // pQuery = (tQUERYRESP *) &pNameHdr->NameRR.NetBiosName[lNameSize]; if ( ((PUCHAR)pQuery)[1] == QUEST_STATUS ) { // // *** This is an AdapterStatus response! *** // tNODESTATUS *pNodeStatus = (tNODESTATUS *)&pNameHdr->NameRR.NetBiosName[lNameSize]; // // Bug# 125627 // Check for valid Pdu data + Size // The PDU is of the form: // tNAMEHDR --> TransactionId ==> Offset=0, Length=2 bytes // : // --> NameRR.NetbiosName ==> Offset=13, Length = lNameSize // --> NodeStatusResponse ==> Offset=13+lNameSize, Length >=11 // --> NodeName[i] ==> Offset=13+lNameSize+11+(i*NBT_NODE_NAME_SIZE) // MinimumBytes = FIELD_OFFSET(tNAMEHDR,NameRR.NetBiosName) + lNameSize + 11; if ((lNumBytes < MinimumBytes) || // so that we can read in "pNodeStatus->NumNames" (lNumBytes < (MinimumBytes + pNodeStatus->NumNames*NBT_NODE_NAME_SIZE))) { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.QueryFromNet: WARNING Bad AdapterStatusResp size -- lNumBytes=<%d> < <%d>\n", lNumBytes, (MinimumBytes + pNodeStatus->NumNames*NBT_NODE_NAME_SIZE))); NbtTrace(NBT_TRACE_NAMESRV, ("Bad AdapterStatusResp size -- lNumBytes=%d < %d from %!ipaddr!", lNumBytes, (MinimumBytes + pNodeStatus->NumNames*NBT_NODE_NAME_SIZE), SrcAddress)); ASSERT(0); return(STATUS_DATA_NOT_ACCEPTED); } status = DecodeNodeStatusResponse(pNameHdr, lNumBytes, pName, lNameSize, SrcAddress); return(STATUS_DATA_NOT_ACCEPTED); } // // *** We are processing a Name Query response! *** // // // check the Query response pdu size before dereferencing it! // The PDU is of the form: // tNAMEHDR --> TransactionId ==> Offset=0, Length=2 bytes // : // --> NameRR.NetbiosName ==> Offset=13, Length = lNameSize // --> QueryResponse ==> Offset=13+lNameSize, Length >=10 // // if (IS_POS_RESPONSE(OpCodeFlags)) { MinimumBytes = 13 + lNameSize + 16; } else { MinimumBytes = 13 + lNameSize + 10; // Upto Length field only } if (lNumBytes < MinimumBytes) { KdPrint (("Nbt.QueryFromNet: WARNING -- Bad QueryResp size, lNumBytes=<%d>, lNameSize=<%d>\n", lNumBytes, lNameSize)); NbtTrace(NBT_TRACE_NAMESRV, ("Bad QueryResp size, lNumBytes=<%d>, lNameSize=<%d> from %!ipaddr!", lNumBytes, lNameSize, SrcAddress)); return(STATUS_DATA_NOT_ACCEPTED); } pAddrs = (tADDSTRUCT *) &pQuery->Flags; RdLength = ntohs(*(USHORT*)((PUCHAR)pQuery + 8)); AddrStructLength = lNumBytes - (ULONG)((ULONG_PTR)&pQuery->Flags - (ULONG_PTR)pNameHdr); if (RdLength < AddrStructLength) { AddrStructLength = RdLength; } CountAddrs = AddrStructLength / tADDSTRUCT_SIZE; // // Call into IP to determine the outgoing interface for each address returned // if ((NbtConfig.ConnectOnRequestedInterfaceOnly) && (!(ntohs(pAddrs[0].NbFlags) & FL_GROUP)) && (CountAddrs && ((CountAddrs*tADDSTRUCT_SIZE) == AddrStructLength)) && (pQueryAddrs = (tQUERY_ADDRS *) NbtAllocMem(CountAddrs*sizeof(tQUERY_ADDRS),NBT_TAG2('13')))) { CTEZeroMemory(pQueryAddrs, CountAddrs*sizeof(tQUERY_ADDRS)); for (i = 0; i < CountAddrs; i++) { pQueryAddrs[i].IpAddress = pAddrs[i].IpAddr; pDeviceContext->pFastQuery(pAddrs[i].IpAddr,&pQueryAddrs[i].Interface,&pQueryAddrs[i].Metric); } } // // call this routine to find the name, since it does not interpret the // state of the name as does FindName() // CTESpinLock(&NbtConfig.JointLock,OldIrq1); status = FindOnPendingList(pName,pNameHdr,FALSE,NETBIOS_NAME_SIZE,&pNameAddr); if (NT_SUCCESS(status)) { pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; // remove any timer block and call the completion routine if ((pTimer = pNameAddr->pTimer)) { ULONG Flags; tDGRAM_SEND_TRACKING *pTracker; tDEVICECONTEXT *pDevContext; USHORT NSOthersIndex, NSOthersLeft; ULONG ContextFlags; pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; Flags = pTracker->Flags; // // Since this Wins server was able to respond, set the last responsive // pointer // if ((SrcIsNameServer(SrcAddress,SrcPort)) && (pTracker->Flags & NBT_NAME_SERVER_OTHERS)) { pTracker->pDeviceContext->lLastResponsive = pTracker->NSOthersIndex; } // // If this is not a response to a request sent by the PROXY code // and // MSNode && Error Response code && Src is NameServer && Currently // resolving with the name server, then switch to broadcast // ... or if it is a Pnode or Mnode and EnableLmHost or // ResolveWithDns is on, then // let the timer expire again, and try Lmhost. // if ( #ifdef PROXY_NODE pNameAddr->ProxyReqType == NAMEREQ_REGULAR && #endif (IS_NEG_RESPONSE(OpCodeFlags))) { NbtTrace(NBT_TRACE_NAMESRV, ("Receive negative response from %!ipaddr! for %!NBTNAME!<%02x>", SrcAddress, pNameAddr->Name, (unsigned)pNameAddr->Name[15])); if ((NodeType & (PNODE | MNODE | MSNODE)) && (Flags & (NBT_NAME_SERVER | NBT_NAME_SERVER_BACKUP))) { // this should let MsnodeCompletion try the // backup WINS or broadcast processing next // pTracker->Flags |= WINS_NEG_RESPONSE; ExpireTimer (pTimer, &OldIrq1); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } if (pQueryAddrs) { CTEFreeMem (pQueryAddrs); } return(STATUS_DATA_NOT_ACCEPTED); } // // Check if any of the addresses that came over the net // belong to the set of failed addresses that we are tracking // if (pNameAddr->pTracker) // pTracker will be NULL for Proxy requests { if (pQueryAddrs) { // // Go through the list of addresses to see which ones can be // reached through this Device // InterfaceContext = pNameAddr->pTracker->pDeviceContext->IPInterfaceContext; for (i=0; ipTracker->pFailedIpAddresses) && (pTimer->ClientCompletion)) { pFailedAddresses = pNameAddr->pTracker->pFailedIpAddresses; if ((CountAddrs*tADDSTRUCT_SIZE) == AddrStructLength) { // // If some of these addresses had failed earlier, they should be purged // i = 0; while ((i < MAX_FAILED_IP_ADDRESSES) && (pFailedAddresses[i])) { for (j = 0; j < CountAddrs; j++) { if (pFailedAddresses[i] == (ULONG) ntohl(pAddrs[j].IpAddr)) { pAddrs[j] = pAddrs[CountAddrs-1]; // Copy the last entry pAddrs[CountAddrs-1].IpAddr = 0; // Set the last entry to 0 CountAddrs--; j--; // Now, read this new IP address } } i++; } } } if (0 == (AddrStructLength = CountAddrs*tADDSTRUCT_SIZE)) { NbtTrace(NBT_TRACE_NAMESRV, ("No new address. Treated as negative response. " "from %!ipaddr! for %!NBTNAME!<%02x>", SrcAddress, pNameAddr->Name, (unsigned)pNameAddr->Name[15])); // // No new addresses were found -- consider this a negative response // if ((NodeType & (PNODE | MNODE | MSNODE)) && (pTracker->Flags & (NBT_NAME_SERVER | NBT_NAME_SERVER_BACKUP))) { // this should let MsnodeCompletion try the // backup WINS or broadcast // processing when the timer times out. // pTracker->Flags |= WINS_NEG_RESPONSE; ExpireTimer (pTimer, &OldIrq1); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } return(STATUS_DATA_NOT_ACCEPTED); } // // save the Tracker's essential info since the call to StopTimer could // free pTracker // pNameAddr->pTracker->ResolutionContextFlags = pTracker->Flags; pNameAddr->pTracker->NSOthersIndex = pTracker->NSOthersIndex; pNameAddr->pTracker->NSOthersLeft = pTracker->NSOthersLeft; } else if (pQueryAddrs) { CTEFreeMem (pQueryAddrs); pQueryAddrs = NULL; } CHECK_PTR(pNameAddr); pDevContext = pTracker->pDeviceContext; // // this routine puts the timer block back on the timer Q, and // handles race conditions to cancel the timer when the timer // is expiring. // pNameAddr->pTimer = NULL; NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE); // Since StopTimer can Deref name! status = StopTimer(pTimer,&pClientCompletion,&Context); LOCATION(0x42); // // we need to synchronize removing the name from the list // with MsNodeCompletion // if (pClientCompletion) { LOCATION(0x41); // // Remove from the pending list RemoveEntryList(&pNameAddr->Linkage); InitializeListHead(&pNameAddr->Linkage); // check the name query response ret code to see if the name // query succeeded or not. if (IS_POS_RESPONSE(OpCodeFlags)) { BOOLEAN ResolvedByWins; LOCATION(0x40); // // Keep track of how many names are resolved by WINS and // keep a list of names not resolved by WINS // if (!(ResolvedByWins = SrcIsNameServer(SrcAddress,SrcPort))) { SaveBcastNameResolved(pName); } IncrementNameStats(NAME_QUERY_SUCCESS, ResolvedByWins); #ifdef PROXY_NODE // Set flag if the node queries is a PNODE // IF_PROXY(NodeType) { pNameAddr->fPnode = (pNameHdr->NameRR.NetBiosName[lNameSize+QUERY_NBFLAGS_OFFSET] & NODE_TYPE_MASK) == PNODE_VAL_IN_PKT; IF_DBG(NBT_DEBUG_PROXY) KdPrint(("QueryFromNet: POSITIVE RESPONSE to name query - %16.16s(%X)\n", pNameAddr->Name, pNameAddr->Name[15])); } #endif pNameAddr->AdapterMask |= pDevContext->AdapterMask; IpAddr = ((tADDSTRUCT *)&pQuery->Flags)->IpAddr; NbtTrace(NBT_TRACE_NAMESRV, ("Positive response for %!NBTNAME!<%02x> %!ipaddr! from %!ipaddr!", pNameAddr->Name, (unsigned)pNameAddr->Name[15], IpAddr, SrcAddress)); status = UpdateNameState((tADDSTRUCT *)&pQuery->Flags, pNameAddr, AddrStructLength, &ContextFlags, pDevContext, SrcIsNameServer(SrcAddress,SrcPort), (tDGRAM_SEND_TRACKING *)Context, OldIrq1); // // since pNameAddr can be freed in UpdateNameState do not // access it here // pNameAddr = NULL; // status = STATUS_SUCCESS; } else // negative query response received { LOCATION(0x3f); NbtTrace(NBT_TRACE_NAMESRV, ("Negative response for %!NBTNAME!<%02x> from %!ipaddr!", pNameAddr->Name, (unsigned)pNameAddr->Name[15], SrcAddress)); // // Release the name. It will get dereferenced by the // cache timeout function (RemoteHashTimeout). // pNameAddr->NameTypeState &= ~NAME_STATE_MASK; pNameAddr->NameTypeState |= STATE_RELEASED; // // the proxy maintains a negative cache of names that // do not exist in WINS. These are timed out by // the remote hash timer just as the resolved names // are timed out. // if (!(NodeType & PROXY)) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } else if (pNameAddr->ProxyReqType != NAMEREQ_PROXY_REGISTRATION) { // // Add to cache as negative name entry // AddToHashTable (NbtConfig.pRemoteHashTbl, pNameAddr->Name, NbtConfig.pScope, pNameAddr->IpAddress, 0, pNameAddr, NULL, pDevContext, (USHORT) (SrcIsNameServer(SrcAddress,SrcPort) ? NAME_RESOLVED_BY_WINS:NAME_RESOLVED_BY_BCAST)); // // this could delete the name so do not reference after this point // } status = STATUS_BAD_NETWORK_PATH; } // // Set the backup name server to be the main name server // since we got a response from it. // Bug# 95280: Do this only if the Primary Wins is down! // if ( (!(Flags & WINS_NEG_RESPONSE)) && ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr == (ULONG)(htonl(pDeviceContext->lBackupServer))) { // switching the backup and the primary nameservers in // the config data structure since we got a name // registration response from the backup // SwitchToBackup(pDeviceContext); } CTESpinFree(&NbtConfig.JointLock,OldIrq1); // the completion routine has not run yet, so run it // #ifdef VXD // // chicago only has 4k of stack (yes, it's the operating system of 1995) // schedule an event to make this tcp connection later to reduce stack usage // CTEQueueForNonCritNonDispProcessing( DelayedSessEstablish, (tDGRAM_SEND_TRACKING *)Context, (PVOID)status, pClientCompletion, pDeviceContext); #else // // If pending is returned, we have submitted the check-for-addr request to lmhsvc. // if (status != STATUS_PENDING) { CompleteClientReq(pClientCompletion, (tDGRAM_SEND_TRACKING *)Context, status); } #endif return(STATUS_DATA_NOT_ACCEPTED); } else { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } } CTESpinFree(&NbtConfig.JointLock,OldIrq1); } else if (!NbtConfig.MultiHomed) { // *** Name Query reponse ==> Name not on Pending list *** // // it is possible for two multihomed machines connected on two subnets // to respond on both subnets to a name query. Therefore the querying // multihomed machine will ultimately receive two name query responses // each with a different ip address and think that a name conflict has // occurred when it hasn't. There is no way to detect this case // so just disable the conflict detection code below. Conflicts will // still be detected by WINS and by the node owning the name if someone // else tries to get the name, but conflicts will no longer be detected // by third parties that have the name in their cache. // (Currently this case will be handled only if we are not multi-homed!) // // // This code implements a Conflict timer for name // queries. Look at name query response, and then send // name conflict demands to the subsequent responders. // There is no timer involved, and this node will always respond // negatively to name query responses sent to it, for names // it has in its remote cache, if the timer has been stopped. // ( meaning that one response has been successfully received ). // // The name is not in the NameQueryPending list, so check the // remote table. // status = FindInHashTable(NbtConfig.pRemoteHashTbl, pName, pScope, &pNameAddr); NbtTrace(NBT_TRACE_NAMESRV, ("FindInHashTable return %!status! for %!NBTNAME!<%02x> from %!ipaddr!", status, pName, (unsigned)pName[15], SrcAddress)); // check the src IP address and compare it to the one in the // remote hash table // Since it is possible for the name server to send a response // late, do not accidently respond to those as conflicts. // Since a bcast query of a group name will generally result in // multiple responses, each with a different address, ignore // this case. // Also, ignore if the name is a preloaded lmhosts entry (though // can't think of an obvious case where we would receive a response // when a name is preloaded!) // if (NT_SUCCESS(status) && !(pNameAddr->NameTypeState & PRELOADED) && (!IsRemoteAddress(pNameAddr, SrcAddress)) && (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) && (pNameAddr->NameTypeState & STATE_RESOLVED) && (!IsNameServerForDevice (SrcAddress, pDeviceContext))) { // // Reference the name so that it doesn't disappear when // we are dereferencing it below! Bug# 233464 // NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_QUERY_RESPONSE); // // a different node is responding to the name query // so tell them to buzz off. // status = UdpSendResponse( lNameSize, pNameHdr, pNameAddr, (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], pDeviceContext, CONFLICT_ERROR, eNAME_REGISTRATION_RESPONSE, OldIrq1); NbtTrace(NBT_TRACE_NAMESRV, ("UdpSendResponse return %!status! for %!NBTNAME!<%02x> from %!ipaddr!", status, pName, (unsigned)pName[15], SrcAddress)); // // remove the name from the remote cache so the next time // we need to talk to it we do a name query // CTESpinLock(&NbtConfig.JointLock,OldIrq1); // set the name to the released state so that multiple // conflicting responses don't come in and decrement the // reference count to zero - in the case where some other // part of NBT is still using the name, that part of NBT // should do the final decrement - i.e. a datagram send to this // name. pNameAddr->NameTypeState &= ~NAME_STATE_MASK; pNameAddr->NameTypeState |= STATE_RELEASED; // // don't deref if someone else is using it now... // if (pNameAddr->RefCount == 2) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_QUERY_RESPONSE, TRUE); } CTESpinFree(&NbtConfig.JointLock,OldIrq1); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } if (pQueryAddrs) { CTEFreeMem (pQueryAddrs); pQueryAddrs = NULL; } return(STATUS_DATA_NOT_ACCEPTED); } else // *** REQUEST *** { NTSTATUS Locstatus; // // check the pdu size for errors // if (lNumBytes < (FIELD_OFFSET(tNAMEHDR,NameRR.NetBiosName) + lNameSize + 4)) { // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.QueryFromNet[2]: WARNING!!! Rejecting Request -- lNumBytes=<%d> < <%d>\n", lNumBytes, (FIELD_OFFSET(tNAMEHDR,NameRR.NetBiosName) + lNameSize + 4))); ASSERT(0); return(STATUS_DATA_NOT_ACCEPTED); } CTESpinLock(&NbtConfig.JointLock,OldIrq1); // call this routine // to see if the name is in the local table. // status = FindInHashTable(NbtConfig.pLocalHashTbl, pName, pScope, &pNameAddr); pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; if (NT_SUCCESS(status) && ((pNameAddr->NameTypeState & STATE_RESOLVED) || (pNameAddr->NameTypeState & STATE_RESOLVING))) { // // check if this is a node status request, since is looks very similar // to a name query, except that the NBSTAT field is 0x21 instead of // 0x20 // if ( ((PUCHAR)pQuery)[1] == QUEST_STATUS ) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); // // Reply only if this was not broadcast to us. // if (!fBroadcast) { Locstatus = SendNodeStatusResponse(pNameHdr, lNumBytes, pName, lNameSize, SrcAddress, SrcPort, pDeviceContext); } else { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("NBT: Bcast nodestatus req.- dropped\n")); } } else { // // check if this message came from Us or it is not // a broadcast since WINS on this machine could send it. // Note: this check must be AFTER the check for a node status request // since we can send node status requests to ourselves. // if ((!SrcIsUs(SrcAddress)) || (!(OpCodeFlags & FL_BROADCAST) #ifndef VXD && pWinsInfo #endif )) { // // build a positive name query response pdu // Locstatus = UdpSendResponse( lNameSize, pNameHdr, pNameAddr, (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], pDeviceContext, 0, eNAME_QUERY_RESPONSE, OldIrq1); } else CTESpinFree(&NbtConfig.JointLock,OldIrq1); } return(STATUS_DATA_NOT_ACCEPTED); } else if (((PUCHAR)pQuery)[1] == QUEST_STATUS && !fBroadcast && RtlCompareMemory(pName, NBT_BROADCAST_NAME, NETBIOS_NAMESIZE) == NETBIOS_NAMESIZE) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); Locstatus = SendNodeStatusResponse(pNameHdr, lNumBytes, pName, lNameSize, SrcAddress, SrcPort, pDeviceContext); return(STATUS_DATA_NOT_ACCEPTED); } else if ( !(OpCodeFlags & FL_BROADCAST) ) { // Build a negative response if this query was directed rather than // broadcast (since we do not want to Nack all broadcasts!) // check that it is not a node status request... // NbtTrace(NBT_TRACE_NAMESRV, ("FindInHashTable return %!status! for %!NBTNAME!<%02x> from %!ipaddr!", status, pName, (unsigned)pName[15], SrcAddress)); pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; if ( ((PUCHAR)pQuery)[1] == QUEST_NETBIOS ) { Locstatus = UdpSendResponse( lNameSize, pNameHdr, NULL, (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], pDeviceContext, 0, eNAME_QUERY_RESPONSE, OldIrq1); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } return(STATUS_DATA_NOT_ACCEPTED); } CTESpinFree(&NbtConfig.JointLock,OldIrq1); #ifdef PROXY_NODE // // check if this message came from Us !! (and return if so) // if (SrcIsUs(SrcAddress)) { return(STATUS_DATA_NOT_ACCEPTED); } pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize]; IF_PROXY(NodeType) { // check that it is not a node status request... if (((PUCHAR)pQuery)[1] == QUEST_NETBIOS ) { // // We have a broadcast name query request for a name that // is not in our local name table. If we are a proxy we need // to resolve the query if not already resolved and respond // with the address(es) to the node that sent the query. // // Note: We will respond only if the address that the name // resolves to is not on our subnet. For our own subnet addresses // the node that has the address will respond. // CTESpinLock(&NbtConfig.JointLock,OldIrq1); // // call this routine which looks for names without regard // to their state // status = FindInHashTable(NbtConfig.pRemoteHashTbl, pName, pScope, &pNameAddr); if (!NT_SUCCESS(status)) { status = FindOnPendingList(pName,pNameHdr,TRUE,NETBIOS_NAME_SIZE,&pNameAddr); if (!NT_SUCCESS(status)) { // // cache the name and contact the name // server to get the name to IP mapping // CTESpinFree(&NbtConfig.JointLock,OldIrq1); status = RegOrQueryFromNet( FALSE, //means it is a name query pDeviceContext, pNameHdr, lNameSize, pName, pScope); return(STATUS_DATA_NOT_ACCEPTED); } else { // // the name is on the pending list doing a name query // now, so ignore this name query request // CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } } else { // // The name can be in the RESOLVED, RESOLVING or RELEASED // state. // // // If in the RELEASED state, its reference count has to be // > 0 // //ASSERT(pNameAddr->NameTypeState & (STATE_RESOLVED | STATE_RESOLVING) || (pNameAddr->NameTypeState & STATE_RELEASED) && (pNameAddr->RefCount > 0)); // // Send a response only if the name is in the RESOLVED state // if (pNameAddr->NameTypeState & STATE_RESOLVED) { // // The PROXY sends a response if the address of the // node queries is not on the same subnet as the // node doing the query (or as us). It also responds // if the name is a group name or if it belongs to // a Pnode. Note: In theory there is no reason to // respond for group names since a member of the group // on the subnet will respond with their address if they // are alive - if they are B or M nodes - perhaps // the fPnode bit is not set correctly for groups, so // therefore always respond in case all the members // are pnodes. // // // If we have multiple network addresses in the same // broadcast area, then this test won't be sufficient // if ( ((SrcAddress & pDeviceContext->SubnetMask) != (pNameAddr->IpAddress & pDeviceContext->SubnetMask)) || (pNameAddr->fPnode) || !(pNameAddr->NameTypeState & NAMETYPE_UNIQUE) ) { IF_DBG(NBT_DEBUG_PROXY) KdPrint(("QueryFromNet: QUERY SATISFIED by PROXY CACHE -- name is %16.16s(%X); %s entry ; Address is (%d)\n", pNameAddr->Name,pNameAddr->Name[15], (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) ? "UNIQUE" : "INET_GROUP", pNameAddr->IpAddress)); // //build a positive name query response pdu // // UdpSendQueryResponse frees the spin lock // status = UdpSendResponse( lNameSize, pNameHdr, pNameAddr, (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], pDeviceContext, 0, eNAME_QUERY_RESPONSE, OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } } else { IF_DBG(NBT_DEBUG_PROXY) KdPrint(("QueryFromNet: REQUEST for Name %16.16s(%X) in %s state\n", pNameAddr->Name, pNameAddr->Name[15],( pNameAddr->NameTypeState & STATE_RELEASED ? "RELEASED" : "RESOLVING"))); NbtTrace(NBT_TRACE_PROXY, ("%!NBTNAME!<%02x> is in state %x for proxy request from %!ipaddr!", pName, (unsigned)pName[15], pNameAddr->NameTypeState & STATE_RELEASED, SrcAddress)); } CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } } } // end of proxy code #endif } // end of else (it is a name query request) return(STATUS_DATA_NOT_ACCEPTED); } //---------------------------------------------------------------------------- NTSTATUS RegResponseFromNet( IN tDEVICECONTEXT *pDeviceContext, IN PVOID pSrcAddress, IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNumBytes, IN USHORT OpCodeFlags ) /*++ Routine Description: This routine handles name registration responses from the net (i.e. from the name server most of the time since a broadcast name registration passes when there is no response. *** The response could be from an NBT node when it notices that the name registration is for a name that it has already claimed - i.e. the node is sending a NAME_CONFLICT_DEMAND - in this case the Rcode in the PDU will be CFT_ERR = 7. Arguments: Return Value: NTSTATUS - success or not - failure means no response to net --*/ { NTSTATUS status; ULONG lNameSize; CHAR pName[NETBIOS_NAME_SIZE]; PUCHAR pScope; tNAMEADDR *pNameAddr; //Get rid of this later. Use pNameAddr tTIMERQENTRY *pTimer; COMPLETIONCLIENT pClientCompletion; PVOID Context; PTRANSPORT_ADDRESS pSourceAddress; CTELockHandle OldIrq1; ULONG SrcAddress; SHORT SrcPort; pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); SrcPort = ntohs(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port); // // be sure the Pdu is at least a minimum size // if (lNumBytes < (NBT_MINIMUM_REGRESPONSE + NbtConfig.ScopeLength -1)) { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Registration Response TOO short = %X, Src = %X\n", lNumBytes,SrcAddress)); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("%.*X\n",lNumBytes/sizeof(ULONG),pNameHdr)); return(STATUS_DATA_NOT_ACCEPTED); } // // if Wins is locally attached then we will get registrations from // ourselves!! // if (SrcIsUs(SrcAddress) #ifndef VXD && !pWinsInfo #endif ) { return(STATUS_DATA_NOT_ACCEPTED); } // get the name out of the network pdu and pass to routine to check // local table *TODO* this assumes just one name in the Query response... // We need to handle group lists from the WINS server status = ConvertToAscii( (PCHAR)&pNameHdr->NameRR, lNumBytes - FIELD_OFFSET(tNAMEHDR,NameRR), pName, &pScope, &lNameSize); if (!NT_SUCCESS(status)) { // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.RegResponseFromNet: WARNING!!! Rejecting Request -- ConvertToAscii FAILed\n")); return(STATUS_DATA_NOT_ACCEPTED); } CTESpinLock(&NbtConfig.JointLock,OldIrq1); status = FindInHashTable(NbtConfig.pLocalHashTbl, pName, pScope, &pNameAddr); if (NT_SUCCESS(status) && (pNameAddr->AdapterMask & pDeviceContext->AdapterMask)) { NTSTATUS Localstatus; // // check the state of the name since this could be a registration // response or a name conflict demand // switch (pNameAddr->NameTypeState & NAME_STATE_MASK) { case STATE_CONFLICT: // // We only allow this state if we are currently trying to // get ourselves out of a Conflict scenario // We need to distinguish from the case where the name is // in conflict due to being dereferenced out // if (!pNameAddr->pAddressEle) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); break; } case STATE_RESOLVING: case STATE_RESOLVED: if (IS_POS_RESPONSE(OpCodeFlags)) { if (OpCodeFlags & FL_RECURAVAIL) { // turn on the refreshed bit in NextRefresh now // (when the timer completion routine is called) // only count names registered, not REFRESHES too! // if (pNameAddr->NameTypeState & STATE_RESOLVING) { IncrementNameStats(NAME_REGISTRATION_SUCCESS, SrcIsNameServer(SrcAddress,SrcPort)); } status = STATUS_SUCCESS; } else { // // in this case the name server is telling this node // to do an end node challenge on the name. However // this node does not have the code to do a challenge // so assume that this is a positive registration // response. // status = STATUS_SUCCESS; } } else if (!SrcIsNameServer(SrcAddress,SrcPort) && pNameAddr->pTimer == NULL) { // // 05/17/00 Fix "a malicious user can flush a cache entry by sending a unsolicited negative response" // Drop the response if it is not from a name server and runs out of time // status = STATUS_DATA_NOT_ACCEPTED; KdPrint(("Waring: discard a timeout registration response from a non-namesever\n")); CTESpinFree(&NbtConfig.JointLock,OldIrq1); NbtLogEvent (EVENT_NBT_DUPLICATE_NAME, SrcAddress, 0x105); break; } else if ((OpCodeFlags & FL_RCODE) >= FL_NAME_ACTIVE) { // if we are multihomed, then we only allow the name server // to send Name Active errors, since in normal operation this node // could generate two different IP address for the same name // query and confuse another client node into sending a Name // Conflict. So jump out if a name conflict has been received // from another node. // if ((NbtConfig.MultiHomed) && ((OpCodeFlags & FL_RCODE) == FL_NAME_CONFLICT)) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); break; } if (!IS_MESSENGER_NAME(pNameAddr->Name)) { // // We need to Q this event to a Worker thread since it // requires the name to be converted to Unicode // NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_LOG_EVENT); status = CTEQueueForNonDispProcessing (DelayedNbtLogDuplicateNameEvent, (PVOID) pNameAddr, IntToPtr(SrcAddress), IntToPtr(0x101), pDeviceContext, TRUE); if (!NT_SUCCESS(status)) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_LOG_EVENT, TRUE); NbtLogEvent (EVENT_NBT_DUPLICATE_NAME, SrcAddress, 0x101); } } status = STATUS_DUPLICATE_NAME; // // if the name is resolved and we get a negative response // then mark the name as in the conflict state so it can't // be used for any new sessions and this node will no longer // defend it. // if (pNameAddr->NameTypeState & STATE_RESOLVED) { pNameAddr->NameTypeState &= ~NAME_STATE_MASK; pNameAddr->NameTypeState |= STATE_CONFLICT; pNameAddr->ConflictMask |= pDeviceContext->AdapterMask; } } else { // // we got some kind of WINS server failure ret code // so just ignore it and assume the name registration // succeeded. // status = STATUS_SUCCESS; } // remove any timer block and call the completion routine // if the name is in the Resolving state only // LOCATION(0x40); if ((pTimer = pNameAddr->pTimer)) { tDGRAM_SEND_TRACKING *pTracker; USHORT SendTransactId; tDEVICECONTEXT *pDevContext; // check the transaction id to be sure it is the same as the one // sent. // LOCATION(0x41); pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; SendTransactId = pTracker->TransactionId; pDevContext = pTracker->pDeviceContext; if (pNameHdr->TransactId != SendTransactId) { LOCATION(0x42); CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } LOCATION(0x43); CHECK_PTR(pNameAddr); // // This could be either a Refresh or a name registration. In // either case, stop the timer and call the completion routine // for the client.(below). // pNameAddr->pTimer = NULL; Localstatus = StopTimer(pTimer,&pClientCompletion,&Context); // check if it is a response from the name server // and a M, or P or MS node, since we will need to send // refreshes to the name server for these node types // pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; // only accept pdus from the name server to change the Ttl. // The first check passes the case where WINS is on this machine // if ( #ifndef VXD (pWinsInfo && (SrcIsUs (SrcAddress))) || #endif (SrcAddress == pDeviceContext->lNameServerAddress)) { if (!(NodeType & BNODE) && (status == STATUS_SUCCESS) && (IS_POS_RESPONSE(OpCodeFlags))) { SetupRefreshTtl(pNameHdr,pNameAddr,lNameSize); // a name refresh response if in the resolved state } } else if ( SrcAddress == pDeviceContext->lBackupServer) { // switching the backup and the primary nameservers in // the config data structure since we got a name // registration response from the backup // SwitchToBackup(pDeviceContext); if (!(NodeType & BNODE) && (status == STATUS_SUCCESS) && (IS_POS_RESPONSE(OpCodeFlags))) { SetupRefreshTtl(pNameHdr,pNameAddr,lNameSize); } } // // mark name as refreshed if we got through to WINS Ok // if ((pClientCompletion) && (IS_POS_RESPONSE(OpCodeFlags))) { pNameAddr->RefreshMask |= pDevContext->AdapterMask; } CTESpinFree(&NbtConfig.JointLock,OldIrq1); // the completion routine has not run yet, so run it - this // is the Registration Completion routine and we DO want it to // run to mark the entry refreshed (NextRefresh) if (pClientCompletion) { LOCATION(0x44); (*pClientCompletion)(Context,status); } } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } break; default: // // if multiple (neg)registration responses are received, subsequent ones // after the first will go through this path because the state of // the name will have been changed by the first to CONFLICT // CTESpinFree(&NbtConfig.JointLock,OldIrq1); } } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } // end of else block (If name is not in local table) return(STATUS_DATA_NOT_ACCEPTED); } //---------------------------------------------------------------------------- NTSTATUS CheckRegistrationFromNet( IN tDEVICECONTEXT *pDeviceContext, IN PVOID pSrcAddress, IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNumBytes ) /*++ Routine Description: This routine handles name registrations from the network that are potentially duplicates of names in the local name table. It compares name registrations against its local table and defends any attempts to take a name that is owned by this node. This routine handles name registration REQUESTS. Arguments: Return Value: NTSTATUS - success or not - failure means no response to net --*/ { NTSTATUS status; ULONG lNameSize; CHAR pName[NETBIOS_NAME_SIZE]; PUCHAR pScope; tNAMEADDR *pNameAddr; tTIMERQENTRY *pTimer; PTRANSPORT_ADDRESS pSourceAddress; USHORT RegType; CTELockHandle OldIrq1; ULONG SrcAddress; pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); // // check the pdu size for errors // if (lNumBytes < (NBT_MINIMUM_REGREQUEST + (NbtConfig.ScopeLength-1))) { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Registration Request TOO short = %X,Src = %X\n", lNumBytes,SrcAddress)); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("%.*X\n",lNumBytes/sizeof(ULONG),pNameHdr)); return(STATUS_DATA_NOT_ACCEPTED); } // // check if this message came from Us !! (and return if so) // if (SrcIsUs(SrcAddress)) { return(STATUS_DATA_NOT_ACCEPTED); } // get the name out of the network pdu and pass to routine to check // local table *TODO* this assumes just one name in the Query response... status = ConvertToAscii( (PCHAR)&pNameHdr->NameRR, lNumBytes - FIELD_OFFSET(tNAMEHDR,NameRR), pName, &pScope, &lNameSize); if (!NT_SUCCESS(status)) { // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.CheckRegistrationFromNet: WARNING! Rejecting Request -- ConvertToAscii FAILed\n")); return(STATUS_DATA_NOT_ACCEPTED); } CTESpinLock(&NbtConfig.JointLock,OldIrq1); status = FindInHashTable(NbtConfig.pLocalHashTbl, pName, pScope, &pNameAddr); if (NT_SUCCESS(status)) { // don't defend the broadcast name if ((pName[0] == '*') || (STATUS_SUCCESS != GetNbFlags (pNameHdr, lNameSize, lNumBytes, &RegType))) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.CheckRegistrationFromNet: WARNING! Rejecting Request -- GetNbFlags FAILed\n")); return(STATUS_DATA_NOT_ACCEPTED); } // we defend against anyone trying to take a unique name, or anyone // trying to register a unique name for a group name we have. - if // the name is registered on this adapter // if (((pNameAddr->NameTypeState & NAMETYPE_UNIQUE) || ((pNameAddr->NameTypeState & NAMETYPE_GROUP) && ((RegType & FL_GROUP) == 0))) && (pNameAddr->AdapterMask & pDeviceContext->AdapterMask)) { // // check the state of the name since this could be registration // for the same name while we are registering the name. If another // node claims the name at the same time, then cancel the name // registration. // switch (pNameAddr->NameTypeState & NAME_STATE_MASK) { case STATE_RESOLVING: CHECK_PTR(pNameAddr); // remove any timer block and call the completion routine if ((pTimer = pNameAddr->pTimer)) { COMPLETIONCLIENT pClientCompletion; PVOID Context; pNameAddr->pTimer = NULL; status = StopTimer(pTimer,&pClientCompletion,&Context); if (pClientCompletion) { if (!IS_MESSENGER_NAME(pNameAddr->Name)) { // // We need to Q this event to a Worker thread since it // requires the name to be converted to Unicode // NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_LOG_EVENT); status = CTEQueueForNonDispProcessing (DelayedNbtLogDuplicateNameEvent, (PVOID) pNameAddr, IntToPtr(SrcAddress), IntToPtr(0x102), pDeviceContext, TRUE); if (!NT_SUCCESS(status)) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_LOG_EVENT, TRUE); NbtLogEvent (EVENT_NBT_DUPLICATE_NAME, SrcAddress, 0x102); } } status = STATUS_DUPLICATE_NAME; // CHANGE the state of the entry // the completion routine has not run yet, so run it CTESpinFree(&NbtConfig.JointLock,OldIrq1); (*pClientCompletion)(Context,status); CTESpinLock(&NbtConfig.JointLock,OldIrq1); } } break; case STATE_RESOLVED: // // We must defend our name against this Rogue attempting to steal // our Name! ( unless the name is "*") // status = UdpSendResponse( lNameSize, pNameHdr, pNameAddr, (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], pDeviceContext, REGISTRATION_ACTIVE_ERR, eNAME_REGISTRATION_RESPONSE, OldIrq1); CTESpinLock(&NbtConfig.JointLock,OldIrq1); break; } } CTESpinFree(&NbtConfig.JointLock,OldIrq1); } else { // // NOTE: We have the Joint Lock // // // The name is not in the local name table, so check if the proxy is // on and if we want the proxy to check name registrations. The // trouble with checking name registrations is that the proxy code // only does a name query, so it will fail any node that is trying // to change its address such as a RAS client coming in on a downlevel // NT machine that only does broadcast name registrations. If // that same user had previously dialled in on a WINS supporting RAS // machine, their address would be in WINS, then dialling in on the // downlevel machine would find that 'old' registration and deny // the new one ( even though it is the same machine just changing // its ip address). // #ifdef PROXY_NODE if ((NodeType & PROXY) && (NbtConfig.EnableProxyRegCheck)) { BOOLEAN fResp = (BOOLEAN)FALSE; // // If name is RESOLVED in the remote table, has a different // address than the node claiming the name, is not on the // same subnet or is a Pnode then send a negative name // registration response // // // call this routine to find the name, since it does not // interpret the state of the name as does FindName() // status = FindInHashTable(NbtConfig.pRemoteHashTbl, pName, pScope, &pNameAddr); if (!NT_SUCCESS(status)) { // // We need to send a query to WINS to // see if the name is already taken or not. // CTESpinFree(&NbtConfig.JointLock,OldIrq1); status = RegOrQueryFromNet( TRUE, //means it is a reg. from the net pDeviceContext, pNameHdr, lNameSize, pName, pScope ); return(STATUS_DATA_NOT_ACCEPTED); } // // If the name is in the RESOLVED state, we need to determine // whether we should respond or not. For a name that is not // the RESOLVED state, the decision is simple. We don't respond if (pNameAddr->NameTypeState & STATE_RESOLVED) { ULONG IPAdd; if (STATUS_SUCCESS != GetNbFlags(pNameHdr, lNameSize, lNumBytes, &RegType)) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.CheckRegistrationFromNet[2]: WARNING! Rejecting Request -- GetNbFlags FAILed\n")); return (STATUS_DATA_NOT_ACCEPTED); } // // If a unique name is being registered but our cache shows // the name to be a group name (normal or internet), we // send a negative name registration response. // // If a node on the same subnet has responded negatively also, // it is ok. It is possible that WINS/NBNS has old // information but there is no easy way to determine the // network address of the node that registered the group. // (For a normal group, there could have been several // nodes that registered it -- their addresses are not stored // by WINS. // if (!(RegType & FL_GROUP) && !(pNameAddr->NameTypeState & NAMETYPE_UNIQUE)) { fResp = TRUE; } else { tGENERALRR UNALIGNED *pResrcRecord; // get the Ip address out of the Registration request pResrcRecord = (tGENERALRR *) &pNameHdr->NameRR.NetBiosName[lNameSize]; IPAdd = ntohl(pResrcRecord->IpAddress); // // If a group name is being registered but our cache shows // the name to be a unique name (normal or internet) or // if a UNIQUE name is being registered but clashes with // a unique name with a different address, we check if the // the addresses belong to the same subnet. If they do, we // don't respond, else we send a negative registration // response. // // Note: We never respond to a group registration // that clashes with a group name in our cache. // if (((RegType & FL_GROUP) && (pNameAddr->NameTypeState & NAMETYPE_UNIQUE)) || (!(RegType & FL_GROUP) && (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) && IPAdd != pNameAddr->IpAddress)) { IF_DBG(NBT_DEBUG_PROXY) KdPrint(("CheckReg:Subnet Mask = (%x)\nIPAdd=(%x)\npNameAddr->IPAdd = (%x)\npNameAddr->fPnode=(%d)\nIt is %s name %16.16s(%X)\nRegType Of Name Recd is %x\n---------------\n", pDeviceContext->SubnetMask, IPAdd, pNameAddr->IpAddress, pNameAddr->fPnode, pNameAddr->NameTypeState & NAMETYPE_GROUP ? "GROUP" : "UNIQUE", pName, pName[15], RegType)); // // Are the querying node and the queried node on the // same subnet ? // if (((IPAdd & pDeviceContext->SubnetMask) != (pNameAddr->IpAddress & pDeviceContext->SubnetMask)) || (pNameAddr->fPnode)) { fResp = TRUE; } } } // // If a negative response needs to be sent, send it now // if (fResp) { IF_DBG(NBT_DEBUG_PROXY) KdPrint(("CheckRegistrationFromNet: Sending a negative name registration response for name %16.16s(%X) to node with address (%d)\n", pNameAddr->Name, pNameAddr->Name[15], IPAdd)); // // a different node is responding to the name query // so tell them to buzz off. // status = UdpSendResponse( lNameSize, pNameHdr, pNameAddr, (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], pDeviceContext, REGISTRATION_ACTIVE_ERR, eNAME_REGISTRATION_RESPONSE, OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } } // end of if (NAME is in the RESOLVED state) } #endif CTESpinFree(&NbtConfig.JointLock,OldIrq1); } return(STATUS_DATA_NOT_ACCEPTED); } //---------------------------------------------------------------------------- NTSTATUS NameReleaseFromNet( IN tDEVICECONTEXT *pDeviceContext, IN PVOID pSrcAddress, IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNumBytes ) /*++ Routine Description: This routine handles name releases that arrive from the Net. The idea is to delete the name from the remote cache if it exists there so that this node does not erroneously use that cached information anymore. Arguments: Return Value: NTSTATUS - success or not - failure means no response to net --*/ { NTSTATUS status; LONG lNameSize; CHAR pName[NETBIOS_NAME_SIZE]; PUCHAR pScope; tNAMEADDR *pNameAddr; PTRANSPORT_ADDRESS pSourceAddress; CTELockHandle OldIrq1; USHORT OpCodeFlags; tTIMERQENTRY *pTimer; ULONG SrcAddress; ULONG Flags; USHORT SendTransactId; tDGRAM_SEND_TRACKING *pTracker; BOOLEAN bLocalTable; tGENERALRR UNALIGNED *pRemainder; USHORT SrcPort; ULONG Rcode; pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); SrcPort = ntohs(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port); // // check if we should not release our names on demand // if (NbtConfig.NoNameReleaseOnDemand) { // // Don't log this event if // 1. WINS is running locally // 2. The request is coming from the WINS server // CTESpinLock(&NbtConfig.JointLock,OldIrq1); if (NULL == pWinsInfo && !SrcIsNameServer(SrcAddress,SrcPort)) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } return(STATUS_DATA_NOT_ACCEPTED); } // // check the pdu size for errors // if (lNumBytes < (NBT_MINIMUM_REGRESPONSE + NbtConfig.ScopeLength -1)) { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Release Request/Response TOO short = %X, Src = %X\n",lNumBytes, SrcAddress)); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("%.*X\n",lNumBytes/sizeof(ULONG),pNameHdr)); return(STATUS_DATA_NOT_ACCEPTED); } // // check if this message came from Us !! // if (SrcIsUs(SrcAddress)) { return(STATUS_DATA_NOT_ACCEPTED); } // get the name out of the network pdu and pass to routine to check status = ConvertToAscii( (PCHAR)&pNameHdr->NameRR, lNumBytes - FIELD_OFFSET(tNAMEHDR,NameRR), pName, &pScope, &lNameSize); if (!NT_SUCCESS(status)) { // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.NameReleaseFromNet: WARNING!!! Rejecting Request -- ConvertToAscii FAILed\n")); return(STATUS_DATA_NOT_ACCEPTED); } OpCodeFlags = pNameHdr->OpCodeFlags; pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); // // *** RESPONSE *** // if (OpCodeFlags & OP_RESPONSE) { // // check the pdu size for errors // if (lNumBytes < (NBT_MINIMUM_REGRESPONSE + NbtConfig.ScopeLength -1)) { return(STATUS_DATA_NOT_ACCEPTED); } // // call this routine to find the name, since it does not interpret the // state of the name as does FindName() // CTESpinLock(&NbtConfig.JointLock,OldIrq1); status = FindInHashTable(NbtConfig.pLocalHashTbl, pName, pScope, &pNameAddr); if (!NT_SUCCESS(status)) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } // Get the Timer block if (!(pTimer = pNameAddr->pTimer)) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } // // the name server is responding to a name release request // // check the transaction id to be sure it is the same as the one // sent. // pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; SendTransactId = pTracker->TransactionId; if (pNameHdr->TransactId != SendTransactId) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } // for MS & M nodes if there is a response from the name server, // then switch to broadcast name release. // // switch (NodeType & NODE_MASK) { case MNODE: case MSNODE: if (SrcIsNameServer(SrcAddress,SrcPort)) { Flags = pTracker->Flags; if (Flags & NBT_NAME_SERVER) { // // the next timeout will then switch to broadcast name // release. // pTimer->Retries = 1; } } CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); case PNODE: // // // this routine puts the timer block back on the timer Q, and // handles race conditions to cancel the timer when the timer // is expiring. // if ((pTimer = pNameAddr->pTimer)) { COMPLETIONCLIENT pClientCompletion; PVOID Context; CHECK_PTR(pNameAddr); pNameAddr->pTimer = NULL; status = StopTimer(pTimer,&pClientCompletion,&Context); CTESpinFree(&NbtConfig.JointLock,OldIrq1); // the completion routine has not run yet, so run it if (pClientCompletion) { (*pClientCompletion)(Context,STATUS_SUCCESS); } } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } return(STATUS_DATA_NOT_ACCEPTED); case BNODE: default: // // normally there should be no response to a name release // from a Bnode, but if there is, ignore it. // CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } } else { // // It is a RELEASE REQUEST - so decide if the name should be removed // from the remote or local table // // check for errors Bug# 125651 (NBT_MINIMUM_REGREQUEST == 68) // // Check for valid PDU size: // lNumBytes >= 12 + [1+lNameSize] + 22(sizeof(tGENERALRR)) // Check for Overflow error during comparisons with local names: // lNumBytes >= 12 + [1+32(EncodedNetBios name)+Scope(==1 if NULL Scope)] + 22 // if ((lNumBytes < ((NBT_MINIMUM_REGREQUEST-33) + lNameSize)) || (lNumBytes < (NBT_MINIMUM_REGREQUEST + (NbtConfig.ScopeLength-1)))) { // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.NameReleaseFromNet[2]: WARNING!!! Rejecting Request -- lNumBytes<%d> < <%d>\n", lNumBytes, (NBT_MINIMUM_REGREQUEST + (NbtConfig.ScopeLength-1)))); return(STATUS_DATA_NOT_ACCEPTED); } CTESpinLock(&NbtConfig.JointLock,OldIrq1); // check the REMOTE hash table for the name... // status = FindInHashTable(NbtConfig.pRemoteHashTbl, pName, pScope, &pNameAddr); bLocalTable = FALSE; if (!NT_SUCCESS(status)) { // // check the LOCAL name table for the name since the name server // could be doing the equivalent of a name conflict demand // status = FindInHashTable(NbtConfig.pLocalHashTbl, pName, pScope, &pNameAddr); bLocalTable = TRUE; } if (NT_SUCCESS(status)) { // check if the address being released corresponds to the one in // the table - if not then ignore the release request - since someone // else presumably tried to get the name, was refused and now is // sending a name release request. // pRemainder = (tGENERALRR *)&pNameHdr->NameRR.NetBiosName[lNameSize]; if (pNameAddr->IpAddress != (ULONG)ntohl(pRemainder->IpAddress)) { status = STATUS_UNSUCCESSFUL; } } else { // // This name is neither in our local or remote hash table, so don't // process any further! // Bug#: 144944 // CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } if (NT_SUCCESS(status)) { // // Don't remove group names, since a single group member // releasing the name does not matter. Group names time // out of the Remote table. // if (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) { switch (pNameAddr->NameTypeState & NAME_STATE_MASK) { case STATE_RESOLVING: // // stop any timer that may be going // CHECK_PTR(pNameAddr); // // Local table means that it is a name registration // and we must avoid calling CompleteClientReq // if (pTimer = pNameAddr->pTimer) { COMPLETIONCLIENT pClientCompletion; PVOID pContext; pNameAddr->pTimer = NULL; status = StopTimer(pTimer,&pClientCompletion,&pContext); // this will complete the irp(s) back to the clients if (pClientCompletion) { CTESpinFree(&NbtConfig.JointLock,OldIrq1); if (bLocalTable) { (*pClientCompletion) (pContext,STATUS_DUPLICATE_NAME); } else { CompleteClientReq (pClientCompletion, pContext, STATUS_TIMEOUT); } CTESpinLock(&NbtConfig.JointLock,OldIrq1); } } break; case STATE_RESOLVED: // dereference the name if it is in the remote table, // this should change the state to RELEASED. For the // local table just change the state to CONFLICT, since // the local client still thinks it has the name open, // however in the conflict state the name cannot be use // to place new sessions and this node will not respond // to name queries for the name. // if (!bLocalTable) { // // if this is a pre-loaded name, just leave it alone // if (!(pNameAddr->NameTypeState & PRELOADED)) { // // if someone is still using the name, do not // dereference it, since that would leave the // ref count at 1, and allow RemoteHashTimeout // code to remove it before the client using // the name is done with it. Once the client is // done with it (i.e. a connect request), they // will deref it , setting the ref count to 1 and // it will be suitable for reuse. // if (pNameAddr->RefCount > 1) { pNameAddr->NameTypeState &= ~NAME_STATE_MASK; pNameAddr->NameTypeState |= STATE_RELEASED; } else { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } } } else { pNameAddr->NameTypeState &= ~NAME_STATE_MASK; pNameAddr->NameTypeState |= STATE_CONFLICT; pNameAddr->ConflictMask |= pDeviceContext->AdapterMask; NbtLogEvent (EVENT_NBT_NAME_RELEASE, SrcAddress, 0x103); } break; default: break; } } // // tell WINS that the name released ok // Rcode = 0; } else { Rcode = NAME_ERROR; } // // Only respond if not a broadcast... // if (!(OpCodeFlags & FL_BROADCAST)) { status = UdpSendResponse( lNameSize, pNameHdr, NULL, (PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0], pDeviceContext, Rcode, eNAME_RELEASE, OldIrq1); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq1); } } // end of Release Request processing return (STATUS_DATA_NOT_ACCEPTED); } //---------------------------------------------------------------------------- NTSTATUS WackFromNet( IN tDEVICECONTEXT *pDeviceContext, IN PVOID pSrcAddress, IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNumBytes ) /*++ Routine Description: This routine handles Wait Acks from the name server. It finds the corresponding name service transaction and changes the timeout of that transaction according to the TTL field in the WACK. Arguments: Return Value: NTSTATUS - success or not - failure means no response to net --*/ { NTSTATUS status; ULONG lNameSize; CHAR pName[NETBIOS_NAME_SIZE]; PUCHAR pScope; tNAMEADDR *pNameAddr; CTELockHandle OldIrq1; ULONG Ttl; tTIMERQENTRY *pTimerEntry; // // check the pdu size for errors // if (lNumBytes < (NBT_MINIMUM_WACK + NbtConfig.ScopeLength -1)) { KdPrint(("Nbt:WACK TOO short = %X\n",lNumBytes)); return(STATUS_DATA_NOT_ACCEPTED); } // get the name out of the network pdu and pass to routine to check status = ConvertToAscii( (PCHAR)&pNameHdr->NameRR, lNumBytes - FIELD_OFFSET(tNAMEHDR,NameRR), pName, &pScope, &lNameSize); if (!NT_SUCCESS(status)) { // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.WackFromNet: WARNING!!! Rejecting Request -- ConvertToAscii FAILed\n")); return(STATUS_DATA_NOT_ACCEPTED); } CTESpinLock(&NbtConfig.JointLock,OldIrq1); #ifdef VXD if ( FindContextDirect( pNameHdr->TransactId ) != NULL ) { status = STATUS_SUCCESS; } else { #endif // VXD status = FindInHashTable(NbtConfig.pLocalHashTbl, pName, pScope, &pNameAddr); #ifdef VXD } #endif // VXD if (NT_SUCCESS(status)) { Ttl = *(ULONG UNALIGNED *)((ULONG_PTR)&pNameHdr->NameRR.NetBiosName[0] + lNameSize + FIELD_OFFSET(tQUERYRESP,Ttl) ); Ttl = ntohl(Ttl); if (pTimerEntry = pNameAddr->pTimer) { // convert seconds to milliseconds and put into the DeltaTime // field so that when the next timeout occurs it changes the timer // value to this new one. Depending on how many timeouts are left // this could cause the client to wait several times the WACK timeout // value. For example a Name query nominally has two retries, so if // the WACK returns before the first retry then the total time waited // will be 2*Ttl. This is not a problem since the real reason for // the timeout is to prevent waiting forever for a dead name server. // If the server returns a WACK it is not dead and the chances are // that it will return a response before the timeout anyway. // // The timeout routine checks if TIMER_RETIMED is set and restarts // the timeout without any processing if that is true ( and clears // the flag too). // Ttl *= 1000; if (Ttl > pTimerEntry->DeltaTime) { pTimerEntry->DeltaTime = Ttl; pTimerEntry->Flags |= TIMER_RETIMED; } } } CTESpinFree(&NbtConfig.JointLock,OldIrq1); return(STATUS_DATA_NOT_ACCEPTED); } //---------------------------------------------------------------------------- VOID SetupRefreshTtl( IN tNAMEHDR UNALIGNED *pNameHdr, IN tNAMEADDR *pNameAddr, IN LONG lNameSize ) /*++ Routine Description: This routine handles name refresh timeouts. It looks at the Ttl in the registration response and determines if the Node's refresh timeout should be lengthened or shortened. To do this both the Ttl and the name associated with the Ttl are kept in the Config structure. If the Ttl becomes longer for the shortest names Ttl, then all the names use the longer value. Arguments: Return Value: NTSTATUS - success or not - failure means no response to net --*/ { NTSTATUS status; ULONG Ttl; tTIMERQENTRY *pTimerQEntry; // the Ttl in the pdu is in seconds. We need to convert it to milliseconds // to use for our timer. This limits the timeout value to about 50 days // ( 2**32 / 3600/24/1000 - milliseconds converted to days.) // Ttl = *(ULONG UNALIGNED *) ((PUCHAR)&pNameHdr->NameRR.NetBiosName[0] + lNameSize + FIELD_OFFSET(tQUERYRESP,Ttl)); Ttl = ntohl(Ttl); // the Ttl value may overflow the value we can store in Milliseconds, // check for this case, and if it happens, use the longest timeout possible // that still runs refresh, - i.e. NBT_MAXIMUM_TTL disables refresh // altogether, so use NBT_MAXIMUM_TTL-1). if (Ttl >= 0xFFFFFFFF/1000) { Ttl = NBT_MAXIMUM_TTL - 1; } else { Ttl *= 1000; // convert to milliseconds } // a zero Ttl means infinite, so set time the largest timeout // if (Ttl == 0) { Ttl = NBT_MAXIMUM_TTL; // set very large number which turns off refreshes } else if (Ttl < NBT_MINIMUM_TTL) { Ttl = NBT_MINIMUM_TTL; } // Set the Ttl for the name record // pNameAddr->Ttl = Ttl; // // decide what to do about the existing timer.... // If the new timeout is shorter, then cancel the // current timeout and start another one. // if (Ttl < NbtConfig.MinimumTtl) { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Shortening Refresh Ttl from %d to %d\n", NbtConfig.MinimumTtl, Ttl)); NbtConfig.MinimumTtl = (ULONG)Ttl; // // don't allow the stop timer routine to call the completion routine // for the timer. // if (pTimerQEntry = NbtConfig.pRefreshTimer) { CHECK_PTR(pTimerQEntry); pTimerQEntry->TimeoutRoutine = NULL; status = StopTimer(pTimerQEntry,NULL,NULL); } // keep the timeout for checking refreshes to about 10 minutes // max. (MAX_REFRESH_CHECK_INTERVAL). If the refresh interval // is less than 80 minutes then always use a refresh divisor of // 8 - this allows the initial default ttl of 16 minutes to result // in retries every 2 minutes. // NbtConfig.RefreshDivisor = NbtConfig.MinimumTtl/MAX_REFRESH_CHECK_INTERVAL; if (NbtConfig.RefreshDivisor < REFRESH_DIVISOR) { NbtConfig.RefreshDivisor = REFRESH_DIVISOR; } // // start the timer // status = StartTimer(RefreshTimeout, Ttl/NbtConfig.RefreshDivisor, NULL, // context value NULL, // context2 value NULL, NULL, NULL, // This Timer is a global timer &NbtConfig.pRefreshTimer, 0, TRUE); #if DBG if (!NT_SUCCESS(status)) { KdPrint(("Nbt:Failed to start a new timer for refresh\n")); } #endif } else if (Ttl > NbtConfig.MinimumTtl) { tHASHTABLE *pHashTable; LONG i; PLIST_ENTRY pHead,pEntry; // PUT this code back in again, since it is possible that the name // server could miss registering a name due to being busy and if we // lengthen the timeout here then that name will not get into wins for // a very long time. // the shortest Ttl got longer, check if there is another shortest // Ttl by scanning the local name table. // pHashTable = NbtConfig.pLocalHashTbl; for (i=0;i < pHashTable->lNumBuckets ;i++ ) { pHead = &pHashTable->Bucket[i]; pEntry = pHead->Flink; while (pEntry != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); // // Find a valid name with a lower TTL if possible // if ((pNameAddr->Name[0] != '*') && ((pNameAddr->NameTypeState & STATE_RESOLVED)) && (pNameAddr->Ttl < (ULONG)Ttl) && (!IsBrowserName(pNameAddr->Name)) && (!(pNameAddr->NameTypeState & NAMETYPE_QUICK))) { if (pNameAddr->Ttl >= NBT_MINIMUM_TTL) { NbtConfig.MinimumTtl = pNameAddr->Ttl; } return; } pEntry = pEntry->Flink; } } // // if we get to here then there is no shorter ttl, so use the new // ttl received from the WINS as the ttl. The next time the refresh // timer expires it will restart with this new ttl // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Lengthening Refresh Ttl from %d to %d\n", NbtConfig.MinimumTtl, Ttl)); NbtConfig.MinimumTtl = Ttl; // keep the timeout for checking refreshes to about 10 minutes // max. (MAX_REFRESH_CHECK_INTERVAL). If the refresh interval // is less than 80 minutes then always use a refresh divisor of // 8 - this allows the initial default ttl of 16 minutes to result // in retries every 2 minutes. // NbtConfig.RefreshDivisor = NbtConfig.MinimumTtl/MAX_REFRESH_CHECK_INTERVAL; if (NbtConfig.RefreshDivisor < REFRESH_DIVISOR) { NbtConfig.RefreshDivisor = REFRESH_DIVISOR; } } } //---------------------------------------------------------------------------- NTSTATUS DecodeNodeStatusResponse( IN tNAMEHDR UNALIGNED *pNameHdr, IN ULONG Length, IN PUCHAR pName, IN ULONG lNameSize, IN tIPADDRESS SrcIpAddress ) /*++ Routine Description: This routine handles putting the node status response pdu into the clients MDL. Arguments: Return Value: none --*/ { NTSTATUS status; PLIST_ENTRY pHead; PLIST_ENTRY pEntry; PLIST_ENTRY pNext; tNODESTATUS UNALIGNED *pNodeStatus; CTELockHandle OldIrq; CTELockHandle OldIrq2; tDGRAM_SEND_TRACKING *pTracker; tTIMERQENTRY *pTimer; COMPLETIONCLIENT pClientCompletion; PVOID pClientContext; BOOL MatchFound=FALSE; tIPADDRESS IpAddress; PVOID pBuffer; // first find the originating request in the NodeStatus list CTESpinLock(&NbtConfig.JointLock,OldIrq2); CTESpinLock(&NbtConfig,OldIrq); pEntry = pHead = &NbtConfig.NodeStatusHead; while ((pEntry = pEntry->Flink) != pHead) { pTracker = CONTAINING_RECORD(pEntry,tDGRAM_SEND_TRACKING,Linkage); ASSERT (NBT_VERIFY_HANDLE (pTracker, NBT_VERIFY_TRACKER)); ASSERT (pTracker->TrackerType == NBT_TRACKER_SEND_NODE_STATUS); if (!(pTimer = pTracker->pTimer)) { continue; } MatchFound = FALSE; // // find who sent the request originally // if (pTracker->Flags & REMOTE_ADAPTER_STAT_FLAG) { IpAddress = Nbt_inet_addr(pTracker->pNameAddr->Name, REMOTE_ADAPTER_STAT_FLAG); } else { IpAddress = 0; } if ((CTEMemEqu(pName,pTracker->pNameAddr->Name,NETBIOS_NAME_SIZE)) || ((IpAddress==SrcIpAddress)&&(IpAddress!=0))) { // // if we directed node status request to an ipaddr without knowing // its netbios name, then name is stored as "* ". // if ((pName[0] == '*') && (IpAddress == 0) && (pTracker->pNameAddr->pIpAddrsList)) { int i=0; // // SrcIpAddress may not match the ipaddr to which we sent if // remote host is multihomed: so search whole list of all // ipaddrs for that host // ASSERT(pTracker->pNameAddr->pIpAddrsList); while(pTracker->pNameAddr->pIpAddrsList[i]) { if (pTracker->pNameAddr->pIpAddrsList[i++] == SrcIpAddress) { MatchFound = TRUE; break; } } } else { MatchFound = TRUE; } } if (MatchFound) { RemoveEntryList(pEntry); InitializeListHead (&pTracker->Linkage); // in case the Timeout routine is running // this is the amount of data left, that we do not want to go // beyond, otherwise the system will bugcheck // Length -= FIELD_OFFSET(tNAMEHDR,NameRR.NetBiosName) + lNameSize; pNodeStatus = (tNODESTATUS *)&pNameHdr->NameRR.NetBiosName[lNameSize]; CTESpinFree(&NbtConfig,OldIrq); status = StopTimer(pTimer,&pClientCompletion,&pClientContext); CTESpinFree(&NbtConfig.JointLock,OldIrq2); if (pClientCompletion) { tDGRAM_SEND_TRACKING *pClientTracker = (tDGRAM_SEND_TRACKING *) pClientContext; pClientTracker->RemoteIpAddress = SrcIpAddress; pClientTracker->pNodeStatus = pNodeStatus; pClientTracker->NodeStatusLen = Length; (*pClientCompletion) (pClientContext, STATUS_SUCCESS); } CTESpinLock(&NbtConfig.JointLock,OldIrq2); CTESpinLock(&NbtConfig,OldIrq); break; } } CTESpinFree(&NbtConfig,OldIrq); CTESpinFree(&NbtConfig.JointLock,OldIrq2); return(STATUS_UNSUCCESSFUL); } //---------------------------------------------------------------------------- typedef enum _dest_type { IP_ADDR, DNS, NETBIOS } DEST_TYPE; DEST_TYPE GetDestType( IN tDGRAM_SEND_TRACKING *pClientTracker ) /*++ Routine Description: Classifies name passed in as an IP addr/Netbios name/Dns name Arguments: Return Value: DEST_TYPE --*/ { IF_DBG(NBT_DEBUG_NETBIOS_EX) KdPrint(("Nbt.GetDestType: Name=<%16.16s:%x>\n", pClientTracker->pDestName, pClientTracker->pDestName[15])); if (Nbt_inet_addr(pClientTracker->pDestName, 0)) { return IP_ADDR; } else if (pClientTracker->RemoteNameLength > NETBIOS_NAME_SIZE) { return DNS; } else { return NETBIOS; } } //---------------------------------------------------------------------------- VOID ExtractServerNameCompletion( IN tDGRAM_SEND_TRACKING *pClientTracker, IN NTSTATUS status ) /*++ Routine Description: This Routine searches for the server name (name ending with 0x20) from the list of names returned by node status response, and adds that name to the remote hash table. Arguments: pNodeStatus Node status response from the remote host pClientContext Tracker for the seutp phase IpAddress Ip address of the node that just responded Return Value: none --*/ { ULONG i; UCHAR NodeFlags, NameExtension; PCHAR pName; PCHAR pBestName = NULL; tSESSIONREQ *pSessionReq; PUCHAR pCopyTo; DEST_TYPE DestType; ULONG TrackerFlags; COMPLETIONCLIENT pClientCompletion; PVOID pClientContext; tNODESTATUS UNALIGNED *pNodeStatus = pClientTracker->pNodeStatus; tIPADDRESS IpAddress = pClientTracker->RemoteIpAddress; tDEVICECONTEXT *pDeviceContext = NULL; BOOL bForce20NameLookup = FALSE; ASSERT (NBT_VERIFY_HANDLE (pClientTracker, NBT_VERIFY_TRACKER)); if (STATUS_SUCCESS == status) { status = STATUS_REMOTE_NOT_LISTENING; DestType = GetDestType(pClientTracker); TrackerFlags = pClientTracker->Flags; NameExtension = pClientTracker->pDestName[NETBIOS_NAME_SIZE-1]; // // If the not a Netbios name, and the 16th character is ASCII, // then look for the Server name // if ((DestType != NETBIOS) && (NameExtension > 0x20 ) && (NameExtension < 0x7f )) { NameExtension = SPECIAL_SERVER_SUFFIX; } IF_DBG(NBT_DEBUG_NETBIOS_EX) KdPrint(("ExtractSrvName: DestType: %d\n", DestType)); bForce20NameLookup = FALSE; again: if (bForce20NameLookup) { DestType = DNS; NameExtension = 0x20; } for(i =0; iNumNames; i++) { pName = &pNodeStatus->NodeName[i].Name[0]; NodeFlags = pNodeStatus->NodeName[i].Flags; // // make sure it's a unique name (for connects only, for dgram sends, group names are fine) // and is not in conflict or released // if ((NodeFlags & (NODE_NAME_CONFLICT | NODE_NAME_RELEASED)) || !(((TrackerFlags & SESSION_SETUP_FLAG) && !(NodeFlags & GROUP_STATUS)) || (TrackerFlags & (DGRAM_SEND_FLAG | REMOTE_ADAPTER_STAT_FLAG)))) { continue; } if ((DestType == IP_ADDR) || (DestType == DNS)) { if (pName[NETBIOS_NAME_SIZE-1] != NameExtension) { continue; } // // For IP addresses and DNS names, we map the 0x20 name to the corresp 0x0 name // for datagram sends. // if (pClientTracker->Flags & DGRAM_SEND_FLAG) { IF_DBG(NBT_DEBUG_NETBIOS_EX) KdPrint(("ExtractServerName: Mapping 0x20 name to 0x0\n")); pName[NETBIOS_NAME_SIZE-1] = 0x0; } } // // For Netbios names (resolved via DNS), we match the 16th byte exactly // else if (pName[NETBIOS_NAME_SIZE-1] != pClientTracker->pDestName[NETBIOS_NAME_SIZE-1]) { continue; } pDeviceContext = GetDeviceFromInterface(ntohl(IpAddress), TRUE); status = STATUS_SUCCESS; break; // found the name: done with the for loop } if( !NT_SUCCESS(status) && !bForce20NameLookup && pClientTracker->RemoteNameLength == NETBIOS_NAME_SIZE) { bForce20NameLookup = TRUE; goto again; } if (NT_SUCCESS(status)) { // // fix up the connection tracker to point to the right name, now // that we know the server name to connect to // // // The FIND_NAME_FLAG was set to indicate that this is not a session setup attempt so // we can avoid the call to ConvertToHalfAscii. // if (!(pClientTracker->Flags & FIND_NAME_FLAG)) { if (pClientTracker->Flags & SESSION_SETUP_FLAG) { CTEMemCopy(pClientTracker->SendBuffer.pBuffer,pName,NETBIOS_NAME_SIZE); CTEMemCopy(pClientTracker->pConnEle->RemoteName,pName,NETBIOS_NAME_SIZE); #ifdef VXD CTEMemCopy(&pClientTracker->pClientIrp->ncb_callname[0],pName,NETBIOS_NAME_SIZE); #endif // VXD pSessionReq = pClientTracker->SendBuffer.pDgramHdr; // // overwrite the Dest HalfAscii name in the Session Pdu with the correct name // pCopyTo = ConvertToHalfAscii((PCHAR)&pSessionReq->CalledName.NameLength, pName, NbtConfig.pScope, NbtConfig.ScopeLength); } else if (pClientTracker->Flags & DGRAM_SEND_FLAG) { PCHAR pCopyTo; tDGRAMHDR *pDgramHdr; // // Overwrite the dest name, so SendDgramContinue can find the name // in the caches. // CTEMemCopy(pClientTracker->pDestName,pName,NETBIOS_NAME_SIZE); // // Copy over the actual dest name in half-ascii // This is immediately after the SourceName; so offset the // dest by the length of the src name. // pDgramHdr = pClientTracker->SendBuffer.pDgramHdr; pCopyTo = (PVOID)&pDgramHdr->SrcName.NameLength; IF_DBG(NBT_DEBUG_NETBIOS_EX) KdPrint(("pCopyTo:%lx\n", pCopyTo)); pCopyTo += 1 + // Length field 2 * NETBIOS_NAME_SIZE + // actual name in half-ascii NbtConfig.ScopeLength; // length of scope IF_DBG(NBT_DEBUG_NETBIOS_EX) KdPrint(("pCopyTo:%lx\n", pCopyTo)); ConvertToHalfAscii (pCopyTo, pName, NbtConfig.pScope, NbtConfig.ScopeLength); IF_DBG(NBT_DEBUG_NETBIOS_EX) KdPrint(("Copied the remote name for dgram sends\n")); } } else { KdPrint(("ExtractServerName: Find name going on\n")); } // // Add this server name to the remote hashtable // if Nameaddr can't be added, it means an entry already exists // Get that entry and update its ipaddr. // LockAndAddToHashTable (NbtConfig.pRemoteHashTbl, pName, NbtConfig.pScope, IpAddress, NBT_UNIQUE, NULL, NULL, pDeviceContext, (USHORT) ((TrackerFlags & NBT_DNS_SERVER) ? NAME_RESOLVED_BY_DNS | NAME_RESOLVED_BY_ADAP_STAT: NAME_RESOLVED_BY_ADAP_STAT)); } } if (pDeviceContext) { NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_OUT_FROM_IP, FALSE); } pClientCompletion = pClientTracker->CompletionRoutine; pClientContext = pClientTracker; // Use this same tracker as the context CompleteClientReq(pClientCompletion, pClientContext, status); } //---------------------------------------------------------------------------- VOID CopyNodeStatusResponseCompletion( IN tDGRAM_SEND_TRACKING *pClientTracker, IN NTSTATUS status ) /*++ Routine Description: This Routine copies data received from the net node status response to the client's irp. It is called from inbound.c when a node status response comes in from the wire. Arguments: pIrp - a ptr to an IRP Return Value: none --*/ { ULONG NumNames; ULONG i; PADAPTER_STATUS pAdapterStatus = NULL; PNAME_BUFFER pNameBuffer; ULONG BuffSize; ULONG AccumLength; PUCHAR pAscii; UCHAR Flags; ULONG DataLength; ULONG DestSize ; tSTATISTICS UNALIGNED *pStatistics; ULONG SrcIpAddress; ULONG TotalLength; tNODESTATUS *pNodeStat; COMPLETIONCLIENT pClientCompletion; PIRP pIrp; tNAMEADDR *pNameAddr; CTELockHandle OldIrq, OldIrq1; CHECK_PTR(pClientTracker); SrcIpAddress = pClientTracker->RemoteIpAddress; TotalLength = pClientTracker->NodeStatusLen; pClientTracker->NodeStatusLen = 0; pNodeStat = (tNODESTATUS *) pClientTracker->pNodeStatus; pClientTracker->pNodeStatus = NULL; pIrp = pClientTracker->ClientContext; ASSERT (NBT_VERIFY_HANDLE (pClientTracker, NBT_VERIFY_TRACKER)); ASSERT (pClientTracker->TrackerType == NBT_TRACKER_ADAPTER_STATUS); pClientTracker->pDeviceContext = NULL; // Can be set below if we need add the name to the cache if (STATUS_SUCCESS == status) { // // Bug# 125629: // We have already verified in QueryFromNet (just before calling // DecodeNodeStatusResponse) that the NodeStatus structure is // large enough to cover the NumNames field + it has the number of // names specified in that field // NumNames = pNodeStat->NumNames; BuffSize = sizeof(ADAPTER_STATUS) + NumNames*sizeof(NAME_BUFFER); // sanity check that we are not allocating more than 64K for this stuff if (BuffSize > 0xFFFF) { status = STATUS_UNSUCCESSFUL; goto ExitRoutine; } pAdapterStatus = NbtAllocMem((USHORT)BuffSize,NBT_TAG('9')); if (!pAdapterStatus) { status = STATUS_INSUFFICIENT_RESOURCES; goto ExitRoutine; } // Fill out the adapter status structure with zeros first CTEZeroMemory((PVOID)pAdapterStatus,BuffSize); // get the source MAC address from the statistics portion of the pdu // if (TotalLength >= (NumNames*sizeof(tNODENAME) + sizeof(tSTATISTICS))) { pStatistics = (tSTATISTICS UNALIGNED *)((PUCHAR)&pNodeStat->NodeName[0] + NumNames*sizeof(tNODENAME)); CTEMemCopy(&pAdapterStatus->adapter_address[0], &pStatistics->UnitId[0], sizeof(tMAC_ADDRESS)); } pAdapterStatus->rev_major = 0x03; pAdapterStatus->adapter_type = 0xFE; // pretend it is an ethernet adapter // // get the ptr to the statistics field if there is one in the pdu // AccumLength = NumNames * sizeof(tNODENAME) + FIELD_OFFSET(tNODESTATUS, NodeName) + sizeof(USHORT) + FIELD_OFFSET( tSTATISTICS, SessionDataPacketSize ) ; if (AccumLength <= TotalLength) { // // there is a whole statistics portion to the adapter status command, // so we can get the session pdu size out of it. // pAdapterStatus->max_sess = ntohs((USHORT)*((PUCHAR)pNodeStat + AccumLength - sizeof(USHORT))); } // get the address of the name buffer at the end of the adapter status // structure so we can copy the names into this area. pNameBuffer = (NAME_BUFFER *) ((ULONG_PTR)pAdapterStatus + sizeof(ADAPTER_STATUS)); // set the AccumLength to the start of the node name array in the buffer // so we can count through the buffer and be sure not to run off the end // AccumLength = FIELD_OFFSET(tNODESTATUS, NodeName); // // We need to determine the outgoing Device for the remote machine, in // case we need to add any names below. // pClientTracker->pDeviceContext = GetDeviceFromInterface (htonl(SrcIpAddress), TRUE); for(i =0; i< NumNames; i++) { AccumLength += sizeof(tNODENAME); if (AccumLength > TotalLength) { // // The remote buffer is incomplete, what else can we do? // status = STATUS_UNSUCCESSFUL; goto ExitCleanup; } pAdapterStatus->name_count++ ; pAscii = (PCHAR)&pNodeStat->NodeName[i].Name[0]; Flags = pNodeStat->NodeName[i].Flags; pNameBuffer->name_flags = (Flags & GROUP_STATUS) ? GROUP_NAME : UNIQUE_NAME; // // map the name states // if (Flags & NODE_NAME_CONFLICT) { if (Flags & NODE_NAME_RELEASED) pNameBuffer->name_flags |= DUPLICATE_DEREG; else pNameBuffer->name_flags |= DUPLICATE; } else if (Flags & NODE_NAME_RELEASED) { pNameBuffer->name_flags |= DEREGISTERED; } else { pNameBuffer->name_flags |= REGISTERED; } pNameBuffer->name_num = (UCHAR)i+1; CTEMemCopy(pNameBuffer->name,pAscii,NETBIOS_NAME_SIZE); // // If the name is the 0x20 name, see if we can add it to the remote hashtable // (only if the name is not already there)! // if ((pAscii[NETBIOS_NAME_SIZE-1] == 0x20) && ((Flags & (NODE_NAME_CONFLICT | NODE_NAME_RELEASED)) == 0)) { NbtAddEntryToRemoteHashTable (pClientTracker->pDeviceContext, NAME_RESOLVED_BY_ADAP_STAT, pAscii, SrcIpAddress, NbtConfig.RemoteTimeoutCount*60, // from minutes to secs UNIQUE_STATUS); } pNameBuffer++; } // // Reduce the name count if we can't fit the buffer // #ifdef VXD DestSize = ((NCB*)pIrp)->ncb_length ; #else DestSize = MmGetMdlByteCount( pIrp->MdlAddress ) ; #endif CHECK_PTR(pAdapterStatus); if ( BuffSize > DestSize ) { if ( DestSize < sizeof( ADAPTER_STATUS )) { pAdapterStatus->name_count = 0 ; } else { pAdapterStatus->name_count = (WORD) (DestSize- sizeof(ADAPTER_STATUS)) / sizeof(NAME_BUFFER) ; } } // // Copy the built adapter status structure // #ifdef VXD if ( BuffSize > DestSize ) { status = STATUS_BUFFER_OVERFLOW ; BuffSize = DestSize ; } else { status = STATUS_SUCCESS ; } CTEMemCopy(((NCB*)pIrp)->ncb_buffer, pAdapterStatus, BuffSize); ((NCB*)pIrp)->ncb_length = (WORD) BuffSize; // Set here to be compatible with NT #else status = TdiCopyBufferToMdl (pAdapterStatus, 0, BuffSize, pIrp->MdlAddress, 0, &DataLength); pIrp->IoStatus.Information = DataLength; pIrp->IoStatus.Status = status; #endif } ExitCleanup: if (pAdapterStatus) { CTEMemFree((PVOID)pAdapterStatus); } ExitRoutine: CTESpinLock(&NbtConfig.JointLock,OldIrq); // // the tracker block was unlinked in DecodeNodeStatusResponse, // and its header was freed when the send completed, so just relink // it here - this deref should do the relink. // if (pIrp) { if (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW ) // Only partial data copied { // -1 means the receive length is already set in the irp CTEIoComplete(pIrp,status,0xFFFFFFFF); } else { // // failed to get the adapter status, so // return failure status to the client. // CTEIoComplete(pIrp,STATUS_IO_TIMEOUT,0); } } if (pClientTracker->pDeviceContext) { NBT_DEREFERENCE_DEVICE (pClientTracker->pDeviceContext, REF_DEV_OUT_FROM_IP, TRUE); } NBT_DEREFERENCE_TRACKER (pClientTracker, TRUE); CTESpinFree(&NbtConfig.JointLock,OldIrq); } //---------------------------------------------------------------------------- NTSTATUS SendNodeStatusResponse( IN tNAMEHDR UNALIGNED *pInNameHdr, IN ULONG Length, IN PUCHAR pName, IN ULONG lNameSize, IN tIPADDRESS SrcIpAddress, IN USHORT SrcPort, IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This routine handles putting the node status response pdu into the clients MDL. Arguments: Return Value: none --*/ { NTSTATUS status; PUCHAR pScope; PUCHAR pInScope; ULONG Position; ULONG CountNames; ULONG BuffSize; tNODESTATUS UNALIGNED *pNodeStatus; tNAMEHDR *pNameHdr; CTELockHandle OldIrq2; ULONG i; PLIST_ENTRY pHead; PLIST_ENTRY pEntry; tADDRESSELE *pAddressEle; tNAMEADDR *pNameAddr; tDGRAM_SEND_TRACKING *pTracker; ULONG InScopeLength; tSTATISTICS UNALIGNED *pStatistics; tNODENAME UNALIGNED *pNode; CTEULONGLONG AdapterMask; ULONG Len; if (Length > sizeof(tNAMEHDR) + lNameSize - 1 + sizeof(ULONG)) { return(STATUS_DATA_NOT_ACCEPTED); } CTESpinLock(&NbtConfig.JointLock,OldIrq2); // verify that the requesting node is in the same scope as this node, so // get a ptr to the scope, which starts 16*2 (32) bytes into the // netbios name in the pdu. // pInScope = (PUCHAR)&pInNameHdr->NameRR.NetBiosName[(NETBIOS_NAME_SIZE <<1)]; pScope = NbtConfig.pScope; Position = sizeof(tNAMEHDR) - sizeof(tNETBIOS_NAME) +1 + (NETBIOS_NAME_SIZE <<1); // check the scope length InScopeLength = Length - Position - sizeof(ULONG); if (InScopeLength != NbtConfig.ScopeLength) { status = STATUS_DATA_NOT_ACCEPTED; goto ErrorExit; } // compare scopes for equality and avoid running off the end of the pdu // i= 0; while (i < NbtConfig.ScopeLength) { if (*pInScope != *pScope) { status = STATUS_DATA_NOT_ACCEPTED; goto ErrorExit; } i++; pInScope++; pScope++; } // get the count of names, excluding '*...' which we do not send... // CountNames = CountLocalNames(&NbtConfig); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Node Status Response, with %d names\n",CountNames)); // this is only a byte field, so only allow up to 255 names. if (CountNames > 255) { CountNames = 255; } // Allocate Memory for the adapter status // - ULONG for the Nbstat and IN that are part of Length. CountNames-1 // because there is one name in sizeof(tNODESTATUS) already // BuffSize = Length + sizeof(tNODESTATUS) - sizeof(ULONG) + (CountNames-1)*sizeof(tNODENAME) + sizeof(tSTATISTICS); pNameHdr = (tNAMEHDR *)NbtAllocMem((USHORT)BuffSize,NBT_TAG('A')); if (!pNameHdr) { CTESpinFree(&NbtConfig.JointLock,OldIrq2); return(STATUS_INSUFFICIENT_RESOURCES); } // copy the request to the response and change a few bits around // CTEMemCopy((PVOID)pNameHdr,(PVOID)pInNameHdr,Length); pNameHdr->OpCodeFlags = OP_RESPONSE | FL_AUTHORITY; pNameHdr->QdCount = 0; pNameHdr->AnCount = 1; pNodeStatus = (tNODESTATUS UNALIGNED *)&pNameHdr->NameRR.NetBiosName[lNameSize]; pNodeStatus->Ttl = 0; pNode = (tNODENAME UNALIGNED *)&pNodeStatus->NodeName[0]; AdapterMask = pDeviceContext->AdapterMask; i = 0; pEntry = pHead = &NbtConfig.AddressHead; while ((pEntry = pEntry->Flink) != pHead) { pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); pNameAddr = pAddressEle->pNameAddr; pNode->Flags = (pAddressEle->NameType == NBT_UNIQUE) ? UNIQUE_STATUS : GROUP_STATUS; // all names have this one set // pNode->Flags |= NODE_NAME_ACTIVE; switch (pNameAddr->NameTypeState & NAME_STATE_MASK) { default: case STATE_RESOLVED: break; case STATE_CONFLICT: pNode->Flags |= NODE_NAME_CONFLICT; break; case STATE_RELEASED: pNode->Flags |= NODE_NAME_RELEASED; break; case STATE_RESOLVING: // don't count these names. continue; } switch (NodeType & NODE_MASK) { case BNODE: pNode->Flags |= STATUS_BNODE; break; case MSNODE: case MNODE: pNode->Flags |= STATUS_MNODE; break; case PNODE: pNode->Flags |= STATUS_PNODE; } CHECK_PTR(pNode); // Copy the name in the pdu CTEMemCopy((PVOID)&pNode->Name[0], (PVOID)pNameAddr->Name, NETBIOS_NAME_SIZE); pNode->Resrved = 0; // check for the permanent name...and add it too // if (pNameAddr->NameTypeState & NAMETYPE_QUICK) { // // the permanent name is added as a Quick Add in the name table // do not put the permanent name into the response // continue; } else if ((pNameAddr->Name[0] == '*') || (pNameAddr->NameTypeState & STATE_RESOLVING) || (!(pNameAddr->AdapterMask & AdapterMask))) { // // do not put the broadcast name into the response, since neither // NBF or WFW NBT puts it there. // Also, to not respond with resolving names, or names that are // not registered on this adapter (multihomed case) // continue; } i++; pNode++; CHECK_PTR(pNode); if (i >= CountNames) { break; } } CHECK_PTR(pNameHdr); CHECK_PTR(pNodeStatus); // // set the count of names in the response packet // pNodeStatus->NumNames = (UCHAR)i; Len = i*sizeof(tNODENAME) + 1 + sizeof(tSTATISTICS); //+1 for NumNames Byte pNodeStatus->Length = (USHORT)htons(Len); // fill in some of the statistics fields which occur after the name table // in the PDU // pStatistics = (tSTATISTICS UNALIGNED *)((PUCHAR)&pNodeStatus->NodeName[0] + i*sizeof(tNODENAME)); CTEZeroMemory((PVOID)pStatistics,sizeof(tSTATISTICS)); // // put the MAC address in the response // CTEMemCopy(&pStatistics->UnitId[0], &pDeviceContext->MacAddress.Address[0], sizeof(tMAC_ADDRESS)); // // Now send the node status message // status = GetTracker(&pTracker, NBT_TRACKER_NODE_STATUS_RESPONSE); CTESpinFree(&NbtConfig.JointLock,OldIrq2); if (!NT_SUCCESS(status)) { CTEMemFree((PVOID)pNameHdr); } else { CHECK_PTR(pTracker); pTracker->SendBuffer.HdrLength = BuffSize; pTracker->SendBuffer.pDgramHdr = (PVOID)pNameHdr; pTracker->SendBuffer.pBuffer = NULL; pTracker->SendBuffer.Length = 0; pTracker->pDeviceContext = pDeviceContext; status = UdpSendDatagram(pTracker, SrcIpAddress, QueryRespDone, // this routine frees memory and puts the tracker back pTracker, SrcPort, // NBT_NAMESERVICE_UDP_PORT 31343 - reply to port request came on... NBT_NAME_SERVICE); } return(status); ErrorExit: CTESpinFree(&NbtConfig.JointLock,OldIrq2); return(status); } //---------------------------------------------------------------------------- NTSTATUS UpdateNameState( IN tADDSTRUCT UNALIGNED *pAddrStruct, IN tNAMEADDR *pNameAddr, IN ULONG Len, #ifdef MULTIPLE_WINS IN PULONG pContextFlags, #endif IN tDEVICECONTEXT *pDeviceContext, IN BOOLEAN NameServerIsSrc, IN tDGRAM_SEND_TRACKING *pTracker, IN CTELockHandle OldIrq1 ) /*++ Routine Description: This routine handles putting a list of names into the hash table when a response is received that contains one or more than one ip address. Arguments: Return Value: none --*/ { ULONG i, CountAddrs; tIPADDRESS *pIpList; ULONG ExtraNames; NTSTATUS status = STATUS_SUCCESS; CTELockHandle OldIrq; USHORT NameAddFlags = (NameServerIsSrc ? NAME_RESOLVED_BY_WINS : NAME_RESOLVED_BY_BCAST); // // put all of the addresses into a list that is pointed to by the pNameAddr record // Terminate it with -1 (0 means a broadcast address) // ASSERT(pNameAddr->pIpAddrsList == NULL); CountAddrs = Len / tADDSTRUCT_SIZE; if ((CountAddrs > NBT_MAX_INTERNET_GROUP_ADDRS)|| // probably a badly formated packet (max value=1000) (CountAddrs*tADDSTRUCT_SIZE != Len) || (!(pNameAddr->pIpAddrsList = NbtAllocMem(sizeof(tIPADDRESS)*(1+CountAddrs),NBT_TAG('8'))))) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); return (STATUS_UNSUCCESSFUL); } /* * Replace broadcast address -1 (0xffffffff) with 0 because -1 is reserved as terminator */ for (i = 0; i < CountAddrs; i++) { pNameAddr->pIpAddrsList[i] = (pAddrStruct[i].IpAddr == (tIPADDRESS)(-1))? 0: htonl(pAddrStruct[i].IpAddr); } pNameAddr->pIpAddrsList[CountAddrs] = (tIPADDRESS)(-1); // a pos. response to a previous query, so change the state in the // hash table to RESOLVED CHECK_PTR(pNameAddr); if (pNameAddr->NameTypeState & STATE_RESOLVING) { pNameAddr->NameTypeState &= ~NAME_STATE_MASK; pNameAddr->NameTypeState |= STATE_RESOLVED; // check if a group name... // if (ntohs(pAddrStruct->NbFlags) & FL_GROUP) { pNameAddr->NameTypeState &= ~NAME_TYPE_MASK; // It is difficult to differentiate nameserver responses from // single node responses, when a single ip address is returned. // It could be the name server returning an Inet Group with one // entry or another node simply responding with its address. // // If the response is just a single broadcast address, we store // it as type NAMETYPE_GROUP, otherwise we should store it as // NAMETYPE_INET_GROUP -- we should remove the check for NameServer // since it does not always work for muti-homed, or cluster Wins servers // // WINS puts -1 to say it's a groupname // if ((CountAddrs == 1) && (pAddrStruct->IpAddr == (ULONG)-1)) { // using zero here tells the UdpSendDatagramCode to // send to the subnet broadcast address when talking to // that address. // // For Bnodes store the Address of the node that responded // to the group name query, since we do allow sessions to // group names for BNODES since they can resolve the name to // and IP address, whereas other nodes cannot. // // store the ipaddr regardless of nodetype. We don't know if this info will be // used to setup a session or send a datagram. We do check NameTypeState // while setting up session, so no need to filter out NodeType info here. ASSERT(pAddrStruct->IpAddr == (ULONG)-1); pNameAddr->IpAddress = 0; pNameAddr->NameTypeState |= NAMETYPE_GROUP; } else { NameAddFlags |= NAME_ADD_INET_GROUP; pNameAddr->NameTypeState |= NAMETYPE_INET_GROUP; } } else { if (CountAddrs > 1) { tIPADDRESS IpAddress; NBT_WORK_ITEM_CONTEXT *pContext; // the name query response contains several ip addresses for // a multihomed host, so pick an address that matches one of // our subnet masks // // Do the old thing for datagram sends/name queries. // #ifndef VXD if ((NbtConfig.TryAllAddr) && (pTracker) && (pTracker->Flags & SESSION_SETUP_FLAG)) { if (NT_SUCCESS(status = ChooseBestIpAddress(pAddrStruct, Len, pDeviceContext, pTracker, &IpAddress, TRUE))) { // // At this point, pTracker->IPList contains the sorted list of destination // IP addresses. Submit this list to the lmhsvc service to ping each and // return which is reachable. // pContext = (NBT_WORK_ITEM_CONTEXT *) NbtAllocMem (sizeof(NBT_WORK_ITEM_CONTEXT), NBT_TAG('H')); if (pContext) { pContext->pTracker = NULL; // no query tracker pContext->pClientContext = pTracker; // the client tracker pContext->ClientCompletion = SessionSetupContinue; pContext->pDeviceContext = pTracker->pDeviceContext; StartLmHostTimer(pContext, TRUE); CTESpinFree(&NbtConfig.JointLock,OldIrq1); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.UpdateNameState: Kicking off CheckAddr : %lx\n", pAddrStruct)); status = NbtProcessLmhSvcRequest(pContext, NBT_PING_IP_ADDRS); CTESpinLock(&NbtConfig.JointLock,OldIrq1); if (NT_SUCCESS(status)) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); // PendingQ name ASSERT (status == STATUS_PENDING); return (status); // shd be STATUS_PENDING } CTEFreeMem (pContext); KdPrint(("Nbt.UpdateNameState: ERROR %lx -- NbtProcessLmhSvcRequest\n", status)); } else { KdPrint(("Nbt.UpdateNameState: ERROR -- Couldn't alloc mem for pContext\n")); } // // We failed above, but we still the addresses that were returned, so // just pick up the first Ip address! // pNameAddr->IpAddress = IpAddress; status = STATUS_SUCCESS; } else { KdPrint(("Nbt.UpdateNameState: ERROR -- ChooseBestIpAddress returned %lx\n", status)); } } else #endif { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt:Choosing best IP addr...\n")); if (NT_SUCCESS (status = ChooseBestIpAddress(pAddrStruct,Len,pDeviceContext, pTracker, &IpAddress, FALSE))) { pNameAddr->IpAddress = IpAddress; } #ifdef MULTIPLE_WINS #ifdef VXD // // This is a hack to make VNBT work for multi-homed machines since // currently we don't ping the addresses as in the case of NT above // to find a good address // // Reset the ContextFlags so that we re-query the same server to make // sure we try all the addresses // if (pTracker) { *pContextFlags = pTracker->ResolutionContextFlags; } #endif // VXD #endif // MULTIPLE_WINS } } else { // it is already set to a unique address...since that is the default // when the name is queried originally. pNameAddr->IpAddress = ntohl(pAddrStruct->IpAddr); } } } if (NT_SUCCESS(status)) { AddToHashTable (NbtConfig.pRemoteHashTbl, pNameAddr->Name, NbtConfig.pScope, pNameAddr->IpAddress, 0, pNameAddr, NULL, pDeviceContext, NameAddFlags); } else { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } return(STATUS_SUCCESS); } //---------------------------------------------------------------------------- ULONG MakeList( IN tDEVICECONTEXT *pDeviceContext, IN ULONG CountAddrs, IN tADDSTRUCT UNALIGNED *pAddrStruct, IN tIPADDRESS *pAddrArray, IN ULONG SizeOfAddrArray, IN BOOLEAN IsSubnetMatch ) /*++ Routine Description: This routine gets a list of ip addresses that match the network number This can either be the subnet number or the network number depending on the boolean IsSubnetMatch Arguments: Return Value: none --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; tDEVICECONTEXT *pTmpDevContext; ULONG MatchAddrs = 0; tADDSTRUCT UNALIGNED *pAddrs; ULONG i; ULONG IpAddr, NetworkNumber, NetworkNumberInIpAddr; UCHAR IpAddrByte; pHead = &NbtConfig.DeviceContexts; pEntry = pHead; while ((pEntry = pEntry->Flink) != pHead) { pAddrs = pAddrStruct; pTmpDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); // // DeviceContext is non-null, if a check has to be made on a specific // DeviceContext. Otherwise it's null (i.e. check all DeviceContexts) // if (pDeviceContext) { if (pTmpDevContext != pDeviceContext) { continue; } } // // Check whether the Caller requested a Subnet or Network number match, // and if they are 0 for this Device, go to the next device // if (IsSubnetMatch) { NetworkNumber = pTmpDevContext->SubnetMask & pTmpDevContext->IpAddress; } else { NetworkNumber = pTmpDevContext->NetMask; } // // If the Subnet Mask or Network Mask is 0, then there is no use // proceeding further since the Device is probably not up // if (!NetworkNumber) { continue; } // extract the ipaddress from each address structure for ( i = 0 ; i < CountAddrs; i++ ) { NetworkNumberInIpAddr = IpAddr = ntohl(pAddrs->IpAddr); if (IsSubnetMatch) { if (((pTmpDevContext->SubnetMask & IpAddr) == NetworkNumber) && (MatchAddrs < SizeOfAddrArray/sizeof(ULONG))) { // put the ipaddress into a list incase multiple match // and we want to select one randomly // pAddrArray[MatchAddrs++] = IpAddr; } pAddrs++; } else { IpAddrByte = ((PUCHAR)&IpAddr)[3]; if ((IpAddrByte & 0x80) == 0) { // class A address - one byte netid NetworkNumberInIpAddr &= 0xFF000000; } else if ((IpAddrByte & 0xC0) ==0x80) { // class B address - two byte netid NetworkNumberInIpAddr &= 0xFFFF0000; } else if ((IpAddrByte & 0xE0) ==0xC0) { // class C address - three byte netid NetworkNumberInIpAddr &= 0xFFFFFF00; } if ((NetworkNumberInIpAddr == NetworkNumber) && (MatchAddrs < SizeOfAddrArray/sizeof(ULONG))) { // put the ipaddress into a list incase multiple match // and we want to select one randomly // pAddrArray[MatchAddrs++] = IpAddr; } pAddrs++; } } } return(MatchAddrs); } //---------------------------------------------------------------------------- NTSTATUS ChooseBestIpAddress( IN tADDSTRUCT UNALIGNED *pAddrStruct, IN ULONG Len, IN tDEVICECONTEXT *pDeviceContext, OUT tDGRAM_SEND_TRACKING *pTracker, OUT tIPADDRESS *pIpAddress, IN BOOLEAN fReturnAddrList ) /*++ Routine Description: This routine gets a list of ip addresses and attempts to pick one of them as the best address. This occurs when WINS returns a list of addresses for a multihomed host and we want want the one that is on a subnet corresponding to one of the network cards. Failing to match on subnet mask, results in a random selection from the addresses. Arguments: Return Value: none --*/ { ULONG CountAddrs, NextAddr, MatchAddrs = 0; ULONG i, j, Random; tIPADDRESS MatchAddrArray[60]; tADDSTRUCT temp; CTESystemTime TimeValue; // one or more addresses were returned, // so pick one that is best // CountAddrs = Len / tADDSTRUCT_SIZE; if (CountAddrs*tADDSTRUCT_SIZE == Len) { // // Randomize all of the addresses! // for (i=CountAddrs-1; i>0; i--) { CTEQuerySystemTime(TimeValue); Random = RandomizeFromTime(TimeValue, (i+1)); ASSERT (Random < CountAddrs); if (Random != i) { // // Exchange the address at Random with i! // temp = pAddrStruct[Random]; pAddrStruct[Random] = pAddrStruct[i]; pAddrStruct[i] = temp; } } // // First check if any addresses are on the same subnet as this // devicecontext. // MatchAddrs = MakeList(pDeviceContext, CountAddrs, pAddrStruct, MatchAddrArray, sizeof(MatchAddrArray), TRUE); // // if none of the ipaddrs is on the same subnet as this DeviceContext, // try other DeviceContexts // if (!MatchAddrs) { MatchAddrs = MakeList(NULL, CountAddrs, pAddrStruct, MatchAddrArray, sizeof(MatchAddrArray), TRUE); } // if none of the addresses match the subnet address of any of the // DeviceContexts, then go through the same checks looking for matches // that have the same network number as the Device this name was resolved on. // Bug # 212432 // if (!MatchAddrs) { MatchAddrs = MakeList(pDeviceContext, CountAddrs, pAddrStruct, MatchAddrArray, sizeof(MatchAddrArray), FALSE); } // // if none of the addresses match the subnet address of any of the // DeviceContexts, then go through the same check looking for matches // that have the same network number for any connected device. // if (!MatchAddrs) { MatchAddrs = MakeList(NULL, CountAddrs, pAddrStruct, MatchAddrArray, sizeof(MatchAddrArray), FALSE); } } else { // the pdu length is not an even multiple of the tADDSTRUCT data // structure return(STATUS_UNSUCCESSFUL); } // // We had already randomized the list earlier, so now just pick up // the first address for the IpAddress value! // if (MatchAddrs) { *pIpAddress = MatchAddrArray[0]; } else // No match { *pIpAddress = htonl(pAddrStruct[0].IpAddr); } // // See if the Caller requested only 1 IP address // if (!fReturnAddrList) { return (STATUS_SUCCESS); } // // Move all addresses which matched any of the Subnets or Network numbers // to the top of the list (if no matches, then we will copy the whole list as is) // if (MatchAddrs) { // // Sort the IP addr list on basis of best IP addr. in MatchAddrArray // // NOTE: this is not a strictly sorted list (the actual sort might be too expensive), // instead we take all the addresses that match the subnet mask (say) and // clump the remaining ones in the same group. This way we ensure that whatever // we chose as the best address is still given preference as compared to the // other addresses. // // // NextAddr is the index of the next Address in AddrStruct which can be switched // NextAddr = 0; for (i=0; i <=> Address[%d]=<%x>\n", NextAddr, pAddrStruct[NextAddr].IpAddr, j, pAddrStruct[j].IpAddr)); temp = pAddrStruct[NextAddr]; pAddrStruct[NextAddr] = pAddrStruct[j]; pAddrStruct[j] = temp; } NextAddr++; // Fill in next Address break; } } if (NextAddr >= CountAddrs) { break; } } } // // We will have to return the list of IP addresses in the // Tracker's IpList field, so ensure that pTracker is valid // if (!pTracker) { return(STATUS_UNSUCCESSFUL); } // // Now copy all the addresses into the Tracker's IP List // pTracker->IpList = NbtAllocMem(sizeof(ULONG)*(1+CountAddrs),NBT_TAG('8')); if (!pTracker->IpList) { return (STATUS_INSUFFICIENT_RESOURCES); } for (j=0; jIpList[j] = pAddrStruct[j].IpAddr; } pTracker->IpList[CountAddrs] = 0; pTracker->NumAddrs = CountAddrs; return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- #ifdef MULTIPLE_WINS BOOLEAN IsNameServerForDevice( IN ULONG SrcAddress, IN tDEVICECONTEXT *pDevContext ) /*++ Routine Description: This function checks the src address against this adapter's name server addresses to see if it is a name server. Arguments: Return Value: BOOLEAN - TRUE or FALSE --*/ { int i; if ((pDevContext->lNameServerAddress == SrcAddress) || (pDevContext->lBackupServer == SrcAddress)) { return(TRUE); } for (i=0; i < pDevContext->lNumOtherServers; i++) { if (pDevContext->lOtherServers[i] == SrcAddress) { return (TRUE); } } return (FALSE); } //---------------------------------------------------------------------------- #endif BOOLEAN SrcIsNameServer( IN ULONG SrcAddress, IN USHORT SrcPort ) /*++ Routine Description: This function checks the src address against all adapters' name server address to see if it came from a name server. Arguments: Return Value: NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; tDEVICECONTEXT *pDevContext; pHead = &NbtConfig.DeviceContexts; pEntry = pHead->Flink; if (SrcPort == NbtConfig.NameServerPort) { while (pEntry != pHead) { pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); #ifdef MULTIPLE_WINS if (IsNameServerForDevice(SrcAddress, pDevContext)) #else if ((pDevContext->lNameServerAddress == SrcAddress) || (pDevContext->lBackupServer == SrcAddress)) #endif { return(TRUE); } pEntry = pEntry->Flink; } } #ifndef VXD // // If wins is on this machine the above SrcIsNameServer // check may not be sufficient since this machine is // the name server and that check checks the nameservers // used for name queries. If WINS is on this machine it // could have sent from any local adapter's IP address // if (pWinsInfo) { return(SrcIsUs(SrcAddress)); } #endif return(FALSE); } //---------------------------------------------------------------------------- BOOLEAN SrcIsUs( IN ULONG SrcAddress ) /*++ Routine Description: This function checks the src address against all adapters'Ip addresses address to see if it came from this node. Arguments: Return Value: NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; tDEVICECONTEXT *pDevContext; pHead = &NbtConfig.DeviceContexts; pEntry = pHead->Flink; while (pEntry != pHead) { pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); if (pDevContext->IpAddress == SrcAddress) { return(TRUE); } pEntry = pEntry->Flink; } return(FALSE); } //---------------------------------------------------------------------------- VOID SwitchToBackup( IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This function switches the primary and backup name server addresses. Arguments: Return Value: NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL --*/ { ULONG SaveAddr; // // Bug: 30511: Dont switch servers if no backup. // if (pDeviceContext->lBackupServer == LOOP_BACK) { IF_DBG(NBT_DEBUG_REFRESH) KdPrint(("Nbt:Will not Switch to backup name server: devctx: %X, refreshtobackup=%X\n", pDeviceContext, pDeviceContext->RefreshToBackup)); return; } SaveAddr = pDeviceContext->lNameServerAddress; pDeviceContext->lNameServerAddress = pDeviceContext->lBackupServer; pDeviceContext->lBackupServer = SaveAddr; IF_DBG(NBT_DEBUG_REFRESH) KdPrint(("Nbt:Switching to backup name server: devctx: %X, refreshtobackup=%X\n", pDeviceContext, pDeviceContext->RefreshToBackup)); // keep track if we are on the backup or not. pDeviceContext->RefreshToBackup = ~pDeviceContext->RefreshToBackup; pDeviceContext->SwitchedToBackup = ~pDeviceContext->SwitchedToBackup; } //---------------------------------------------------------------------------- NTSTATUS GetNbFlags( IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNameSize, IN LONG lNumBytes, OUT USHORT *pRegType ) /*++ Routine Description: This function finds the Nbflags field in certain pdu types and returns it. Arguments: Return Value: NTSTATUS - STATUS_SUCCESS or STATUS_REMOTE_NOT_LISTENING Called By: RegResponseFromNet --*/ { LONG DnsLabelLength, Offset; // // Bug#s 125648, 125649 // The Data is packed in the form: // tNameHdr --> TransactId ==> Offset 0, Length = 2 bytes // : // --> NameRR.NetbiosName ==> Offset=13, Length = lNameSize // --> tGENERALRR ==> Offset=13+lNameSize, Length >= 22bytes // // We need to verify that the NameHdr contains the minimum Buffer space required // to hold the entire PDU data // if (lNumBytes < (FIELD_OFFSET(tNAMEHDR,NameRR.NetBiosName) + lNameSize + NBT_MINIMUM_RR_LENGTH)) { ASSERT (0); return (STATUS_UNSUCCESSFUL); } // // if the question name is not a pointer to the first name then we // must find the end of that name and adjust it so when added to // to lNameSize we endup at NB_FLAGS // if ((pNameHdr->NameRR.NetBiosName[lNameSize+PTR_OFFSET] & PTR_SIGNATURE) != PTR_SIGNATURE) { // add one to include the null on the end of the string + the NB, IN TTL, // and length fields( another 10 bytes NO_PTR_OFFSET) + PTR_OFFSET(4). // Offset = FIELD_OFFSET(tNAMEHDR,NameRR.NetBiosName) + lNameSize + PTR_OFFSET; if (STATUS_SUCCESS != (strnlen ((PUCHAR) &pNameHdr->NameRR.NetBiosName [lNameSize+PTR_OFFSET], lNumBytes - (Offset+16), // +16 bytes to end of GeneralRR &DnsLabelLength))) { ASSERT (0); return (STATUS_UNSUCCESSFUL); } // add one to include the null on the end of the string DnsLabelLength++; } else { DnsLabelLength = 2; } Offset = lNameSize+PTR_OFFSET+DnsLabelLength+NO_PTR_OFFSET; *pRegType = ntohs((USHORT) pNameHdr->NameRR.NetBiosName[Offset]); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS FindOnPendingList( IN PUCHAR pName, IN tNAMEHDR UNALIGNED *pNameHdr, IN BOOLEAN DontCheckTransactionId, IN ULONG BytesToCompare, OUT tNAMEADDR **ppNameAddr ) /*++ Routine Description: This function is called to look for a name query request on the pending list. It searches the list linearly to look for the name. The Joint Lock is held when calling this routine. Arguments: Return Value: --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; tNAMEADDR *pNameAddr; tTIMERQENTRY *pTimer; pHead = pEntry = &NbtConfig.PendingNameQueries; while ((pEntry = pEntry->Flink) != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); // // there could be multiple entries with the same name so check the // transaction id too // if (DontCheckTransactionId || ((pTimer = pNameAddr->pTimer) && (((tDGRAM_SEND_TRACKING *)pTimer->Context)->TransactionId == pNameHdr->TransactId)) && (CTEMemEqu(pNameAddr->Name,pName,BytesToCompare))) { *ppNameAddr = pNameAddr; return(STATUS_SUCCESS); } } return(STATUS_UNSUCCESSFUL); } #if DBG //---------------------------------------------------------------------------- VOID PrintHexString( IN tNAMEHDR UNALIGNED *pNameHdr, IN ULONG lNumBytes ) /*++ Routine Description: This function is called to determine whether the name packet we heard on the net has the same address as the one in the remote hash table. If it has the same address or if it does not have any address, we return SUCCESS, else we return NOT_LISTENING. Arguments: Return Value: NTSTATUS - STATUS_SUCCESS or STATUS_REMOTE_NOT_LISTENING Called By: RegResponseFromNet --*/ { ULONG i,Count=0; PUCHAR pHdr=(PUCHAR)pNameHdr; for (i=0;i= 16) { Count = 0; KdPrint(("\n")); } else Count++; } KdPrint(("\n")); } #endif #ifdef PROXY_NODE //---------------------------------------------------------------------------- NTSTATUS ChkIfValidRsp( IN tNAMEHDR UNALIGNED *pNameHdr, IN LONG lNameSize, IN tNAMEADDR *pNameAddr ) /*++ Routine Description: This function is called to determine whether the name packet we heard on the net has the same address as the one in the remote hash table. If it has the same address or if it does not have any address, we return SUCCESS, else we return NOT_LISTENING. Arguments: Return Value: NTSTATUS - STATUS_SUCCESS or STATUS_REMOTE_NOT_LISTENING Called By: RegResponseFromNet --*/ { ULONG IpAdd; IpAdd = ntohl( pNameHdr->NameRR.NetBiosName[lNameSize+IPADDRESS_OFFSET] ); // // If the IP address in the packet received is same as the one // in the table we return success, else we are not interested // in the packet (we want to just drop the packet) // if ( (IpAdd == pNameAddr->IpAddress) ) { return(STATUS_SUCCESS); } else { return(STATUS_REMOTE_NOT_LISTENING); } } #endif