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.
2040 lines
64 KiB
2040 lines
64 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
}
|
|
}
|