//----------------------------------------------------------------------------- // // Copyright (c) 2002 Microsoft Corporation // // Abstract: // // Include file for DNS diagnostic tool. // // Author: // // gpulla // //----------------------------------------------------------------------------- // IISRTL, ATQ APIs #include #include // Winsock #include // DNS API #include #include // Metabase property definitions #include #include // Metabase COM access APIs #define INITGUID #include // DSGetDC #include #include #include // ADSI headers #include // ldap stuff #include // SMTP specific stuff #include // 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, ...);