442 lines
11 KiB
C++
442 lines
11 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) 2002 Microsoft Corporation
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Include file for DNS diagnostic tool.
|
|
//
|
|
// Author:
|
|
//
|
|
// gpulla
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// IISRTL, ATQ APIs
|
|
#include <atq.h>
|
|
#include <irtlmisc.h>
|
|
|
|
// Winsock
|
|
#include <winsock2.h>
|
|
|
|
// DNS API
|
|
#include <dns.h>
|
|
#include <dnsapi.h>
|
|
|
|
// Metabase property definitions
|
|
#include <smtpinet.h>
|
|
#include <iiscnfg.h>
|
|
|
|
// Metabase COM access APIs
|
|
#define INITGUID
|
|
#include <iadmw.h>
|
|
|
|
// DSGetDC
|
|
#include <lm.h>
|
|
#include <lmapibuf.h>
|
|
#include <dsgetdc.h>
|
|
|
|
// ADSI headers
|
|
#include <activeds.h>
|
|
|
|
// ldap stuff
|
|
#include <winldap.h>
|
|
|
|
// SMTP specific stuff
|
|
#include <rwnew.h>
|
|
|
|
// These #defines are needed for cdns.h
|
|
#define MAX_EMAIL_NAME 64
|
|
#define MAX_DOMAIN_NAME 250
|
|
#define MAX_INTERNET_NAME (MAX_EMAIL_NAME + MAX_DOMAIN_NAME + 2)
|
|
|
|
#include "cdns.h"
|
|
|
|
//
|
|
// Program return codes and descriptions
|
|
// 0 is always success and the other error codes indicate some failure.
|
|
//
|
|
|
|
// The name was resolved successfully to one or more IP addresses.
|
|
#define DNSDIAG_RESOLVED 0
|
|
|
|
// The name could not be resolved due to some unspecified error.
|
|
#define DNSDIAG_FAILURE 1
|
|
|
|
// The name does not exist - a "NOT FOUND" error was returned by a server
|
|
// authoritative for the domain in which the name is.
|
|
#define DNSDIAG_NON_EXISTENT 2
|
|
|
|
// The name could not be found in DNS.
|
|
#define DNSDIAG_NOT_FOUND 3
|
|
|
|
// Loopback detected
|
|
#define DNSDIAG_LOOPBACK 4
|
|
|
|
extern int g_nProgramStatus;
|
|
|
|
//
|
|
// Helper function to set the global program return status code to a more
|
|
// specific error than the generic DNSDIAG_FAILURE. Note that this is not
|
|
// thread-safe.
|
|
//
|
|
inline void SetProgramStatus(DWORD dwCode)
|
|
{
|
|
if(g_nProgramStatus == DNSDIAG_FAILURE)
|
|
g_nProgramStatus = dwCode;
|
|
}
|
|
|
|
extern DWORD g_cDnsObjects;
|
|
|
|
//
|
|
// handy function for DWORD -> stringized IP conversion.
|
|
// inet_ntoa is cumbersome to use directly since the DWORD needs to
|
|
// be either cast to an in_addr struct, or copied to an in_addr first
|
|
// This function casts the *address* of the DWORD to in_addr ptr, and
|
|
// then de-references the pointer to make the cast work, before passing
|
|
// it to inet_ntoa. The string returned by inet_ntoa is valid till
|
|
// another winsock call is made on the thread (see SDK documentation).
|
|
//
|
|
|
|
inline char *iptostring(DWORD dw)
|
|
{
|
|
return inet_ntoa(
|
|
*( (in_addr *) &dw )
|
|
);
|
|
}
|
|
|
|
void PrintIPArray(PIP_ARRAY pipArray, char *pszPrefix = "")
|
|
{
|
|
for(DWORD i = 0; i < pipArray->cAddrCount; i++)
|
|
printf("%s%s\n", pszPrefix, iptostring(pipArray->aipAddrs[i]));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Description:
|
|
// Utility function to print descriptions of errors that may occur while
|
|
// reading from the metabase.
|
|
//
|
|
// Arguments:
|
|
// IN HRESULT hr - Metabase access error HRESULT
|
|
//
|
|
// Returns:
|
|
// String (static) indicating the error that occurred.
|
|
//------------------------------------------------------------------------------
|
|
inline const char *MDErrorToString(HRESULT hr)
|
|
{
|
|
static const DWORD dwErrors[] =
|
|
{
|
|
ERROR_ACCESS_DENIED,
|
|
ERROR_INSUFFICIENT_BUFFER,
|
|
ERROR_INVALID_PARAMETER,
|
|
ERROR_PATH_NOT_FOUND,
|
|
MD_ERROR_DATA_NOT_FOUND
|
|
};
|
|
|
|
static const char *szErrors[] =
|
|
{
|
|
"ERROR_ACCESS_DENIED",
|
|
"ERROR_INSUFFICIENT_BUFFER",
|
|
"ERROR_INVALID_PARAMETER",
|
|
"ERROR_PATH_NOT_FOUND",
|
|
"MD_ERROR_DATA_NOT_FOUND"
|
|
};
|
|
|
|
static const char szUnknown[] = "Unknown Error";
|
|
|
|
for(int i = 0; i < sizeof(dwErrors)/sizeof(DWORD); i++)
|
|
{
|
|
if(HRESULT_FROM_WIN32(dwErrors[i]) == hr)
|
|
return szErrors[i];
|
|
}
|
|
|
|
return szUnknown;
|
|
}
|
|
|
|
inline const char *QueryType(DWORD dwDnsQueryType)
|
|
{
|
|
DWORD i = 0;
|
|
static const DWORD rgdwQueryTypes[] =
|
|
{
|
|
DNS_TYPE_MX,
|
|
DNS_TYPE_A,
|
|
DNS_TYPE_CNAME
|
|
};
|
|
|
|
static const char *rgszQueryTypes[] =
|
|
{
|
|
"MX",
|
|
"A",
|
|
"CNAME"
|
|
};
|
|
|
|
static const char szUnknown[] = "Unknown Type";
|
|
|
|
for(i = 0; i < sizeof(rgdwQueryTypes)/sizeof(DWORD); i++)
|
|
{
|
|
if(rgdwQueryTypes[i] == dwDnsQueryType)
|
|
return rgszQueryTypes[i];
|
|
}
|
|
return szUnknown;
|
|
}
|
|
|
|
inline void GetSmtpFlags(DWORD dwFlags, char *pszFlags, DWORD cchFlags)
|
|
{
|
|
if(dwFlags == DNS_FLAGS_TCP_ONLY)
|
|
{
|
|
_snprintf(pszFlags, cchFlags, " TCP only");
|
|
return;
|
|
}
|
|
|
|
if(dwFlags == DNS_FLAGS_UDP_ONLY)
|
|
{
|
|
_snprintf(pszFlags, cchFlags, " UDP only");
|
|
return;
|
|
}
|
|
|
|
if(dwFlags == DNS_FLAGS_NONE)
|
|
{
|
|
_snprintf(pszFlags, cchFlags, " UDP default, TCP on truncation");
|
|
return;
|
|
}
|
|
|
|
_snprintf(pszFlags, cchFlags, " Unknown flag");
|
|
}
|
|
|
|
inline void GetDnsFlags(DWORD dwFlags, char *pszFlags, DWORD cchFlags)
|
|
{
|
|
DWORD i = 0;
|
|
DWORD dwScratchFlags = dwFlags; // Copy of dwFlags: will be overwritten
|
|
char *pszStartBuffer = pszFlags;
|
|
int cchWritten = 0;
|
|
BOOL fFlagsSet = FALSE;
|
|
|
|
static const DWORD rgdwDnsFlags[] =
|
|
{
|
|
DNS_QUERY_STANDARD,
|
|
DNS_QUERY_USE_TCP_ONLY,
|
|
DNS_QUERY_NO_RECURSION,
|
|
DNS_QUERY_BYPASS_CACHE,
|
|
DNS_QUERY_CACHE_ONLY,
|
|
DNS_QUERY_TREAT_AS_FQDN,
|
|
};
|
|
|
|
static const char *rgszDnsFlags[] =
|
|
{
|
|
"DNS_QUERY_STANDARD",
|
|
"DNS_QUERY_USE_TCP_ONLY",
|
|
"DNS_QUERY_NO_RECURSION",
|
|
"DNS_QUERY_BYPASS_CACHE",
|
|
"DNS_QUERY_CACHE_ONLY",
|
|
"DNS_QUERY_TREAT_AS_FQDN"
|
|
};
|
|
|
|
for(i = 0; i < sizeof(rgdwDnsFlags)/sizeof(DWORD);i++)
|
|
{
|
|
if(rgdwDnsFlags[i] & dwScratchFlags)
|
|
{
|
|
fFlagsSet = TRUE;
|
|
dwScratchFlags &= ~rgdwDnsFlags[i];
|
|
cchWritten = _snprintf(pszFlags, cchFlags, " %s", rgszDnsFlags[i]);
|
|
if(cchWritten < 0)
|
|
{
|
|
sprintf(pszStartBuffer, " %s", "Error");
|
|
return;
|
|
}
|
|
pszFlags += cchWritten;
|
|
cchFlags -= cchWritten;
|
|
}
|
|
}
|
|
|
|
if(!fFlagsSet)
|
|
sprintf(pszStartBuffer, " %s", "No flags");
|
|
|
|
if(dwScratchFlags)
|
|
sprintf(pszStartBuffer, " %x is %s", dwScratchFlags, "Unknown!");
|
|
}
|
|
|
|
void PrintRecordList(PDNS_RECORD pDnsRecordList, char *pszPrefix = "");
|
|
|
|
void PrintRecord(PDNS_RECORD pDnsRecord, char *pszPrefix = "");
|
|
|
|
class CSimpleDnsServerList : public CDnsServerList
|
|
{
|
|
public:
|
|
|
|
//
|
|
// It is meaningful to have this > 1 only if you have several async queries
|
|
// pending at the same time. In the DNS tool only 1 async query is
|
|
// outstanding at any given time.
|
|
//
|
|
|
|
DWORD ConnectsAllowedInProbation()
|
|
{ return 1; }
|
|
|
|
//
|
|
// SMTP actually has 3 retries before failing over, but that is because it
|
|
// has dozens of queries going out per minute. If even a small percent of
|
|
// those fail due to network errors, DNS servers will be quickly marked down.
|
|
// This is not a factor here though.
|
|
//
|
|
|
|
DWORD ErrorsBeforeFailover()
|
|
{ return 1; }
|
|
|
|
void LogServerDown(
|
|
DWORD dwServerIp,
|
|
BOOL fUdp,
|
|
DWORD dwErr,
|
|
DWORD cUpServers)
|
|
{ return; }
|
|
|
|
};
|
|
|
|
class CAsyncTestDns : public CAsyncMxDns
|
|
{
|
|
private:
|
|
BOOL m_fGlobalList;
|
|
HANDLE m_hCompletion;
|
|
public:
|
|
|
|
//
|
|
// Custom new/delete operators. They simply call into the global operators,
|
|
// but additionally they track the number of DNS objects still "alive". This
|
|
// is needed so that we know when we can shutdown ATQ/IISRTL. Terminating
|
|
// ATQ/IISRTL before all DNS objects have been completely destructed can mean
|
|
// leaked ATQ contexts and all sorts of AV's (and ASSERTS in debug). Note that
|
|
// it is inadequate to signal termination in ~CAsyncTestDns, since the base
|
|
// class destructor ~CAsyncDns is yet to be called at that point.
|
|
//
|
|
|
|
void *operator new(size_t size)
|
|
{
|
|
void *pvNew = ::new BYTE[sizeof(CAsyncTestDns)];
|
|
|
|
InterlockedIncrement((PLONG)&g_cDnsObjects);
|
|
return pvNew;
|
|
}
|
|
|
|
void operator delete(void *pv, size_t size)
|
|
{
|
|
::delete ((CAsyncTestDns *)pv);
|
|
InterlockedDecrement((PLONG)&g_cDnsObjects);
|
|
}
|
|
|
|
CAsyncTestDns(char *pszMyFQDN, BOOL fGlobalList, HANDLE hCompletion)
|
|
: CAsyncMxDns(pszMyFQDN),
|
|
m_fGlobalList(fGlobalList),
|
|
m_hCompletion(hCompletion)
|
|
{ }
|
|
|
|
~CAsyncTestDns();
|
|
|
|
// virtual functions implemented by us
|
|
BOOL RetryAsyncDnsQuery(BOOL fUdp);
|
|
|
|
void HandleCompletedData(DNS_STATUS status);
|
|
|
|
BOOL IsShuttingDown()
|
|
{ return FALSE; }
|
|
|
|
BOOL IsAddressMine(DWORD dwIp);
|
|
};
|
|
|
|
class CDnsLogToFile : public CDnsLogger
|
|
{
|
|
public:
|
|
// Definitions of virtual functions
|
|
void DnsPrintfMsg(char *szFormat, ...);
|
|
|
|
void DnsPrintfErr(char *szFormat, ...);
|
|
|
|
void DnsPrintfDbg(char *szFormat, ...);
|
|
|
|
void DnsLogAsyncQuery(
|
|
char *pszQuestionName,
|
|
WORD wQuestionType,
|
|
DWORD dwFlags,
|
|
BOOL fUdp,
|
|
CDnsServerList *pDnsServerList);
|
|
|
|
void DnsLogApiQuery(
|
|
char *pszQuestionName,
|
|
WORD wQuestionType,
|
|
DWORD dwApiFlags,
|
|
BOOL fGlobal,
|
|
PIP_ARRAY pipServers);
|
|
|
|
void DnsLogResponse(
|
|
DWORD dwStatus,
|
|
PDNS_RECORD pDnsRecordList,
|
|
PBYTE pbMsg,
|
|
DWORD wMessageLength);
|
|
|
|
// Utility functions
|
|
void DnsLogServerList(CDnsServerList *pDnsServerList);
|
|
|
|
void DnsLogRecordList(PDNS_RECORD pDnsRecordList)
|
|
{ PrintRecordList(pDnsRecordList); }
|
|
|
|
void DnsPrintRecord(PDNS_RECORD pDnsRecord)
|
|
{ PrintRecord(pDnsRecord); }
|
|
|
|
void DnsPrintIPArray(PIP_ARRAY pipArray)
|
|
{ PrintIPArray(pipArray); }
|
|
|
|
};
|
|
|
|
BOOL ParseCommandLine(
|
|
int argc,
|
|
char *argv[],
|
|
char *pszHostName,
|
|
DWORD cchHostName,
|
|
CDnsLogToFile **ppDnsLogger,
|
|
PIP_ARRAY pipArray,
|
|
DWORD cMaxServers,
|
|
BOOL *pfUdp,
|
|
DWORD *pdwDnsFlags,
|
|
BOOL *pfGlobalList,
|
|
BOOL *pfTryAllServers);
|
|
|
|
HRESULT HrGetVsiConfig(
|
|
LPSTR pszTargetHost,
|
|
DWORD dwVsid,
|
|
PDWORD pdwFlags,
|
|
PIP_ARRAY pipDnsServers,
|
|
DWORD cMaxServers,
|
|
BOOL *pfUdp,
|
|
BOOL *pfGlobalList,
|
|
PIP_ARRAY pipServerBindings,
|
|
DWORD cMaxServerBindings);
|
|
|
|
DWORD IsExchangeInstalled(BOOL *pfInstalled);
|
|
|
|
DWORD DsGetConfiguration(
|
|
char *pszServer,
|
|
DWORD dwVsid,
|
|
PIP_ARRAY pipDnsServers,
|
|
DWORD cMaxServers,
|
|
PBOOL pfExternal);
|
|
|
|
DWORD DsFindExchangeServer(
|
|
PLDAP pldap,
|
|
LPSTR szBaseDN,
|
|
LPSTR szHostDnsName,
|
|
LPSTR *ppszServerDN,
|
|
BOOL *pfFound);
|
|
|
|
PLDAP BindToDC();
|
|
|
|
BOOL GetServerBindings(
|
|
WCHAR *pwszMultiSzBindings,
|
|
PIP_ARRAY pipServerBindings,
|
|
DWORD cMaxServerBindings);
|
|
|
|
void SetMsgColor();
|
|
void SetErrColor();
|
|
void SetNormalColor();
|
|
|
|
void msgprintf(char *szFormat, ...);
|
|
void errprintf(char *szFormat, ...);
|
|
void dbgprintf(char *szFormat, ...);
|