diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/nbt/vxd/dns.c | 984 |
1 files changed, 984 insertions, 0 deletions
diff --git a/private/ntos/nbt/vxd/dns.c b/private/ntos/nbt/vxd/dns.c new file mode 100644 index 000000000..f32bfccf4 --- /dev/null +++ b/private/ntos/nbt/vxd/dns.c @@ -0,0 +1,984 @@ +/*++ + +Copyright (c) 1989-1996 Microsoft Corporation + +Module Name: + + DNS.c + +Abstract: + + VxD-specific DNS routines. + + These routines try to resolve NetBIOS names using DNS. + +Author: + + Earle R. Horton (ERH) 13-Feb-1996 + +Revision History: + +--*/ + +#include "nbtprocs.h" + +// +// function prototypes for completion routines that are local to this file +// +//---------------------------------------------------------------------------- +VOID +DnsCompletion( + PVOID pContext, + PVOID pContext2, + tTIMERQENTRY *pTimerQEntry + ) +/*++ + +Routine Description: + + This routine is called by the timer code when the timer expires. It must + decide if another name query should be sent to the DNS server, and if not, + then it calls the client's completion routine (in completion2). + +Arguments: + + +Return Value: + + The function value is the status of the operation. + + +Notes: +--*/ + +{ + + NTSTATUS status; + tDGRAM_SEND_TRACKING *pTracker; + tDEVICECONTEXT *pDeviceContext; + CTELockHandle OldIrq; + COMPLETIONCLIENT pClientCompletion; + PCHAR pchDomainName; + USHORT Flags; + BOOL fOneMoreTry; + tDGRAM_SEND_TRACKING *pClientTracker; + + + KdPrint(("DnsCompletion entered\r\n")); + + pTracker = (tDGRAM_SEND_TRACKING *)pContext; + pDeviceContext = pTracker->pDeviceContext; + + + // if the client completion routine is not set anymore, then the + // timer has been cancelled and this routine should just clean up its + // buffers associated with the tracker (and return) + // + if (!pTimerQEntry) + { + // return the tracker block to its queue + LOCATION(0x52); + DereferenceTrackerNoLock((tDGRAM_SEND_TRACKING *)pContext); + return; + } + + + // + // to prevent a client from stopping the timer and deleting the + // pNameAddr, grab the lock and check if the timer has been stopped + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pTimerQEntry->Flags & TIMER_RETIMED) + { + pTimerQEntry->Flags &= ~TIMER_RETIMED; + pTimerQEntry->Flags |= TIMER_RESTART; + // + // if we are not bound to this card than use a very short timeout + // + if (!pTracker->pDeviceContext->pNameServerFileObject) + { + pTimerQEntry->DeltaTime = 10; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + + if (!pTimerQEntry->ClientCompletion) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + return; + } + + pClientTracker = (tDGRAM_SEND_TRACKING *)pTimerQEntry->ClientContext; + + // + // if the tracker has been cancelled, don't do any more queries + // + if (pClientTracker->Flags & TRACKER_CANCELLED) + { + pClientCompletion = pTimerQEntry->ClientCompletion; + + // remove the link from the name table to this timer block + CHECK_PTR(((tNAMEADDR *)pTimerQEntry->pCacheEntry)); + + ((tNAMEADDR *)pTimerQEntry->pCacheEntry)->pTimer = NULL; + + // to synch. with the StopTimer routine, Null the client + // completion routine so it gets called just once. + // + CHECK_PTR(pTimerQEntry); + pTimerQEntry->ClientCompletion = NULL; + + // + // remove the name from the hash table, since it did not + // resolve via DNS either + // + CHECK_PTR(pTracker->pNameAddr); + pTracker->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pTracker->pNameAddr->NameTypeState |= STATE_RELEASED; + pTracker->pNameAddr->pTimer = NULL; + + // + // This call will remove the name from the PendingNameQueries List + // + NbtDereferenceName(pTracker->pNameAddr); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // there can be a list of trackers Q'd up on this name + // query, so we must complete all of them! + // + CompleteClientReq(pClientCompletion, + pClientTracker, + STATUS_CANCELLED); + + // return the tracker block to its queue + LOCATION(0x51); + DereferenceTracker(pTracker); + + KdPrint(("DNS resolution cancelled by client\r\n")); + + return; + + } + + // If done with all the (3) retries with primary, try secondary DNS srvr + // If secondary not defined, or done with secondary as well, stop. + // + + fOneMoreTry = TRUE; + + if (!(--pTimerQEntry->Retries)) + { + // + // if backup server is not defined, or if it is defined but we just + // finished trying backup server, go back and try primary server for + // "other domains" + // e.g. DNSDomains was defined as "msft.dom2.com,msft.dom3.com,msft.dom" + // We were pointing at msft.dom2.com. Now, we are done with that (and + // didn't get a response), so try msft.dom3.com + // + if ( ( !pDeviceContext->lDnsBackupServer ) || + ( pDeviceContext->lDnsBackupServer == LOOP_BACK ) || + ( pTracker->Flags & NBT_DNS_SERVER_BACKUP) ) + { + // + // if we just got done trying primary domain name, try all the + // "other domains" specified + // + if (pTracker->pchDomainName == NbtConfig.pDomainName) + { + pTracker->pchDomainName = NbtConfig.pDNSDomains; + if ( pTracker->pchDomainName ) + { + pTracker->Flags &= ~NBT_DNS_SERVER_BACKUP; + pTracker->Flags |= NBT_DNS_SERVER; + pTimerQEntry->Retries = NbtConfig.uNumRetries; + } + else + { + fOneMoreTry = FALSE; + } + } + + // + // if we had already started on "other domains", advance to the + // next domain within "other domains" + // + else + { + pchDomainName = pTracker->pchDomainName; + while( *pchDomainName != ',' && // dom names separated by comma + *pchDomainName != ' ' && // or space + *pchDomainName != '\0' ) + pchDomainName++; + + if ( *pchDomainName == '\0' ) + fOneMoreTry = FALSE; + else + { + pchDomainName++; + pTracker->pchDomainName = pchDomainName; + pTracker->Flags &= ~NBT_DNS_SERVER_BACKUP; + pTracker->Flags |= NBT_DNS_SERVER; + pTimerQEntry->Retries = NbtConfig.uNumRetries; + } + } + } + + // ok, prepare to try the backup server + else + { + pTimerQEntry->Retries = NbtConfig.uNumRetries; + + pTracker->Flags &= ~NBT_DNS_SERVER; + pTracker->Flags |= NBT_DNS_SERVER_BACKUP; + } + } + + // we aren't done yet: send one more query and restart the timer + if (fOneMoreTry) + { + pTracker->RefCount++; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + NULL,NULL,NULL, + 0,0, + eDNS_NAME_QUERY, + TRUE); + + DereferenceTracker(pTracker); + + pTimerQEntry->Flags |= TIMER_RESTART; + + KdPrint(("One more DNS query sent out\r\n")); + } + + // yup, all done: didn't find the name! give client above the bad news + else + { + tDGRAM_SEND_TRACKING *pClientTracker; + + + pClientTracker = (tDGRAM_SEND_TRACKING *)pTimerQEntry->ClientContext; + + pClientCompletion = pTimerQEntry->ClientCompletion; + + // remove the link from the name table to this timer block + CHECK_PTR(((tNAMEADDR *)pTimerQEntry->pCacheEntry)); + + ((tNAMEADDR *)pTimerQEntry->pCacheEntry)->pTimer = NULL; + + // to synch. with the StopTimer routine, Null the client + // completion routine so it gets called just once. + // + CHECK_PTR(pTimerQEntry); + pTimerQEntry->ClientCompletion = NULL; + + // + // remove the name from the hash table, since it did not + // resolve via DNS either + // + CHECK_PTR(pTracker->pNameAddr); + pTracker->pNameAddr->NameTypeState &= ~NAME_STATE_MASK; + pTracker->pNameAddr->NameTypeState |= STATE_RELEASED; + pTracker->pNameAddr->pTimer = NULL; + + // + // This call will remove the name from the PendingNameQueries List + // + NbtDereferenceName(pTracker->pNameAddr); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // there can be a list of trackers Q'd up on this name + // query, so we must complete all of them! + // + CompleteClientReq(pClientCompletion, + pClientTracker, + STATUS_TIMEOUT); + + // return the tracker block to its queue + LOCATION(0x51); + DereferenceTracker(pTracker); + + KdPrint(("DNS resolution failed: told client\r\n")); + } + +} + +//---------------------------------------------------------------------------- +NTSTATUS +DoDnsResolve ( + IN NBT_WORK_ITEM_CONTEXT *Context + ) +/*++ + +Routine Description: + + This function is used to allow NBT to query DNS. This is very much like + the name query sent out to WINS server or broadcast. Response from the + DNS server, if any, is handled by the QueryFromNet() routine. + +Arguments: + + *Context (NBT_WORK_ITEM_CONTEXT) + +Return Value: + + STATUS_PENDING (unless something goes wrong) + +Notes: +--*/ + +{ + + tDGRAM_SEND_TRACKING *pTracker; + tDEVICECONTEXT *pDeviceContext; + ULONG Timeout; + USHORT Retries; + NTSTATUS status; + PVOID pClientCompletion; + PVOID pCompletionRoutine; + PVOID pClientContext; + + + + KdPrint(("DoDnsResolve entered\r\n")); + + pTracker = Context->pTracker; + + pDeviceContext = pTracker->pDeviceContext; + + // + // If the primary DNS server is not defined, just return error. + if ( (!pDeviceContext->lDnsServerAddress) || + ( pDeviceContext->lDnsServerAddress == LOOP_BACK) ) + { + return( NRC_CMDTMO ); + } + + pTracker->Flags &= ~(NBT_BROADCAST|NBT_NAME_SERVER|NBT_NAME_SERVER_BACKUP); + pTracker->Flags |= NBT_DNS_SERVER; + + pClientContext = Context->pClientContext; + pClientCompletion = Context->ClientCompletion; + pCompletionRoutine = DnsCompletion; + + // + // free that memory now + // + CTEMemFree(Context); + + // + // Put on the pending name queries list again so that when the query + // response comes in from DNS we can find the pNameAddr record. + // + ExInterlockedInsertTailList(&NbtConfig.PendingNameQueries, + &pTracker->pNameAddr->Linkage, + &NbtConfig.JointLock.SpinLock); + + Timeout = (ULONG)pNbtGlobConfig->uRetryTimeout; + Retries = pNbtGlobConfig->uNumRetries; + + pTracker->RefCount++; + + // + // first time, we want to try the primary domain name + // + pTracker->pchDomainName = NbtConfig.pDomainName; + + status = UdpSendNSBcast(pTracker->pNameAddr, + NbtConfig.pScope, + pTracker, + pCompletionRoutine, + pClientContext, + pClientCompletion, + Retries, + Timeout, + eDNS_NAME_QUERY, + TRUE); + + DereferenceTracker(pTracker); + + KdPrint(("Leaving DoDnsResolve\r\n")); + + return( status ); + + +} + +//---------------------------------------------------------------------------- +PCHAR +DnsStoreName +( + OUT PCHAR pDest, + IN PCHAR pName, + IN PCHAR pDomainName, + IN enum eNSTYPE eNsType + ) +/*++ + +Routine Description: + + This routine copies the netbios name (and appends the scope on the + end) in the DNS namequery packet + +Arguments: + + +Return Value: + + the address of the next byte in the destination after the the name + has been copied + +--*/ +{ + LONG i; + LONG count; + PCHAR pStarting; + PCHAR pSrc; + LONG DomNameSize; + LONG OneMoreSubfield; + + LONG lMaxCount; + CHAR cTerminator; + + if (eNsType == eDIRECT_DNS_NAME_QUERY) + { + lMaxCount = 255; + cTerminator = 0; + } + else + { + lMaxCount = NETBIOS_NAME_SIZE-1; + cTerminator = 0x20; + } + + + pStarting = pDest++; + count = 0; + // + // copy until we reach the space padding + // + while ( ( count < lMaxCount ) && (*pName != cTerminator) ) + { + *pDest++ = *pName++; + count++; + } + + *pStarting = (CHAR)count; + + // + // check if domain name exists. koti.microsoft.com will be represented + // as 4KOTI9microsoft3com0 (where nos. => no. of bytes of subfield) + // + pSrc = pDomainName; + if (pSrc && pSrc[0] != '\0') + { + OneMoreSubfield = 1; + + while( OneMoreSubfield ) + { + count = 0; + pStarting = pDest++; + // + // remember, the domain name we receive can also be a set of "other + // domains" to try in the form "msft.dom2.com,msft.dom3.com" + // + while ( *pSrc != '.' && *pSrc != '\0' && *pSrc != ',') + { + *pDest++ = *pSrc++; + count++; + } + *pStarting = (CHAR)count; + + if (*pSrc == '\0' || *pSrc == ',') + OneMoreSubfield = 0; + else + pSrc++; + } + } + + *pDest++ = 0; + + + // return the address of the next byte of the destination + return(pDest); +} + + + + +//---------------------------------------------------------------------------- +VOID +DnsExtractName( + IN PCHAR pNameHdr, + IN LONG NumBytes, + OUT PCHAR pName, + OUT PULONG pNameSize + ) +/*++ + +Routine Description: + + This routine extracts the name from the packet and then appends the scope + onto the end of the name to make a full name. + +Arguments: + NumBytes - the total number of bytes in the message - may include + more than just the name itself + +Return Value: + + +--*/ +{ + + + LONG i; + int iIndex; + LONG lValue; + ULONG UNALIGNED *pHdr; + PCHAR pSavName; + ULONG Len; + + + KdPrint(("DnsExtractName entered\r\n")); + + // + // how long is the name we received + // + Len = (ULONG)((UCHAR)*pNameHdr); + + ++pNameHdr; // to increment past the length byte + + pSavName = pName; + + // copy the name (no domain) as given by DNS server (i.e., just copy + // foobar when DNS returned foobar.microsoft.com in the response + // (this is likely to be less than the usualy 16 byte len) + // + for (i=0; i < Len ;i++ ) + { + *pName = *pNameHdr; + pNameHdr++; + if (i < NETBIOS_NAME_SIZE) + { + pName++; + } + } + + // + // now, make it look like NBNS responded, by adding the 0x20 pad + // + for (i=Len; i<NETBIOS_NAME_SIZE; i++) + { + *pName++ = 0x20; + } + + // + // convert all chars to uppercase since all our names are in uppercase! + // + for (i=0; i<NETBIOS_NAME_SIZE; i++) + { + if (*pSavName >= 'a' && *pSavName <= 'z') + *pSavName = *pSavName - ('a'-'A'); + + pSavName++; + } + + // + // at this point we are pointing to the '.' after foobar. Find the + // length of the entire name + // + while ( (*pNameHdr != '\0') && (Len < NumBytes) ) + { + pNameHdr++; + Len++; + } + + Len++; // to account for the trailing 0 + + *pNameSize = Len; + + KdPrint(("Leaving DnsExtractName\r\n")); + + return; +} + + +//---------------------------------------------------------------------------- +ULONG +domnamelen( + IN PCHAR pDomainName + ) +/*++ + +Routine Description: + + This routine determines the length of the domainname. This is basically + strlen, except that the DNSDomain field is stored as a bunch of + domain names separated by commas, so we treat '\0' as well as ',' as + string terminators for this function. + +Arguments: + + +Return Value: + + length of the domain name + +--*/ +{ + + ULONG ulDomnameLen=0; + + if (pDomainName) + { + while(*pDomainName != '\0' && *pDomainName != ',') + { + pDomainName++; + ulDomnameLen++; + } + } + + return( ulDomnameLen ); +} + +//---------------------------------------------------------------------------- +VOID +ProcessDnsResponse( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameHdr, + IN LONG lNumBytes, + IN USHORT OpCodeFlags + ) +/*++ + +Routine Description: + + This function sets the state of the name being resolved appropriately + depending on whether DNS sends a positive or a negative response to our + query; calls the client completion routine and stops any more DNS queries + from going. + +Arguments: + + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_UNSUCCESSFUL + +--*/ +{ + + + NTSTATUS status; + tDNS_QUERYRESP UNALIGNED *pQuery; + tNAMEADDR *pResp; + tTIMERQENTRY *pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SrcAddress; + CTELockHandle OldIrq1; + LONG lNameSize; + LONG lTraversedSoFar=0; + CHAR pName[NETBIOS_NAME_SIZE]; + CHAR pJunkBuf[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + PUCHAR pchQry; + + + + KdPrint(("ProcessDnsResponse entered\r\n")); + + + // make sure this is a response + + if ( !(OpCodeFlags & OP_RESPONSE) ) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: Bad OpCodeFlags\r\n")); + + return; + } + + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr); + + // get the name out of the network pdu and pass to routine to check + // local table + DnsExtractName( (PCHAR)&pNameHdr->NameRR.NameLength, + lNumBytes, + pName, + &lNameSize + ); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + // + // we chopped off 16th byte while sending a query, so compare only first + // 15 characters for a match + // + status = FindOnPendingList(pName,pNameHdr,FALSE,NETBIOS_NAME_SIZE-1,&pResp); + + if (!NT_SUCCESS(status)) + { + // + // The name is not there in the remote name table. Nothing + // more to do. Just return. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: name not found\r\n")); + + return; + } + + // + // If the response we received doesn't resolve the name, we silently return, + // but make sure reties is set to 1 so that when timer fires again, we don't + // send another name query to the same server but instead timeout the + // attempt on this server + // + if ((pTimer = pResp->pTimer)) + { + pTimer->Retries = 1; + } + + + // + // check the pdu size for errors + // + if (lNumBytes < DNS_MINIMUM_QUERYRESPONSE) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: Bad lNumBytes\r\n")); + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return; + } + +// +// BUGBUG: should we require authoritative responses from DNS servers? +// + + // + // if it's a negative response, quit now! + // + if (IS_NEG_RESPONSE(OpCodeFlags)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; + } + + // + // if there is no answer section, return! + // + if ( !pNameHdr->AnCount ) + { + CDbgPrint(DBGFLAG_ERROR,("ProcessDnsResponse: No answer section\r\n")); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + return; + } + + // + // lNameSize is the length of the entire name, excluding the length byte + // for the first label (including length bytes of subsequent labels) and + // including the trailing 0 (tNAMEHDR struc takes care for 1st byte) + // + pchQry = (PUCHAR)&pNameHdr->NameRR.NetBiosName[lNameSize]; + + lTraversedSoFar += lNameSize; + + // + // if the Question section is returned with the response then we have + // a little more work to do! In this case, pQuery is pointing at the + // beginning of the QTYPE field (end of the QNAME) + // + if ( pNameHdr->QdCount ) + { + pchQry += sizeof(tQUESTIONMODS); + lTraversedSoFar += sizeof(tQUESTIONMODS); + + // most common case: 1st byte will be 0xC0, which means next byte points + // to the actual name. We don't care about the name, so we skip over + // both the bytes + // + if ( (*pchQry) == PTR_TO_NAME ) + { + pchQry += sizeof(tDNS_LABEL); + lTraversedSoFar += sizeof(tDNS_LABEL); + } + + // + // if some implementation doesn't optimize and copies the whole name + // again, skip over the length of the name + // + else + { + pchQry += (lNameSize+1); // +1 because of the 1st length byte! + lTraversedSoFar += (lNameSize+1); + } + } + + pQuery = (tDNS_QUERYRESP *)pchQry; + + // + // if this rr is telling us about canonical name, skip over it and go to + // where the ipaddr is + // + if (ntohs(pQuery->RrType) == DNS_CNAME) + { + // + // since this is CNAME, there is no ipaddr. Instead, the data is the + // canonical name whose length we are adding, and subtract ipaddr's len + // + pchQry += (sizeof(tDNS_QUERYRESP) - sizeof(ULONG)); + pchQry += ntohs(pQuery->Length); + lTraversedSoFar += ntohs(pQuery->Length) + sizeof(tDNS_QUERYRESP) - sizeof(ULONG); + + ASSERT(lNumBytes > lTraversedSoFar); + + // most common case: 1st byte will be 0xC0, which means next byte points + // to the actual name. We don't care about the name, so we skip over + // both the bytes + // + if ( (*pchQry) == PTR_TO_NAME ) + { + pchQry += sizeof(tDNS_LABEL); + lTraversedSoFar += sizeof(tDNS_LABEL); + } + + // + // if some implementation doesn't optimize and copies the whole name + // again, skip over the length of the name + // + else + { + // we have already taken the name out. we are calling this routine + // just to see how big the canonical name is (i.e.lNameSize), to skip + // past it + // + DnsExtractName( pchQry, + lNumBytes-lTraversedSoFar, + pJunkBuf, + &lNameSize + ); + + // + // lNameSize is the length of the entire name, excluding the length byte + // for the first label (including length bytes of subsequent labels) and + // including the trailing 0 (tNAMEHDR struc takes care for 1st byte) + // + pchQry += lNameSize+1; // +1 for the length byte of first label + + } + + pQuery = (tDNS_QUERYRESP *)pchQry; + } + + + // if we came this far, it's a positive response. stop the timer and do + // the needful.. + + // remove any timer block and call the completion routine + if (pTimer) + { + USHORT Flags; + tDGRAM_SEND_TRACKING *pTracker; + + pTracker = (tDGRAM_SEND_TRACKING *)pTimer->Context; + + // + // this routine puts the timer block back on the timer Q, and + // handles race conditions to cancel the timer when the timer + // is expiring. + status = StopTimer(pTimer,&pClientCompletion,&Context); + + // + // Synchronize with DnsCompletion + // + if (pClientCompletion) + { + CHECK_PTR(pResp); + pResp->pTimer = NULL; + + // + // Remove from the PendingNameQueries List + // + RemoveEntryList(&pResp->Linkage); + InitializeListHead(&pResp->Linkage); + + KdPrint(("ProcessDnsResponse: positive DNS response received\r\n")); + + if (pResp->NameTypeState & STATE_RESOLVING) + { + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_RESOLVED; + + pResp->IpAddress = ntohl(pQuery->IpAddress); + + pResp->AdapterMask = (CTEULONGLONG)-1; + status = AddRecordToHashTable(pResp,NbtConfig.pScope); + + if (!NT_SUCCESS(status)) + { + // + // the name must already be in the hash table, + // so dereference it to remove it + // + NbtDereferenceName(pResp); + } + + IncrementNameStats(NAME_QUERY_SUCCESS, TRUE); + } + + status = STATUS_SUCCESS; + + // + // Set the backup name server to be the main name server + // since we got a response from it. + // + if ( SrcAddress == pDeviceContext->lDnsBackupServer ) + { + pDeviceContext->lDnsBackupServer = + pDeviceContext->lDnsServerAddress; + + pDeviceContext->lDnsServerAddress = SrcAddress; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // the completion routine has not run yet, so run it + (void) CTEQueueForNonDispProcessing( + (tDGRAM_SEND_TRACKING *)Context, + (PVOID)status, + pClientCompletion, + DelayedSessEstablish, + pDeviceContext); + } + + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + } + + return; + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + KdPrint(("Leaving ProcessDnsResponse\r\n")); + + return; + +} |