Leaked source code of windows server 2003
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

/*-----------------------------------------------------------------------------
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