Windows2003-3790/inetsrv/iis/svcs/smtp/dnsdiag/dnsdiag.h
2020-09-30 16:53:55 +02:00

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, ...);