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.
1815 lines
50 KiB
1815 lines
50 KiB
/*-----------------------------------------------------------------------------
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
DNS Server debugger extension
|
|
|
|
Written by Jeff Westhead Feb 2001
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
#include "dbgexts.h"
|
|
|
|
|
|
//
|
|
// DNS Server includes
|
|
//
|
|
|
|
#define LDAP_UNICODE 1
|
|
#include <winldap.h> // public LDAP
|
|
#include <winber.h> // for ber formatting
|
|
#include <ntldap.h> // public server ldap constants
|
|
#include <rpc.h> // RPC def needed for ntdsapi.h
|
|
#include <ntdsapi.h> // DS access bit definitions
|
|
#include <ntdsadef.h> // DS constants
|
|
#include <dsrole.h>
|
|
#include <time.h>
|
|
#define DNSLIB_SECURITY // include security defs
|
|
|
|
#define SDK_DNS_RECORD // DNS_RECORD in SDK format
|
|
#define NO_DNSAPI_DLL // build without requiring dnsapi.dll
|
|
#include <dnslib.h> // DNS library routines
|
|
|
|
#ifndef FASTCALL
|
|
#define FASTCALL
|
|
#endif
|
|
|
|
#include "dnsrpc_s.h" // DNS RPC definitions
|
|
|
|
#include "srvcfg.h"
|
|
#include "file.h"
|
|
#include "tree.h"
|
|
#include "name.h"
|
|
#include "record.h"
|
|
#include "update.h"
|
|
#include "dpart.h"
|
|
#include "EventControl.h"
|
|
#include "zone.h"
|
|
#include "registry.h"
|
|
#include "msginfo.h"
|
|
#include "socket.h"
|
|
#include "packetq.h"
|
|
#include "dbase.h"
|
|
#include "recurse.h"
|
|
#include "nameutil.h"
|
|
#include "stats.h"
|
|
#include "debug.h"
|
|
#include "memory.h"
|
|
#include "dfile.h"
|
|
#include "wins.h"
|
|
#include "rrfunc.h"
|
|
#include "dnsprocs.h"
|
|
#include "rescodes.h"
|
|
#include "sdutl.h"
|
|
#include "ds.h"
|
|
#include "timeout.h"
|
|
|
|
|
|
//
|
|
// Print DNS server statistics
|
|
//
|
|
|
|
LPSTR MemTagStrings[] = // Stolen from dns\server\client\print.c
|
|
{
|
|
MEMTAG_NAME_NONE ,
|
|
MEMTAG_NAME_PACKET_UDP ,
|
|
MEMTAG_NAME_PACKET_TCP ,
|
|
MEMTAG_NAME_NAME ,
|
|
MEMTAG_NAME_ZONE ,
|
|
MEMTAG_NAME_UPDATE ,
|
|
MEMTAG_NAME_UPDATE_LIST ,
|
|
MEMTAG_NAME_TIMEOUT ,
|
|
MEMTAG_NAME_NODEHASH ,
|
|
MEMTAG_NAME_DS_DN ,
|
|
MEMTAG_NAME_DS_MOD , // 10
|
|
MEMTAG_NAME_DS_RECORD ,
|
|
MEMTAG_NAME_DS_OTHER ,
|
|
MEMTAG_NAME_THREAD ,
|
|
MEMTAG_NAME_NBSTAT ,
|
|
MEMTAG_NAME_DNSLIB ,
|
|
MEMTAG_NAME_TABLE ,
|
|
MEMTAG_NAME_SOCKET ,
|
|
MEMTAG_NAME_CONNECTION ,
|
|
MEMTAG_NAME_REGISTRY ,
|
|
MEMTAG_NAME_RPC , // 20
|
|
MEMTAG_NAME_STUFF ,
|
|
MEMTAG_NAME_FILEBUF ,
|
|
MEMTAG_NAME_REMOTE ,
|
|
MEMTAG_NAME_SAFE ,
|
|
|
|
MEMTAG_NAME_RECORD ,
|
|
MEMTAG_NAME_RECORD_FILE ,
|
|
MEMTAG_NAME_RECORD_DS ,
|
|
MEMTAG_NAME_RECORD_AXFR ,
|
|
MEMTAG_NAME_RECORD_IXFR ,
|
|
MEMTAG_NAME_RECORD_DYNUP , // 30
|
|
MEMTAG_NAME_RECORD_ADMIN ,
|
|
MEMTAG_NAME_RECORD_AUTO ,
|
|
MEMTAG_NAME_RECORD_CACHE ,
|
|
MEMTAG_NAME_RECORD_NOEXIST ,
|
|
MEMTAG_NAME_RECORD_WINS ,
|
|
MEMTAG_NAME_RECORD_WINSPTR ,
|
|
MEMTAG_NAME_RECORD_COPY ,
|
|
|
|
MEMTAG_NAME_NODE ,
|
|
MEMTAG_NAME_NODE_FILE ,
|
|
MEMTAG_NAME_NODE_DS , // 40
|
|
MEMTAG_NAME_NODE_AXFR ,
|
|
MEMTAG_NAME_NODE_IXFR ,
|
|
MEMTAG_NAME_NODE_DYNUP ,
|
|
MEMTAG_NAME_NODE_ADMIN ,
|
|
MEMTAG_NAME_NODE_AUTO ,
|
|
MEMTAG_NAME_NODE_CACHE ,
|
|
MEMTAG_NAME_NODE_NOEXIST ,
|
|
MEMTAG_NAME_NODE_WINS ,
|
|
MEMTAG_NAME_NODE_WINSPTR ,
|
|
MEMTAG_NAME_NODE_COPY ,
|
|
|
|
NULL, // safety
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
//
|
|
// Functions
|
|
//
|
|
|
|
|
|
//
|
|
// Iteration control. Be sure to call resetIterationCount at
|
|
// the start of any iterating operation that will call checkIterationCount.
|
|
// Note: call checkIterationCount aggressively, meaning at the top of
|
|
// every single loop that iterates through a server data structure. It
|
|
// is important that we be able to preset a limit to ALL iterating
|
|
// functions.
|
|
//
|
|
|
|
int g_iIterationCount = 0;
|
|
int g_iMaxIterations = 0;
|
|
|
|
void
|
|
resetIterationCount()
|
|
{
|
|
g_iIterationCount = 0;
|
|
} // resetIterationCount
|
|
|
|
bool
|
|
checkIterationCount()
|
|
{
|
|
if ( g_iMaxIterations != 0 && g_iIterationCount++ > g_iMaxIterations )
|
|
{
|
|
dprintf(
|
|
"Max iteration count (%d) reached - terminating operation\n",
|
|
g_iMaxIterations );
|
|
return false;
|
|
}
|
|
return true;
|
|
} // checkIterationCount
|
|
|
|
|
|
bool
|
|
myReadMemory(
|
|
ULONG64 pMemory,
|
|
void * ppBuffer,
|
|
ULONG lLength,
|
|
bool fQuiet )
|
|
{
|
|
bool okay = true;
|
|
DWORD bytes = 0;
|
|
if ( !ReadMemory( pMemory, ppBuffer, lLength, &bytes ) ||
|
|
bytes != lLength )
|
|
{
|
|
if ( !fQuiet )
|
|
{
|
|
dprintf(
|
|
"DNSDBG: error reading %lu bytes of process memory at %p\n",
|
|
lLength,
|
|
pMemory );
|
|
}
|
|
okay = false;
|
|
}
|
|
return okay;
|
|
} // myReadMemory
|
|
|
|
|
|
bool
|
|
myReadString(
|
|
ULONG64 pString,
|
|
char * pszStringBuffer,
|
|
ULONG lLength )
|
|
{
|
|
bool okay = true;
|
|
|
|
for ( ULONG i = 0; i < lLength - 1; ++i )
|
|
{
|
|
CHAR szbuffer[ 2 ];
|
|
|
|
if ( !myReadMemory( pString + i, &szbuffer , 1, true ) )
|
|
{
|
|
okay = false;
|
|
break;
|
|
}
|
|
pszStringBuffer[ i ] = szbuffer[ 0 ];
|
|
if ( pszStringBuffer[ i ] == '\0' )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
pszStringBuffer[ i ] = '\0';
|
|
return okay;
|
|
} // myReadMemory
|
|
|
|
|
|
|
|
|
|
HRESULT CALLBACK
|
|
Stats(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
DWORD bytes;
|
|
bool okay = true;
|
|
|
|
ULONG64 pstatsTable = GetExpression( args );
|
|
|
|
dprintf( "DNS Server statistics at %p\n", ( ULONG64 ) pstatsTable );
|
|
|
|
for ( int i = 0; ; ++i )
|
|
{
|
|
struct StatsTableEntry statsEntry;
|
|
if ( !ReadMemory(
|
|
pstatsTable + i * sizeof( StatsTableEntry ),
|
|
&statsEntry,
|
|
sizeof( statsEntry ),
|
|
&bytes ) ||
|
|
bytes != sizeof( statsEntry ) )
|
|
{
|
|
dprintf(
|
|
"Unable to read stat at %p index %d\n",
|
|
( ULONG64 ) ( ( PBYTE ) pstatsTable + i * sizeof( statsEntry ) ),
|
|
i );
|
|
okay = false;
|
|
break;
|
|
}
|
|
|
|
if ( statsEntry.Id == 0 )
|
|
{
|
|
break; // Stat array terminator found!
|
|
}
|
|
|
|
#if 0
|
|
dprintf(
|
|
"Stat entry %d id=%08X length=%d\n",
|
|
i,
|
|
statsEntry.Id,
|
|
statsEntry.wLength );
|
|
#endif
|
|
|
|
switch( statsEntry.Id )
|
|
{
|
|
#define StatDword( szIndent, statStruct, statMember ) \
|
|
dprintf( \
|
|
"%s%-25s = %d\n", \
|
|
szIndent, \
|
|
#statMember, \
|
|
statStruct.##statMember )
|
|
|
|
case DNSSRV_STATID_MEMORY:
|
|
{
|
|
dprintf( "\n***** Memory Stats id 0x%08X\n\n", statsEntry.Id );
|
|
|
|
DNSSRV_MEMORY_STATS s;
|
|
if ( ( okay = myReadMemory(
|
|
( ULONG64 ) statsEntry.pStats,
|
|
&s,
|
|
sizeof( s ),
|
|
false ) ) == false )
|
|
{
|
|
break;
|
|
}
|
|
|
|
#define StatIndent " "
|
|
|
|
StatDword( StatIndent, s, Memory );
|
|
StatDword( StatIndent, s, Alloc );
|
|
StatDword( StatIndent, s, Free );
|
|
dprintf( "\n" );
|
|
|
|
StatDword( StatIndent, s, StdUsed );
|
|
StatDword( StatIndent, s, StdReturn );
|
|
StatDword( StatIndent, s, StdInUse );
|
|
StatDword( StatIndent, s, StdMemory );
|
|
dprintf( "\n" );
|
|
|
|
StatDword( StatIndent, s, StdToHeapAlloc );
|
|
StatDword( StatIndent, s, StdToHeapFree );
|
|
StatDword( StatIndent, s, StdToHeapInUse );
|
|
StatDword( StatIndent, s, StdToHeapMemory );
|
|
dprintf( "\n" );
|
|
|
|
StatDword( StatIndent, s, StdBlockAlloc );
|
|
StatDword( StatIndent, s, StdBlockUsed );
|
|
StatDword( StatIndent, s, StdBlockReturn );
|
|
StatDword( StatIndent, s, StdBlockInUse );
|
|
StatDword( StatIndent, s, StdBlockFreeList );
|
|
StatDword( StatIndent, s, StdBlockFreeListMemory );
|
|
StatDword( StatIndent, s, StdBlockMemory );
|
|
dprintf( "\n" );
|
|
|
|
dprintf( " Memtag Name Alloc Free InUse Memory\n" );
|
|
for ( int m = 0; m < MEMTAG_COUNT; ++m )
|
|
{
|
|
dprintf(
|
|
" %2d %-14s %10d %10d %10d %10d\n",
|
|
m,
|
|
MemTagStrings[ m ],
|
|
s.MemTags[ m ].Alloc,
|
|
s.MemTags[ m ].Free,
|
|
s.MemTags[ m ].Alloc - s.MemTags[ m ].Free,
|
|
s.MemTags[ m ].Memory );
|
|
}
|
|
|
|
#if 0
|
|
|
|
// THIS WOULD BE COOL BUT IT DOESN'T QUITE WORK!!
|
|
|
|
ULONG64 module = 0;
|
|
ULONG typeId = 0;
|
|
CHAR szField[ MAX_PATH ];
|
|
ULONG offset = 0;
|
|
int iField;
|
|
HRESULT hr;
|
|
|
|
hr = g_ExtSymbols->GetSymbolTypeId(
|
|
"DNSSRV_MEMORY_STATS", &typeId, &module );
|
|
dprintf( "GetSymbolTypeId returned 0x%lx\n", hr );
|
|
for ( iField = 0; ; ++i )
|
|
{
|
|
*szField = '\0';
|
|
hr = g_ExtSymbols2->GetFieldName(
|
|
module, typeId, iField,
|
|
szField, sizeof( szField ), NULL );
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = g_ExtSymbols->GetFieldOffset(
|
|
module, typeId, szField, &offset);
|
|
if ( hr == S_OK )
|
|
{
|
|
dprintf( "Field %s at offset %d\n", szField, offset );
|
|
}
|
|
else
|
|
{
|
|
dprintf(
|
|
"GetFieldOffset %s iField=%d failed 0x%lx\n",
|
|
szField, iField, hr );
|
|
break;
|
|
}
|
|
}
|
|
else if ( hr == E_INVALIDARG )
|
|
{
|
|
// Done enumerating fields
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
dprintf(
|
|
"GetFieldName iField=%d failed 0x%lx \"%s\"\n",
|
|
iField, hr, szField );
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
default:
|
|
dprintf(
|
|
"Unknown stat entry %d id=%08X length=%d\n",
|
|
i,
|
|
statsEntry.Id,
|
|
statsEntry.wLength );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // TimeoutArrays
|
|
|
|
|
|
bool
|
|
printNode(
|
|
char * pszIndent,
|
|
ULONG64 pNodeFull,
|
|
bool fVerbose )
|
|
{
|
|
bool okay = true;
|
|
|
|
char szEmptyString[] = "";
|
|
if ( !pszIndent )
|
|
{
|
|
pszIndent = szEmptyString;
|
|
}
|
|
|
|
DB_NODE node;
|
|
okay = myReadMemory( pNodeFull, &node, sizeof( node ), false );
|
|
if ( !okay )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Force NULL termination in case this node was allocated
|
|
// longer than a standard node.
|
|
//
|
|
|
|
* ( ( PUCHAR ) &node + sizeof( node ) - 1 ) = '\0';
|
|
|
|
//
|
|
// Read the string separately to try and get the whole string.
|
|
//
|
|
|
|
char szNodeName[ 65 ];
|
|
if ( !myReadString(
|
|
( ULONG64 ) node.szLabel,
|
|
szNodeName,
|
|
sizeof( szNodeName ) ) )
|
|
{
|
|
strcpy( szNodeName, node.szLabel );
|
|
}
|
|
|
|
dprintf(
|
|
"%sNODE %p %s%s %-16s abin %03d tbin %03d c%d",
|
|
pszIndent,
|
|
pNodeFull,
|
|
node.pZone ? "Z" : " ", // has zone pointer?
|
|
node.pRRList ? "R" : " ", // has RR list pointer?
|
|
szNodeName ? szNodeName : "(ROOT)",
|
|
node.uchAccessBin,
|
|
node.uchTimeoutBin,
|
|
node.cReferenceCount );
|
|
int flags = node.dwNodeFlags;
|
|
dprintf(
|
|
" f%04X %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
|
flags,
|
|
flags & NODE_NO_DELETE ? "Nodel" : "",
|
|
flags & NODE_SELECT ? "Sel" : "",
|
|
flags & NODE_FORCE_ENUM ? "Enum" : "",
|
|
flags & NODE_IN_TIMEOUT ? "Tmot" : "",
|
|
flags & NODE_AVAIL_TO_AUTHUSER ? "Avauth" : "",
|
|
flags & NODE_ZONETREE ? "Ztree" : "",
|
|
flags & NODE_AUTH_ZONE_ROOT ? "Authrt" : "",
|
|
flags & NODE_WILDCARD_PARENT ? "Wildp" : "",
|
|
flags & NODE_CNAME ? "Cname" : "",
|
|
flags & NODE_ZONE_ROOT ? "Zroot" : "",
|
|
flags & NODE_SECURE_EXPIRED ? "Secexp" : "",
|
|
flags & NODE_TOMBSTONE ? "Tomb" : "",
|
|
flags & NODE_THIS_HOST ? "This" : "",
|
|
flags & NODE_NOEXIST ? "Noex" : "" );
|
|
if ( fVerbose )
|
|
{
|
|
dprintf(
|
|
"%s par %p up %p left %p right %p children %p\n",
|
|
pszIndent,
|
|
( ULONG64 ) node.pParent,
|
|
( ULONG64 ) node.pSibUp,
|
|
( ULONG64 ) node.pSibLeft,
|
|
( ULONG64 ) node.pSibRight,
|
|
( ULONG64 ) node.pChildren );
|
|
}
|
|
|
|
Cleanup:
|
|
return okay;
|
|
} // printNode
|
|
|
|
|
|
//
|
|
// Print summary of timeout system
|
|
//
|
|
|
|
void
|
|
internalTimeoutScanner(
|
|
ULONG64 pTimeoutBinArray,
|
|
bool fPrintStats,
|
|
bool fPrintArrays,
|
|
bool fPrintNodes,
|
|
bool fPrintNodesHighDetail )
|
|
{
|
|
DWORD nonEmptyTopLevelBins = 0;
|
|
DWORD arraysFound = 0;
|
|
DWORD nodesFound = 0;
|
|
DWORD maxBinDepth = 0;
|
|
DWORD maxNodesInBin = 0;
|
|
DWORD bytes;
|
|
bool okay = true;
|
|
|
|
PTIMEOUT_ARRAY timeoutBinArray[ TIMEOUT_BIN_COUNT ];
|
|
|
|
//
|
|
// Read the timeout bin array
|
|
//
|
|
|
|
if ( !ReadMemory(
|
|
pTimeoutBinArray,
|
|
timeoutBinArray,
|
|
sizeof( timeoutBinArray ),
|
|
&bytes ) ||
|
|
bytes != sizeof( timeoutBinArray ) )
|
|
{
|
|
dprintf(
|
|
"Unable to read timeout bin array (%d bytes) at %p\n",
|
|
sizeof( timeoutBinArray ),
|
|
( ULONG64 ) pTimeoutBinArray );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// For each bin in the array, traverse the bin to count
|
|
// nodes.
|
|
//
|
|
|
|
resetIterationCount();
|
|
|
|
for ( int i = 0; okay && i < TIMEOUT_BIN_COUNT; ++i )
|
|
{
|
|
if ( !checkIterationCount() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( !timeoutBinArray[ i ] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Traverse all the arrays in this bin.
|
|
//
|
|
|
|
++nonEmptyTopLevelBins;
|
|
DWORD nodesInThisBin = 0;
|
|
DWORD arraysInThisBin = 0;
|
|
DWORD depth = 0;
|
|
TIMEOUT_ARRAY array;
|
|
for ( ULONG64 pArray = ( ULONG64 ) timeoutBinArray[ i ];
|
|
okay && pArray != NULL;
|
|
pArray = ( ULONG64 ) array.pNext )
|
|
{
|
|
if ( !checkIterationCount() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Read this array.
|
|
//
|
|
|
|
if ( !ReadMemory(
|
|
pArray,
|
|
&array,
|
|
sizeof( array ),
|
|
&bytes ) ||
|
|
bytes != sizeof( array ) )
|
|
{
|
|
dprintf(
|
|
"Unable to read timeout array for bin %d (%d bytes) at %p\n",
|
|
i,
|
|
sizeof( array ),
|
|
( ULONG64 ) pArray );
|
|
okay = false;
|
|
break;
|
|
}
|
|
|
|
++arraysInThisBin;
|
|
++arraysFound;
|
|
++depth;
|
|
nodesInThisBin += array.Count;
|
|
nodesFound += array.Count;
|
|
|
|
if ( fPrintArrays )
|
|
{
|
|
dprintf(
|
|
" ARRAY %3d in bin %3d has %2d elements %p\n",
|
|
arraysInThisBin,
|
|
i,
|
|
array.Count,
|
|
( ULONG64 ) pArray );
|
|
}
|
|
|
|
if ( !fPrintNodes && !fPrintNodesHighDetail )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Traverse all the nodes in this array.
|
|
//
|
|
|
|
for ( DWORD j = 0; okay && j < array.Count; ++j )
|
|
{
|
|
if ( !checkIterationCount() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
char sz[ 20 ];
|
|
sprintf( sz, " %2d ", j + 1 );
|
|
okay = printNode(
|
|
sz,
|
|
( ULONG64 ) array.pNode[ j ],
|
|
false );
|
|
}
|
|
}
|
|
if ( !okay )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( fPrintArrays && arraysInThisBin )
|
|
{
|
|
dprintf(
|
|
"BIN %3d has %3d arrays %5d nodes\n",
|
|
i,
|
|
arraysInThisBin,
|
|
nodesInThisBin );
|
|
}
|
|
|
|
//
|
|
// Record high water marks.
|
|
//
|
|
|
|
if ( nodesInThisBin > maxNodesInBin )
|
|
{
|
|
maxNodesInBin = nodesInThisBin;
|
|
}
|
|
if ( depth > maxBinDepth )
|
|
{
|
|
maxBinDepth = depth;
|
|
}
|
|
}
|
|
|
|
if ( okay && fPrintStats )
|
|
{
|
|
dprintf(
|
|
"Timeout summary for TimeoutBinArray at %p\n",
|
|
pTimeoutBinArray );
|
|
dprintf(
|
|
" nodes found in timeout system %d\n"
|
|
" arrays found in timeout system %d\n"
|
|
" non-empty top level bins %d out of %d\n"
|
|
" maximum bin depth %d\n"
|
|
" maximum nodes in single bin %d\n"
|
|
" average arrays per non-empty bin %.2f\n",
|
|
nodesFound,
|
|
arraysFound,
|
|
nonEmptyTopLevelBins,
|
|
TIMEOUT_BIN_COUNT,
|
|
maxBinDepth,
|
|
maxNodesInBin,
|
|
nonEmptyTopLevelBins ?
|
|
( ( double ) arraysFound /
|
|
( double ) nonEmptyTopLevelBins + 0.5 ) : 0 );
|
|
}
|
|
|
|
Cleanup:
|
|
return;
|
|
} // internalTimeoutScanner
|
|
|
|
|
|
HRESULT CALLBACK
|
|
TimeoutSummary(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 ptimeoutBinArray = GetExpression( args );
|
|
|
|
internalTimeoutScanner(
|
|
ptimeoutBinArray,
|
|
true, // print stats
|
|
false, // print arrays
|
|
false, // print nodes
|
|
false ); // print nodes - high detail
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // TimeoutSummary
|
|
|
|
|
|
HRESULT CALLBACK
|
|
TimeoutArrays(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 ptimeoutBinArray = GetExpression( args );
|
|
|
|
internalTimeoutScanner(
|
|
ptimeoutBinArray,
|
|
true, // print stats
|
|
true, // print arrays
|
|
false, // print nodes
|
|
false ); // print nodes - high detail
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // TimeoutArrays
|
|
|
|
|
|
HRESULT CALLBACK
|
|
TimeoutNodes(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 ptimeoutBinArray = GetExpression( args );
|
|
|
|
internalTimeoutScanner(
|
|
ptimeoutBinArray,
|
|
true, // print stats
|
|
true, // print arrays
|
|
true, // print nodes
|
|
false ); // print nodes - high detail
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // TimeoutNodes
|
|
|
|
|
|
/*
|
|
This gets called (by DebugExtensionNotify when target is halted and is accessible
|
|
*/
|
|
HRESULT
|
|
NotifyOnTargetAccessible(PDEBUG_CONTROL Control)
|
|
{
|
|
dprintf( "DNS Server debugger extension dll detected a break" );
|
|
if ( Connected )
|
|
{
|
|
dprintf( " (" );
|
|
switch (TargetMachine) {
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
dprintf( "x86" );
|
|
break;
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
dprintf( "ia64" );
|
|
break;
|
|
default:
|
|
dprintf( "Unknown Architecture" );
|
|
break;
|
|
}
|
|
}
|
|
dprintf( ")\n" );
|
|
|
|
dprintf( "\nDebugger extension build at " __DATE__ " " __TIME__ "\n\n" );
|
|
|
|
//
|
|
// show the top frame and execute dv to dump the locals here and return
|
|
//
|
|
Control->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK |
|
|
DEBUG_OUTCTL_NOT_LOGGED,
|
|
".frame", // Command to be executed
|
|
DEBUG_EXECUTE_DEFAULT );
|
|
Control->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK |
|
|
DEBUG_OUTCTL_NOT_LOGGED,
|
|
"dv", // Command to be executed
|
|
DEBUG_EXECUTE_DEFAULT );
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
char * g_ZoneTypeNames[] =
|
|
{
|
|
"Cache",
|
|
"Primary",
|
|
"Secondary",
|
|
"Stub",
|
|
"Forwarder"
|
|
};
|
|
|
|
|
|
bool
|
|
printZoneProperties(
|
|
ULONG64 pZoneFull,
|
|
bool fVerbose )
|
|
{
|
|
bool okay = true;
|
|
|
|
ZONE_INFO zone;
|
|
if ( !myReadMemory( pZoneFull, &zone, sizeof( zone ), false ) )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
|
|
char sz[ 256 ];
|
|
if ( !myReadString(
|
|
( ULONG64 ) zone.pszZoneName,
|
|
sz,
|
|
sizeof( sz ) ) )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( fVerbose )
|
|
{
|
|
dprintf( "\n" );
|
|
}
|
|
|
|
dprintf(
|
|
"ZONE %p %s (%d)%-9s %s\n",
|
|
pZoneFull,
|
|
zone.fDsIntegrated ? "DS " : "File",
|
|
zone.fZoneType,
|
|
zone.fZoneType <= DNS_ZONE_TYPE_FORWARDER ?
|
|
g_ZoneTypeNames[ zone.fZoneType ] : "UNKNOWN-TYPE",
|
|
sz );
|
|
|
|
if ( fVerbose )
|
|
{
|
|
#define ZonePtr( szIndent, zoneMember ) \
|
|
dprintf( \
|
|
"%s%-25s = %p\n", \
|
|
szIndent, \
|
|
#zoneMember, \
|
|
( ULONG64 ) zone.##zoneMember )
|
|
#define ZoneStr( szIndent, zoneMember, stringValue ) \
|
|
dprintf( \
|
|
"%s%-25s = %s\n", \
|
|
szIndent, \
|
|
#zoneMember, \
|
|
stringValue )
|
|
#define ZoneDword( szIndent, zoneMember ) \
|
|
dprintf( \
|
|
"%s%-25s = %8d = 0x%08X\n", \
|
|
szIndent, \
|
|
#zoneMember, \
|
|
zone.##zoneMember, \
|
|
zone.##zoneMember )
|
|
|
|
#define ZoneIndent " "
|
|
|
|
*sz = '\0';
|
|
if ( zone.pszDataFile )
|
|
{
|
|
if ( !myReadString(
|
|
( ULONG64 ) zone.pszDataFile,
|
|
sz,
|
|
sizeof( sz ) ) )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
ZoneStr( ZoneIndent, pszDataFile, sz );
|
|
|
|
if ( zone.fDsIntegrated )
|
|
{
|
|
*sz = '\0';
|
|
if ( zone.pwszZoneDN )
|
|
{
|
|
if ( !myReadString(
|
|
( ULONG64 ) zone.pwszZoneDN,
|
|
sz,
|
|
sizeof( sz ) ) )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
ZoneStr( ZoneIndent, pwszZoneDN, sz );
|
|
ZonePtr( ZoneIndent, pDpInfo );
|
|
}
|
|
|
|
ZonePtr( ZoneIndent, pSoaRR );
|
|
ZonePtr( ZoneIndent, pZoneRoot );
|
|
ZonePtr( ZoneIndent, pTreeRoot );
|
|
ZonePtr( ZoneIndent, pZoneTreeLink );
|
|
dprintf( "\n" );
|
|
|
|
ZonePtr( ZoneIndent, pLoadZoneRoot );
|
|
ZonePtr( ZoneIndent, pLoadTreeRoot );
|
|
ZonePtr( ZoneIndent, pLoadOrigin );
|
|
ZonePtr( ZoneIndent, pOldTree );
|
|
dprintf( "\n" );
|
|
|
|
ZoneDword( ZoneIndent, iRRCount );
|
|
ZoneDword( ZoneIndent, dwSerialNo );
|
|
ZoneDword( ZoneIndent, dwLoadSerialNo );
|
|
ZoneDword( ZoneIndent, dwLastXfrSerialNo );
|
|
ZoneDword( ZoneIndent, dwNewSerialNo );
|
|
ZoneDword( ZoneIndent, dwDefaultTtl );
|
|
dprintf( "\n" );
|
|
|
|
ZoneDword( ZoneIndent, dwNextTransferTime );
|
|
dprintf( "\n" );
|
|
|
|
ZoneDword( ZoneIndent, dwDcPromoConvert );
|
|
dprintf( "\n" );
|
|
}
|
|
|
|
Cleanup:
|
|
return okay;
|
|
} // printZoneProperties
|
|
|
|
|
|
//
|
|
// Dump zone list with basic info on each zone.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
ZoneList(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
bool okay = true;
|
|
int zoneIdx = 0;
|
|
|
|
ULONG64 plistheadZone = GetExpression( args );
|
|
|
|
LIST_ENTRY listEntry;
|
|
if ( !myReadMemory( plistheadZone, &listEntry, sizeof( listEntry ), false ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
resetIterationCount();
|
|
|
|
ZONE_INFO zone;
|
|
for ( ULONG64 pzone = ( ULONG64 ) listEntry.Flink;
|
|
pzone;
|
|
pzone = ( ULONG64 ) zone.ListEntry.Flink )
|
|
{
|
|
if ( !checkIterationCount() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( zoneIdx != 0 && pzone == plistheadZone )
|
|
{
|
|
break; // Looped back to list head, so we're done.
|
|
}
|
|
|
|
if ( ++zoneIdx > 1000 )
|
|
{
|
|
dprintf( "Runaway zone list? Aborting enumeration...\n" );
|
|
break;
|
|
}
|
|
|
|
if ( !myReadMemory( pzone, &zone, sizeof( zone ), false ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
printZoneProperties( pzone, false );
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // ZoneList
|
|
|
|
|
|
//
|
|
// Dump zone info.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
Zone(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 p = GetExpression( args );
|
|
printZoneProperties( p, true );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // Zone
|
|
|
|
|
|
//
|
|
// Dump node info.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
Node(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 p = GetExpression( args );
|
|
printNode( NULL, p, true );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // Node
|
|
|
|
|
|
bool
|
|
printHash(
|
|
char * pszIndent,
|
|
ULONG64 pHashFull,
|
|
bool fVerbose )
|
|
{
|
|
bool okay = true;
|
|
|
|
char szEmptyString[] = "";
|
|
if ( !pszIndent )
|
|
{
|
|
pszIndent = szEmptyString;
|
|
}
|
|
|
|
SIB_HASH_TABLE hash;
|
|
okay = myReadMemory( pHashFull, &hash, sizeof( hash ), false );
|
|
if ( !okay )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dprintf(
|
|
"%sHASH %p lvl%02d\n",
|
|
pszIndent,
|
|
pHashFull,
|
|
hash.cLevel );
|
|
|
|
Cleanup:
|
|
return okay;
|
|
} // printHash
|
|
|
|
|
|
//
|
|
// Traverse a database tree of hashes and nodes. This function
|
|
// is called recursively.
|
|
//
|
|
|
|
struct
|
|
{
|
|
int iRecurseCalls;
|
|
int iHashTables;
|
|
int iEmptyHashBuckets;
|
|
int iNonEmptyHashBuckets;
|
|
int iNodes;
|
|
int iNoExistNodes;
|
|
int iTimeoutNodes;
|
|
int iNodesWithNoRRs;
|
|
int iNodesWithNoChildren;
|
|
} g_TreeScannerStats;
|
|
|
|
//
|
|
// iRecurseLevel: true level of function recursion
|
|
// iChildLevel: level of node depth
|
|
//
|
|
|
|
bool
|
|
internalTreeScanner(
|
|
ULONG64 pTreeRoot,
|
|
bool fPrintSummary,
|
|
bool fPrintHashNodes,
|
|
bool fPrintHashBuckets,
|
|
bool fPrintNodes,
|
|
bool fPrintNodesHighDetail,
|
|
int iRecurseLevel, // pass 0 on initial call
|
|
int iChildLevel ) // pass 0 on initial call
|
|
{
|
|
bool okay = true;
|
|
|
|
// dprintf( "internalTreeScanner p=%p level=%d\n", pTreeRoot, iRecurseLevel );
|
|
|
|
if ( !checkIterationCount() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( iRecurseLevel == 0 )
|
|
{
|
|
RtlZeroMemory( &g_TreeScannerStats, sizeof( g_TreeScannerStats ) );
|
|
}
|
|
|
|
if ( ++g_TreeScannerStats.iRecurseCalls > 10000000 )
|
|
{
|
|
dprintf( "Aborting: hit limit of 10000000 recurse calls\n" );
|
|
return true;
|
|
}
|
|
|
|
char szIndent[] =
|
|
" "
|
|
" ";
|
|
szIndent[ min( ( int ) strlen( szIndent ), iChildLevel * 2 ) ] = '\0';
|
|
|
|
//
|
|
// Grab the first DWORD so we can determine if this is
|
|
// a hash or a node.
|
|
//
|
|
|
|
DWORD dw;
|
|
if ( !myReadMemory( pTreeRoot, &dw, sizeof( dw ), false ) )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( IS_HASH_TABLE( &dw ) )
|
|
{
|
|
++g_TreeScannerStats.iHashTables;
|
|
if ( fPrintHashNodes )
|
|
{
|
|
okay = printHash( szIndent, pTreeRoot, false );
|
|
}
|
|
|
|
SIB_HASH_TABLE hash;
|
|
okay = myReadMemory( pTreeRoot, &hash, sizeof( hash ), false );
|
|
if ( !okay )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Iterate through all the buckets in this hash node.
|
|
//
|
|
|
|
for ( int i = 0; i <= LAST_HASH_INDEX; ++i )
|
|
{
|
|
PDB_NODE pnode = hash.aBuckets[ i ];
|
|
DWORD count = hash.aBucketCount[ i ];
|
|
|
|
if ( pnode )
|
|
{
|
|
++g_TreeScannerStats.iNonEmptyHashBuckets;
|
|
|
|
if ( fPrintHashBuckets )
|
|
{
|
|
dprintf(
|
|
"%sBUCKET %p index %3d count %3d\n",
|
|
szIndent,
|
|
( ULONG64 ) pnode,
|
|
i,
|
|
count );
|
|
}
|
|
|
|
okay = internalTreeScanner(
|
|
( ULONG64 ) pnode,
|
|
fPrintSummary,
|
|
fPrintHashNodes,
|
|
fPrintHashBuckets,
|
|
fPrintNodes,
|
|
fPrintNodesHighDetail,
|
|
iRecurseLevel + 1,
|
|
iChildLevel );
|
|
if ( !okay )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++g_TreeScannerStats.iEmptyHashBuckets;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is a node.
|
|
//
|
|
|
|
++g_TreeScannerStats.iNodes;
|
|
|
|
DB_NODE node;
|
|
okay = myReadMemory( pTreeRoot, &node, sizeof( node ), false );
|
|
if ( !okay )
|
|
{
|
|
okay = false;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Force NULL termination in case this node was allocated
|
|
// longer than a standard node.
|
|
//
|
|
|
|
* ( ( PUCHAR ) &node + sizeof( node ) - 1 ) = '\0';
|
|
|
|
//
|
|
// Scan the left child tree then this node then the right child tree.
|
|
//
|
|
|
|
if ( node.pSibLeft )
|
|
{
|
|
okay = internalTreeScanner(
|
|
( ULONG64 ) node.pSibLeft,
|
|
fPrintSummary,
|
|
fPrintHashNodes,
|
|
fPrintHashBuckets,
|
|
fPrintNodes,
|
|
fPrintNodesHighDetail,
|
|
iRecurseLevel + 1,
|
|
iChildLevel );
|
|
if ( !okay )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( node.dwNodeFlags & NODE_NOEXIST )
|
|
{
|
|
++g_TreeScannerStats.iNoExistNodes;
|
|
}
|
|
if ( node.dwNodeFlags & NODE_IN_TIMEOUT )
|
|
{
|
|
++g_TreeScannerStats.iTimeoutNodes;
|
|
}
|
|
if ( !node.pRRList )
|
|
{
|
|
++g_TreeScannerStats.iNodesWithNoRRs;
|
|
}
|
|
if ( !node.pChildren )
|
|
{
|
|
++g_TreeScannerStats.iNodesWithNoChildren;
|
|
}
|
|
|
|
if ( fPrintNodes )
|
|
{
|
|
okay = printNode( szIndent, pTreeRoot, fPrintNodesHighDetail );
|
|
if ( !okay )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( node.pChildren )
|
|
{
|
|
okay = internalTreeScanner(
|
|
( ULONG64 ) node.pChildren,
|
|
fPrintSummary,
|
|
fPrintHashNodes,
|
|
fPrintHashBuckets,
|
|
fPrintNodes,
|
|
fPrintNodesHighDetail,
|
|
iRecurseLevel + 1,
|
|
iChildLevel + 1 );
|
|
if ( !okay )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( node.pSibRight )
|
|
{
|
|
okay = internalTreeScanner(
|
|
( ULONG64 ) node.pSibRight,
|
|
fPrintSummary,
|
|
fPrintHashNodes,
|
|
fPrintHashBuckets,
|
|
fPrintNodes,
|
|
fPrintNodesHighDetail,
|
|
iRecurseLevel + 1,
|
|
iChildLevel );
|
|
if ( !okay )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( iRecurseLevel == 0 )
|
|
{
|
|
if ( !okay )
|
|
{
|
|
dprintf( "\nERROR ITERATING TREE - STATS INCOMPLETE!\n\n" );
|
|
}
|
|
if ( fPrintSummary )
|
|
{
|
|
dprintf(
|
|
"Tree summary for tree rooted at %p\n",
|
|
pTreeRoot );
|
|
dprintf(
|
|
" hash tables %d\n"
|
|
" empty hash buckets %d\n"
|
|
" non-empty hash buckets %d\n"
|
|
"\n"
|
|
" nodes %d\n"
|
|
" no-exist nodes %d\n"
|
|
" timeout nodes %d\n"
|
|
" nodes with no RRs %d\n"
|
|
" nodes with no children %d\n",
|
|
g_TreeScannerStats.iHashTables,
|
|
g_TreeScannerStats.iEmptyHashBuckets,
|
|
g_TreeScannerStats.iNonEmptyHashBuckets,
|
|
g_TreeScannerStats.iNodes,
|
|
g_TreeScannerStats.iNoExistNodes,
|
|
g_TreeScannerStats.iTimeoutNodes,
|
|
g_TreeScannerStats.iNodesWithNoRRs,
|
|
g_TreeScannerStats.iNodesWithNoChildren );
|
|
|
|
}
|
|
}
|
|
return okay;
|
|
}
|
|
|
|
//
|
|
// Dump node tree.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
Tree(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 p = GetExpression( args );
|
|
|
|
resetIterationCount();
|
|
|
|
internalTreeScanner(
|
|
p,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
false,
|
|
0, 0 );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // Node
|
|
|
|
|
|
//
|
|
// Print summary of a node tree.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
TreeSummary(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 p = GetExpression( args );
|
|
|
|
resetIterationCount();
|
|
|
|
internalTreeScanner(
|
|
p,
|
|
true,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
0, 0 );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // Node
|
|
|
|
|
|
//
|
|
// Print info about a packet queue.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
PacketQ(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 p = GetExpression( args );
|
|
|
|
PACKET_QUEUE q;
|
|
CHAR szQueueName[ 80 ];
|
|
|
|
if ( !myReadMemory(
|
|
( ULONG64 ) p,
|
|
&q,
|
|
sizeof( q ),
|
|
false ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !myReadMemory(
|
|
( ULONG64 ) q.pszName,
|
|
szQueueName,
|
|
sizeof( szQueueName ),
|
|
false ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
#define BoolFlagStr( f ) ( ( f ) ? "true" : "false" )
|
|
|
|
dprintf(
|
|
"Packet queue %p\n"
|
|
" queue name %s\n"
|
|
" first message %p\n"
|
|
" event %p\n"
|
|
" default timeout %d\n"
|
|
" minimum timeout %d\n",
|
|
p,
|
|
szQueueName,
|
|
( ULONG64 ) q.listHead.Flink,
|
|
( ULONG64 ) q.hEvent,
|
|
q.dwDefaultTimeout,
|
|
q.dwMinimumTimeout );
|
|
|
|
dprintf(
|
|
" counters:\n"
|
|
" length %d\n"
|
|
" queued %d\n"
|
|
" dequeued %d\n"
|
|
" timed out %d\n",
|
|
q.cLength,
|
|
q.cQueued,
|
|
q.cDequeued,
|
|
q.cTimedOut );
|
|
|
|
dprintf(
|
|
" flags:\n"
|
|
" query time order %s\n"
|
|
" discard expired on queuing %s\n"
|
|
" discard dups on queuing %s\n"
|
|
" XID for referrals %d\n",
|
|
BoolFlagStr( q.fQueryTimeOrder ),
|
|
BoolFlagStr( q.fDiscardExpiredOnQueuing ),
|
|
BoolFlagStr( q.fDiscardDuplicatesOnQueuing ),
|
|
( int ) q.wXid );
|
|
|
|
#undef BoolFlagStr
|
|
|
|
Cleanup:
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // PacketQ
|
|
|
|
|
|
//
|
|
// Print info about a packet queue.
|
|
//
|
|
|
|
void
|
|
execDebugCmd(
|
|
char * pszDebuggerCommand )
|
|
{
|
|
g_ExtControl->Execute(
|
|
DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK |
|
|
DEBUG_OUTCTL_NOT_LOGGED,
|
|
pszDebuggerCommand,
|
|
DEBUG_EXECUTE_DEFAULT );
|
|
} // execDebugCmd
|
|
|
|
|
|
HRESULT CALLBACK
|
|
Globals(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
char szIndent1[] = " ";
|
|
char szIndent2[] = " ";
|
|
|
|
dprintf( "Zone list\n%s", szIndent1 );
|
|
execDebugCmd( "dd dns!listheadZone l 2" );
|
|
|
|
dprintf( "\nStatistics table\n%s", szIndent1 );
|
|
execDebugCmd( "dd dns!StatsTable l 1" );
|
|
|
|
dprintf( "\nPacket queues\n" );
|
|
|
|
dprintf( "%sRecursion packet queue\n%s", szIndent1, szIndent2 );
|
|
execDebugCmd( "dd dns!g_pRecursionQueue l 1" );
|
|
|
|
dprintf( "%sSecondary packet queue\n%s", szIndent1, szIndent2 );
|
|
execDebugCmd( "dd dns!g_SecondaryQueue l 1" );
|
|
|
|
dprintf( "%sUpdate packet queue\n%s", szIndent1, szIndent2 );
|
|
execDebugCmd( "dd dns!g_UpdateQueue l 1" );
|
|
|
|
dprintf( "%sUpdate forwarding packet queue\n%s", szIndent1, szIndent2 );
|
|
execDebugCmd( "dd dns!g_UpdateForwardingQueue l 1" );
|
|
|
|
dprintf( "%sSecure negotiation packet queue\n%s", szIndent1, szIndent2 );
|
|
execDebugCmd( "dd dns!g_SecureNegoQueue l 1" );
|
|
|
|
dprintf( "%sWINS packet queue\n%s", szIndent1, szIndent2 );
|
|
execDebugCmd( "dd dns!g_pWinsQueue l 1" );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // Globals
|
|
|
|
|
|
//
|
|
// Dump a message.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
Msg(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 p = GetExpression( args );
|
|
|
|
BYTE msg[ sizeof( DNS_MSGINFO ) + 512 ];
|
|
PDNS_MSGINFO pmsg = ( PDNS_MSGINFO ) msg;
|
|
if ( !myReadMemory( p, msg, sizeof( msg ), false ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
#define BoolFlagStr( f ) ( ( f ) ? "true" : "false" )
|
|
|
|
dprintf(
|
|
"DNS message %p\n"
|
|
"Buffer info:\n"
|
|
" Tag %08X\n"
|
|
" BufferLength %d\n"
|
|
" MaxBufferLength %d\n"
|
|
" MessageLength %d\n"
|
|
" BytesToReceive %d\n"
|
|
" pBufferEnd %p\n"
|
|
" pCurrent %p\n",
|
|
( ULONG64 ) p,
|
|
pmsg->Tag,
|
|
pmsg->BufferLength,
|
|
pmsg->MaxBufferLength,
|
|
( int ) pmsg->MessageLength,
|
|
( int ) pmsg->BytesToReceive,
|
|
( ULONG64 ) pmsg->pBufferEnd,
|
|
( ULONG64 ) pmsg->pCurrent );
|
|
|
|
dprintf(
|
|
"Remote info:\n"
|
|
" Socket %d\n"
|
|
" RemoteAddr Family %d\n"
|
|
" RemoteAddr Length %d\n"
|
|
" RemoteAddr %08X = %d.%d.%d.%d\n",
|
|
( int ) pmsg->Socket,
|
|
( int ) pmsg->RemoteAddr.SockaddrIn.sin_family,
|
|
( int ) pmsg->RemoteAddr.SockaddrLength,
|
|
( int ) pmsg->RemoteAddr.SockaddrIn.sin_addr.s_addr,
|
|
( int ) pmsg->RemoteAddr.SockaddrIn.sin_addr.S_un.S_un_b.s_b1,
|
|
( int ) pmsg->RemoteAddr.SockaddrIn.sin_addr.S_un.S_un_b.s_b2,
|
|
( int ) pmsg->RemoteAddr.SockaddrIn.sin_addr.S_un.S_un_b.s_b3,
|
|
( int ) pmsg->RemoteAddr.SockaddrIn.sin_addr.S_un.S_un_b.s_b4 );
|
|
|
|
dprintf(
|
|
"Lookup info:\n"
|
|
" pnodeCurrent %p\n"
|
|
" pnodeClosest %p\n"
|
|
" pzoneCurrent %p\n"
|
|
" pnodeGlue %p\n"
|
|
" pnodeDelegation %p\n"
|
|
" pnodeCache %p\n"
|
|
" pnodeCacheClosest %p\n",
|
|
( ULONG64 ) pmsg->pnodeCurrent,
|
|
( ULONG64 ) pmsg->pnodeClosest,
|
|
( ULONG64 ) pmsg->pzoneCurrent,
|
|
( ULONG64 ) pmsg->pnodeGlue,
|
|
( ULONG64 ) pmsg->pnodeDelegation,
|
|
( ULONG64 ) pmsg->pnodeCache,
|
|
( ULONG64 ) pmsg->pnodeCacheClosest );
|
|
|
|
dprintf(
|
|
" pnodeNxt %p\n"
|
|
" wTypeCurrent %d\n"
|
|
" wOffsetCurrent %d\n"
|
|
" pNodeQuestion %p\n"
|
|
" pNodeQuestionClosest %p\n"
|
|
" pQuestion %p\n"
|
|
" wQuestionType %d\n",
|
|
( ULONG64 ) pmsg->pnodeNxt,
|
|
( int ) pmsg->wTypeCurrent,
|
|
( int ) pmsg->wOffsetCurrent,
|
|
( ULONG64 ) pmsg->pNodeQuestion,
|
|
( ULONG64 ) pmsg->pNodeQuestionClosest,
|
|
( ULONG64 ) pmsg->pQuestion,
|
|
( int ) pmsg->wQuestionType );
|
|
|
|
dprintf(
|
|
"Queuing info:\n"
|
|
" wQueuingXid %d\n"
|
|
" dwQueryTime %d\n"
|
|
" dwMsQueryTime %d\n"
|
|
" dwQueuingTime %d\n"
|
|
" dwExpireTime %d\n",
|
|
( int ) pmsg->wQueuingXid,
|
|
pmsg->dwQueryTime,
|
|
pmsg->dwMsQueryTime,
|
|
pmsg->dwQueuingTime,
|
|
pmsg->dwExpireTime );
|
|
|
|
dprintf(
|
|
"EDNS info\n"
|
|
" fFoundOptInIncomingMsg %s\n"
|
|
" fInsertOptInOutgoingMsg %s\n"
|
|
" cExtendedRCodeBits %d\n"
|
|
" cVersion %d\n"
|
|
" wUdpPayloadSize %d\n"
|
|
" wOptOffset %d\n"
|
|
" wOriginalQueryPayloadSize %d\n",
|
|
BoolFlagStr( pmsg->Opt.fFoundOptInIncomingMsg ),
|
|
BoolFlagStr( pmsg->Opt.fInsertOptInOutgoingMsg ),
|
|
( int ) pmsg->Opt.cExtendedRCodeBits,
|
|
( int ) pmsg->Opt.cVersion,
|
|
( int ) pmsg->Opt.wUdpPayloadSize,
|
|
( int ) pmsg->Opt.wOptOffset,
|
|
( int ) pmsg->Opt.wOriginalQueryPayloadSize );
|
|
|
|
dprintf(
|
|
"Recursion info:\n"
|
|
" pRecurseMsg %p\n"
|
|
" pnodeRecurseRetry %p\n"
|
|
" pNsList %p\n",
|
|
( ULONG64 ) pmsg->pRecurseMsg,
|
|
( ULONG64 ) pmsg->pnodeRecurseRetry,
|
|
( ULONG64 ) pmsg->pNsList );
|
|
|
|
dprintf(
|
|
"Flags:\n"
|
|
" fDelete %s\n"
|
|
" fTcp %s\n"
|
|
" fMessageComplete %s\n"
|
|
" Section %d\n"
|
|
" fDoAdditional %s\n"
|
|
" fRecurseIfNecessary %s\n"
|
|
" fRecursePacket %s\n"
|
|
" fQuestionRecursed %s\n"
|
|
" fQuestionCompleted %s\n",
|
|
BoolFlagStr( pmsg->fDelete ),
|
|
BoolFlagStr( pmsg->fTcp ),
|
|
BoolFlagStr( pmsg->fMessageComplete ),
|
|
( int ) pmsg->Section,
|
|
BoolFlagStr( pmsg->fDoAdditional ),
|
|
BoolFlagStr( pmsg->fRecurseIfNecessary ),
|
|
BoolFlagStr( pmsg->fRecursePacket ),
|
|
BoolFlagStr( pmsg->fQuestionRecursed ),
|
|
BoolFlagStr( pmsg->fQuestionCompleted ) );
|
|
|
|
dprintf(
|
|
" fRecurseQuestionSent %s\n"
|
|
" fRecurseTimeoutWait %s\n"
|
|
" nTimeoutCount %d\n"
|
|
" nForwarder %d\n"
|
|
" fReplaceCname %s\n"
|
|
" cCnameAnswerCount %d\n"
|
|
" fNoCompressionWrite %s\n"
|
|
" fWins %s\n"
|
|
" fQuestionWildcard %s\n"
|
|
" fNsList %s\n",
|
|
BoolFlagStr( pmsg->fRecurseQuestionSent ),
|
|
BoolFlagStr( pmsg->fRecurseTimeoutWait ),
|
|
( int ) pmsg->nTimeoutCount,
|
|
( int ) pmsg->nForwarder,
|
|
BoolFlagStr( pmsg->fReplaceCname ),
|
|
( int ) pmsg->cCnameAnswerCount,
|
|
BoolFlagStr( pmsg->fNoCompressionWrite ),
|
|
BoolFlagStr( pmsg->fWins ),
|
|
BoolFlagStr( pmsg->fQuestionWildcard ),
|
|
BoolFlagStr( pmsg->fNsList ) );
|
|
|
|
dprintf(
|
|
"DNS header:\n"
|
|
" Xid %04X\n"
|
|
" RecursionDesired %d\n"
|
|
" Truncation %d\n"
|
|
" Authoritative %d\n"
|
|
" Opcode %d\n"
|
|
" IsResponse %d\n",
|
|
( int ) pmsg->Head.Xid,
|
|
( int ) pmsg->Head.RecursionDesired,
|
|
( int ) pmsg->Head.Truncation,
|
|
( int ) pmsg->Head.Authoritative,
|
|
( int ) pmsg->Head.Opcode,
|
|
( int ) pmsg->Head.IsResponse );
|
|
|
|
dprintf(
|
|
" ResponseCode %d\n"
|
|
" RecursionAvailable %d\n"
|
|
" QuestionCount %d\n"
|
|
" AnswerCount %d\n"
|
|
" NameServerCount %d\n"
|
|
" AdditionalCount %d\n",
|
|
( int ) pmsg->Head.ResponseCode,
|
|
( int ) pmsg->Head.RecursionAvailable,
|
|
( int ) pmsg->Head.QuestionCount,
|
|
( int ) pmsg->Head.AnswerCount,
|
|
( int ) pmsg->Head.NameServerCount,
|
|
( int ) pmsg->Head.AdditionalCount );
|
|
|
|
ULONG64 pmsgbody = ( ULONG64 ) ( ( PBYTE ) p +
|
|
( ( PBYTE ) pmsg->MessageBody -
|
|
( PBYTE ) pmsg ) );
|
|
dprintf(
|
|
"DNS message body at %p\n",
|
|
pmsgbody );
|
|
|
|
Cleanup:
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // Msg
|
|
|
|
|
|
//
|
|
// Set a max count to be respected by all other iterating commands.
|
|
// This is cool for example if you have a tree with a million nodes
|
|
// and you just want to dump the first few hundred to get an idea of
|
|
// what's going on.
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
MaxIterations(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
g_iMaxIterations = ( int ) GetExpression( args );
|
|
|
|
dprintf(
|
|
"MaxIterations set to %d%s\n",
|
|
g_iMaxIterations,
|
|
g_iMaxIterations == 0 ? " (unlimited)" : "" );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // MaxIterations
|
|
|
|
|
|
//
|
|
// Help for the other commands
|
|
//
|
|
|
|
HRESULT CALLBACK
|
|
help(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR args )
|
|
{
|
|
INIT_API();
|
|
|
|
dprintf(
|
|
"Help for dnsdbg.dll\n"
|
|
" Globals"
|
|
" Print current values of various globals.\n"
|
|
" ZoneList ADDR\n"
|
|
" Print zone list. Argument is address of listheadZone.\n"
|
|
" Zone ADDR\n"
|
|
" Print zone info. Argument is address of ZONE_INFO struct.\n"
|
|
" Node ADDR\n"
|
|
" Print node info. Argument is address of DB_NODE struct.\n" );
|
|
dprintf(
|
|
" Tree ADDR\n"
|
|
" Traverse and print a tree of nodes. Argument is address of the\n"
|
|
" tree root. The tree root can be a hash table or a DB_NODE.\n"
|
|
" TreeSummary ADDR\n"
|
|
" Traverse and print summary for a node tree. Argument same as Tree.\n"
|
|
" Stats ADDR\n"
|
|
" Print server statistics. Argument is address of StatsTable\n" );
|
|
dprintf(
|
|
" TimeoutSummary ADDR\n"
|
|
" Print summary of nodes in the timeout system.\n"
|
|
" Argument is address of TimeoutBinArray\n"
|
|
" TimeoutArrays ADDR\n"
|
|
" Print timeout system arrays. Argument is address of TimeoutBinArray\n"
|
|
" TimeoutNodes ADDR\n"
|
|
" Print timeout system arrays and nodes.\n"
|
|
" Argument is address of TimeoutBinArray\n"
|
|
" PacketQ ADDR\n"
|
|
" Print packet queue info. Argument is address of PACKET_QUEUE struct.\n"
|
|
" Msg ADDR\n"
|
|
" Print message info. Argument is address of DNS_MSGINFO struct.\n" );
|
|
dprintf(
|
|
" MaxIterations COUNT\n"
|
|
" Set max nodes or objects to visit. Argument is count in hex.\n"
|
|
" help - shows this help\n" );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
} // help
|