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.
3619 lines
84 KiB
3619 lines
84 KiB
/*++
|
|
|
|
Copyright (c) 1996-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
packet.c
|
|
|
|
Abstract:
|
|
|
|
Domain Name System (DNS) API
|
|
|
|
Packet writing utilities.
|
|
|
|
Author:
|
|
|
|
Jim Gilroy (jamesg) October, 1996
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "local.h"
|
|
|
|
|
|
//
|
|
// Receive buffer size
|
|
// - use 16K, max size where compression useful
|
|
// other choices would be ethernet UDP frag size
|
|
// (1472 or 1280 depending on who you talk to)
|
|
//
|
|
|
|
DWORD g_RecvBufSize = 0x4000;
|
|
|
|
|
|
//
|
|
// Class values for UPDATE packets
|
|
// (Key concept here -- designed by committee)
|
|
//
|
|
// These arrays are indexed by
|
|
// !wDataLength -- row
|
|
// Delete flag -- column
|
|
//
|
|
|
|
WORD PrereqClassArray[2][2] =
|
|
{
|
|
DNS_RCLASS_INTERNET, // data != 0, no delete
|
|
0, // data != 0, delete => ERROR
|
|
DNS_RCLASS_ANY, // no data, no delete
|
|
DNS_RCLASS_NONE, // no data, delete
|
|
};
|
|
|
|
WORD UpdateClassArray[2][2] =
|
|
{
|
|
DNS_RCLASS_INTERNET, // data != 0, no delete
|
|
DNS_RCLASS_NONE, // data != 0, delete
|
|
0, // no data, no delete => ERROR
|
|
DNS_RCLASS_ANY, // no data, delete
|
|
};
|
|
|
|
|
|
|
|
PDNS_MSG_BUF
|
|
Dns_AllocateMsgBuf(
|
|
IN WORD wBufferLength OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate message buffer.
|
|
|
|
Arguments:
|
|
|
|
wBufferLength - optional length of message buffer; default is MAX
|
|
UDP size
|
|
|
|
Return Value:
|
|
|
|
Ptr to message buffer.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
PDNS_MSG_BUF pmsg;
|
|
BOOL ftcp = FALSE;
|
|
WORD allocLength;
|
|
|
|
//
|
|
// default allocation to "classical" UDP max buffer length
|
|
// this is good enough for writing questions
|
|
// recv size buffers should request larger size
|
|
//
|
|
|
|
if ( wBufferLength < DNS_RFC_MAX_UDP_PACKET_LENGTH )
|
|
{
|
|
allocLength = DNS_RFC_MAX_UDP_PACKET_LENGTH;
|
|
}
|
|
else if ( wBufferLength == MAXWORD )
|
|
{
|
|
allocLength = (WORD) g_RecvBufSize;
|
|
}
|
|
else
|
|
{
|
|
allocLength = wBufferLength;
|
|
ftcp = TRUE;
|
|
}
|
|
|
|
pmsg = ALLOCATE_HEAP( SIZEOF_MSG_BUF_OVERHEAD + allocLength );
|
|
if ( !pmsg )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// limit UDP sends to classical RFC UDP limit (512 bytes)
|
|
// regardless of actual allocation
|
|
// write routines use pBufferEnd to determine writeability
|
|
//
|
|
// DCR: allow big UDP send buffers for update?
|
|
// problem is that must roll back NOT just OPT, but also
|
|
// big buffer
|
|
//
|
|
// DCR: not really necessary as if write exceeds 512, can
|
|
// just flip to TCP anyway
|
|
//
|
|
|
|
pmsg->BufferLength = allocLength;
|
|
|
|
if ( !ftcp )
|
|
{
|
|
allocLength = DNS_RFC_MAX_UDP_PACKET_LENGTH;
|
|
}
|
|
pmsg->pBufferEnd = (PCHAR)&pmsg->MessageHead + allocLength;
|
|
pmsg->fTcp = (BOOLEAN)ftcp;
|
|
|
|
//
|
|
// init -- this follows fTcp set as flag is used to set fields
|
|
//
|
|
|
|
Dns_InitializeMsgBuf( pmsg );
|
|
|
|
return( pmsg );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Dns_InitializeMsgBuf(
|
|
IN OUT PDNS_MSG_BUF pMsg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize message buffer to "clean" state.
|
|
|
|
Arguments:
|
|
|
|
pMsg -- message to init
|
|
|
|
Return Value:
|
|
|
|
Ptr to message buffer.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
// clear info + header
|
|
//
|
|
// DCR_CLEANUP: can't take this approach without reworking Allocate
|
|
// function which sets BufferLength and pBufferEnd
|
|
// if this function is NOT independently used, then we can fix
|
|
// it to clean and completely
|
|
//
|
|
// RtlZeroMemory(
|
|
// pMsg,
|
|
// ((PBYTE)&pMsg->MessageBody - (PBYTE)pMsg) );
|
|
|
|
|
|
// setup addressing info
|
|
|
|
pMsg->Socket = 0;
|
|
pMsg->Socket4 = 0;
|
|
pMsg->Socket6 = 0;
|
|
|
|
// set for packet reception
|
|
|
|
if ( pMsg->fTcp )
|
|
{
|
|
SET_MESSAGE_FOR_TCP_RECV( pMsg );
|
|
}
|
|
else
|
|
{
|
|
SET_MESSAGE_FOR_UDP_RECV( pMsg );
|
|
}
|
|
|
|
// clear header
|
|
|
|
RtlZeroMemory(
|
|
(PBYTE) &pMsg->MessageBody,
|
|
sizeof(DNS_HEADER) );
|
|
|
|
// set for rewriting
|
|
|
|
pMsg->pCurrent = pMsg->MessageBody;
|
|
pMsg->pPreOptEnd = NULL;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Writing to packet
|
|
//
|
|
|
|
PCHAR
|
|
_fastcall
|
|
Dns_WriteDottedNameToPacket(
|
|
IN OUT PCHAR pch,
|
|
IN PCHAR pchStop,
|
|
IN PSTR pszName,
|
|
IN PSTR pszDomain, OPTIONAL
|
|
IN WORD wDomainOffset, OPTIONAL
|
|
IN BOOL fUnicodeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write lookup name to packet.
|
|
|
|
Arguments:
|
|
|
|
pch -- ptr to current position in packet buffer
|
|
|
|
pchStop -- end of packet buffer
|
|
|
|
pszName - dotted FQDN to write
|
|
|
|
pszDomain - domain name already in packet (OPTIONAL); note this is
|
|
a fragment of the SAME STRING as pszName; i.e. ptr compare
|
|
NOT strcmp is done to determine if at domain name
|
|
|
|
wDomainOffset - offset in packet of domain name; MUST include this
|
|
if pszDomain is given
|
|
|
|
fUnicodeName -- pszName is unicode string
|
|
TRUE -- name is unicode
|
|
FALSE -- name is UTF8
|
|
|
|
Return Value:
|
|
|
|
Ptr to next position in packet buffer.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
CHAR ch;
|
|
PCHAR pchlabelStart;
|
|
UCHAR countLabel = 0;
|
|
ULONG countName = 0;
|
|
PSTR pnameWire;
|
|
PWSTR pnameUnicode;
|
|
CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|
WCHAR nameWideBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|
|
|
|
|
// protect message buffer overrun
|
|
|
|
if ( pch >= pchStop )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
// allow root to be indicated by NULL
|
|
|
|
if ( !pszName )
|
|
{
|
|
*pch++ = 0;
|
|
return( pch );
|
|
}
|
|
|
|
//
|
|
// protect stack buffers from bogus names
|
|
//
|
|
|
|
if ( fUnicodeName )
|
|
{
|
|
countName = wcslen( (LPWSTR) pszName );
|
|
}
|
|
else
|
|
{
|
|
countName = strlen( pszName );
|
|
}
|
|
if ( countName >= DNS_MAX_NAME_BUFFER_LENGTH )
|
|
{
|
|
return NULL;
|
|
}
|
|
countName = 0;
|
|
|
|
//
|
|
// UTF8 name with extended chars?
|
|
// - then must go up to unicode for canonicalizing
|
|
//
|
|
// DCR: shouldn't be sending in un-canonical UTF8
|
|
// should
|
|
// - stay in unicode all the way
|
|
// - using canon unicode all the way
|
|
// - use canon wire names all the way
|
|
//
|
|
|
|
if ( !fUnicodeName )
|
|
{
|
|
if ( !Dns_IsStringAscii( pszName ) )
|
|
{
|
|
DWORD bufLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
|
|
|
|
if ( ! Dns_NameCopy(
|
|
(PCHAR) nameWideBuffer,
|
|
& bufLength,
|
|
pszName,
|
|
0, // length unknown
|
|
DnsCharSetUtf8,
|
|
DnsCharSetUnicode
|
|
) )
|
|
{
|
|
return( NULL );
|
|
}
|
|
if ( ! Dns_MakeCanonicalNameInPlaceW(
|
|
nameWideBuffer,
|
|
0 ) )
|
|
{
|
|
return( NULL );
|
|
}
|
|
pnameUnicode = (PWSTR) nameWideBuffer;
|
|
fUnicodeName = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// unicode name -- if extended, canonicalize first
|
|
//
|
|
// DCR_FIX0: should functionalize -- raw unicode to wire
|
|
//
|
|
|
|
else
|
|
{
|
|
pnameUnicode = (PWSTR) pszName;
|
|
|
|
if ( !Dns_IsWideStringAscii( pnameUnicode ) )
|
|
{
|
|
if ( ! Dns_MakeCanonicalNameW(
|
|
nameWideBuffer,
|
|
//DNS_MAX_NAME_BUFFER_LENGTH_UNICODE,
|
|
DNS_MAX_NAME_BUFFER_LENGTH,
|
|
pnameUnicode,
|
|
0 ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
pnameUnicode = nameWideBuffer;
|
|
}
|
|
}
|
|
|
|
//
|
|
// convert unicode name to UTF8
|
|
// - if extended chars, then downcase before hit the wire
|
|
//
|
|
|
|
if ( fUnicodeName )
|
|
{
|
|
if ( ! Dns_NameCopyUnicodeToWire(
|
|
nameBuffer,
|
|
pnameUnicode ) )
|
|
{
|
|
return( NULL );
|
|
}
|
|
pnameWire = nameBuffer;
|
|
}
|
|
else
|
|
{
|
|
pnameWire = pszName;
|
|
}
|
|
|
|
//
|
|
// special case "." root name
|
|
// - allows us to fail all other zero length labels cleanly
|
|
//
|
|
|
|
if ( *pnameWire == '.' )
|
|
{
|
|
if ( *(pnameWire+1) != 0 )
|
|
{
|
|
return( NULL );
|
|
}
|
|
*pch++ = 0;
|
|
return( pch );
|
|
}
|
|
|
|
//
|
|
// write lookup name
|
|
// - leave
|
|
|
|
pchlabelStart = pch++;
|
|
|
|
while( ch = *pnameWire++ )
|
|
{
|
|
// out of space
|
|
|
|
if ( pch >= pchStop )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
// not at end of label -- just copy character
|
|
|
|
if ( ch != '.' )
|
|
{
|
|
*pch++ = ch;
|
|
countLabel++;
|
|
countName++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// at end of label
|
|
// - write label count
|
|
// - reset counter
|
|
// - if reached domain name, write compression and quit
|
|
//
|
|
|
|
if ( countLabel > DNS_MAX_LABEL_LENGTH ||
|
|
countLabel == 0 ||
|
|
countName > DNS_MAX_NAME_LENGTH )
|
|
{
|
|
return( NULL );
|
|
}
|
|
*pchlabelStart = countLabel;
|
|
countLabel = 0;
|
|
countName++;
|
|
pchlabelStart = pch++;
|
|
|
|
if ( pnameWire == pszDomain )
|
|
{
|
|
if ( ++pch >= pchStop )
|
|
{
|
|
return( NULL );
|
|
}
|
|
*(UNALIGNED WORD *)pchlabelStart =
|
|
htons( (WORD)(wDomainOffset | (WORD)0xC000) );
|
|
return( pch );
|
|
}
|
|
}
|
|
|
|
if ( countLabel > DNS_MAX_LABEL_LENGTH ||
|
|
countName > DNS_MAX_NAME_LENGTH )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// NULL terminate
|
|
// - if no terminating ".", do end of label processing also
|
|
// - return ptr to char after terminating NULL
|
|
|
|
if ( countLabel )
|
|
{
|
|
*pchlabelStart = countLabel;
|
|
*pch++ = 0;
|
|
}
|
|
else
|
|
{
|
|
DNS_ASSERT( pch == pchlabelStart + 1 );
|
|
*pchlabelStart = 0;
|
|
}
|
|
return( pch );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
_fastcall
|
|
Dns_WriteStringToPacket(
|
|
IN OUT PCHAR pch,
|
|
IN PCHAR pchStop,
|
|
IN PSTR pszString,
|
|
IN BOOL fUnicodeString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write string to packet.
|
|
|
|
Arguments:
|
|
|
|
pch -- ptr to current position in packet buffer
|
|
|
|
pchStop -- end of packet buffer
|
|
|
|
pszString - string to write
|
|
|
|
fUnicodeString -- pszString is unicode string
|
|
|
|
Return Value:
|
|
|
|
Ptr to next position in packet buffer.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
DWORD length;
|
|
|
|
//
|
|
// handle NULL string
|
|
//
|
|
|
|
if ( !pszString )
|
|
{
|
|
if ( pch >= pchStop )
|
|
{
|
|
return( NULL );
|
|
}
|
|
*pch++ = 0;
|
|
return( pch );
|
|
}
|
|
|
|
//
|
|
// get string length
|
|
// - get required buf length, then whack whack off space
|
|
// for terminating NULL
|
|
// - zero error case, becomes very large number and is
|
|
// caught by length>MAXCHAR test
|
|
//
|
|
|
|
length = Dns_GetBufferLengthForStringCopy(
|
|
pszString,
|
|
0,
|
|
fUnicodeString ? DnsCharSetUnicode : DnsCharSetUtf8,
|
|
DnsCharSetUtf8 );
|
|
length--;
|
|
|
|
//
|
|
// set length byte
|
|
//
|
|
|
|
if ( length > MAXUCHAR )
|
|
{
|
|
SetLastError( ERROR_INVALID_DATA );
|
|
return( NULL );
|
|
}
|
|
*pch++ = (UCHAR) length;
|
|
|
|
if ( pch + length > pchStop )
|
|
{
|
|
SetLastError( ERROR_MORE_DATA );
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// copy string
|
|
//
|
|
// note unicode conversion will write NULL terminator, so
|
|
// test again for space in packet
|
|
//
|
|
|
|
if ( fUnicodeString )
|
|
{
|
|
if ( pch + length + 1 > pchStop )
|
|
{
|
|
SetLastError( ERROR_MORE_DATA );
|
|
return( NULL );
|
|
}
|
|
Dns_StringCopy(
|
|
pch,
|
|
NULL,
|
|
pszString,
|
|
length,
|
|
DnsCharSetUnicode,
|
|
DnsCharSetUtf8 );
|
|
}
|
|
else
|
|
{
|
|
memcpy(
|
|
pch,
|
|
pszString,
|
|
length );
|
|
}
|
|
|
|
return( pch+length );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
Dns_WriteQuestionToMessage(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN PDNS_NAME pszName,
|
|
IN WORD wType,
|
|
IN BOOL fUnicodeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write question to packet.
|
|
|
|
Note: Routine DOES NOT clear message info or message header.
|
|
This is optimized for use immediately following Dns_CreateMessage().
|
|
|
|
Arguments:
|
|
|
|
pMsg - message info
|
|
|
|
pszName - DNS name of question
|
|
|
|
wType - question type
|
|
|
|
fUnicodeName - name is in unicode
|
|
|
|
Return Value:
|
|
|
|
Ptr to next char in buffer, if successful.
|
|
NULL if error writing question name.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pch;
|
|
|
|
// use recursion, as default
|
|
|
|
pMsg->MessageHead.RecursionDesired = TRUE;
|
|
|
|
// restart write at message header
|
|
|
|
pch = pMsg->MessageBody;
|
|
|
|
// write question name
|
|
|
|
pch = Dns_WriteDottedNameToPacket(
|
|
pch,
|
|
pMsg->pBufferEnd,
|
|
(PCHAR) pszName,
|
|
NULL,
|
|
0,
|
|
fUnicodeName );
|
|
if ( !pch )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
// write question structure
|
|
|
|
*(UNALIGNED WORD *) pch = htons( wType );
|
|
pch += sizeof(WORD);
|
|
*(UNALIGNED WORD *) pch = DNS_RCLASS_INTERNET;
|
|
pch += sizeof(WORD);
|
|
|
|
// set question RR section count
|
|
|
|
pMsg->MessageHead.QuestionCount = 1;
|
|
|
|
// header fields in host order
|
|
|
|
pMsg->fSwapped = FALSE;
|
|
|
|
// reset current ptr
|
|
|
|
pMsg->pCurrent = pch;
|
|
|
|
IF_DNSDBG( INIT )
|
|
{
|
|
DnsDbg_Message(
|
|
"Packet after question write:",
|
|
pMsg );
|
|
}
|
|
return( pch );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_WriteRecordStructureToMessage(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN WORD wType,
|
|
IN WORD wClass,
|
|
IN DWORD dwTtl,
|
|
IN WORD wDataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
No data RR cases:
|
|
|
|
This includes prereqs and deletes except for specific record cases.
|
|
|
|
Arguments:
|
|
|
|
pch - ptr to next byte in packet buffer
|
|
|
|
pchStop - end of packet buffer
|
|
|
|
wClass - class
|
|
|
|
wType - desired RR type
|
|
|
|
dwTtl - time to live
|
|
|
|
wDataLength - data length
|
|
|
|
Return Value:
|
|
|
|
Ptr to next postion in buffer, if successful.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
PDNS_WIRE_RECORD pdnsRR;
|
|
PCHAR pchdata;
|
|
|
|
IF_DNSDBG( WRITE2 )
|
|
{
|
|
DNS_PRINT(( "Dns_WriteRecordStructureToMessage(%p).\n", pMsg ));
|
|
}
|
|
|
|
//
|
|
// out of space
|
|
//
|
|
|
|
pdnsRR = (PDNS_WIRE_RECORD) pMsg->pCurrent;
|
|
pchdata = (PCHAR) pdnsRR + sizeof( DNS_WIRE_RECORD );
|
|
|
|
if ( pchdata + wDataLength >= pMsg->pBufferEnd )
|
|
{
|
|
DNS_PRINT(( "ERROR out of space writing record to packet.\n" ));
|
|
return( ERROR_MORE_DATA );
|
|
}
|
|
|
|
//
|
|
// write RR wire structure
|
|
//
|
|
|
|
*(UNALIGNED WORD *) &pdnsRR->RecordType = htons( wType );
|
|
*(UNALIGNED WORD *) &pdnsRR->RecordClass = htons( wClass );
|
|
*(UNALIGNED DWORD *) &pdnsRR->TimeToLive = htonl( dwTtl );
|
|
*(UNALIGNED WORD *) &pdnsRR->DataLength = htons( wDataLength );
|
|
|
|
//
|
|
// update current ptr
|
|
//
|
|
|
|
pMsg->pCurrent = pchdata;
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_WriteRecordStructureToPacket(
|
|
IN OUT PCHAR pchBuf,
|
|
IN PDNS_RECORD pRecord,
|
|
IN BOOL fUpdatePacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write wire record structure for given record.
|
|
|
|
Arguments:
|
|
|
|
pchBuf - ptr to next byte in packet buffer
|
|
|
|
pRecord - record to write
|
|
|
|
fUpdatePacket -- TRUE if building update message;
|
|
for update the section flags in the pRecords are interpreted
|
|
for update; otherwise query semantics are used
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PDNS_WIRE_RECORD pwireRecord;
|
|
WORD class;
|
|
DWORD ttl;
|
|
|
|
IF_DNSDBG( WRITE2 )
|
|
{
|
|
DNS_PRINT((
|
|
"Writing RR struct (%p) to packet buffer at %p.\n",
|
|
pRecord,
|
|
pchBuf
|
|
));
|
|
DnsDbg_Record(
|
|
"Record being written:",
|
|
pRecord );
|
|
}
|
|
|
|
//
|
|
// get TTL, it will be set to zero for several of the update cases
|
|
//
|
|
|
|
ttl = pRecord->dwTtl;
|
|
|
|
//
|
|
// determine class
|
|
// - class variable is in net order (for perf)
|
|
// - default is class IN, but may be ANY or NONE for certain updates
|
|
//
|
|
|
|
if ( fUpdatePacket )
|
|
{
|
|
switch( pRecord->Flags.S.Section )
|
|
{
|
|
case DNSREC_PREREQ:
|
|
|
|
class = PrereqClassArray
|
|
[ pRecord->wDataLength == 0 ][ pRecord->Flags.S.Delete ];
|
|
ttl = 0;
|
|
break;
|
|
|
|
case DNSREC_UPDATE:
|
|
case DNSREC_ADDITIONAL:
|
|
|
|
class = UpdateClassArray
|
|
[ pRecord->wDataLength == 0 ][ pRecord->Flags.S.Delete ];
|
|
|
|
if ( class != DNS_RCLASS_INTERNET )
|
|
{
|
|
ttl = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DNS_PRINT(( "ERROR: invalid RR section.\n" ));
|
|
return( ERROR_INVALID_DATA );
|
|
}
|
|
if ( class == 0 )
|
|
{
|
|
DNS_PRINT(( "ERROR: no update class corresponding to RR flags.\n" ));
|
|
return( ERROR_INVALID_DATA );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
class = DNS_RCLASS_INTERNET;
|
|
}
|
|
|
|
//
|
|
// write RR wire structure
|
|
// - zero datalength to handle no data case
|
|
//
|
|
|
|
pwireRecord = (PDNS_WIRE_RECORD) pchBuf;
|
|
|
|
*(UNALIGNED WORD *) &pwireRecord->RecordType = htons( pRecord->wType );
|
|
*(UNALIGNED WORD *) &pwireRecord->RecordClass = class;
|
|
*(UNALIGNED DWORD *) &pwireRecord->TimeToLive = htonl( ttl );
|
|
*(UNALIGNED WORD *) &pwireRecord->DataLength = 0;
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
Dns_WriteRecordStructureToPacketEx(
|
|
IN OUT PCHAR pchBuf,
|
|
IN WORD wType,
|
|
IN WORD wClass,
|
|
IN DWORD dwTtl,
|
|
IN WORD wDataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write wire record structure for given record.
|
|
|
|
Arguments:
|
|
|
|
pchBuf - ptr to next byte in packet buffer
|
|
|
|
Return Value:
|
|
|
|
Ptr to data section of record.
|
|
|
|
--*/
|
|
{
|
|
PDNS_WIRE_RECORD pwireRecord;
|
|
|
|
// DCR_PERF: optimize RR write to packet?
|
|
|
|
pwireRecord = (PDNS_WIRE_RECORD) pchBuf;
|
|
|
|
*(UNALIGNED WORD *) &pwireRecord->RecordType = htons( wType );
|
|
*(UNALIGNED WORD *) &pwireRecord->RecordClass = htons( wClass );
|
|
*(UNALIGNED DWORD *) &pwireRecord->TimeToLive = htonl( dwTtl );
|
|
*(UNALIGNED WORD *) &pwireRecord->DataLength = htons( wDataLength );
|
|
|
|
return( pchBuf + sizeof(DNS_WIRE_RECORD) );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Dns_SetRecordDatalength(
|
|
IN OUT PDNS_WIRE_RECORD pRecord,
|
|
IN WORD wDataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reset record datalength.
|
|
|
|
Arguments:
|
|
|
|
pRecord - wire record to reset
|
|
|
|
wDataLength - data length
|
|
|
|
Return Value:
|
|
|
|
Ptr to data section of record.
|
|
|
|
--*/
|
|
{
|
|
WORD flippedWord;
|
|
|
|
INLINE_WORD_FLIP( flippedWord, wDataLength );
|
|
|
|
*(UNALIGNED WORD *) &pRecord->DataLength = flippedWord;
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_WriteOptToMessage(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN WORD wPayload,
|
|
IN DWORD Rcode,
|
|
IN BYTE Version,
|
|
IN PBYTE pData,
|
|
IN WORD wDataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write OPT record to message.
|
|
|
|
Arguments:
|
|
|
|
pMsg -- message
|
|
|
|
wPayload -- max length client can receive in UDP
|
|
|
|
Rcode -- RCODE, if extended some of this ends up in OPT
|
|
|
|
Version -- EDNS version
|
|
|
|
pData -- ptr to data buffer of OPT data
|
|
|
|
wDataLength -- length of pData
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successfully writen.
|
|
ErrorCode on failure.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
PCHAR pstart;
|
|
|
|
//
|
|
// DCR: use variable OPT fields
|
|
//
|
|
|
|
//
|
|
// save existing pCurrent
|
|
// - this allows dual wire write
|
|
//
|
|
|
|
ASSERT( !pMsg->pPreOptEnd );
|
|
|
|
pstart = pMsg->pCurrent;
|
|
|
|
//
|
|
// write OPT record name (root)
|
|
//
|
|
|
|
*pstart = 0;
|
|
pMsg->pCurrent++;
|
|
|
|
//
|
|
// write OPT -- basic info, no options
|
|
// - if OPT didn't fit, clear pPreOptEnd pointer
|
|
// which serves as signal that OPT exists
|
|
//
|
|
|
|
status = Dns_WriteRecordStructureToMessage(
|
|
pMsg,
|
|
DNS_TYPE_OPT,
|
|
(WORD) g_RecvBufSize, // recv buffer size (in Class)
|
|
0, // no flags\extended RCODE (in TTL)
|
|
0 // no data length
|
|
);
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
// increment message record count
|
|
|
|
SET_TO_WRITE_ADDITIONAL_RECORDS(pMsg);
|
|
CURRENT_RR_COUNT_FIELD(pMsg)++;
|
|
|
|
pMsg->pPreOptEnd = pstart;
|
|
}
|
|
else
|
|
{
|
|
// on failure, reset current
|
|
|
|
pMsg->pCurrent = pstart;
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_WriteStandardRequestOptToMessage(
|
|
IN OUT PDNS_MSG_BUF pMsg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write standard OPT record to message.
|
|
|
|
This contains just the buffer size and version info,
|
|
no error code or options.
|
|
|
|
Arguments:
|
|
|
|
pMsg -- message
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successfully writen.
|
|
ErrorCode on failure.
|
|
|
|
--*/
|
|
{
|
|
if ( g_UseEdns == 0 )
|
|
{
|
|
return( ERROR_REQUEST_REFUSED );
|
|
}
|
|
|
|
return Dns_WriteOptToMessage(
|
|
pMsg,
|
|
(WORD) g_RecvBufSize,
|
|
0, // no rcode
|
|
0, // standard version
|
|
NULL, // no data
|
|
0 // no data length
|
|
);
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_AddRecordsToMessage(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN PDNS_RECORD pRecord,
|
|
IN BOOL fUpdateMessage
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add record or list of records to message.No data RR cases:
|
|
|
|
This includes prereqs and deletes except for specific record cases.
|
|
|
|
EXPORTED: resolver, mcast
|
|
|
|
Arguments:
|
|
|
|
pMsg - message buffer to write to
|
|
|
|
pRecord - ptr to record (or first of list of records) to write to packet
|
|
|
|
fUpdateMessage -- If TRUE, the message is going to contain an update.
|
|
Therefore the section flags in the pRecord
|
|
should be interpreted for update. Otherwise this is
|
|
for a query message and section flags should be
|
|
interpreted for query.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pch = pMsg->pCurrent;
|
|
PCHAR pendBuffer = pMsg->pBufferEnd;
|
|
WORD currentSection = 0;
|
|
WORD section;
|
|
PSTR pnamePrevious = NULL;
|
|
PSTR pnameRecord;
|
|
WORD compressedPreviousName;
|
|
WORD offsetPreviousName;
|
|
PDNS_WIRE_RECORD pwireRecord;
|
|
PCHAR pchnext;
|
|
WORD index;
|
|
DNS_STATUS status = ERROR_SUCCESS;
|
|
BOOL funicode = FALSE;
|
|
|
|
//
|
|
// write each record in list
|
|
//
|
|
|
|
while ( pRecord )
|
|
{
|
|
//
|
|
// determine section for record
|
|
// - may not write to previous section
|
|
|
|
section = (WORD) pRecord->Flags.S.Section;
|
|
if ( section < currentSection )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Attempt to write record at %p, with section %d\n"
|
|
"\tless than previous section written %d.\n",
|
|
pRecord,
|
|
pRecord->Flags.S.Section,
|
|
currentSection ));
|
|
return( ERROR_INVALID_DATA );
|
|
}
|
|
else if ( section > currentSection )
|
|
{
|
|
currentSection = section;
|
|
SET_CURRENT_RR_COUNT_SECTION( pMsg, section );
|
|
}
|
|
|
|
//
|
|
// write record name
|
|
// - if same as previous, write compressed name
|
|
// - if first write from pRecord
|
|
// - write full name
|
|
// - clear reserved field for offsetting
|
|
//
|
|
|
|
pnameRecord = (PCHAR) pRecord->pName;
|
|
|
|
if ( pnamePrevious &&
|
|
( !pnameRecord ||
|
|
( funicode
|
|
? !wcscmp( (PWSTR)pnamePrevious, (PWSTR)pnameRecord )
|
|
: !strcmp( pnamePrevious, pnameRecord ) ) ) )
|
|
{
|
|
// compression should always be BACK ptr
|
|
|
|
DNS_ASSERT( offsetPreviousName < pch - (PCHAR)&pMsg->MessageHead );
|
|
|
|
if ( pendBuffer <= pch + sizeof(WORD) )
|
|
{
|
|
return( ERROR_MORE_DATA );
|
|
}
|
|
if ( ! compressedPreviousName )
|
|
{
|
|
compressedPreviousName = htons( (WORD)(0xC000 | offsetPreviousName) );
|
|
}
|
|
*(UNALIGNED WORD *)pch = compressedPreviousName;
|
|
pch += sizeof( WORD );
|
|
}
|
|
else
|
|
{
|
|
offsetPreviousName = (WORD)(pch - (PCHAR)&pMsg->MessageHead);
|
|
compressedPreviousName = 0;
|
|
pnamePrevious = pnameRecord;
|
|
funicode = (RECORD_CHARSET(pRecord) == DnsCharSetUnicode);
|
|
|
|
pch = Dns_WriteDottedNameToPacket(
|
|
pch,
|
|
pendBuffer,
|
|
pnamePrevious,
|
|
NULL,
|
|
0,
|
|
funicode );
|
|
|
|
if ( !pch )
|
|
{
|
|
// DCR: distinguish out of space errors from name errors during write
|
|
return( DNS_ERROR_INVALID_NAME );
|
|
}
|
|
}
|
|
|
|
//
|
|
// write record structure
|
|
//
|
|
|
|
if ( pch + sizeof(DNS_WIRE_RECORD) >= pendBuffer )
|
|
{
|
|
return( ERROR_MORE_DATA );
|
|
}
|
|
|
|
status = Dns_WriteRecordStructureToPacket(
|
|
pch,
|
|
pRecord,
|
|
fUpdateMessage );
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
pwireRecord = (PDNS_WIRE_RECORD) pch;
|
|
pch += sizeof( DNS_WIRE_RECORD );
|
|
|
|
//
|
|
// record data
|
|
//
|
|
|
|
if ( pRecord->wDataLength )
|
|
{
|
|
index = INDEX_FOR_TYPE( pRecord->wType );
|
|
DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
|
|
|
|
if ( index && RR_WriteTable[ index ] )
|
|
{
|
|
pchnext = RR_WriteTable[ index ](
|
|
pRecord,
|
|
pch,
|
|
pendBuffer
|
|
);
|
|
if ( ! pchnext )
|
|
{
|
|
status = GetLastError();
|
|
DNS_PRINT((
|
|
"ERROR: Record write routine failure for record type %d.\n\n"
|
|
"\tstatus = %d\n",
|
|
pRecord->wType,
|
|
status ));
|
|
return( status );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// write unknown types -- as RAW data only
|
|
|
|
DNS_PRINT((
|
|
"WARNING: Writing unknown type %d to message\n",
|
|
pRecord->wType ));
|
|
|
|
if ( pendBuffer - pch <= pRecord->wDataLength )
|
|
{
|
|
return( ERROR_MORE_DATA );
|
|
}
|
|
memcpy(
|
|
pch,
|
|
(PCHAR) &pRecord->Data,
|
|
pRecord->wDataLength );
|
|
pchnext = pch + pRecord->wDataLength;
|
|
}
|
|
|
|
//
|
|
// set packet record data length
|
|
//
|
|
|
|
DNS_ASSERT( (pchnext - pch) < MAXWORD );
|
|
*(UNALIGNED WORD *) &pwireRecord->DataLength =
|
|
htons( (WORD)(pchnext - pch) );
|
|
pch = pchnext;
|
|
}
|
|
|
|
// increment message record count
|
|
|
|
CURRENT_RR_COUNT_FIELD(pMsg)++;
|
|
|
|
pRecord = pRecord->pNext;
|
|
}
|
|
|
|
//
|
|
// resest message current ptr
|
|
//
|
|
|
|
pMsg->pCurrent = pch;
|
|
|
|
IF_DNSDBG( INIT )
|
|
{
|
|
DnsDbg_Message(
|
|
"Packet after adding records:",
|
|
pMsg );
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
PDNS_MSG_BUF
|
|
Dns_BuildPacket(
|
|
IN PDNS_HEADER pHeader,
|
|
IN BOOL fNoHeaderCounts,
|
|
IN PDNS_NAME pszQuestionName,
|
|
IN WORD wQuestionType,
|
|
IN PDNS_RECORD pRecords,
|
|
IN DWORD dwFlags,
|
|
IN BOOL fUpdatePacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build packet.
|
|
|
|
Arguments:
|
|
|
|
pHeader -- DNS header to send
|
|
|
|
fNoHeaderCounts - do NOT include record counts in copying header
|
|
|
|
pszName -- DNS name to query
|
|
|
|
wType -- query type
|
|
|
|
pRecords -- address to receive ptr to record list returned from query
|
|
|
|
dwFlags -- query flags
|
|
|
|
fUpdatePacket -- If TRUE, the packet is going to contain an update.
|
|
Therefore the section flags in the pRecords
|
|
should be interpreted for update. Otherwise this is
|
|
for a query and section flags will be interpreted for
|
|
query.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
PDNS_MSG_BUF pmsg;
|
|
DWORD length;
|
|
DNS_STATUS status;
|
|
|
|
DNSDBG( WRITE, (
|
|
"Dns_BuildPacket()\n"
|
|
"\tname %s\n"
|
|
"\ttype %d\n"
|
|
"\theader %p\n"
|
|
"\t - counts %d\n"
|
|
"\trecords %p\n"
|
|
"\tflags %08x\n"
|
|
"\tfUpdatePacket %d\n",
|
|
pszQuestionName,
|
|
wQuestionType,
|
|
pHeader,
|
|
fNoHeaderCounts,
|
|
pRecords,
|
|
dwFlags,
|
|
fUpdatePacket ));
|
|
|
|
//
|
|
// allocate packet
|
|
// - if just a question, standard UDP will do it
|
|
// - if contains records, then use TCP buffer
|
|
//
|
|
|
|
length = 0;
|
|
if ( pRecords )
|
|
{
|
|
length = DNS_TCP_DEFAULT_PACKET_LENGTH;
|
|
}
|
|
pmsg = Dns_AllocateMsgBuf( (WORD)length );
|
|
if ( !pmsg )
|
|
{
|
|
status = DNS_ERROR_NO_MEMORY;
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// write question?
|
|
//
|
|
|
|
if ( pszQuestionName )
|
|
{
|
|
if ( ! Dns_WriteQuestionToMessage(
|
|
pmsg,
|
|
(PDNS_NAME) pszQuestionName,
|
|
wQuestionType,
|
|
(BOOL)!!(dwFlags & DNSQUERY_UNICODE_NAME)
|
|
) )
|
|
{
|
|
status = ERROR_INVALID_NAME;
|
|
goto Failed;
|
|
}
|
|
}
|
|
|
|
//
|
|
// build packet records
|
|
//
|
|
|
|
if ( pRecords )
|
|
{
|
|
status = Dns_AddRecordsToMessage(
|
|
pmsg,
|
|
pRecords,
|
|
fUpdatePacket );
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: failure writing records to message.\n" ));
|
|
goto Failed;
|
|
}
|
|
}
|
|
|
|
//
|
|
// append standard request OPT -- if possible
|
|
//
|
|
// DCR: currently no OPT for update
|
|
//
|
|
|
|
if ( !fUpdatePacket )
|
|
{
|
|
Dns_WriteStandardRequestOptToMessage( pmsg );
|
|
}
|
|
|
|
//
|
|
// overwrite header?
|
|
//
|
|
|
|
if ( pHeader )
|
|
{
|
|
length = sizeof(DNS_HEADER);
|
|
if ( fNoHeaderCounts )
|
|
{
|
|
length = sizeof(DWORD);
|
|
}
|
|
RtlCopyMemory(
|
|
& pmsg->MessageHead,
|
|
pHeader,
|
|
length );
|
|
}
|
|
|
|
// override recursion default, if desired
|
|
|
|
if ( dwFlags & DNS_QUERY_NO_RECURSION )
|
|
{
|
|
pmsg->MessageHead.RecursionDesired = FALSE;
|
|
}
|
|
|
|
// init XID if not set
|
|
|
|
if ( pmsg->MessageHead.Xid == 0 )
|
|
{
|
|
pmsg->MessageHead.Xid = Dns_GetRandomXid( pmsg );
|
|
}
|
|
|
|
return( pmsg );
|
|
|
|
Failed:
|
|
|
|
SetLastError( status );
|
|
FREE_HEAP( pmsg );
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Reading from packet
|
|
//
|
|
|
|
PCHAR
|
|
_fastcall
|
|
Dns_SkipPacketName(
|
|
IN PCHAR pch,
|
|
IN PCHAR pchEnd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Skips over name in packet
|
|
|
|
Arguments:
|
|
|
|
pch - ptr to start of name to skip
|
|
|
|
pchEnd - ptr to byte after end of packet
|
|
|
|
Return Value:
|
|
|
|
Ptr to next byte in buffer
|
|
NULL if bad name
|
|
|
|
--*/
|
|
{
|
|
register UCHAR cch;
|
|
register UCHAR cflag;
|
|
|
|
//
|
|
// Loop until end of name
|
|
//
|
|
|
|
while ( pch < pchEnd )
|
|
{
|
|
cch = *pch++;
|
|
cflag = cch & 0xC0;
|
|
|
|
//
|
|
// normal label
|
|
// - skip to next label and continue
|
|
// - stop only if at 0 (root) label
|
|
//
|
|
|
|
if ( cflag == 0 )
|
|
{
|
|
if ( cch )
|
|
{
|
|
pch += cch;
|
|
continue;
|
|
}
|
|
return( pch );
|
|
}
|
|
|
|
//
|
|
// compression
|
|
// - skip second byte in compression and return
|
|
//
|
|
|
|
else if ( cflag == 0xC0 )
|
|
{
|
|
pch++;
|
|
return( pch );
|
|
}
|
|
else
|
|
{
|
|
DNSDBG( READ, (
|
|
"ERROR: bad packet name label byte %02 at 0x%p\n",
|
|
cch,
|
|
pch - 1 ));
|
|
|
|
return( NULL );
|
|
}
|
|
}
|
|
|
|
DNSDBG( READ, (
|
|
"ERROR: packet name at %p reads past end of packet at %p\n",
|
|
pch,
|
|
pchEnd ));
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
Dns_IsSamePacketQuestion(
|
|
IN PDNS_MSG_BUF pMsg1,
|
|
IN PDNS_MSG_BUF pMsg2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compares questions in two messages.
|
|
|
|
Arguments:
|
|
|
|
pMsg1 -- first message
|
|
|
|
pMsg2 -- second message
|
|
|
|
Return Value:
|
|
|
|
TRUE if message questions equal.
|
|
FALSE if questions not equal.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pquestion1;
|
|
PCHAR pquestion2;
|
|
PCHAR pnameEnd;
|
|
DWORD questionLength;
|
|
|
|
//
|
|
// validate and size the question fields
|
|
// - size must match
|
|
//
|
|
|
|
pquestion1 = pMsg1->MessageBody;
|
|
|
|
pnameEnd = Dns_SkipPacketName(
|
|
pquestion1,
|
|
pMsg1->pBufferEnd );
|
|
if ( !pnameEnd )
|
|
{
|
|
return FALSE;
|
|
}
|
|
questionLength = (DWORD)( pnameEnd - pquestion1 );
|
|
|
|
pquestion2 = pMsg2->MessageBody;
|
|
|
|
pnameEnd = Dns_SkipPacketName(
|
|
pquestion2,
|
|
pMsg2->pBufferEnd );
|
|
|
|
if ( !pnameEnd ||
|
|
questionLength != (DWORD)(pnameEnd - pquestion2) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// for speed, first do flat mem compare
|
|
// - this will hit 99% case as rarely would
|
|
// a server rewrite the question name
|
|
//
|
|
|
|
if ( RtlEqualMemory(
|
|
pquestion1,
|
|
pquestion2,
|
|
questionLength ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// then do case sensitive compare
|
|
// - note, we do simple ANSI casing
|
|
// assume UTF8 extended chars MUST be downcased on the
|
|
// wire per spec
|
|
//
|
|
|
|
return !_strnicmp( pquestion1, pquestion2, questionLength );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
_fastcall
|
|
Dns_SkipPacketRecord(
|
|
IN PCHAR pchRecord,
|
|
IN PCHAR pchMsgEnd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Skips over packet RR.
|
|
This is RR structure and data, not the owner name.
|
|
|
|
Arguments:
|
|
|
|
pchRecord - ptr to start of RR structure.
|
|
|
|
pchMsgEnd - end of message
|
|
|
|
Return Value:
|
|
|
|
Ptr to next record in packet.
|
|
NULL if RR outside packet or invalid.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// skip RR struct
|
|
//
|
|
|
|
pchRecord += sizeof(DNS_WIRE_RECORD);
|
|
if ( pchRecord > pchMsgEnd )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
// read datalength and skip data
|
|
// datalength field is a WORD, at end of record header
|
|
|
|
pchRecord += InlineFlipUnalignedWord( pchRecord - sizeof(WORD) );
|
|
|
|
if ( pchRecord > pchMsgEnd )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
return( pchRecord );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
Dns_SkipToRecord(
|
|
IN PDNS_HEADER pMsgHead,
|
|
IN PCHAR pMsgEnd,
|
|
IN INT iCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Skips over some number of DNS records.
|
|
|
|
Arguments:
|
|
|
|
pMsgHead -- ptr to begining of DNS message.
|
|
|
|
pMsgEnd -- ptr to end of DNS message
|
|
|
|
iCount -- if > 0, number of records to skip
|
|
if <= 0, number of records from end of packet
|
|
|
|
Return Value:
|
|
|
|
Ptr to next
|
|
NULL if bad packet.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pch;
|
|
INT i;
|
|
WORD recordCount;
|
|
|
|
//
|
|
// determine how many records to skip
|
|
//
|
|
|
|
recordCount = pMsgHead->QuestionCount
|
|
+ pMsgHead->AnswerCount
|
|
+ pMsgHead->NameServerCount
|
|
+ pMsgHead->AdditionalCount;
|
|
|
|
// iCount > 0 is skip count, MUST not be larger than
|
|
// actual count
|
|
|
|
if ( iCount > 0 )
|
|
{
|
|
if ( iCount > recordCount )
|
|
{
|
|
return( NULL );
|
|
}
|
|
}
|
|
|
|
// iCount <= 0 then (-iCount) is number of records
|
|
// from the last record
|
|
|
|
else
|
|
{
|
|
iCount += recordCount;
|
|
if ( iCount < 0 )
|
|
{
|
|
return( NULL );
|
|
}
|
|
}
|
|
|
|
// skip message header
|
|
|
|
pch = (PCHAR) (pMsgHead + 1);
|
|
if ( iCount == 0 )
|
|
{
|
|
return( pch );
|
|
}
|
|
|
|
//
|
|
// skip records
|
|
//
|
|
|
|
for ( i=0; i<iCount; i++ )
|
|
{
|
|
pch = Dns_SkipPacketName( pch, pMsgEnd );
|
|
if ( !pch )
|
|
{
|
|
return pch;
|
|
}
|
|
|
|
// skip question or RR
|
|
|
|
if ( i < pMsgHead->QuestionCount )
|
|
{
|
|
pch += sizeof(DNS_WIRE_QUESTION);
|
|
if ( pch > pMsgEnd )
|
|
{
|
|
return( NULL );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pch = Dns_SkipPacketRecord( pch, pMsgEnd );
|
|
if ( !pch )
|
|
{
|
|
return pch;
|
|
}
|
|
}
|
|
}
|
|
|
|
DNSDBG( READ, (
|
|
"Leaving SkipToRecord, current ptr = %p, offset = %04x\n"
|
|
"\tskipped %d records\n",
|
|
pch,
|
|
(WORD) (pch - (PCHAR)pMsgHead),
|
|
iCount ));
|
|
|
|
return( pch );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
_fastcall
|
|
Dns_ReadPacketName(
|
|
IN OUT PCHAR pchNameBuffer,
|
|
OUT PWORD pwNameLength,
|
|
IN OUT PWORD pwNameOffset, OPTIONAL
|
|
OUT PBOOL pfNameSameAsPrevious, OPTIONAL
|
|
IN PCHAR pchName,
|
|
IN PCHAR pchStart,
|
|
IN PCHAR pchEnd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads packet name and converts it to DNS-dotted format.
|
|
|
|
Arguments:
|
|
|
|
pchNameBuffer - buffer to write name into; contains previous name, if any
|
|
|
|
pwNameLength - length of name written to buffer; does not include
|
|
terminating NULL
|
|
|
|
pwNameOffset - addr with previous names offset (zero if no previous name);
|
|
on return, contains this name's offset
|
|
OPTIONAL but must exist if fNameSameAsPrevious exists
|
|
|
|
pfNameSameAsPrevious - addr of flag set if this name is same as previous;
|
|
OPTIONAL but must exist if pwNameOffset exists
|
|
|
|
pchName - ptr to name in packet
|
|
|
|
pchStart - start of DNS message
|
|
|
|
pchEnd - ptr to byte after end of DNS message
|
|
|
|
Return Value:
|
|
|
|
Ptr to next byte in packet.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
register PUCHAR pch = pchName;
|
|
register UCHAR cch;
|
|
register UCHAR cflag;
|
|
PCHAR pchdotted;
|
|
PCHAR pbufferEnd;
|
|
PCHAR pchreturn = NULL;
|
|
|
|
DNS_ASSERT( pch > pchStart && pchEnd > pchStart );
|
|
DNS_ASSERT( (pwNameOffset && pfNameSameAsPrevious) ||
|
|
(!pwNameOffset && !pfNameSameAsPrevious) );
|
|
|
|
|
|
//
|
|
// read through labels and/or compression until reach end of name
|
|
//
|
|
|
|
pbufferEnd = pchNameBuffer + DNS_MAX_NAME_LENGTH;
|
|
pchdotted = pchNameBuffer;
|
|
|
|
while ( pch < pchEnd )
|
|
{
|
|
cch = *pch++;
|
|
|
|
//
|
|
// at root label
|
|
// - if root name, write single '.'
|
|
// - otherwise strip trailing dot from last label
|
|
// - save length written
|
|
// - NULL teminate name
|
|
// - set same as previous FALSE
|
|
// - save packet offset to this name
|
|
// - return next byte in buffer
|
|
//
|
|
|
|
if ( cch == 0 )
|
|
{
|
|
if ( pchdotted == pchNameBuffer )
|
|
{
|
|
*pchdotted++ = '.';
|
|
}
|
|
else
|
|
{
|
|
pchdotted--;
|
|
}
|
|
*pwNameLength = (WORD)(pchdotted - pchNameBuffer);
|
|
*pchdotted = 0;
|
|
if ( pwNameOffset )
|
|
{
|
|
*pfNameSameAsPrevious = FALSE;
|
|
*pwNameOffset = (WORD)(pchName - pchStart);
|
|
}
|
|
return( pchreturn ? pchreturn : pch );
|
|
}
|
|
|
|
cflag = cch & 0xC0;
|
|
|
|
//
|
|
// regular label
|
|
// - copy label to buffer
|
|
// - jump to next label
|
|
|
|
if ( cflag == 0 )
|
|
{
|
|
PCHAR pchnext = pch + cch;
|
|
|
|
if ( pchnext >= pchEnd )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Packet name at %p extends past end of buffer\n",
|
|
pchName ));
|
|
goto Failed;
|
|
}
|
|
if ( pchdotted + cch + 1 >= pbufferEnd )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Packet name at %p exceeds max length\n",
|
|
pchName ));
|
|
goto Failed;
|
|
}
|
|
memcpy(
|
|
pchdotted,
|
|
pch,
|
|
cch );
|
|
|
|
pchdotted += cch;
|
|
*pchdotted++ = '.';
|
|
pch = pchnext;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// offset
|
|
// - get offset
|
|
// - if offset at start of name compare to previous name offset
|
|
// - otherwise follow offset to build new name
|
|
//
|
|
|
|
if ( cflag == 0xC0 )
|
|
{
|
|
WORD offset;
|
|
PCHAR pchoffset;
|
|
|
|
offset = (cch ^ 0xC0);
|
|
offset <<= 8;
|
|
offset |= *pch;
|
|
pchoffset = --pch;
|
|
|
|
//
|
|
// first offset
|
|
// - save return pointer
|
|
//
|
|
// if name is entirely offset
|
|
// - same as previous offset -- done
|
|
// - if not still save this offset rather than offset
|
|
// to name itself (first answer is usually just offset
|
|
// to question, subsequent answer RRs continue to reference
|
|
// question offset, not first answer)
|
|
//
|
|
|
|
if ( !pchreturn )
|
|
{
|
|
DNS_ASSERT( pch >= pchName );
|
|
pchreturn = pch+2;
|
|
|
|
if ( pchoffset == pchName && pwNameOffset )
|
|
{
|
|
if ( *pwNameOffset == offset )
|
|
{
|
|
*pfNameSameAsPrevious = TRUE;
|
|
return( pchreturn );
|
|
}
|
|
else
|
|
{
|
|
// save offset that comprises name
|
|
// then kill out copy of return ptr so don't
|
|
// return offset to pchName when finish copy
|
|
|
|
*pwNameOffset = offset;
|
|
*pfNameSameAsPrevious = FALSE;
|
|
pwNameOffset = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// make jump to new bytes and continue
|
|
// - verify offset is BEFORE current name
|
|
// and BEFORE current ptr
|
|
|
|
pch = pchStart + offset;
|
|
|
|
if ( pch >= pchName || pch >= pchoffset )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Bogus name offset %d, encountered at %p\n"
|
|
"\tto location %p past current position or original name.\n",
|
|
offset,
|
|
pchoffset,
|
|
pch ));
|
|
goto Failed;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// any other label byte is bogus
|
|
|
|
else
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: bogus name label byte %02x at %p\n",
|
|
cch,
|
|
pch - 1 ));
|
|
goto Failed;
|
|
}
|
|
}
|
|
|
|
DNS_PRINT((
|
|
"ERROR: packet name at %p reads to ptr %p past end of packet at %p\n",
|
|
pchName,
|
|
pch,
|
|
pchEnd ));
|
|
|
|
//
|
|
// failed
|
|
// - return NULL
|
|
// - set OUT params, keeps prefix happy on higher level calls
|
|
//
|
|
|
|
Failed:
|
|
|
|
*pwNameLength = 0;
|
|
if ( pwNameOffset )
|
|
{
|
|
*pwNameOffset = 0;
|
|
}
|
|
if ( pfNameSameAsPrevious )
|
|
{
|
|
*pfNameSameAsPrevious = FALSE;
|
|
}
|
|
return ( NULL );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
_fastcall
|
|
Dns_ReadPacketNameAllocate(
|
|
IN OUT PCHAR * ppchName,
|
|
OUT PWORD pwNameLength,
|
|
IN OUT PWORD pwPrevNameOffset, OPTIONAL
|
|
OUT PBOOL pfNameSameAsPrevious, OPTIONAL
|
|
IN PCHAR pchPacketName,
|
|
IN PCHAR pchStart,
|
|
IN PCHAR pchEnd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads packet name and creates (allocates) a copy in DNS-dotted format.
|
|
|
|
Arguments:
|
|
|
|
ppchName - addr to recv resulting name ptr
|
|
|
|
pwNameLength - length of name written to buffer
|
|
|
|
pwNameOffset - addr with previous names offset (zero if no previous name);
|
|
on return, contains this name's offset
|
|
OPTIONAL but must exist if fNameSameAsPrevious exists
|
|
|
|
fNameSameAsPrevious - addr of flag set if this name is same as previous;
|
|
OPTIONAL but must exist if pwNameOffset exists
|
|
|
|
pchPacketName - pch to name in packet
|
|
|
|
pchStart - start of DNS message
|
|
|
|
pchEnd - ptr to byte after end of DNS message
|
|
|
|
Return Value:
|
|
|
|
Ptr to next byte in packet.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pch;
|
|
PCHAR pallocName;
|
|
CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|
WORD nameLength = DNS_MAX_NAME_BUFFER_LENGTH;
|
|
|
|
//
|
|
// read packet name into buffer
|
|
//
|
|
|
|
pch = Dns_ReadPacketName(
|
|
nameBuffer,
|
|
& nameLength,
|
|
pwPrevNameOffset,
|
|
pfNameSameAsPrevious,
|
|
pchPacketName,
|
|
pchStart,
|
|
pchEnd );
|
|
if ( !pch )
|
|
{
|
|
return( pch );
|
|
}
|
|
|
|
//
|
|
// allocate buffer for packet name
|
|
// - nameLength does not include terminating NULL
|
|
//
|
|
|
|
nameLength++;
|
|
pallocName = (PCHAR) ALLOCATE_HEAP( nameLength );
|
|
if ( !pallocName )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
pallocName,
|
|
nameBuffer,
|
|
nameLength );
|
|
|
|
*ppchName = pallocName;
|
|
*pwNameLength = --nameLength;
|
|
|
|
DNSDBG( READ, (
|
|
"Allocated copy of packet name %s length %d\n",
|
|
pallocName,
|
|
nameLength ));
|
|
|
|
return( pch );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_ExtractRecordsFromMessage(
|
|
IN PDNS_MSG_BUF pMsg,
|
|
IN BOOL fUnicode,
|
|
OUT PDNS_RECORD * ppRecord
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extract records from packet.
|
|
|
|
Arguments:
|
|
|
|
pMsg - message buffer to write to
|
|
|
|
fUnicode - flag indicating strings in record should be unicode
|
|
|
|
Return Value:
|
|
|
|
Ptr to parsed record list if any.
|
|
NULL if no record list or error.
|
|
|
|
--*/
|
|
{
|
|
PDNS_MESSAGE_BUFFER pDnsBuffer = (PDNS_MESSAGE_BUFFER) &pMsg->MessageHead;
|
|
|
|
return Dns_ExtractRecordsFromBuffer(
|
|
pDnsBuffer,
|
|
pMsg->MessageLength,
|
|
fUnicode,
|
|
ppRecord );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_ParseMessage(
|
|
OUT PDNS_PARSED_MESSAGE pParse,
|
|
IN PDNS_MESSAGE_BUFFER pDnsBuffer,
|
|
IN WORD wMessageLength,
|
|
IN DWORD Flags,
|
|
IN DNS_CHARSET OutCharSet
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse DNS message.
|
|
|
|
Arguments:
|
|
|
|
pParse - ptr to blob to receive parsed message
|
|
|
|
pDnsBuffer - message buffer to read from
|
|
|
|
wMessageLength -- message length
|
|
|
|
Flags - parsing options
|
|
|
|
OutCharSet - DNS character set; only UTF8 and unicode supported
|
|
|
|
Return Value:
|
|
|
|
RCODE error status on successful parse (including NO_ERROR)
|
|
DNS_INFO_NO_RECORDS -- on auth-empty response
|
|
// referral
|
|
DNS_ERROR_BAD_PACKET -- on bad packet
|
|
|
|
Note: even on failure caller must free data
|
|
|
|
--*/
|
|
{
|
|
register PCHAR pch;
|
|
PDNS_HEADER pwireMsg = (PDNS_HEADER) pDnsBuffer;
|
|
PCHAR pchpacketEnd;
|
|
DNS_PARSED_RR parsedRR;
|
|
PSTR pnameOwner;
|
|
PSTR pnameNew = NULL;
|
|
PWORD pCurrentCountField = NULL;
|
|
WORD countRR;
|
|
WORD countSection;
|
|
WORD typePrevious = 0;
|
|
WORD nameOffset = 0;
|
|
WORD nameLength;
|
|
WORD type;
|
|
WORD index;
|
|
BYTE section;
|
|
BOOL fnameSameAsPrevious;
|
|
PDNS_RECORD pnewRR;
|
|
DNS_RRSET rrset;
|
|
DNS_RRSET rrsetAlias;
|
|
DNS_RRSET rrsetSig;
|
|
DNS_STATUS status;
|
|
DNS_STATUS rcodeStatus;
|
|
CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|
DNS_RECORD recordTemp;
|
|
|
|
DNSDBG( READ, (
|
|
"Dns_ParseMessage( %p, len=%d )\n",
|
|
pDnsBuffer,
|
|
wMessageLength
|
|
));
|
|
|
|
//
|
|
// clear parsing blob
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
pParse,
|
|
sizeof(DNS_PARSED_MESSAGE) );
|
|
|
|
//
|
|
// only UTF8 or unicode is supported directly
|
|
//
|
|
|
|
if ( OutCharSet != DnsCharSetUnicode &&
|
|
OutCharSet != DnsCharSetUtf8 )
|
|
{
|
|
ASSERT( FALSE );
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// error code
|
|
// - map RCODE to DNS error
|
|
// - if other than NAME_ERROR, don't bother parsing
|
|
//
|
|
// DCR: option to parse with other errors
|
|
//
|
|
|
|
rcodeStatus = pwireMsg->ResponseCode;
|
|
if ( rcodeStatus != 0 )
|
|
{
|
|
rcodeStatus = Dns_MapRcodeToStatus( pwireMsg->ResponseCode );
|
|
|
|
if ( rcodeStatus != DNS_ERROR_RCODE_NAME_ERROR &&
|
|
!(Flags & DNS_PARSE_FLAG_RCODE_ALL) )
|
|
{
|
|
DNSDBG( READ, (
|
|
"No records extracted from response\n"
|
|
"\tresponse code = %d\n",
|
|
pwireMsg->ResponseCode ));
|
|
return( rcodeStatus );
|
|
}
|
|
}
|
|
|
|
//
|
|
// clear record holders
|
|
// - do now so safe in bad packet cleanup
|
|
//
|
|
|
|
DNS_RRSET_INIT( rrset );
|
|
DNS_RRSET_INIT( rrsetAlias );
|
|
DNS_RRSET_INIT( rrsetSig );
|
|
|
|
//
|
|
// copy message header
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
& pParse->Header,
|
|
pwireMsg,
|
|
sizeof(DNS_HEADER) );
|
|
|
|
//
|
|
// read RRs in list of records
|
|
//
|
|
// loop through all resource records
|
|
// - skip question
|
|
// - build DNS_RECORD structure for other records
|
|
//
|
|
|
|
pchpacketEnd = (PCHAR)pwireMsg + wMessageLength;
|
|
pch = pDnsBuffer->MessageBody;
|
|
|
|
section = DNSREC_QUESTION;
|
|
pCurrentCountField = &pwireMsg->QuestionCount;
|
|
countSection = pwireMsg->QuestionCount;
|
|
countRR = 0;
|
|
|
|
while( 1 )
|
|
{
|
|
//
|
|
// changing sections
|
|
// save section number and RR count for current section
|
|
// note need immediate loop back to handle empty section
|
|
//
|
|
|
|
countRR++;
|
|
if ( countRR > countSection )
|
|
{
|
|
if ( section == DNSREC_QUESTION )
|
|
{
|
|
// no-op
|
|
}
|
|
else if ( section == DNSREC_ANSWER )
|
|
{
|
|
pParse->pAnswerRecords = rrset.pFirstRR;
|
|
}
|
|
else if ( section == DNSREC_AUTHORITY )
|
|
{
|
|
pParse->pAuthorityRecords = rrset.pFirstRR;
|
|
}
|
|
else if ( section == DNSREC_ADDITIONAL )
|
|
{
|
|
pParse->pAdditionalRecords = rrset.pFirstRR;
|
|
break;
|
|
}
|
|
section++;
|
|
pCurrentCountField++;
|
|
countSection = *(pCurrentCountField);
|
|
countRR = 0;
|
|
typePrevious = 0; // force new RR set
|
|
DNS_RRSET_INIT( rrset );
|
|
continue;
|
|
}
|
|
|
|
// validity check next RR
|
|
|
|
if ( pch >= pchpacketEnd )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: reading bad packet %p.\n"
|
|
"\tat end of packet length with more records to process\n"
|
|
"\tpacket length = %ld\n"
|
|
"\tcurrent offset = %ld\n",
|
|
wMessageLength,
|
|
pch - (PCHAR)pwireMsg
|
|
));
|
|
goto PacketError;
|
|
}
|
|
|
|
//
|
|
// read name, determining if same as previous name
|
|
//
|
|
|
|
IF_DNSDBG( READ2 )
|
|
{
|
|
DnsDbg_Lock();
|
|
DNS_PRINT((
|
|
"Reading record at offset %x\n",
|
|
(WORD)(pch - (PCHAR)pwireMsg) ));
|
|
|
|
DnsDbg_PacketName(
|
|
"Record name ",
|
|
pch,
|
|
pwireMsg,
|
|
pchpacketEnd,
|
|
"\n" );
|
|
DnsDbg_Unlock();
|
|
}
|
|
pch = Dns_ReadPacketName(
|
|
nameBuffer,
|
|
& nameLength,
|
|
& nameOffset,
|
|
& fnameSameAsPrevious,
|
|
pch,
|
|
(PCHAR) pwireMsg,
|
|
pchpacketEnd );
|
|
if ( ! pch )
|
|
{
|
|
DNS_PRINT(( "ERROR: bad packet name.\n" ));
|
|
goto PacketError;
|
|
}
|
|
IF_DNSDBG( READ2 )
|
|
{
|
|
DNS_PRINT((
|
|
"Owner name of record %s\n"
|
|
"\tlength = %d\n"
|
|
"\toffset = %d\n"
|
|
"\tfSameAsPrevious = %d\n",
|
|
nameBuffer,
|
|
nameLength,
|
|
nameOffset,
|
|
fnameSameAsPrevious ));
|
|
}
|
|
|
|
//
|
|
// question
|
|
//
|
|
|
|
if ( section == DNSREC_QUESTION )
|
|
{
|
|
PSTR pnameQuestion = NULL;
|
|
|
|
if ( !(Flags & DNS_PARSE_FLAG_NO_QUESTION) )
|
|
{
|
|
pnameQuestion = Dns_NameCopyAllocate(
|
|
nameBuffer,
|
|
(UCHAR) nameLength,
|
|
DnsCharSetUtf8, // UTF8 in
|
|
OutCharSet
|
|
);
|
|
}
|
|
pParse->pQuestionName = (LPTSTR) pnameQuestion;
|
|
|
|
if ( pch + sizeof(DNS_WIRE_QUESTION) > pchpacketEnd )
|
|
{
|
|
DNS_PRINT(( "ERROR: question exceeds packet length.\n" ));
|
|
goto PacketError;
|
|
}
|
|
pParse->QuestionType = InlineFlipUnalignedWord( pch );
|
|
pch += sizeof(WORD);
|
|
pParse->QuestionClass = InlineFlipUnalignedWord( pch );
|
|
pch += sizeof(WORD);
|
|
|
|
if ( Flags & DNS_PARSE_FLAG_ONLY_QUESTION )
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// extract RR info, type, datalength
|
|
// - verify RR within message
|
|
//
|
|
|
|
pch = Dns_ReadRecordStructureFromPacket(
|
|
pch,
|
|
pchpacketEnd,
|
|
& parsedRR );
|
|
if ( !pch )
|
|
{
|
|
DNS_PRINT(( "ERROR: bad RR struct out of packet.\n" ));
|
|
goto PacketError;
|
|
}
|
|
type = parsedRR.Type;
|
|
|
|
//
|
|
// type change -- then have new RR set
|
|
// - setup for new name
|
|
// - check and see if first non-alias answer
|
|
//
|
|
|
|
if ( type != typePrevious )
|
|
{
|
|
fnameSameAsPrevious = FALSE;
|
|
typePrevious = type;
|
|
}
|
|
|
|
//
|
|
// screen out OPT
|
|
//
|
|
// DCR: make screening configurable for API
|
|
//
|
|
|
|
if ( type == DNS_TYPE_OPT )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// screen out SIGs -- if not desired
|
|
//
|
|
#if 0
|
|
if ( type == DNS_TYPE_SIG &&
|
|
flag & NOSIG )
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// make copy of new name
|
|
//
|
|
// DCR_FIX0: name same as previous
|
|
// flag indicates only that name not compressed to previous
|
|
// name (or previous compression)
|
|
// actually need abolute ingnore case compare
|
|
// with last records name to be sure that name not previous
|
|
//
|
|
|
|
if ( !fnameSameAsPrevious )
|
|
{
|
|
pnameNew = Dns_NameCopyAllocate(
|
|
nameBuffer,
|
|
(UCHAR) nameLength,
|
|
DnsCharSetUtf8, // UTF8 string in
|
|
OutCharSet
|
|
);
|
|
if ( !pnameNew )
|
|
{
|
|
status = DNS_ERROR_NO_MEMORY;
|
|
goto Failed;
|
|
}
|
|
pnameOwner = pnameNew;
|
|
DNSDBG( OFF, (
|
|
"Copy of owner name of record being read from packet %s\n",
|
|
nameBuffer ));
|
|
}
|
|
DNS_ASSERT( pnameOwner );
|
|
DNS_ASSERT( pnameNew || fnameSameAsPrevious );
|
|
|
|
//
|
|
// TSIG record requires owner name for versioning
|
|
//
|
|
|
|
recordTemp.pName = (PTCHAR) pnameOwner;
|
|
|
|
//
|
|
// read RR data for type
|
|
//
|
|
|
|
index = INDEX_FOR_TYPE( type );
|
|
DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
|
|
|
|
if ( !index || !RR_ReadTable[ index ] )
|
|
{
|
|
// unknown types -- index to NULL type to use
|
|
// FlatRecordRead()
|
|
|
|
DNS_PRINT((
|
|
"WARNING: Reading unknown record type %d from message\n",
|
|
parsedRR.Type ));
|
|
|
|
index = DNS_TYPE_NULL;
|
|
}
|
|
|
|
pnewRR = RR_ReadTable[ index ](
|
|
&recordTemp,
|
|
OutCharSet,
|
|
(PCHAR) pDnsBuffer,
|
|
parsedRR.pchData,
|
|
pch // end of record data
|
|
);
|
|
if ( ! pnewRR )
|
|
{
|
|
status = GetLastError();
|
|
ASSERT( status != ERROR_SUCCESS );
|
|
|
|
DNS_PRINT((
|
|
"ERROR: RR_ReadTable routine failure for type %d.\n"
|
|
"\tstatus = %d\n"
|
|
"\tpchdata = %p\n"
|
|
"\tpchend = %p\n",
|
|
parsedRR.Type,
|
|
status,
|
|
parsedRR.pchData,
|
|
pch ));
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
status = DNS_ERROR_NO_MEMORY;
|
|
}
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// write record info
|
|
// - first RR in set gets new name allocation
|
|
// and is responsible for cleanup
|
|
// - no data cleanup necessary as all data is
|
|
// contained in the RR allocation
|
|
//
|
|
|
|
pnewRR->pName = (PTCHAR) pnameOwner;
|
|
pnewRR->wType = type;
|
|
pnewRR->dwTtl = parsedRR.Ttl;
|
|
pnewRR->Flags.S.Section = section;
|
|
pnewRR->Flags.S.CharSet = OutCharSet;
|
|
FLAG_FreeOwner( pnewRR ) = !fnameSameAsPrevious;
|
|
FLAG_FreeData( pnewRR ) = FALSE;
|
|
|
|
//
|
|
// add RR to list
|
|
//
|
|
|
|
if ( type == DNS_TYPE_SIG &&
|
|
pParse->QuestionType != DNS_TYPE_SIG )
|
|
{
|
|
DNS_RRSET_ADD( rrsetSig, pnewRR );
|
|
}
|
|
else if ( type == DNS_TYPE_CNAME &&
|
|
pParse->QuestionType != DNS_TYPE_ALL &&
|
|
pParse->QuestionType != DNS_TYPE_CNAME &&
|
|
section == DNSREC_ANSWER )
|
|
{
|
|
DNS_RRSET_ADD( rrsetAlias, pnewRR );
|
|
}
|
|
else
|
|
{
|
|
DNS_RRSET_ADD( rrset, pnewRR );
|
|
}
|
|
|
|
// clear new ptr, as name now part of record
|
|
// this is strictly used to determine when pnameOwner
|
|
// must be cleaned up on failure
|
|
|
|
pnameNew = NULL;
|
|
|
|
} // end loop through packet's records
|
|
|
|
//
|
|
// set response info
|
|
//
|
|
// DCR: if don't want single SIG, easy to break out by section
|
|
//
|
|
|
|
pParse->pAliasRecords = rrsetAlias.pFirstRR;
|
|
|
|
pParse->pSigRecords = rrsetSig.pFirstRR;
|
|
|
|
//
|
|
// break out various query NO_ERROR responses
|
|
// - empty response
|
|
// - referral
|
|
// - garbage
|
|
//
|
|
|
|
if ( pwireMsg->AnswerCount == 0 &&
|
|
rcodeStatus == 0 &&
|
|
pwireMsg->Opcode == DNS_OPCODE_QUERY &&
|
|
pwireMsg->IsResponse )
|
|
{
|
|
PDNS_RECORD prrAuth = pParse->pAuthorityRecords;
|
|
|
|
if ( (prrAuth && prrAuth->wType == DNS_TYPE_SOA) ||
|
|
(!prrAuth && pwireMsg->Authoritative) )
|
|
{
|
|
rcodeStatus = DNS_INFO_NO_RECORDS;
|
|
DNSDBG( READ, ( "Empty-auth response at %p.\n", pwireMsg ));
|
|
}
|
|
else if ( prrAuth &&
|
|
prrAuth->wType == DNS_TYPE_NS &&
|
|
!pwireMsg->Authoritative &&
|
|
(!pwireMsg->RecursionAvailable || !pwireMsg->RecursionDesired) )
|
|
{
|
|
rcodeStatus = DNS_ERROR_REFERRAL_RESPONSE;
|
|
DNSDBG( READ, ( "Referral response at %p.\n", pwireMsg ));
|
|
}
|
|
else
|
|
{
|
|
rcodeStatus = DNS_ERROR_BAD_PACKET;
|
|
DNSDBG( ANY, ( "Bogus NO_ERROR response at %p.\n", pwireMsg ));
|
|
DNS_ASSERT( FALSE );
|
|
}
|
|
}
|
|
|
|
// verify never turn RCODE result into SUCCESS
|
|
|
|
ASSERT( pwireMsg->ResponseCode == 0 || rcodeStatus != ERROR_SUCCESS );
|
|
ASSERT( pnameNew == NULL );
|
|
|
|
pParse->Status = rcodeStatus;
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DnsDbg_ParsedMessage(
|
|
"Parsed message:\n",
|
|
pParse );
|
|
}
|
|
return( rcodeStatus );
|
|
|
|
|
|
PacketError:
|
|
|
|
DNS_PRINT(( "ERROR: bad packet in buffer.\n" ));
|
|
status = DNS_ERROR_BAD_PACKET;
|
|
|
|
Failed:
|
|
|
|
FREE_HEAP( pnameNew );
|
|
|
|
Dns_RecordListFree( rrset.pFirstRR );
|
|
Dns_RecordListFree( rrsetAlias.pFirstRR );
|
|
Dns_RecordListFree( rrsetSig.pFirstRR );
|
|
|
|
pParse->Status = status;
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Dns_FreeParsedMessageFields(
|
|
IN OUT PDNS_PARSED_MESSAGE pParse
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free a parsed DNS message struct.
|
|
|
|
Arguments:
|
|
|
|
pParse - ptr to blob to receive parsed message
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DNSDBG( TRACE, (
|
|
"Dns_FreeParsedMessageFields( %p )\n",
|
|
pParse ));
|
|
|
|
// question name
|
|
|
|
FREE_HEAP( pParse->pQuestionName );
|
|
|
|
// records
|
|
|
|
Dns_RecordListFree( pParse->pAliasRecords );
|
|
Dns_RecordListFree( pParse->pAnswerRecords );
|
|
Dns_RecordListFree( pParse->pAdditionalRecords );
|
|
Dns_RecordListFree( pParse->pAuthorityRecords );
|
|
Dns_RecordListFree( pParse->pSigRecords );
|
|
|
|
// clear to avoid confusion or double free
|
|
|
|
RtlZeroMemory(
|
|
pParse,
|
|
sizeof(DNS_PARSED_MESSAGE) );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_ExtractRecordsFromBuffer(
|
|
IN PDNS_MESSAGE_BUFFER pDnsBuffer,
|
|
IN WORD wMessageLength,
|
|
IN BOOL fUnicode,
|
|
OUT PDNS_RECORD * ppRecord
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extract records from packet buffer.
|
|
|
|
Arguments:
|
|
|
|
pDnsBuffer - message buffer to read from
|
|
|
|
fUnicode - flag indicating strings in record should be unicode
|
|
|
|
Return Value:
|
|
|
|
Ptr to parsed record list if any.
|
|
NULL if no record list or error.
|
|
|
|
--*/
|
|
{
|
|
PDNS_RECORD prr;
|
|
DNS_STATUS status;
|
|
DNS_PARSED_MESSAGE parseBlob;
|
|
|
|
DNSDBG( READ, (
|
|
"Dns_ExtractRecordsFromBuffer( %p, len=%d )\n",
|
|
pDnsBuffer,
|
|
wMessageLength
|
|
));
|
|
|
|
//
|
|
// call real parsing function
|
|
//
|
|
|
|
status = Dns_ParseMessage(
|
|
& parseBlob,
|
|
pDnsBuffer,
|
|
wMessageLength,
|
|
DNS_PARSE_FLAG_NO_QUESTION,
|
|
fUnicode
|
|
? DnsCharSetUnicode
|
|
: DnsCharSetUtf8
|
|
);
|
|
|
|
//
|
|
// concatentate into one blob
|
|
// - work backwards so only touch each record once
|
|
//
|
|
// DCR: we have no real support for returning SIG records
|
|
// DCR: should append SIGs also?
|
|
//
|
|
|
|
prr = Dns_RecordListAppend(
|
|
parseBlob.pAuthorityRecords,
|
|
parseBlob.pAdditionalRecords
|
|
);
|
|
|
|
prr = Dns_RecordListAppend(
|
|
parseBlob.pAnswerRecords,
|
|
prr
|
|
);
|
|
|
|
prr = Dns_RecordListAppend(
|
|
parseBlob.pAliasRecords,
|
|
prr
|
|
);
|
|
|
|
*ppRecord = prr;
|
|
|
|
//
|
|
// cleanup
|
|
// - clear what we pick up
|
|
// - then free rest of blob
|
|
//
|
|
|
|
parseBlob.pAuthorityRecords = NULL;
|
|
parseBlob.pAdditionalRecords = NULL;
|
|
parseBlob.pAnswerRecords = NULL;
|
|
parseBlob.pAliasRecords = NULL;
|
|
|
|
Dns_FreeParsedMessageFields( &parseBlob );
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DnsDbg_RecordSet(
|
|
"Extracted records:\n",
|
|
prr );
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
DNS_STATUS
|
|
Dns_ExtractRecordsFromBuffer(
|
|
IN PDNS_MESSAGE_BUFFER pDnsBuffer,
|
|
IN WORD wMessageLength,
|
|
IN BOOL fUnicode,
|
|
OUT PDNS_RECORD * ppRecord
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extract records from packet buffer.
|
|
|
|
Arguments:
|
|
|
|
pDnsBuffer - message buffer to read from
|
|
|
|
fUnicode - flag indicating strings in record should be unicode
|
|
|
|
Return Value:
|
|
|
|
Ptr to parsed record list if any.
|
|
NULL if no record list or error.
|
|
|
|
--*/
|
|
{
|
|
register PCHAR pch;
|
|
PDNS_HEADER pwireMsg = (PDNS_HEADER) pDnsBuffer;
|
|
PCHAR pchpacketEnd;
|
|
DNS_PARSED_RR parsedRR;
|
|
PSTR pnameOwner;
|
|
PSTR pnameNew = NULL;
|
|
DNS_CHARSET outCharSet;
|
|
WORD countRR;
|
|
WORD countSection;
|
|
WORD typePrevious = 0;
|
|
WORD nameOffset = 0;
|
|
WORD nameLength;
|
|
WORD index;
|
|
BYTE section;
|
|
BOOL fnameSameAsPrevious;
|
|
PDNS_RECORD pnewRR;
|
|
DNS_RRSET rrset;
|
|
DNS_STATUS status;
|
|
DNS_STATUS rcodeStatus;
|
|
CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|
DNS_RECORD recordTemp;
|
|
PWORD pCurrentCountField = NULL;
|
|
|
|
DNSDBG( READ, (
|
|
"Dns_ExtractRecordsFromBuffer( %p, len=%d )\n",
|
|
pDnsBuffer,
|
|
wMessageLength
|
|
));
|
|
|
|
//
|
|
// error code
|
|
// - map RCODE to DNS error
|
|
// - if other than NAME_ERROR, don't bother parsing
|
|
//
|
|
|
|
rcodeStatus = pwireMsg->ResponseCode;
|
|
if ( rcodeStatus != 0 )
|
|
{
|
|
rcodeStatus = Dns_MapRcodeToStatus( pwireMsg->ResponseCode );
|
|
if ( rcodeStatus != DNS_ERROR_RCODE_NAME_ERROR )
|
|
{
|
|
DNSDBG( READ, (
|
|
"No records extracted from response\n"
|
|
"\tresponse code = %d\n",
|
|
pwireMsg->ResponseCode ));
|
|
return( rcodeStatus );
|
|
}
|
|
}
|
|
|
|
DNS_RRSET_INIT( rrset );
|
|
|
|
//
|
|
// detemine char set
|
|
//
|
|
|
|
if ( fUnicode )
|
|
{
|
|
outCharSet = DnsCharSetUnicode;
|
|
}
|
|
else
|
|
{
|
|
outCharSet = DnsCharSetUtf8;
|
|
}
|
|
|
|
//
|
|
// read RRs in list of records
|
|
//
|
|
// loop through all resource records
|
|
// - skip question
|
|
// - build DNS_RECORD structure for other records
|
|
//
|
|
|
|
pchpacketEnd = (PCHAR)pwireMsg + wMessageLength;
|
|
pch = pDnsBuffer->MessageBody;
|
|
|
|
section = DNSREC_QUESTION;
|
|
pCurrentCountField = &pwireMsg->QuestionCount;
|
|
countSection = pwireMsg->QuestionCount;
|
|
countRR = 0;
|
|
|
|
while( 1 )
|
|
{
|
|
//
|
|
// changing sections
|
|
// save section number and RR count for current section
|
|
// note need immediate loop back to handle empty section
|
|
//
|
|
|
|
countRR++;
|
|
if ( countRR > countSection )
|
|
{
|
|
if ( section == DNSREC_ADDITIONAL )
|
|
{
|
|
break;
|
|
}
|
|
section++;
|
|
pCurrentCountField++;
|
|
countSection = *(pCurrentCountField);
|
|
countRR = 0;
|
|
continue;
|
|
}
|
|
|
|
// validity check next RR
|
|
|
|
if ( pch >= pchpacketEnd )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: reading bad packet %p.\n"
|
|
"\tat end of packet length with more records to process\n"
|
|
"\tpacket length = %ld\n"
|
|
"\tcurrent offset = %ld\n",
|
|
pDnsBuffer,
|
|
wMessageLength,
|
|
pch - (PCHAR)pwireMsg
|
|
));
|
|
goto PacketError;
|
|
}
|
|
|
|
//
|
|
// skip question
|
|
//
|
|
|
|
if ( section == DNSREC_QUESTION )
|
|
{
|
|
pch = Dns_SkipPacketName(
|
|
pch,
|
|
pchpacketEnd );
|
|
if ( !pch )
|
|
{
|
|
DNS_PRINT(( "ERROR: bad question name.\n" ));
|
|
goto PacketError;
|
|
}
|
|
pch += sizeof(DNS_WIRE_QUESTION);
|
|
if ( pch > pchpacketEnd )
|
|
{
|
|
DNS_PRINT(( "ERROR: question exceeds packet length.\n" ));
|
|
goto PacketError;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// read name, determining if same as previous name
|
|
//
|
|
|
|
IF_DNSDBG( READ2 )
|
|
{
|
|
DnsDbg_Lock();
|
|
DNS_PRINT((
|
|
"Reading record at offset %x\n",
|
|
(WORD)(pch - (PCHAR)pwireMsg) ));
|
|
|
|
DnsDbg_PacketName(
|
|
"Record name ",
|
|
pch,
|
|
pwireMsg,
|
|
pchpacketEnd,
|
|
"\n" );
|
|
DnsDbg_Unlock();
|
|
}
|
|
pch = Dns_ReadPacketName(
|
|
nameBuffer,
|
|
& nameLength,
|
|
& nameOffset,
|
|
& fnameSameAsPrevious,
|
|
pch,
|
|
(PCHAR) pwireMsg,
|
|
pchpacketEnd );
|
|
if ( ! pch )
|
|
{
|
|
DNS_PRINT(( "ERROR: bad packet name.\n" ));
|
|
goto PacketError;
|
|
}
|
|
IF_DNSDBG( READ2 )
|
|
{
|
|
DNS_PRINT((
|
|
"Owner name of record %s\n"
|
|
"\tlength = %d\n"
|
|
"\toffset = %d\n"
|
|
"\tfSameAsPrevious = %d\n",
|
|
nameBuffer,
|
|
nameLength,
|
|
nameOffset,
|
|
fnameSameAsPrevious ));
|
|
}
|
|
|
|
//
|
|
// extract RR info, type, datalength
|
|
// - verify RR within message
|
|
//
|
|
|
|
pch = Dns_ReadRecordStructureFromPacket(
|
|
pch,
|
|
pchpacketEnd,
|
|
& parsedRR );
|
|
if ( !pch )
|
|
{
|
|
DNS_PRINT(( "ERROR: bad RR struct out of packet.\n" ));
|
|
goto PacketError;
|
|
}
|
|
|
|
//
|
|
// on type change, always have new RR set
|
|
// - setup for new name
|
|
//
|
|
|
|
if ( parsedRR.Type != typePrevious )
|
|
{
|
|
fnameSameAsPrevious = FALSE;
|
|
typePrevious = parsedRR.Type;
|
|
}
|
|
|
|
//
|
|
// screen out OPT
|
|
//
|
|
// DCR: make screening configurable for API
|
|
//
|
|
|
|
if ( parsedRR.Type == DNS_TYPE_OPT )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// make copy of new name
|
|
//
|
|
// DCR_FIX0: name same as previous
|
|
// flag indicates only that name not compressed to previous
|
|
// name (or previous compression)
|
|
// actually need abolute ingnore case compare
|
|
// with last records name to be sure that name not previous
|
|
//
|
|
|
|
if ( !fnameSameAsPrevious )
|
|
{
|
|
pnameNew = Dns_NameCopyAllocate(
|
|
nameBuffer,
|
|
(UCHAR) nameLength,
|
|
DnsCharSetUtf8, // UTF8 string in
|
|
outCharSet
|
|
);
|
|
pnameOwner = pnameNew;
|
|
DNSDBG( READ2, (
|
|
"Copy of owner name of record being read from packet %s\n",
|
|
pnameOwner ));
|
|
}
|
|
DNS_ASSERT( pnameOwner );
|
|
DNS_ASSERT( pnameNew || fnameSameAsPrevious );
|
|
|
|
//
|
|
// TSIG record requires owner name for versioning
|
|
//
|
|
|
|
recordTemp.pName = pnameOwner;
|
|
|
|
//
|
|
// read RR data for type
|
|
//
|
|
|
|
index = INDEX_FOR_TYPE( parsedRR.Type );
|
|
DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
|
|
|
|
if ( !index || !RR_ReadTable[ index ] )
|
|
{
|
|
// unknown types -- index to NULL type to use
|
|
// FlatRecordRead()
|
|
|
|
DNS_PRINT((
|
|
"WARNING: Reading unknown record type %d from message\n",
|
|
parsedRR.Type ));
|
|
|
|
index = DNS_TYPE_NULL;
|
|
}
|
|
|
|
pnewRR = RR_ReadTable[ index ](
|
|
&recordTemp,
|
|
outCharSet,
|
|
(PCHAR) pDnsBuffer,
|
|
parsedRR.pchData,
|
|
pch // end of record data
|
|
);
|
|
if ( ! pnewRR )
|
|
{
|
|
status = GetLastError();
|
|
ASSERT( status != ERROR_SUCCESS );
|
|
|
|
DNS_PRINT((
|
|
"ERROR: RR_ReadTable routine failure for type %d.\n"
|
|
"\tstatus = %d\n"
|
|
"\tpchdata = %p\n"
|
|
"\tpchend = %p\n",
|
|
parsedRR.Type,
|
|
status,
|
|
parsedRR.pchData,
|
|
pch ));
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
status = DNS_ERROR_NO_MEMORY;
|
|
}
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// write record info
|
|
// - first RR in set gets new name allocation
|
|
// and is responsible for cleanup
|
|
// - no data cleanup necessary as all data is
|
|
// contained in the RR allocation
|
|
//
|
|
|
|
pnewRR->pName = pnameOwner;
|
|
pnewRR->wType = parsedRR.Type;
|
|
pnewRR->dwTtl = parsedRR.Ttl;
|
|
pnewRR->Flags.S.Section = section;
|
|
pnewRR->Flags.S.CharSet = outCharSet;
|
|
FLAG_FreeOwner( pnewRR ) = !fnameSameAsPrevious;
|
|
FLAG_FreeData( pnewRR ) = FALSE;
|
|
|
|
// add RR to list
|
|
|
|
DNS_RRSET_ADD( rrset, pnewRR );
|
|
|
|
// clear new ptr, as name now part of record
|
|
// this is strictly used to determine when pnameOwner
|
|
// must be cleaned up on failure
|
|
|
|
pnameNew = NULL;
|
|
|
|
} // end loop through packet's records
|
|
|
|
//
|
|
// return parsed record list
|
|
// - return DNS error for RCODE
|
|
// - set special return code to differentiate empty response
|
|
//
|
|
// DCR: should have special REFERRAL response
|
|
// - could overload NOTAUTH rcode
|
|
// DCR: should have special EMPTY_AUTH response
|
|
// - could have empty-auth overload NXRRSET
|
|
//
|
|
// DCR: best check on distinguishing EMPTY_AUTH from REFERRAL
|
|
//
|
|
|
|
if ( pwireMsg->AnswerCount == 0 && rcodeStatus == 0 )
|
|
{
|
|
if ( !rrset.pFirstRR || rrset.pFirstRR->wType == DNS_TYPE_SOA )
|
|
{
|
|
rcodeStatus = DNS_INFO_NO_RECORDS;
|
|
DNSDBG( READ, ( "Empty-auth response at %p.\n", pwireMsg ));
|
|
}
|
|
#if 0
|
|
else if ( rrset.pFirstRR->wType == DNS_TYPE_NS &&
|
|
!pwireMsg->Authoritative )
|
|
{
|
|
rcodeStatus = DNS_INFO_REFERRAL;
|
|
DNSDBG( READ, ( "Referral response at %p.\n", pwireMsg ));
|
|
}
|
|
else
|
|
{
|
|
rcodeStatus = DNS_ERROR_BAD_PACKET;
|
|
DNSDBG( READ, ( "Bogus NO_ERROR response at %p.\n", pwireMsg ));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// verify never turn RCODE result into SUCCESS
|
|
|
|
ASSERT( pwireMsg->ResponseCode == 0 || rcodeStatus != ERROR_SUCCESS );
|
|
ASSERT( pnameNew == NULL );
|
|
|
|
*ppRecord = rrset.pFirstRR;
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DnsDbg_RecordSet(
|
|
"Extracted records:\n",
|
|
*ppRecord );
|
|
}
|
|
return( rcodeStatus );
|
|
|
|
|
|
PacketError:
|
|
|
|
DNS_PRINT(( "ERROR: bad packet in buffer.\n" ));
|
|
status = DNS_ERROR_BAD_PACKET;
|
|
|
|
Failed:
|
|
|
|
FREE_HEAP( pnameNew );
|
|
|
|
Dns_RecordListFree( rrset.pFirstRR );
|
|
|
|
return( status );
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
VOID
|
|
Dns_NormalizeAllRecordTtls(
|
|
IN OUT PDNS_RECORD pRecord
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds the lowest TTL value in RR set and the sets all
|
|
records to that value.
|
|
|
|
Arguments:
|
|
|
|
pRecord - record set to normalize ttl values of.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PDNS_RECORD pTemp = pRecord;
|
|
DWORD dwTtl;
|
|
WORD wType;
|
|
|
|
//
|
|
// Get the Ttl of the first record (if there is one)
|
|
//
|
|
if ( pTemp )
|
|
{
|
|
dwTtl = pTemp->dwTtl;
|
|
wType = pTemp->wType;
|
|
pTemp = pTemp->pNext;
|
|
}
|
|
|
|
//
|
|
// Walk any remaining records looking for an even lower ttl value
|
|
//
|
|
while ( pTemp &&
|
|
pTemp->wType == wType &&
|
|
pTemp->Flags.S.Section == DNSREC_ANSWER )
|
|
{
|
|
if ( pTemp->dwTtl < dwTtl )
|
|
{
|
|
dwTtl = pTemp->dwTtl;
|
|
}
|
|
|
|
pTemp = pTemp->pNext;
|
|
}
|
|
|
|
//
|
|
// Set all records to this lowest ttl value
|
|
//
|
|
pTemp = pRecord;
|
|
|
|
while ( pTemp &&
|
|
pTemp->wType == wType &&
|
|
pTemp->Flags.S.Section == DNSREC_ANSWER )
|
|
{
|
|
pTemp->dwTtl = dwTtl;
|
|
pTemp = pTemp->pNext;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
Dns_ReadRecordStructureFromPacket(
|
|
IN PCHAR pchPacket,
|
|
IN PCHAR pchMsgEnd,
|
|
IN OUT PDNS_PARSED_RR pParsedRR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read record structure from packet.
|
|
|
|
Arguments:
|
|
|
|
pchPacket - ptr to record structure in packet
|
|
|
|
pchMsgEnd - end of message
|
|
|
|
pParsedRR - ptr to struct to receive parsed RR
|
|
|
|
Return Value:
|
|
|
|
Ptr to next record in packet -- based on datalength.
|
|
Null on error.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pch = pchPacket;
|
|
|
|
DNSDBG( READ2, (
|
|
"Dns_ReadRecordStructureFromPacket(%p).\n",
|
|
pch ));
|
|
|
|
//
|
|
// verify record structure within packet
|
|
//
|
|
|
|
if ( pch + sizeof(DNS_WIRE_RECORD) > pchMsgEnd )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: record structure at %p is not within packet!.\n",
|
|
pchPacket ));
|
|
return( 0 );
|
|
}
|
|
|
|
//
|
|
// flip fields and write to aligned struct
|
|
//
|
|
|
|
pParsedRR->pchRR = pch;
|
|
|
|
pParsedRR->Type = InlineFlipUnalignedWord( pch );
|
|
pch += sizeof(WORD);
|
|
pParsedRR->Class = InlineFlipUnalignedWord( pch );
|
|
pch += sizeof(WORD);
|
|
pParsedRR->Ttl = InlineFlipUnalignedDword( pch );
|
|
pch += sizeof(DWORD);
|
|
pParsedRR->DataLength = InlineFlipUnalignedWord( pch );
|
|
pch += sizeof(WORD);
|
|
|
|
pParsedRR->pchData = pch;
|
|
|
|
//
|
|
// verify datalength does not extend beyond packet end
|
|
//
|
|
|
|
pch += pParsedRR->DataLength;
|
|
pParsedRR->pchNextRR = pch;
|
|
|
|
if ( pch > pchMsgEnd )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: record data at %p (length %d) is not within packet!.\n",
|
|
pch - pParsedRR->DataLength,
|
|
pParsedRR->DataLength ));
|
|
return( 0 );
|
|
}
|
|
|
|
//
|
|
// return ptr to next record in packet
|
|
//
|
|
|
|
return( pch );
|
|
}
|
|
|
|
|
|
|
|
PCHAR
|
|
Dns_ParsePacketRecord(
|
|
IN PCHAR pchPacket,
|
|
IN PCHAR pchMsgEnd,
|
|
IN OUT PDNS_PARSED_RR pParsedRR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read record from packet.
|
|
|
|
Arguments:
|
|
|
|
pchPacket - ptr to record structure in packet
|
|
|
|
pchMsgEnd - end of message
|
|
|
|
pParsedRR - ptr to struct to receive parsed RR
|
|
|
|
Return Value:
|
|
|
|
Ptr to next record in packet -- based on datalength.
|
|
Null on error.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pch;
|
|
|
|
DNSDBG( READ2, (
|
|
"Dns_ParsePacketRecord().\n"
|
|
"\tpRecordStart = %p\n"
|
|
"\tpMsgEnd = %p\n",
|
|
pchPacket,
|
|
pchMsgEnd ));
|
|
|
|
//
|
|
// save and skip name
|
|
//
|
|
|
|
pch = Dns_SkipPacketName(
|
|
pchPacket,
|
|
pchMsgEnd );
|
|
if ( !pch )
|
|
{
|
|
return( pch );
|
|
}
|
|
pParsedRR->pchName = pchPacket;
|
|
|
|
//
|
|
// parse record structure
|
|
//
|
|
|
|
pch = Dns_ReadRecordStructureFromPacket(
|
|
pch,
|
|
pchMsgEnd,
|
|
pParsedRR );
|
|
|
|
return( pch );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// XID utilities
|
|
//
|
|
|
|
// Max increase in XID space, used in mod() op
|
|
// so pick nice prime number
|
|
|
|
#define MAX_XID_BUMP (487)
|
|
|
|
// XID seed
|
|
|
|
WORD g_XidSeed = 0;
|
|
|
|
// last XID for process
|
|
|
|
WORD g_LastXid = 0;
|
|
|
|
// XID mask
|
|
|
|
WORD g_XidMask = 0;
|
|
|
|
|
|
WORD
|
|
Dns_GetRandomXid(
|
|
IN PVOID pSeed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate "random" XID.
|
|
|
|
By "random" what we really mean is an XID that's not the same as previous XIDs
|
|
and is not immediately guessable from the outside.
|
|
|
|
Arguments:
|
|
|
|
pSeed -- seed ptr; from stack or heap, provides differentiation beyond time
|
|
|
|
Return Value:
|
|
|
|
XID generated
|
|
|
|
--*/
|
|
{
|
|
WORD xid;
|
|
|
|
//
|
|
// build "bump" off
|
|
//
|
|
// may have multiple sessions to different processes\threads
|
|
//
|
|
// use tick count (unpredictability)
|
|
// seed counter (non-repetition of jumps and MT separation)
|
|
// seed ptr (MT separation)
|
|
// note, ptr is first pushed down to count 64-bit boundaries, so lack of
|
|
// randomness in last 6bits is not preserved
|
|
//
|
|
|
|
xid = (WORD) (GetTickCount() + (PtrToUlong(pSeed) >> 6) + g_XidSeed++);
|
|
if ( g_XidMask == 0 )
|
|
{
|
|
g_XidMask = xid ;
|
|
}
|
|
|
|
//
|
|
// XID is bump off last XID
|
|
// - the limiting this bump limits possibility of XID reuse
|
|
// in less than 64K/max-bump XIDs
|
|
// - don't use zero XID
|
|
// - flip just to generate more confusion, hackers will have the code
|
|
// and can generate XIDs so the real protection here is the MAX_XID_BUMP
|
|
// which means MAX_XID_BUMP packets need to be generated -- it's not 64K
|
|
// but it's a bunch
|
|
//
|
|
|
|
xid = (xid % MAX_XID_BUMP) + 1 + g_LastXid;
|
|
if ( xid == 0 )
|
|
{
|
|
xid = 5;
|
|
}
|
|
g_LastXid = xid;
|
|
|
|
return htons( xid ^ g_XidMask );
|
|
}
|
|
|
|
//
|
|
// End packet.c
|
|
//
|
|
|