mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
984 lines
26 KiB
984 lines
26 KiB
/*++
|
|
|
|
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;
|
|
|
|
}
|