|
|
//-----------------------------------------------------------------------------
//
// Copyright (c) 2002 Microsoft Corporation
//
// Abstract:
//
// Source file for DNS diagnostic tool. Links into dnslib.lib which has
// the SMTP DNS resolution logic and calls into DNS resolution functions
// while printing diagnostic messages.
//
// Author:
//
// gpulla
//
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
#include "dnsdiag.h"
int g_nProgramStatus = DNSDIAG_FAILURE; CDnsLogger *g_pDnsLogger = NULL; HANDLE g_hCompletion = NULL; BOOL g_fDebug = FALSE; DWORD g_cDnsObjects = 0; HANDLE g_hConsole = INVALID_HANDLE_VALUE; DWORD g_rgBindings[32]; PIP_ARRAY g_pipBindings = (PIP_ARRAY)g_rgBindings; DWORD g_cMaxBindings = sizeof(g_rgBindings)/sizeof(DWORD) - 1;
char g_szUsage[] = "Summary:\n" "\n" "This tool is used to troubleshoot problems with DNS resolution for SMTP. It\n" "simulates SMTPSVC's internal code-path and prints diagnostic messages that\n" "indicate how the resolution is proceeding. The tool must be run on the machine\n" "where the DNS problems are occurring.\n" "\n" "Program return codes:\n" " These are set as the ERRORLEVEL for usage in batch files.\n" "\n" " 0 - The name was resolved successfully to one or more IP addresses.\n" " 1 - The name could not be resolved due to an unspecified error.\n" " 2 - The name does not exist. The error was returned by an authoritative DNS\n" " server for the domain.\n" " 3 - The name could not be located in DNS. This is not an error from the\n" " authoritative DNS server.\n" " 4 - A loopback was detected.\n" "\n" "Usage:\n" "\n" "dnsdiag <hostname> [-d] [options]\n" "\n" "<hostname>\n" " Hostname to query for. Note that this may not be the same as the display\n" " -name of the queue (in ESM, if Exchange is installed). It should be the\n" " fully-qualified domain name of the target for the queue seeing the DNS\n" " errors\n" "\n" "-d\n" " This is a special option to run in debug/verbose mode. There is a lot of\n" " output, and the most important messages (the ones that normally appear when\n" " this mode is not turned on) are highlighted in a different color.\n" "\n" "Options are:\n" "-v <VSID>\n" " If running on an Exchange DMZ machine, you can specify the VSI# of the\n" " VSI to simulate DNS for that SMTP VS. Then this tool will read the\n" " external DNS serverlist for that VSI and query that serverlist for\n" " <hostname> when <hostname> is an \"external\" host. If <hostname> is the\n" " name of an Exchange computer, the query is generated against the default\n" " DNS servers for the local computer.\n" "\n" "-s <serverlist>\n" " DNS servers to use, if you want to specify a specific set of servers.\n" "\n" " If this option is not specified, the default DNS servers on the local\n" " computer are used as specified by -v.\n" "\n" " This option is incompatible with -v.\n" "\n" "-p <protocol>\n" " TCP, UDP or DEF. TCP generates a TCP only query. UDP generates a UDP only\n" " query. DEF generates a default query that will initially query a server with\n" " UDP, and then if that query results in a truncated reply, it will be retried\n" " with TCP.\n" "\n" " If this option is not specified the protocol configured in the metabase for\n" " /smtpsvc/UseTcpDns is used.\n" "\n" " This option is incompatible with the -v option.\n" "\n" "-a\n" " All the DNS servers obtained (either through the registry, active directory,\n" " or -s option) are tried in sequence and the results of querying each are\n" " displayed.\n";
//-----------------------------------------------------------------------------
// Description:
// DNS diagnostic utility. See above for usage.
//-----------------------------------------------------------------------------
int __cdecl main(int argc, char *argv[]) { CAsyncTestDns *pAsyncTestDns = NULL; CSimpleDnsServerList *pDnsSimpleList = NULL; CDnsLogToFile *pDnsLogToFile = NULL; char szMyHostName[MAX_PATH + 1]; char szHostName[MAX_PATH + 1]; BOOL fAtqInitialized = FALSE; BOOL fIISRTLInitialized = FALSE; BOOL fWSAInitialized = FALSE; WORD wVersion = MAKEWORD(2, 2); WSADATA wsaData; BOOL fRet = TRUE; int nRet = 0; DWORD dwStatus = ERROR_SUCCESS; DWORD dwDnsFlags = 0; BOOL fUdp = TRUE; BOOL fGlobalList = FALSE; BOOL fTryAllDnsServers = FALSE; DWORD rgDnsServers[16]; PIP_ARRAY pipArray = (PIP_ARRAY)rgDnsServers; DWORD cMaxServers = sizeof(rgDnsServers)/sizeof(DWORD) - 1; DWORD rgSingleServer[2]; PIP_ARRAY pipSingleServer = (PIP_ARRAY)rgSingleServer; DWORD cNextServer = 0; PIP_ARRAY pipDnsArray = NULL;
ZeroMemory(rgDnsServers, sizeof(rgDnsServers)); ZeroMemory(rgSingleServer, sizeof(rgSingleServer)); ZeroMemory(g_rgBindings, sizeof(g_rgBindings));
if(1 == argc) { SetProgramStatus(DNSDIAG_RESOLVED); printf("%s", g_szUsage); goto Cleanup; }
nRet = WSAStartup(wVersion, &wsaData); if(0 != nRet) { errprintf("Failed Winsock init, error - %d\n", WSAGetLastError()); goto Cleanup; }
fWSAInitialized = TRUE;
if(0 != gethostname(szMyHostName, sizeof(szMyHostName))) { printf("Unable to get local machine name. Error - %d\n", WSAGetLastError()); goto Cleanup; }
g_hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if(g_hConsole == INVALID_HANDLE_VALUE) { printf("Failed to GetStdHandle\n"); goto Cleanup; }
g_hCompletion = CreateEvent(NULL, TRUE, FALSE, NULL); if(NULL == g_hCompletion) goto Cleanup;
dbgprintf("Reading options and configuration.\n"); fRet = ParseCommandLine(argc, argv, szHostName, sizeof(szHostName), &pDnsLogToFile, pipArray, cMaxServers, &fUdp, &dwDnsFlags, &fGlobalList, &fTryAllDnsServers);
if(!fRet) goto Cleanup; g_pDnsLogger = (CDnsLogger *)pDnsLogToFile;
fIISRTLInitialized = InitializeIISRTL(); if(!fIISRTLInitialized) { errprintf("Failed IISRTL init, error - %d\n", GetLastError()); errprintf("Make sure you are running this tool on a server with IIS " "installed\n"); goto Cleanup; }
fAtqInitialized = AtqInitialize(0); if(!fAtqInitialized) { errprintf("Failed ISATQ init, error - %d\n", GetLastError()); errprintf("Make sure you are running this tool on a server with IIS " "installed\n"); goto Cleanup; }
cNextServer = 0; pipSingleServer->cAddrCount = 1;
while(TRUE) { //
// Set pipDnsArray to the DNS servers to be used. If the -a option is
// specified, each DNS server will be tried individually. We will run
// through this while loop and set pipDnsArray to single DNS server
// in turn. If -a is not given, all servers are set on pipDnsServers.
//
if(fTryAllDnsServers) { if(cNextServer >= pipArray->cAddrCount) break;
pipSingleServer->aipAddrs[0] = pipArray->aipAddrs[cNextServer]; cNextServer++; pipDnsArray = pipSingleServer; msgprintf("\n\nQuerying DNS server: %s\n", iptostring(pipSingleServer->aipAddrs[0])); } else { pipDnsArray = pipArray; }
//
// Create the DNS serverlist object and set however many DNS servers we
// want to query on the object
//
pDnsSimpleList = new CSimpleDnsServerList(); if(!pDnsSimpleList) { errprintf("Out of memory creating DNS serverlist object.\n"); goto Cleanup; }
fRet = pDnsSimpleList->Update(pipDnsArray); if(!fRet) { errprintf("Unable to create DNS serverlist\n"); goto Cleanup; }
//
// DNS querying object
//
pAsyncTestDns = new CAsyncTestDns(szMyHostName, fGlobalList, g_hCompletion); if(!pAsyncTestDns) { errprintf("Out of memory allocating DNS object.\n"); goto Cleanup; }
dwStatus = pAsyncTestDns->Dns_QueryLib( szHostName, DNS_TYPE_MX, dwDnsFlags, fUdp, pDnsSimpleList, fGlobalList);
//
// If the query failed we need to manually delete the object. If the
// query succeeded, the completing ATQ threads will delete the object
// after the results have been reported.
//
if(dwStatus != ERROR_SUCCESS) { errprintf("DNS query failed.\n"); delete pAsyncTestDns; pAsyncTestDns = NULL; }
//
// This event is set in the destructor of pAsyncTestDns when the object
// has finally finished the query (either successfully or with a failure).
//
WaitForSingleObject(g_hCompletion, INFINITE); ResetEvent(g_hCompletion);
delete pDnsSimpleList; pDnsSimpleList = NULL;
//
// If -a was specified, we go on to the next iteration, and pick up the
// next DNS server in line. Otherwise we just finish after a single
// query.
//
if(!fTryAllDnsServers) break; }
Cleanup: if(pDnsSimpleList) delete pDnsSimpleList;
while(g_cDnsObjects) Sleep(100);
if(fAtqInitialized) { dbgprintf("Shutting down ATQ\n"); AtqTerminate(); }
if(fIISRTLInitialized) { dbgprintf("Shutting down IISRTL\n"); TerminateIISRTL(); }
if(fWSAInitialized) WSACleanup();
if(g_hCompletion) CloseHandle(g_hCompletion);
dbgprintf("Exit code: %d\n", g_nProgramStatus); exit(g_nProgramStatus); return g_nProgramStatus; }
//-----------------------------------------------------------------------------
// Description:
// Parses the argc and argv and gets the various options. Also reads from
// the metabase and DS to get the configuration as needed.
//
// Arguments:
// IN int argc - Command line arg-count
// IN char *argv[] - Command line args
// OUT char *pszHostName - Pass in buffer to get target host
// IN DWORD cchHostName - Length of above buffer in chars
// OUT CDnsLogToFile **ppDnsLogger - Returns a logging object
// OUT PIP_ARRAY pipArray - Pass in buffer to get DNS servers
// IN int cMaxServers - Number of DNS servers that can be returned above
// OUT BOOL *pfUdp - Use TCP or UDP for the query
// OUT DWORD *pdwDnsFlags - Metabase configured SMTP DNS flags
// OUT BOOL *pfGlobalList - Are the servers global?
// OUT BOOL *pfTryAllServers - The "-a" option
//
// Returns:
// TRUE arguments were successfully parsed, configuration was read without
// problems, and initialization completed without errors.
// FALSE there was an error. Abort. This function prints error messages
// to stdout.
//-----------------------------------------------------------------------------
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) { int i = 0; BOOL fRet = FALSE; HRESULT hr = E_FAIL; BOOL fOptionS = FALSE; BOOL fOptionV = FALSE; BOOL fOptionD = FALSE; BOOL fOptionA = FALSE; BOOL fOptionP = FALSE; DWORD dwVsid = 0; DWORD cServers = 0; DWORD dwIpAddress = INADDR_NONE;
*pszHostName = '\0';
*pfGlobalList = FALSE; *pfTryAllServers = FALSE; if(argc < 2) { errprintf("Must specify a hostname as first argument.\n"); printf("%s", g_szUsage); return FALSE; } else if(argc == 2 && (!_stricmp(argv[1], "/?") || !_stricmp(argv[1], "-?"))) { SetProgramStatus(DNSDIAG_RESOLVED); printf("%s", g_szUsage); return FALSE; }
pszHostName[cchHostName - 1] = '\0'; strncpy(pszHostName, argv[1], cchHostName); if(pszHostName[cchHostName - 1] != '\0') { errprintf("Hostname too long. Maximum that can be handled by this tool is " "%d characters\n", cchHostName); return FALSE; }
i = 2;
while(i < argc) { if(!g_fDebug && !_stricmp(argv[i], "-d")) { i++; g_fDebug = TRUE; printf("Running in debug/verbose mode.\n"); continue; }
if(!fOptionV && !_stricmp(argv[i], "-v")) { i++; if(i >= argc) { printf("Specify an SMTP VSI# for -v option.\n"); goto Cleanup; }
dwVsid = atoi(argv[i]); if(dwVsid <= 0) { printf("Illegal operand to -v. Should be a number > 0.\n"); goto Cleanup; }
fOptionV = TRUE; i++; continue; }
if(!fOptionS && !_stricmp(argv[i], "-s")) { i++; if(i >= argc) { printf("No DNS servers specified for -s option.\n"); goto Cleanup; }
cServers = 0; while(*argv[i] != '-') { dwIpAddress = inet_addr(argv[i]); if(dwIpAddress == INADDR_NONE) { printf("Non IP address \"%s\" in -s option.\n", argv[i]); goto Cleanup; }
if(cServers >= cMaxServers) { printf("Too many servers in -s. Maximum that can be handled" " by this tool is %d.\n", cMaxServers); goto Cleanup; }
pipArray->aipAddrs[cServers] = dwIpAddress; cServers++; i++;
if(i >= argc) break; }
pipArray->cAddrCount = cServers; *pdwDnsFlags = 0; *pfGlobalList = FALSE; fOptionS = TRUE; continue; }
if(!fOptionA && !_stricmp(argv[i], "-a")) { fOptionA = TRUE; *pfTryAllServers = TRUE; i++; continue; }
if(!fOptionP && !_stricmp(argv[i], "-p")) { i++;
if(i >= argc) { printf("Specify protocol for -p option. Either TCP, UDP or" " DEF (for default). Default means that UDP will be tried" " first followed by TCP if the reply was truncated.\n"); }
if(!_stricmp(argv[i], "tcp")) { *pfUdp = FALSE; *pdwDnsFlags = DNS_FLAGS_TCP_ONLY; } else if(!_stricmp(argv[i], "udp")) { *pfUdp = TRUE; *pdwDnsFlags = DNS_FLAGS_UDP_ONLY; } else if(!_stricmp(argv[i], "def")) { *pfUdp = TRUE; *pdwDnsFlags = DNS_FLAGS_NONE; } else { printf("Unrecognized protocol %s\n", argv[i]); goto Cleanup; }
i++; fOptionP = TRUE; continue; }
printf("Unrecognized option \"%s\".\n", argv[i]); printf("%s", g_szUsage); goto Cleanup; }
if(fOptionV) { if(fOptionS) { printf("Options -s and -v are incompatible\n"); goto Cleanup; }
if(fOptionP) { printf("Options -p and -v are incompatible\n"); goto Cleanup; } }
*ppDnsLogger = new CDnsLogToFile(); if(!*ppDnsLogger) { errprintf("Out of memory creating DNS logger.\n"); goto Cleanup; }
if(fOptionV) { hr = HrGetVsiConfig(pszHostName, dwVsid, pdwDnsFlags, pipArray, cMaxServers, pfGlobalList, pfUdp, g_pipBindings, g_cMaxBindings);
if(FAILED(hr)) { errprintf("Unable to get VSI configuration\n"); goto Cleanup; } }
if(pipArray->cAddrCount == 0) { errprintf("Either specify DNS servers using -s, or use -v.\n"); goto Cleanup; }
return TRUE;
Cleanup: if(*ppDnsLogger) { delete *ppDnsLogger; *ppDnsLogger = NULL; }
return FALSE; }
//-----------------------------------------------------------------------------
// Description:
//
// This function reads from the metabase and the Active directory (if
// Exchange is installed) to determine the DNS settings for the VSI that
// is to be simulated. Additionally, if the VSI is configured as a DMZ
// (i.e. with additional external DNS servers configured in the AD), we
// determine if the target server is an Exchange computer by searching
// for it in the directory.
//
// Arguments:
//
// IN LPSTR pszTargetServer - Name to resolve
// IN DWORD dwVsid - VSI to simulate
// OUT PDWORD pdwFlags - Flags to pass to Dns_QueryLib (from metabase)
// OUT PIP_ARRAY pipDnsServers - Returns DNS servers to query
// IN DWORD cMaxServers - Capacity of above buffer
// OUT BOOL *pfGlobalList - TRUE if default DNS servers are to be used
// OUT BOOL *pfUdp - Indicates protocol to connect with DNS
//
// Returns:
//
// S_OK - If the configuration was successfully read
// ERROR HRESULT if something failed. Diagnostic error messages are
// printed.
//-----------------------------------------------------------------------------
HRESULT HrGetVsiConfig( LPSTR pszTargetServer, DWORD dwVsid, PDWORD pdwFlags, PIP_ARRAY pipDnsServers, DWORD cMaxServers, BOOL *pfGlobalList, BOOL *pfUdp, PIP_ARRAY pipServerBindings, DWORD cMaxServerBindings) { HRESULT hr = E_FAIL; DWORD dwErr = ERROR_SUCCESS; BOOL fCoInitialized = FALSE; IMSAdminBase *pIMeta = NULL; METADATA_RECORD mdRecord; DWORD dwLength = 0; PBYTE pbMDData = (PBYTE) pdwFlags; PIP_ARRAY pipTempServers = NULL; BOOL fExternal = FALSE; WCHAR wszVirtualServer[256]; WCHAR wszBindings[256];
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(FAILED(hr)) { errprintf("Unable to initialize COM. The error HRESULT is 0x%08x\n", hr); goto Cleanup; }
fCoInitialized = TRUE;
// Check metabase configuration for DNS
hr = CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, (void **) &pIMeta);
if(FAILED(hr)) { errprintf("Failed to connect to IIS metabase. Make sure the IISADMIN" " service is installed and running and that you are running this" " tool with sufficient permissions. The failure HRESULT is" " 0x%08x\n", hr); goto Cleanup; }
ZeroMemory(&mdRecord, sizeof(mdRecord)); mdRecord.dwMDIdentifier = MD_SMTP_USE_TCP_DNS; mdRecord.dwMDAttributes = METADATA_INHERIT; mdRecord.dwMDUserType = IIS_MD_UT_FILE; mdRecord.dwMDDataType = DWORD_METADATA; mdRecord.dwMDDataLen = sizeof(DWORD); mdRecord.pbMDData = pbMDData;
hr = pIMeta->GetData(METADATA_MASTER_ROOT_HANDLE, L"/LM/SMTPSVC", &mdRecord, &dwLength);
if(hr == MD_ERROR_DATA_NOT_FOUND) { *pdwFlags = DNS_FLAGS_NONE; dbgprintf("The DNS flags are not explicitly set in the metabase, assuming " "default flags - 0x%08x\n", DNS_FLAGS_NONE); } else if(FAILED(hr)) { errprintf("Error reading key MD_SMTP_USE_TCP_DNS (%d) under /SMTPSVC in" " the metabase. The error HRESULT is 0x%08x - %s\n", MD_SMTP_USE_TCP_DNS, hr, MDErrorToString(hr)); goto Cleanup; } else { dbgprintf("These DNS flags are configured in the metabase");
if(*pdwFlags & DNS_FLAGS_UDP_ONLY) dbgprintf(" DNS_FLAGS_UDP_ONLY"); else if(*pdwFlags & DNS_FLAGS_TCP_ONLY) dbgprintf(" DNS_FLAGS_TCP_ONLY");
dbgprintf(" (0x%08x)\n", *pdwFlags); }
mdRecord.dwMDIdentifier = MD_SERVER_BINDINGS; mdRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES; mdRecord.dwMDUserType = IIS_MD_UT_SERVER; mdRecord.dwMDDataType = MULTISZ_METADATA; mdRecord.dwMDDataLen = sizeof(wszBindings); mdRecord.pbMDData = (PBYTE) wszBindings;
swprintf(wszVirtualServer, L"/LM/SMTPSVC/%d", dwVsid); hr = pIMeta->GetData(METADATA_MASTER_ROOT_HANDLE, wszVirtualServer, &mdRecord, &dwLength);
if(hr == MD_ERROR_DATA_NOT_FOUND) { errprintf("No VSI bindings in metabase. The key %S had no data.\n"); goto Cleanup; } else if(FAILED(hr)) { errprintf("Error reading /SMTPSVC/%d/ServerBindings from the metabase." " The error HRESULT is 0x%08x - %s\n", dwVsid, hr, MDErrorToString(hr)); goto Cleanup; } else { if(!GetServerBindings(wszBindings, pipServerBindings, cMaxServerBindings)) { goto Cleanup; }
dbgprintf("These are the local IP addresses (server-bindings)\n"); if(g_fDebug) PrintIPArray(pipServerBindings); }
// UDP is used (for the intial query) iff exclusive TCP_ONLY is not set
*pfUdp = ((*pdwFlags) != DNS_FLAGS_TCP_ONLY); dwErr = DsGetConfiguration(pszTargetServer, dwVsid, pipDnsServers, cMaxServers, &fExternal);
if(dwErr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(dwErr); goto Cleanup; }
//
// If external DNS servers were configured AND pszServer is an external
// target, then then we have all the information we need. Otherwise we
// need to supply the default DNS servers configured on this machine
//
if(pipDnsServers->cAddrCount > 0 && fExternal) goto Cleanup;
*pfGlobalList = TRUE; DnsGetDnsServerList(&pipTempServers); if(NULL == pipTempServers) { errprintf("Unable to get configured DNS servers for this computer\n"); goto Cleanup; }
if(pipTempServers->cAddrCount <= cMaxServers) { CopyMemory(pipDnsServers, pipTempServers, (1 + pipTempServers->cAddrCount) * sizeof(DWORD)); } else { errprintf("Too many DNS servers are configured on this computer for this" " tool to handle. The maximum number that can be handled by this" " tool is %d\n", cMaxServers); goto Cleanup; }
dbgprintf("Using the default DNS servers configured for this computer.\n"); if(g_fDebug) PrintIPArray(pipDnsServers);
Cleanup: if(pIMeta) pIMeta->Release();
if(pipTempServers) DnsApiFree(pipTempServers);
if(fCoInitialized) CoUninitialize();
return hr; }
BOOL GetServerBindings( WCHAR *pwszMultiSzBindings, PIP_ARRAY pipServerBindings, DWORD cMaxServerBindings) { int lErr = 0; DWORD cbOutBuffer = 0; WCHAR *pwszBinding = pwszMultiSzBindings; WCHAR *pwchEnd = pwszBinding; char szBinding[256]; int cchWritten = 0; SOCKET sock; SOCKADDR_IN *lpSockAddrIn = NULL; BYTE rgbBuffer[512]; LPSOCKET_ADDRESS_LIST pIpBuffer = (LPSOCKET_ADDRESS_LIST)rgbBuffer;
if(*pwszBinding == L':') { // Blank binding string
dbgprintf("Encountered blank server binding string for VSI\n");
sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == INVALID_SOCKET) { errprintf("Unable to create socket for WSAIoctl. The Win32 error" " is %d\n", WSAGetLastError()); return FALSE; }
lErr = WSAIoctl(sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, (PBYTE)(pIpBuffer), sizeof(rgbBuffer), &cbOutBuffer, NULL, NULL);
closesocket(sock); if(lErr != 0) { errprintf("Unable to issue WSAIoctl to get local IP addresses." " The Win32 error is %d\n", WSAGetLastError()); return FALSE; }
if(pIpBuffer->iAddressCount > (int)cMaxServerBindings) { errprintf("%d IP addresses were returned for the local machine" " by WSAIoctl. The maximum number that can be accomodated" " by this tool is %d\n", pIpBuffer->iAddressCount, cMaxServerBindings); return FALSE; }
for(pipServerBindings->cAddrCount = 0; (int)pipServerBindings->cAddrCount < pIpBuffer->iAddressCount; pipServerBindings->cAddrCount++) { lpSockAddrIn = (SOCKADDR_IN *) (pIpBuffer->Address[pipServerBindings->cAddrCount].lpSockaddr); CopyMemory( (PVOID)&(pipServerBindings->aipAddrs[pipServerBindings->cAddrCount]), (PVOID)&(lpSockAddrIn->sin_addr), sizeof(DWORD)); } return TRUE; }
while(TRUE) { pwchEnd = wcschr(pwszBinding, L':'); if(pwchEnd == NULL) { errprintf("Illegal format for server binding string. The server" " binding string should be in the format <ipaddress>:<port>." " Instead, the string is \"%S\"\n", pwszBinding); return FALSE; }
*pwchEnd = L'\0'; pwchEnd++;
if(pipServerBindings->cAddrCount > cMaxServerBindings) { errprintf("Too many server bindings for VSI. Maximum that can be" " handled by this tool is %d.\n", cMaxServerBindings); return FALSE; }
// Explicit IP in binding string
cchWritten = wcstombs(szBinding, pwszBinding, sizeof(szBinding)); if(cchWritten < 0) { errprintf("Failed to conversion of %S from widechar to ASCII\n", pwszBinding); return FALSE; }
pipServerBindings->aipAddrs[pipServerBindings->cAddrCount] = inet_addr(szBinding);
if(pipServerBindings->aipAddrs[pipServerBindings->cAddrCount] == INADDR_NONE) { errprintf("Illegal format for binding\n"); return FALSE; }
pipServerBindings->cAddrCount++;
// Skip to end of string
while(*pwchEnd != L'\0') pwchEnd++;
// 2 NULL terminations signal end of MULTI_SZ
pwchEnd++; if(*pwchEnd == L'\0') return TRUE;
pwszBinding = pwchEnd; }
return FALSE; }
//-----------------------------------------------------------------------------
// Description:
// Checks the regkey which is created when Exchange is installed, and uses
// it to determine if Exchange is installed.
// Arguments:
// OUT BOOL *pfBool - Set to TRUE if the regkey exists, FALSE otherwise.
// Returns:
// Win32 Error if something failed.
//-----------------------------------------------------------------------------
DWORD IsExchangeInstalled(BOOL *pfBool) { LONG lResult = 0; HKEY hkExchange; const char szExchange[] = "Software\\Microsoft\\Exchange";
lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szExchange, 0, KEY_READ, &hkExchange);
if(lResult == ERROR_SUCCESS) { dbgprintf("Microsoft Exchange is installed on this machine.\n"); RegCloseKey(hkExchange); *pfBool = TRUE; return ERROR_SUCCESS; } else if(lResult == ERROR_NOT_FOUND || lResult == ERROR_FILE_NOT_FOUND) { dbgprintf("Microsoft Exchange not installed on this machine\n"); *pfBool = FALSE; return ERROR_SUCCESS; }
errprintf("Error opening registry key HKLM\\%s, Win32 err - %d\n", szExchange, lResult); return lResult; }
//-----------------------------------------------------------------------------
// Description:
// Connects to a domain controller and reads configuration options for the
// VSI being simulated. In addition it checks if the target-server (which
// is to be resolved) is an Exchange computer that is a member of the
// Exchange Org or not.
//
// Arguments:
// IN char *pszTargetServer - Server to resolve
// IN DWORD dwVsid - VSI to simulate
// OUT PIP_ARRAY pipExternalDnsServers - External DNS servers on VSI if
// any are returned in this caller allocated buffer.
// IN DWORD cMaxServers - Capacity of above buffer.
// OUT PBOOL pfExternal - Set to TRUE when there are external DNS servers
// configured.
//
// Returns:
// ERROR_SUCCESS if configuration was read without problems.
// Win32 error code if there was a problem. Error messages are written to
// stdout for diagnostic purposes.
//-----------------------------------------------------------------------------
DWORD DsGetConfiguration( char *pszTargetServer, DWORD dwVsid, PIP_ARRAY pipExternalDnsServers, DWORD cMaxServers, PBOOL pfExternal) { DWORD dwErr = ERROR_NOT_FOUND; BOOL fRet = FALSE; PLDAP pldap = NULL; PLDAPMessage pldapMsgContexts = NULL; PLDAPMessage pldapMsgSmtpVsi = NULL; PLDAPMessage pEntry = 0; char szLocalComputerName[256]; DWORD cchLocalComputerName = sizeof(szLocalComputerName);
// Context attributes to read at the base level - so we know where to base
// the rest of our searches from
char *rgszContextAttrs[] = { "configurationNamingContext", NULL };
// Attributes we are interested in for the VSI object
char *rgszSmtpVsiAttrs[] = { "msExchSmtpExternalDNSServers", NULL };
// LDAP result ptrs to store the results of the search
char **rgszConfigurationNamingContext = NULL; char **rgszSmtpVsiExternalDNSServers = NULL; char *pszExchangeServerDN = NULL; char szSmtpVsiDN[256];
char *pchSeparator = NULL; char *pszIPServer = NULL; char *pszStringEnd = NULL;
int i = 0; int cValues = 0; int cch = 0; BOOL fInstalled = FALSE; BOOL fFound = FALSE;
*pfExternal = FALSE; pipExternalDnsServers->cAddrCount = 0;
dwErr = IsExchangeInstalled(&fInstalled); if(ERROR_SUCCESS != dwErr || !fInstalled) return dwErr;
dbgprintf("Querying domain controller for configuration.\n");
pldap = BindToDC(); if(!pldap) { dwErr = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; }
dwErr = ldap_search_s( pldap, // ldap binding
"", // base DN
LDAP_SCOPE_BASE, // scope
"(objectClass=*)", // filter
rgszContextAttrs, // attributes we want to read
FALSE, // FALSE means read value
&pldapMsgContexts); // return results here
if(dwErr != LDAP_SUCCESS) { errprintf("Error encountered during LDAP search. LDAP err - %d.\n", dwErr); goto Cleanup; }
pEntry = ldap_first_entry(pldap, pldapMsgContexts); if(pEntry == NULL) { dwErr = ERROR_INVALID_DATA; errprintf("Base object not found on domain controller!\n"); goto Cleanup; }
rgszConfigurationNamingContext = ldap_get_values(pldap, pEntry, rgszContextAttrs[0]); if(rgszConfigurationNamingContext == NULL) { dwErr = ERROR_INVALID_DATA; errprintf("configurationNamingContext attribute not set on base object of" " domain controller.\n"); goto Cleanup; } if((cValues = ldap_count_values(rgszConfigurationNamingContext)) == 1) { dbgprintf("configurationNamingContext is \"%s\"\n", rgszConfigurationNamingContext[0]); dbgprintf("This will be used as the Base DN for all directory searches.\n"); } else { dwErr = ERROR_INVALID_DATA; errprintf("Unexpected error reading configurationNamingContext. Expected" " a single string value, instead there were %d values set\n", cValues); goto Cleanup; }
// See if the target server is an Exchange Server in the Org
dbgprintf("Checking if the target server %s is an Exchange server\n", pszTargetServer);
dwErr = DsFindExchangeServer(pldap, rgszConfigurationNamingContext[0], pszTargetServer, NULL, &fFound);
//
// If it is in the Org, nothing more to do - we just use the default DNS
// servers configured for the box to do the resolution
//
if(dwErr == LDAP_SUCCESS && fFound) { msgprintf("%s is in the Exchange Org. Global DNS servers will be used.\n", pszTargetServer);
*pfExternal = FALSE; goto Cleanup; }
//
// On the other hand, if the target is not an Exchange computer in the org,
// we need to lookup the VSI object on the local computer and check if it
// is configured with external DNS servers
//
*pfExternal = TRUE; msgprintf("%s is an external server (not in the Exchange Org).\n", pszTargetServer);
cchLocalComputerName = sizeof(szLocalComputerName); fRet = GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, szLocalComputerName, &cchLocalComputerName);
if(!fRet) { dwErr = GetLastError(); errprintf("Unable to retrieve local computer DNS name, Win32 err - %d.\n", dwErr); goto Cleanup; }
dbgprintf("Checking on DC if the VSI being simulated is configured with" " external DNS servers.\n");
// Find the Exchange Server container object for the local computer
dwErr = DsFindExchangeServer(pldap, rgszConfigurationNamingContext[0], szLocalComputerName, &pszExchangeServerDN, &fFound);
if(!fFound || !pszExchangeServerDN) { errprintf("This server \"%s\" was not found in the DS. Make sure you are" " running this tool on an Exchange server in the Organization\n"); dwErr = ERROR_INVALID_DATA; goto Cleanup; } // Construct the DN of the VSI for the server we found. This is fixed relative
// to the Exchange Server DN
cch = _snprintf(szSmtpVsiDN, sizeof(szSmtpVsiDN), "CN=%d,CN=SMTP,CN=Protocols,%s", dwVsid, pszExchangeServerDN);
if(cch < 0) { errprintf("Unable to construct SMTP virtual server's DN. The DN is too" " long for this tool to handle\n"); dwErr = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; }
dbgprintf("DN for the virtual server is \"%s\"\n", szSmtpVsiDN);
// Get the DNS servers attribute for the VSI
dwErr = ldap_search_s( pldap, // ldap binding
szSmtpVsiDN, // base DN
LDAP_SCOPE_SUBTREE, // scope
"(objectClass=*)", // filter
NULL, //rgszSmtpVsiAttrs, // attributes we want to read
FALSE, // FALSE means read value
&pldapMsgSmtpVsi); // return results here
if(dwErr == LDAP_NO_SUCH_OBJECT) { errprintf("No object exists for SMTP virtual server #%d on GC for %s\n", dwVsid, szLocalComputerName); goto Cleanup; }
if(dwErr != LDAP_SUCCESS) { errprintf("Search for SMTP virtual server object failed, LDAP err - %d\n", dwErr); goto Cleanup; }
pEntry = ldap_first_entry(pldap, pldapMsgSmtpVsi); if(pEntry == NULL) { errprintf("SMTP virtual server #%d for server %s was not found in the DS\n", dwVsid, szLocalComputerName); dwErr = ERROR_INVALID_DATA; goto Cleanup; }
rgszSmtpVsiExternalDNSServers = ldap_get_values(pldap, pEntry, rgszSmtpVsiAttrs[0]);
if(rgszSmtpVsiExternalDNSServers == NULL) { dbgprintf("The attribute msExchSmtpExternalDNSServers was not found on" " the SMTP virtual server being simulated.\n");
msgprintf("No external DNS servers on VSI. Using global DNS servers.\n");
dwErr = ERROR_SUCCESS; goto Cleanup; } // This is a string of comma separated IP addresses
if((cValues != ldap_count_values(rgszSmtpVsiExternalDNSServers)) == 1) { errprintf("Unexpected error reading msExchSmtpExternalDNSServers," " cValues - %d\n", cValues); dwErr = ERROR_INVALID_DATA; goto Cleanup; }
dbgprintf("msExchSmtpExternalDNSServers: %s\n", rgszSmtpVsiExternalDNSServers[0]);
pszIPServer = rgszSmtpVsiExternalDNSServers[0]; pszStringEnd = rgszSmtpVsiExternalDNSServers[0] + lstrlen(rgszSmtpVsiExternalDNSServers[0]);
i = 0; pipExternalDnsServers->cAddrCount = 0;
while(pszIPServer < pszStringEnd && *pszIPServer != '\0') { pchSeparator = strchr(pszIPServer, ',');
if(pchSeparator != NULL) // last IP address
*pchSeparator = '\0';
if(i > (int)cMaxServers) { errprintf("Too many DNS servers configured in registry. The maximum" " that this tool can handle is %d\n", cMaxServers); dwErr = ERROR_INVALID_DATA; goto Cleanup; }
pipExternalDnsServers->aipAddrs[i] = inet_addr(pszIPServer); if(pipExternalDnsServers->aipAddrs[i] == INADDR_NONE) { errprintf("The attribute msExchSmtpExternalDNSServers is in an" " invalid format. Expected a comma separated list of IP" " addresses in dotted decimal notation.\n"); goto Cleanup; }
pipExternalDnsServers->cAddrCount++; if(pchSeparator == NULL) // last IP address
break;
// There was a comma, advance to just after it
pszIPServer = pchSeparator + 1; i++; }
if(pipExternalDnsServers->cAddrCount == 0) { errprintf("No IP addresses could be constructed from" " msExchSmtpExternalDNSServers\n"); } else { msgprintf("Using external DNS servers:\n");
SetMsgColor(); PrintIPArray(pipExternalDnsServers); SetNormalColor(); }
dwErr = ERROR_SUCCESS;
Cleanup: if(pszExchangeServerDN) delete [] pszExchangeServerDN;
if(rgszSmtpVsiExternalDNSServers) ldap_value_free(rgszSmtpVsiExternalDNSServers);
if(pldapMsgSmtpVsi) ldap_msgfree(pldapMsgSmtpVsi);
if(rgszConfigurationNamingContext) ldap_value_free(rgszConfigurationNamingContext);
if(pldapMsgContexts) ldap_msgfree(pldapMsgContexts);
if(pldap) ldap_unbind(pldap);
return dwErr; }
//-----------------------------------------------------------------------------
// Description:
// Locates a domain controller for the local machine and opens an LDAP
// connection to it.
// Arguments:
// None.
// Returns:
// LDAP* which can be used for LDAP queries
//-----------------------------------------------------------------------------
PLDAP BindToDC() { DWORD dwErr = LDAP_SUCCESS; PDOMAIN_CONTROLLER_INFO pdci = NULL; char *pszDomainController = NULL; PLDAP pldap = NULL;
dwErr = DsGetDcName( NULL, // Computer name
NULL, // Domain name
NULL, // Domain GUID,
NULL, // Sitename
DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME, &pdci);
if(dwErr != ERROR_SUCCESS) { errprintf("Error getting domain controller FQDN, Win32 err - %d\n", dwErr); goto Cleanup; }
pszDomainController = pdci->DomainControllerName; while(*pszDomainController == '\\') pszDomainController++;
dbgprintf("The domain controller server which will be used for reading" " configuration data is %s\n", pszDomainController);
dbgprintf("Connecting to %s over port %d\n", pszDomainController, LDAP_PORT);
pldap = ldap_open(pszDomainController, LDAP_PORT); if(pldap == NULL) { dwErr = LdapGetLastError(); errprintf("Unable to initialize an LDAP session to the domain controller" " server %s, LDAP err - %d\n", pszDomainController, dwErr); goto Cleanup; }
dwErr = ldap_bind_s(pldap, NULL, NULL, LDAP_AUTH_SSPI); if(dwErr != LDAP_SUCCESS) { errprintf("Unable to authenticate to the domain controller server %s. Make" " sure you are running this tool with appropriate credentials," " LDAP err - %d\n", pszDomainController, dwErr); goto Cleanup; }
Cleanup: if(pdci) NetApiBufferFree((PVOID)pdci);
return pldap; }
//-----------------------------------------------------------------------------
// Description:
// Checks if a given FQDN is the name of an Exchange server in the org.
//
// Arguments:
// IN PLDAP pldap - Open LDAP session to domain controller.
// IN LPSTR szBaseDN - Base DN to search from
// IN LPSTR szServerName - Servername to search for
// OUT LPSTR *ppszServerDN - If a non-NULL char** is passed in, the DN
// of the server (if found) is returned to this. The buffer must be
// freed using delete [].
// OUT BOOL *pfFound - Set to TRUE if the server is found.
//
// Returns:
// ERROR_SUCCESS if configuration was read without problems.
// Win32 error code if there was a problem. Error messages are written to
// stdout for diagnostic purposes.
//-----------------------------------------------------------------------------
DWORD DsFindExchangeServer( PLDAP pldap, LPSTR szBaseDN, LPSTR szServerName, LPSTR *ppszServerDN, BOOL *pfFound) { int i = 0; int cch = 0; int cValues = 0; DWORD dwErr = LDAP_SUCCESS; PLDAPMessage pldapMsgExchangeServer = NULL; PLDAPMessage pEntry = NULL; char *rgszExchangeServerAttrs[] = { "distinguishedName", "networkAddress", NULL }; char **rgszExchangeServerDN = NULL; char **rgszExchangeServerNetworkName = NULL; char szExchangeServerFilter[256]; char szSearchNetworkName[256];
//
// The Exchange Server object has a multivalued attribute, "networkAddress"
// that enumerates all the various names by which the Exchange Server is
// identified such as NetBIOS, DNS etc. We are only interested in the fully
// qualified domain name. This is set on the attribute as the string
// "ncacn_ip_tcp:" prefixed to the server's FQDN.
//
szExchangeServerFilter[sizeof(szExchangeServerFilter) - 1] = '\0'; cch = _snprintf( szExchangeServerFilter, sizeof(szExchangeServerFilter) - 1, "(&(networkAddress=ncacn_ip_tcp:%s)(objectClass=msExchExchangeServer))", szServerName);
if(cch < 0) { errprintf("The servername %s is too long for this tool to handle.\n", szServerName); dwErr = ERROR_INVALID_DATA; goto Cleanup; }
dbgprintf("Searching for an Exchange Server object for %s on the domain" " controller\n", szServerName);
dwErr = ldap_search_s( pldap, szBaseDN, LDAP_SCOPE_SUBTREE, szExchangeServerFilter, rgszExchangeServerAttrs, FALSE, &pldapMsgExchangeServer);
if(dwErr == LDAP_NO_SUCH_OBJECT) { dbgprintf("No Exchange Server object found for %s on domain controller," " LDAP err - LDAP_NO_SUCH_OBJECT\n", szServerName); dwErr = ERROR_SUCCESS; goto Cleanup; }
if(dwErr != LDAP_SUCCESS) { errprintf("LDAP search failed, LDAP err %d\n", dwErr); goto Cleanup; } pEntry = ldap_first_entry(pldap, pldapMsgExchangeServer); if(pEntry == NULL) { dbgprintf("No Exchange Server object found for %s on domain controller,\n", szServerName); dwErr = ERROR_SUCCESS; goto Cleanup; }
dbgprintf("LDAP search returned some results, examining them.\n");
// Loop through the Exchange server objects
while(pEntry) {
dbgprintf("Examining next object for attributes we are interested in.\n");
// Get the Exchange server-DN
rgszExchangeServerDN = ldap_get_values( pldap, pEntry, rgszExchangeServerAttrs[0]);
if(rgszExchangeServerDN == NULL) { errprintf("Unexpected error reading the distinguishedName attribute" " on the Exchange Server object. The attribute was not set" " on the object. This is a required attribute.\n"); dwErr = ERROR_INVALID_DATA; goto Cleanup; } else if((cValues = ldap_count_values(rgszExchangeServerDN)) != 1) { errprintf("Unexpected error reading the distinguishedName attribute" " on the Exchange Server object. The attribute is supposed to" " have a single string value, instead %d values were" " returned.\n", cValues); dwErr = ERROR_INVALID_DATA; goto Cleanup; } else { dbgprintf("Successfully read the distinguishedName attribute on the" " Exchange Server object. The value of the attribute is %s\n", rgszExchangeServerDN[0]); }
// Get the Exchange server network name
rgszExchangeServerNetworkName = ldap_get_values( pldap, pEntry, rgszExchangeServerAttrs[1]);
if(!rgszExchangeServerNetworkName) { errprintf("The networkName attribute was not set on the Exchange" " Server object. This is a required attribute. The DN of the" " problematic object is %s\n", rgszExchangeServerDN[0]); dwErr = ERROR_INVALID_DATA; goto Cleanup; }
// This is a multi-valued string attribute
cch = _snprintf(szSearchNetworkName, sizeof(szSearchNetworkName), "ncacn_ip_tcp:%s", szServerName);
if(cch < 0) { errprintf("Exchange server name too long for this tool to handle\n"); dwErr = ERROR_INVALID_DATA; goto Cleanup; }
cValues = ldap_count_values(rgszExchangeServerNetworkName); dbgprintf("The search returned the following %d values for the" " networkName attribute for the Exchange Server object for %s\n", cValues, szServerName);
dbgprintf("Attempting to match the TCP/IP networkName of the Exchange" " Server object returned from the domain controller against the FQDN" " we are searching for\n");
for(i = 0; i < cValues; i++) { dbgprintf("%d> networkName: %s", i, rgszExchangeServerNetworkName[i]); if(!_stricmp(rgszExchangeServerNetworkName[i], szSearchNetworkName)) { // This is an internal server
dbgprintf("...match succeeded\n"); dbgprintf("%s is an Exchange Server in the Org.\n", szServerName);
*pfFound = TRUE;
if(ppszServerDN != NULL) { *ppszServerDN = new char[lstrlen(rgszExchangeServerDN[0]) + 1]; if(*ppszServerDN == NULL) { errprintf("Out of memory allocating space for Exchange" " Server object DN\n"); dwErr = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; }
lstrcpy(*ppszServerDN, rgszExchangeServerDN[0]); } dwErr = ERROR_SUCCESS; goto Cleanup; } dbgprintf("...match failed\n"); }
dbgprintf("No networkName on this object matched the server we are " " searching for. Checking for more objects returned by search.\n");
pEntry = ldap_next_entry(pldap, pEntry); }
dbgprintf("Done examining all objects returned by search. No match found.\n"); dwErr = ERROR_SUCCESS;
Cleanup: if(rgszExchangeServerNetworkName) ldap_value_free(rgszExchangeServerNetworkName);
if(rgszExchangeServerDN) ldap_value_free(rgszExchangeServerDN);
if(pldapMsgExchangeServer) ldap_msgfree(pldapMsgExchangeServer);
return dwErr; }
//-----------------------------------------------------------------------------
// Description:
// Destructor for async DNS class. It merely signals when the async
// resolve has finished. Since this object is deleted by completing ATQ
// threads on success, we need an explicit way of telling the caller
// when the resolve has finished.
//-----------------------------------------------------------------------------
CAsyncTestDns::~CAsyncTestDns() { if(m_hCompletion != INVALID_HANDLE_VALUE) { SetEvent(m_hCompletion); if(m_fMxLoopBack) SetProgramStatus(DNSDIAG_LOOPBACK); } }
//-----------------------------------------------------------------------------
// Description:
// Virtual method that is called by the async DNS base class when a query
// needs to be retried. This function creates a new DNS object and spins
// off a repeat of the previous async query. The difference is only that
// the DNS serverlist has probably undergone some state changes with some
// servers being marked down or fUdp is different from the original query.
//
// Arguments:
// IN BOOL fUdp - What protocol to use for the retry query
//
// Returns:
// TRUE on success
// FALSE if something failed. Diagnostic messages are printed.
//-----------------------------------------------------------------------------
BOOL CAsyncTestDns::RetryAsyncDnsQuery(BOOL fUdp) { DWORD dwStatus = ERROR_SUCCESS; CAsyncTestDns *pAsyncRetryDns = NULL;
if(GetDnsList()->GetUpServerCount() == 0) { errprintf("No working DNS servers to retry query with.\n"); return FALSE; }
dbgprintf("There are %d DNS servers marked as working. Trying the next" " one\n", GetDnsList()->GetUpServerCount());
pAsyncRetryDns = new CAsyncTestDns(m_FQDNToDrop, m_fGlobalList, m_hCompletion);
if(!pAsyncRetryDns) { errprintf("Unable to create new query. Out of memory.\n"); return FALSE; }
dwStatus = pAsyncRetryDns->Dns_QueryLib( m_HostName, DNS_TYPE_MX, m_dwFlags, fUdp, GetDnsList(), m_fGlobalList);
if(dwStatus == ERROR_SUCCESS) { // New query object will flag completion event
m_hCompletion = INVALID_HANDLE_VALUE; return TRUE; }
errprintf("DNS query failed. The Win32 error is %d.\n", dwStatus); delete pAsyncRetryDns; return FALSE; }
//-----------------------------------------------------------------------------
// Description:
// This is a virtual function declared in the base CAsyncMxDns object.
// When the MX resolution is finished, this virtual function is invoked
// so that the user can do custom app-specific processing. In the case
// of SMTP this consists of spinning off an async connect to the IP
// addresses reported in m_AuxList. In this diagnostic application
// we merely display the results, and if results were not found (an
// error status is passed in), then we print the error message.
//
// In this app, we also set m_hCompletion to signal that the resolve
// is done. The main thread waiting for us in WaitForQueryCompletion
// will then exit.
//
// Arguments:
// IN DWORD status - DNS error code from resolve
//
// Notes:
// Results are available in m_AuxList
//-----------------------------------------------------------------------------
void CAsyncTestDns::HandleCompletedData(DNS_STATUS status) { PLIST_ENTRY pListHead = NULL; PLIST_ENTRY pListTail = NULL; PLIST_ENTRY pListCurrent = NULL; LPSTR pszIpAddr = NULL; DWORD i = 0; PMXIPLIST_ENTRY pMxEntry = NULL; BOOL fFoundIpAddresses = FALSE;
if(status == ERROR_NOT_FOUND) { SetProgramStatus(DNSDIAG_NON_EXISTENT); goto Exit; } else if(!m_AuxList || m_AuxList->NumRecords == 0 || m_AuxList->DnsArray[0] == NULL) { SetProgramStatus(DNSDIAG_NOT_FOUND); errprintf("The target server could not be resolved to IP addresses!\n");
msgprintf("If the VSI/domain is configured with a fallback" " smarthost delivery will be attempted to that smarthost.\n");
goto Exit; }
msgprintf("\nTarget hostnames and IP addresses\n"); msgprintf("---------------------------------\n"); for(i = 0; i < m_AuxList->NumRecords && m_AuxList->DnsArray[i] != NULL; i++) { pListTail = &(m_AuxList->DnsArray[i]->IpListHead); pListHead = m_AuxList->DnsArray[i]->IpListHead.Flink; pListCurrent = pListHead; msgprintf("HostName: \"%s\"\n", m_AuxList->DnsArray[i]->DnsName); if(pListCurrent == pListTail) errprintf("\tNo IP addresses for this name!\n");
while(pListCurrent != pListTail) { // Atleast 1 IP address was found
fFoundIpAddresses = TRUE;
pMxEntry = CONTAINING_RECORD(pListCurrent, MXIPLIST_ENTRY, ListEntry); pszIpAddr = iptostring(pMxEntry->IpAddress); if(pszIpAddr == NULL) { errprintf("\tUnexpected error. Failed to read IP address, going on to next.\n"); pListCurrent = pListCurrent->Flink; continue; }
msgprintf("\t%s\n", pszIpAddr); pListCurrent = pListCurrent->Flink; }; } if(fFoundIpAddresses) SetProgramStatus(DNSDIAG_RESOLVED); else SetProgramStatus(DNSDIAG_NOT_FOUND);
Exit: return; }
//-----------------------------------------------------------------------------
// Description:
// If the -v option is being used to simulate a VSI, this virtual function
// checks if dwIp is one of the IP addresses in the VSI bindings for the
// VS being simulated. g_pipBindings is initialized at startup of this
// app from the metabase.
// Arguments:
// IN DWORD dwIp - IP address to check
// Returns:
// TRUE is dwIp is a local-binding
// FALSE if not
//-----------------------------------------------------------------------------
BOOL CAsyncTestDns::IsAddressMine(DWORD dwIp) { DWORD i = 0;
if(g_pipBindings->cAddrCount == 0) return FALSE;
for(i = 0; i < g_pipBindings->cAddrCount; i++) { if(g_pipBindings->aipAddrs[i] == dwIp) return TRUE; }
return FALSE; }
//-----------------------------------------------------------------------------
// Description:
// Various output functions. These print informational, debugging and error
// messages in various colors depending on the current "mode" as set in
// the global variable g_fDebug.
//
// The CDnsLogToFile is instantiated is a global variable. The DNS library
// checks to see if there is a non-NULL CDnsLogToFile* and if it is
// present the messages are directed to this object.
//-----------------------------------------------------------------------------
void CDnsLogToFile::DnsPrintfMsg(char *szFormat, ...) { va_list argptr;
SetMsgColor(); va_start(argptr, szFormat); vprintf(szFormat, argptr); va_end(argptr); SetNormalColor(); }
void CDnsLogToFile::DnsPrintfErr(char *szFormat, ...) { va_list argptr;
SetErrColor(); va_start(argptr, szFormat); vprintf(szFormat, argptr); va_end(argptr); SetNormalColor(); }
void CDnsLogToFile::DnsPrintfDbg(char *szFormat, ...) { va_list argptr;
if(!g_fDebug) return;
va_start(argptr, szFormat); vprintf(szFormat, argptr); va_end(argptr); }
void CDnsLogToFile::DnsLogAsyncQuery( char *pszQuestionName, WORD wQuestionType, DWORD dwFlags, BOOL fUdp, CDnsServerList *pDnsServerList) { char szFlags[32];
GetSmtpFlags(dwFlags, szFlags, sizeof(szFlags));
SetMsgColor(); printf("Created Async Query:\n"); printf("--------------------\n"); printf("\tQNAME = %s\n", pszQuestionName); printf("\tType = %s (0x%x)\n", QueryType(wQuestionType), wQuestionType); printf("\tFlags = %s (0x%x)\n", szFlags, dwFlags); printf("\tProtocol = %s\n", fUdp ? "UDP" : "TCP"); printf("\tDNS Servers: (DNS cache will not be used)\n"); DnsLogServerList(pDnsServerList); printf("\n"); SetNormalColor(); }
void CDnsLogToFile::DnsLogApiQuery( char *pszQuestionName, WORD wQuestionType, DWORD dwApiFlags, BOOL fGlobal, PIP_ARRAY pipServers) { char szFlags[32];
GetDnsFlags(dwApiFlags, szFlags, sizeof(szFlags));
SetMsgColor(); printf("Querying via DNSAPI:\n"); printf("--------------------\n"); printf("\tQNAME = %s\n", pszQuestionName); printf("\tType = %s (0x%x)\n", QueryType(wQuestionType), wQuestionType); printf("\tFlags = %s, (0x%x)\n", szFlags, dwApiFlags); printf("\tProtocol = Default UDP, TCP on truncation\n"); printf("\tServers: "); if(fGlobal) { printf("(DNS cache will be used)\n"); } else { printf("(DNS cache will not be used)\n"); } if(pipServers) PrintIPArray(pipServers, "\t"); else printf("\tDefault DNS servers on box.\n");
printf("\n"); if(fGlobal == FALSE) SetNormalColor(); }
void CDnsLogToFile::DnsLogResponse( DWORD dwStatus, PDNS_RECORD pDnsRecordList, PBYTE pbMsg, DWORD wLength) { PDNS_RECORD pDnsRecord = pDnsRecordList;
SetMsgColor(); printf("Received DNS Response:\n"); printf("----------------------\n"); switch(dwStatus) { case ERROR_SUCCESS: printf("\tError: %d\n", dwStatus); printf("\tDescription: Success\n"); break;
case DNS_INFO_NO_RECORDS: printf("\tError: %d\n", dwStatus); printf("\tDescription: No records could be located for this name\n"); break;
case DNS_ERROR_RCODE_NAME_ERROR: printf("\tError: %d\n", dwStatus); printf("\tDescription: No records exist for this name.\n"); break;
default: printf("\tError: %d\n", dwStatus); printf("\tDescription: Not available.\n"); break; }
if(pDnsRecord) { printf("\tThese records were received:\n"); PrintRecordList(pDnsRecord, "\t"); printf("\n"); }
SetNormalColor(); }
void CDnsLogToFile::DnsLogServerList(CDnsServerList *pDnsServerList) { LPSTR pszAddress = NULL; PIP_ARRAY pipArray = NULL;
if(!pDnsServerList->CopyList(&pipArray)) { printf("Error, out of memory printing serverlist\n"); return; }
for(DWORD i = 0; i < pDnsServerList->GetCount(); i++) { pszAddress = iptostring(pipArray->aipAddrs[i]); printf("\t%s\n", pszAddress); }
delete pipArray; }
void PrintRecordList(PDNS_RECORD pDnsRecordList, char *pszPrefix) { PDNS_RECORD pDnsRecord = pDnsRecordList;
while(pDnsRecord) { PrintRecord(pDnsRecord, pszPrefix); pDnsRecord = pDnsRecord->pNext; } }
void PrintRecord(PDNS_RECORD pDnsRecord, char *pszPrefix) { LPSTR pszAddress = NULL;
switch(pDnsRecord->wType) { case DNS_TYPE_MX: printf( "%s%s MX %d %s\n", pszPrefix, pDnsRecord->nameOwner, pDnsRecord->Data.MX.wPreference, pDnsRecord->Data.MX.nameExchange); break;
case DNS_TYPE_CNAME: printf( "%s%s CNAME %s\n", pszPrefix, pDnsRecord->nameOwner, pDnsRecord->Data.CNAME.nameHost); break;
case DNS_TYPE_A: pszAddress = iptostring(pDnsRecord->Data.A.ipAddress); printf( "%s%s A %s\n", pszPrefix, pDnsRecord->nameOwner, pszAddress); break;
case DNS_TYPE_SOA: printf("%s%s SOA (SOA records are not used by us)\n", pszPrefix, pDnsRecord->nameOwner); break;
default: printf("%s%s (Record type = %d) Unknown record type\n", pszPrefix, pDnsRecord->nameOwner, pDnsRecord->wType); break; } }
void msgprintf(char *szFormat, ...) { va_list argptr;
SetMsgColor(); va_start(argptr, szFormat); vprintf(szFormat, argptr); va_end(argptr); SetNormalColor(); }
void errprintf(char *szFormat, ...) { va_list argptr;
SetErrColor(); va_start(argptr, szFormat); vprintf(szFormat, argptr); va_end(argptr); SetNormalColor(); }
void dbgprintf(char *szFormat, ...) { va_list argptr;
if(!g_fDebug) return;
va_start(argptr, szFormat); vprintf(szFormat, argptr); va_end(argptr); }
void SetMsgColor() { if(g_fDebug) { SetConsoleTextAttribute(g_hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY); } }
void SetErrColor() { if(g_fDebug) { SetConsoleTextAttribute(g_hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY); } }
void SetNormalColor() { if(g_fDebug) { SetConsoleTextAttribute(g_hConsole, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); } }
|