Leaked source code of windows server 2003
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

  1. //-----------------------------------------------------------------------------
  2. //
  3. // Copyright (c) 2002 Microsoft Corporation
  4. //
  5. // Abstract:
  6. //
  7. // Source file for DNS diagnostic tool. Links into dnslib.lib which has
  8. // the SMTP DNS resolution logic and calls into DNS resolution functions
  9. // while printing diagnostic messages.
  10. //
  11. // Author:
  12. //
  13. // gpulla
  14. //
  15. //-----------------------------------------------------------------------------
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include "dnsdiag.h"
  19. int g_nProgramStatus = DNSDIAG_FAILURE;
  20. CDnsLogger *g_pDnsLogger = NULL;
  21. HANDLE g_hCompletion = NULL;
  22. BOOL g_fDebug = FALSE;
  23. DWORD g_cDnsObjects = 0;
  24. HANDLE g_hConsole = INVALID_HANDLE_VALUE;
  25. DWORD g_rgBindings[32];
  26. PIP_ARRAY g_pipBindings = (PIP_ARRAY)g_rgBindings;
  27. DWORD g_cMaxBindings = sizeof(g_rgBindings)/sizeof(DWORD) - 1;
  28. char g_szUsage[] =
  29. "Summary:\n"
  30. "\n"
  31. "This tool is used to troubleshoot problems with DNS resolution for SMTP. It\n"
  32. "simulates SMTPSVC's internal code-path and prints diagnostic messages that\n"
  33. "indicate how the resolution is proceeding. The tool must be run on the machine\n"
  34. "where the DNS problems are occurring.\n"
  35. "\n"
  36. "Program return codes:\n"
  37. " These are set as the ERRORLEVEL for usage in batch files.\n"
  38. "\n"
  39. " 0 - The name was resolved successfully to one or more IP addresses.\n"
  40. " 1 - The name could not be resolved due to an unspecified error.\n"
  41. " 2 - The name does not exist. The error was returned by an authoritative DNS\n"
  42. " server for the domain.\n"
  43. " 3 - The name could not be located in DNS. This is not an error from the\n"
  44. " authoritative DNS server.\n"
  45. " 4 - A loopback was detected.\n"
  46. "\n"
  47. "Usage:\n"
  48. "\n"
  49. "dnsdiag <hostname> [-d] [options]\n"
  50. "\n"
  51. "<hostname>\n"
  52. " Hostname to query for. Note that this may not be the same as the display\n"
  53. " -name of the queue (in ESM, if Exchange is installed). It should be the\n"
  54. " fully-qualified domain name of the target for the queue seeing the DNS\n"
  55. " errors\n"
  56. "\n"
  57. "-d\n"
  58. " This is a special option to run in debug/verbose mode. There is a lot of\n"
  59. " output, and the most important messages (the ones that normally appear when\n"
  60. " this mode is not turned on) are highlighted in a different color.\n"
  61. "\n"
  62. "Options are:\n"
  63. "-v <VSID>\n"
  64. " If running on an Exchange DMZ machine, you can specify the VSI# of the\n"
  65. " VSI to simulate DNS for that SMTP VS. Then this tool will read the\n"
  66. " external DNS serverlist for that VSI and query that serverlist for\n"
  67. " <hostname> when <hostname> is an \"external\" host. If <hostname> is the\n"
  68. " name of an Exchange computer, the query is generated against the default\n"
  69. " DNS servers for the local computer.\n"
  70. "\n"
  71. "-s <serverlist>\n"
  72. " DNS servers to use, if you want to specify a specific set of servers.\n"
  73. "\n"
  74. " If this option is not specified, the default DNS servers on the local\n"
  75. " computer are used as specified by -v.\n"
  76. "\n"
  77. " This option is incompatible with -v.\n"
  78. "\n"
  79. "-p <protocol>\n"
  80. " TCP, UDP or DEF. TCP generates a TCP only query. UDP generates a UDP only\n"
  81. " query. DEF generates a default query that will initially query a server with\n"
  82. " UDP, and then if that query results in a truncated reply, it will be retried\n"
  83. " with TCP.\n"
  84. "\n"
  85. " If this option is not specified the protocol configured in the metabase for\n"
  86. " /smtpsvc/UseTcpDns is used.\n"
  87. "\n"
  88. " This option is incompatible with the -v option.\n"
  89. "\n"
  90. "-a\n"
  91. " All the DNS servers obtained (either through the registry, active directory,\n"
  92. " or -s option) are tried in sequence and the results of querying each are\n"
  93. " displayed.\n";
  94. //-----------------------------------------------------------------------------
  95. // Description:
  96. // DNS diagnostic utility. See above for usage.
  97. //-----------------------------------------------------------------------------
  98. int __cdecl main(int argc, char *argv[])
  99. {
  100. CAsyncTestDns *pAsyncTestDns = NULL;
  101. CSimpleDnsServerList *pDnsSimpleList = NULL;
  102. CDnsLogToFile *pDnsLogToFile = NULL;
  103. char szMyHostName[MAX_PATH + 1];
  104. char szHostName[MAX_PATH + 1];
  105. BOOL fAtqInitialized = FALSE;
  106. BOOL fIISRTLInitialized = FALSE;
  107. BOOL fWSAInitialized = FALSE;
  108. WORD wVersion = MAKEWORD(2, 2);
  109. WSADATA wsaData;
  110. BOOL fRet = TRUE;
  111. int nRet = 0;
  112. DWORD dwStatus = ERROR_SUCCESS;
  113. DWORD dwDnsFlags = 0;
  114. BOOL fUdp = TRUE;
  115. BOOL fGlobalList = FALSE;
  116. BOOL fTryAllDnsServers = FALSE;
  117. DWORD rgDnsServers[16];
  118. PIP_ARRAY pipArray = (PIP_ARRAY)rgDnsServers;
  119. DWORD cMaxServers = sizeof(rgDnsServers)/sizeof(DWORD) - 1;
  120. DWORD rgSingleServer[2];
  121. PIP_ARRAY pipSingleServer = (PIP_ARRAY)rgSingleServer;
  122. DWORD cNextServer = 0;
  123. PIP_ARRAY pipDnsArray = NULL;
  124. ZeroMemory(rgDnsServers, sizeof(rgDnsServers));
  125. ZeroMemory(rgSingleServer, sizeof(rgSingleServer));
  126. ZeroMemory(g_rgBindings, sizeof(g_rgBindings));
  127. if(1 == argc)
  128. {
  129. SetProgramStatus(DNSDIAG_RESOLVED);
  130. printf("%s", g_szUsage);
  131. goto Cleanup;
  132. }
  133. nRet = WSAStartup(wVersion, &wsaData);
  134. if(0 != nRet)
  135. {
  136. errprintf("Failed Winsock init, error - %d\n", WSAGetLastError());
  137. goto Cleanup;
  138. }
  139. fWSAInitialized = TRUE;
  140. if(0 != gethostname(szMyHostName, sizeof(szMyHostName)))
  141. {
  142. printf("Unable to get local machine name. Error - %d\n",
  143. WSAGetLastError());
  144. goto Cleanup;
  145. }
  146. g_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  147. if(g_hConsole == INVALID_HANDLE_VALUE)
  148. {
  149. printf("Failed to GetStdHandle\n");
  150. goto Cleanup;
  151. }
  152. g_hCompletion = CreateEvent(NULL, TRUE, FALSE, NULL);
  153. if(NULL == g_hCompletion)
  154. goto Cleanup;
  155. dbgprintf("Reading options and configuration.\n");
  156. fRet = ParseCommandLine(argc, argv, szHostName,
  157. sizeof(szHostName), &pDnsLogToFile, pipArray, cMaxServers,
  158. &fUdp, &dwDnsFlags, &fGlobalList, &fTryAllDnsServers);
  159. if(!fRet)
  160. goto Cleanup;
  161. g_pDnsLogger = (CDnsLogger *)pDnsLogToFile;
  162. fIISRTLInitialized = InitializeIISRTL();
  163. if(!fIISRTLInitialized)
  164. {
  165. errprintf("Failed IISRTL init, error - %d\n", GetLastError());
  166. errprintf("Make sure you are running this tool on a server with IIS "
  167. "installed\n");
  168. goto Cleanup;
  169. }
  170. fAtqInitialized = AtqInitialize(0);
  171. if(!fAtqInitialized)
  172. {
  173. errprintf("Failed ISATQ init, error - %d\n", GetLastError());
  174. errprintf("Make sure you are running this tool on a server with IIS "
  175. "installed\n");
  176. goto Cleanup;
  177. }
  178. cNextServer = 0;
  179. pipSingleServer->cAddrCount = 1;
  180. while(TRUE)
  181. {
  182. //
  183. // Set pipDnsArray to the DNS servers to be used. If the -a option is
  184. // specified, each DNS server will be tried individually. We will run
  185. // through this while loop and set pipDnsArray to single DNS server
  186. // in turn. If -a is not given, all servers are set on pipDnsServers.
  187. //
  188. if(fTryAllDnsServers)
  189. {
  190. if(cNextServer >= pipArray->cAddrCount)
  191. break;
  192. pipSingleServer->aipAddrs[0] = pipArray->aipAddrs[cNextServer];
  193. cNextServer++;
  194. pipDnsArray = pipSingleServer;
  195. msgprintf("\n\nQuerying DNS server: %s\n",
  196. iptostring(pipSingleServer->aipAddrs[0]));
  197. }
  198. else
  199. {
  200. pipDnsArray = pipArray;
  201. }
  202. //
  203. // Create the DNS serverlist object and set however many DNS servers we
  204. // want to query on the object
  205. //
  206. pDnsSimpleList = new CSimpleDnsServerList();
  207. if(!pDnsSimpleList)
  208. {
  209. errprintf("Out of memory creating DNS serverlist object.\n");
  210. goto Cleanup;
  211. }
  212. fRet = pDnsSimpleList->Update(pipDnsArray);
  213. if(!fRet)
  214. {
  215. errprintf("Unable to create DNS serverlist\n");
  216. goto Cleanup;
  217. }
  218. //
  219. // DNS querying object
  220. //
  221. pAsyncTestDns = new CAsyncTestDns(szMyHostName, fGlobalList, g_hCompletion);
  222. if(!pAsyncTestDns)
  223. {
  224. errprintf("Out of memory allocating DNS object.\n");
  225. goto Cleanup;
  226. }
  227. dwStatus = pAsyncTestDns->Dns_QueryLib(
  228. szHostName,
  229. DNS_TYPE_MX,
  230. dwDnsFlags,
  231. fUdp,
  232. pDnsSimpleList,
  233. fGlobalList);
  234. //
  235. // If the query failed we need to manually delete the object. If the
  236. // query succeeded, the completing ATQ threads will delete the object
  237. // after the results have been reported.
  238. //
  239. if(dwStatus != ERROR_SUCCESS)
  240. {
  241. errprintf("DNS query failed.\n");
  242. delete pAsyncTestDns;
  243. pAsyncTestDns = NULL;
  244. }
  245. //
  246. // This event is set in the destructor of pAsyncTestDns when the object
  247. // has finally finished the query (either successfully or with a failure).
  248. //
  249. WaitForSingleObject(g_hCompletion, INFINITE);
  250. ResetEvent(g_hCompletion);
  251. delete pDnsSimpleList;
  252. pDnsSimpleList = NULL;
  253. //
  254. // If -a was specified, we go on to the next iteration, and pick up the
  255. // next DNS server in line. Otherwise we just finish after a single
  256. // query.
  257. //
  258. if(!fTryAllDnsServers)
  259. break;
  260. }
  261. Cleanup:
  262. if(pDnsSimpleList)
  263. delete pDnsSimpleList;
  264. while(g_cDnsObjects)
  265. Sleep(100);
  266. if(fAtqInitialized)
  267. {
  268. dbgprintf("Shutting down ATQ\n");
  269. AtqTerminate();
  270. }
  271. if(fIISRTLInitialized)
  272. {
  273. dbgprintf("Shutting down IISRTL\n");
  274. TerminateIISRTL();
  275. }
  276. if(fWSAInitialized)
  277. WSACleanup();
  278. if(g_hCompletion)
  279. CloseHandle(g_hCompletion);
  280. dbgprintf("Exit code: %d\n", g_nProgramStatus);
  281. exit(g_nProgramStatus);
  282. return g_nProgramStatus;
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Description:
  286. // Parses the argc and argv and gets the various options. Also reads from
  287. // the metabase and DS to get the configuration as needed.
  288. //
  289. // Arguments:
  290. // IN int argc - Command line arg-count
  291. // IN char *argv[] - Command line args
  292. // OUT char *pszHostName - Pass in buffer to get target host
  293. // IN DWORD cchHostName - Length of above buffer in chars
  294. // OUT CDnsLogToFile **ppDnsLogger - Returns a logging object
  295. // OUT PIP_ARRAY pipArray - Pass in buffer to get DNS servers
  296. // IN int cMaxServers - Number of DNS servers that can be returned above
  297. // OUT BOOL *pfUdp - Use TCP or UDP for the query
  298. // OUT DWORD *pdwDnsFlags - Metabase configured SMTP DNS flags
  299. // OUT BOOL *pfGlobalList - Are the servers global?
  300. // OUT BOOL *pfTryAllServers - The "-a" option
  301. //
  302. // Returns:
  303. // TRUE arguments were successfully parsed, configuration was read without
  304. // problems, and initialization completed without errors.
  305. // FALSE there was an error. Abort. This function prints error messages
  306. // to stdout.
  307. //-----------------------------------------------------------------------------
  308. BOOL ParseCommandLine(
  309. int argc,
  310. char *argv[],
  311. char *pszHostName,
  312. DWORD cchHostName,
  313. CDnsLogToFile **ppDnsLogger,
  314. PIP_ARRAY pipArray,
  315. DWORD cMaxServers,
  316. BOOL *pfUdp,
  317. DWORD *pdwDnsFlags,
  318. BOOL *pfGlobalList,
  319. BOOL *pfTryAllServers)
  320. {
  321. int i = 0;
  322. BOOL fRet = FALSE;
  323. HRESULT hr = E_FAIL;
  324. BOOL fOptionS = FALSE;
  325. BOOL fOptionV = FALSE;
  326. BOOL fOptionD = FALSE;
  327. BOOL fOptionA = FALSE;
  328. BOOL fOptionP = FALSE;
  329. DWORD dwVsid = 0;
  330. DWORD cServers = 0;
  331. DWORD dwIpAddress = INADDR_NONE;
  332. *pszHostName = '\0';
  333. *pfGlobalList = FALSE;
  334. *pfTryAllServers = FALSE;
  335. if(argc < 2)
  336. {
  337. errprintf("Must specify a hostname as first argument.\n");
  338. printf("%s", g_szUsage);
  339. return FALSE;
  340. }
  341. else if(argc == 2 && (!_stricmp(argv[1], "/?") || !_stricmp(argv[1], "-?")))
  342. {
  343. SetProgramStatus(DNSDIAG_RESOLVED);
  344. printf("%s", g_szUsage);
  345. return FALSE;
  346. }
  347. pszHostName[cchHostName - 1] = '\0';
  348. strncpy(pszHostName, argv[1], cchHostName);
  349. if(pszHostName[cchHostName - 1] != '\0')
  350. {
  351. errprintf("Hostname too long. Maximum that can be handled by this tool is "
  352. "%d characters\n", cchHostName);
  353. return FALSE;
  354. }
  355. i = 2;
  356. while(i < argc)
  357. {
  358. if(!g_fDebug && !_stricmp(argv[i], "-d"))
  359. {
  360. i++;
  361. g_fDebug = TRUE;
  362. printf("Running in debug/verbose mode.\n");
  363. continue;
  364. }
  365. if(!fOptionV && !_stricmp(argv[i], "-v"))
  366. {
  367. i++;
  368. if(i >= argc)
  369. {
  370. printf("Specify an SMTP VSI# for -v option.\n");
  371. goto Cleanup;
  372. }
  373. dwVsid = atoi(argv[i]);
  374. if(dwVsid <= 0)
  375. {
  376. printf("Illegal operand to -v. Should be a number > 0.\n");
  377. goto Cleanup;
  378. }
  379. fOptionV = TRUE;
  380. i++;
  381. continue;
  382. }
  383. if(!fOptionS && !_stricmp(argv[i], "-s"))
  384. {
  385. i++;
  386. if(i >= argc)
  387. {
  388. printf("No DNS servers specified for -s option.\n");
  389. goto Cleanup;
  390. }
  391. cServers = 0;
  392. while(*argv[i] != '-')
  393. {
  394. dwIpAddress = inet_addr(argv[i]);
  395. if(dwIpAddress == INADDR_NONE)
  396. {
  397. printf("Non IP address \"%s\" in -s option.\n", argv[i]);
  398. goto Cleanup;
  399. }
  400. if(cServers >= cMaxServers)
  401. {
  402. printf("Too many servers in -s. Maximum that can be handled"
  403. " by this tool is %d.\n", cMaxServers);
  404. goto Cleanup;
  405. }
  406. pipArray->aipAddrs[cServers] = dwIpAddress;
  407. cServers++;
  408. i++;
  409. if(i >= argc)
  410. break;
  411. }
  412. pipArray->cAddrCount = cServers;
  413. *pdwDnsFlags = 0;
  414. *pfGlobalList = FALSE;
  415. fOptionS = TRUE;
  416. continue;
  417. }
  418. if(!fOptionA && !_stricmp(argv[i], "-a"))
  419. {
  420. fOptionA = TRUE;
  421. *pfTryAllServers = TRUE;
  422. i++;
  423. continue;
  424. }
  425. if(!fOptionP && !_stricmp(argv[i], "-p"))
  426. {
  427. i++;
  428. if(i >= argc)
  429. {
  430. printf("Specify protocol for -p option. Either TCP, UDP or"
  431. " DEF (for default). Default means that UDP will be tried"
  432. " first followed by TCP if the reply was truncated.\n");
  433. }
  434. if(!_stricmp(argv[i], "tcp"))
  435. {
  436. *pfUdp = FALSE;
  437. *pdwDnsFlags = DNS_FLAGS_TCP_ONLY;
  438. }
  439. else if(!_stricmp(argv[i], "udp"))
  440. {
  441. *pfUdp = TRUE;
  442. *pdwDnsFlags = DNS_FLAGS_UDP_ONLY;
  443. }
  444. else if(!_stricmp(argv[i], "def"))
  445. {
  446. *pfUdp = TRUE;
  447. *pdwDnsFlags = DNS_FLAGS_NONE;
  448. }
  449. else
  450. {
  451. printf("Unrecognized protocol %s\n", argv[i]);
  452. goto Cleanup;
  453. }
  454. i++;
  455. fOptionP = TRUE;
  456. continue;
  457. }
  458. printf("Unrecognized option \"%s\".\n", argv[i]);
  459. printf("%s", g_szUsage);
  460. goto Cleanup;
  461. }
  462. if(fOptionV)
  463. {
  464. if(fOptionS)
  465. {
  466. printf("Options -s and -v are incompatible\n");
  467. goto Cleanup;
  468. }
  469. if(fOptionP)
  470. {
  471. printf("Options -p and -v are incompatible\n");
  472. goto Cleanup;
  473. }
  474. }
  475. *ppDnsLogger = new CDnsLogToFile();
  476. if(!*ppDnsLogger)
  477. {
  478. errprintf("Out of memory creating DNS logger.\n");
  479. goto Cleanup;
  480. }
  481. if(fOptionV)
  482. {
  483. hr = HrGetVsiConfig(pszHostName, dwVsid, pdwDnsFlags, pipArray,
  484. cMaxServers, pfGlobalList, pfUdp, g_pipBindings,
  485. g_cMaxBindings);
  486. if(FAILED(hr))
  487. {
  488. errprintf("Unable to get VSI configuration\n");
  489. goto Cleanup;
  490. }
  491. }
  492. if(pipArray->cAddrCount == 0)
  493. {
  494. errprintf("Either specify DNS servers using -s, or use -v.\n");
  495. goto Cleanup;
  496. }
  497. return TRUE;
  498. Cleanup:
  499. if(*ppDnsLogger)
  500. {
  501. delete *ppDnsLogger;
  502. *ppDnsLogger = NULL;
  503. }
  504. return FALSE;
  505. }
  506. //-----------------------------------------------------------------------------
  507. // Description:
  508. //
  509. // This function reads from the metabase and the Active directory (if
  510. // Exchange is installed) to determine the DNS settings for the VSI that
  511. // is to be simulated. Additionally, if the VSI is configured as a DMZ
  512. // (i.e. with additional external DNS servers configured in the AD), we
  513. // determine if the target server is an Exchange computer by searching
  514. // for it in the directory.
  515. //
  516. // Arguments:
  517. //
  518. // IN LPSTR pszTargetServer - Name to resolve
  519. // IN DWORD dwVsid - VSI to simulate
  520. // OUT PDWORD pdwFlags - Flags to pass to Dns_QueryLib (from metabase)
  521. // OUT PIP_ARRAY pipDnsServers - Returns DNS servers to query
  522. // IN DWORD cMaxServers - Capacity of above buffer
  523. // OUT BOOL *pfGlobalList - TRUE if default DNS servers are to be used
  524. // OUT BOOL *pfUdp - Indicates protocol to connect with DNS
  525. //
  526. // Returns:
  527. //
  528. // S_OK - If the configuration was successfully read
  529. // ERROR HRESULT if something failed. Diagnostic error messages are
  530. // printed.
  531. //-----------------------------------------------------------------------------
  532. HRESULT HrGetVsiConfig(
  533. LPSTR pszTargetServer,
  534. DWORD dwVsid,
  535. PDWORD pdwFlags,
  536. PIP_ARRAY pipDnsServers,
  537. DWORD cMaxServers,
  538. BOOL *pfGlobalList,
  539. BOOL *pfUdp,
  540. PIP_ARRAY pipServerBindings,
  541. DWORD cMaxServerBindings)
  542. {
  543. HRESULT hr = E_FAIL;
  544. DWORD dwErr = ERROR_SUCCESS;
  545. BOOL fCoInitialized = FALSE;
  546. IMSAdminBase *pIMeta = NULL;
  547. METADATA_RECORD mdRecord;
  548. DWORD dwLength = 0;
  549. PBYTE pbMDData = (PBYTE) pdwFlags;
  550. PIP_ARRAY pipTempServers = NULL;
  551. BOOL fExternal = FALSE;
  552. WCHAR wszVirtualServer[256];
  553. WCHAR wszBindings[256];
  554. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  555. if(FAILED(hr))
  556. {
  557. errprintf("Unable to initialize COM. The error HRESULT is 0x%08x\n", hr);
  558. goto Cleanup;
  559. }
  560. fCoInitialized = TRUE;
  561. // Check metabase configuration for DNS
  562. hr = CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL,
  563. IID_IMSAdminBase, (void **) &pIMeta);
  564. if(FAILED(hr))
  565. {
  566. errprintf("Failed to connect to IIS metabase. Make sure the IISADMIN"
  567. " service is installed and running and that you are running this"
  568. " tool with sufficient permissions. The failure HRESULT is"
  569. " 0x%08x\n", hr);
  570. goto Cleanup;
  571. }
  572. ZeroMemory(&mdRecord, sizeof(mdRecord));
  573. mdRecord.dwMDIdentifier = MD_SMTP_USE_TCP_DNS;
  574. mdRecord.dwMDAttributes = METADATA_INHERIT;
  575. mdRecord.dwMDUserType = IIS_MD_UT_FILE;
  576. mdRecord.dwMDDataType = DWORD_METADATA;
  577. mdRecord.dwMDDataLen = sizeof(DWORD);
  578. mdRecord.pbMDData = pbMDData;
  579. hr = pIMeta->GetData(METADATA_MASTER_ROOT_HANDLE, L"/LM/SMTPSVC",
  580. &mdRecord, &dwLength);
  581. if(hr == MD_ERROR_DATA_NOT_FOUND)
  582. {
  583. *pdwFlags = DNS_FLAGS_NONE;
  584. dbgprintf("The DNS flags are not explicitly set in the metabase, assuming "
  585. "default flags - 0x%08x\n", DNS_FLAGS_NONE);
  586. }
  587. else if(FAILED(hr))
  588. {
  589. errprintf("Error reading key MD_SMTP_USE_TCP_DNS (%d) under /SMTPSVC in"
  590. " the metabase. The error HRESULT is 0x%08x - %s\n",
  591. MD_SMTP_USE_TCP_DNS, hr, MDErrorToString(hr));
  592. goto Cleanup;
  593. }
  594. else
  595. {
  596. dbgprintf("These DNS flags are configured in the metabase");
  597. if(*pdwFlags & DNS_FLAGS_UDP_ONLY)
  598. dbgprintf(" DNS_FLAGS_UDP_ONLY");
  599. else if(*pdwFlags & DNS_FLAGS_TCP_ONLY)
  600. dbgprintf(" DNS_FLAGS_TCP_ONLY");
  601. dbgprintf(" (0x%08x)\n", *pdwFlags);
  602. }
  603. mdRecord.dwMDIdentifier = MD_SERVER_BINDINGS;
  604. mdRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
  605. mdRecord.dwMDUserType = IIS_MD_UT_SERVER;
  606. mdRecord.dwMDDataType = MULTISZ_METADATA;
  607. mdRecord.dwMDDataLen = sizeof(wszBindings);
  608. mdRecord.pbMDData = (PBYTE) wszBindings;
  609. swprintf(wszVirtualServer, L"/LM/SMTPSVC/%d", dwVsid);
  610. hr = pIMeta->GetData(METADATA_MASTER_ROOT_HANDLE, wszVirtualServer,
  611. &mdRecord, &dwLength);
  612. if(hr == MD_ERROR_DATA_NOT_FOUND)
  613. {
  614. errprintf("No VSI bindings in metabase. The key %S had no data.\n");
  615. goto Cleanup;
  616. }
  617. else if(FAILED(hr))
  618. {
  619. errprintf("Error reading /SMTPSVC/%d/ServerBindings from the metabase."
  620. " The error HRESULT is 0x%08x - %s\n", dwVsid, hr,
  621. MDErrorToString(hr));
  622. goto Cleanup;
  623. }
  624. else
  625. {
  626. if(!GetServerBindings(wszBindings, pipServerBindings,
  627. cMaxServerBindings))
  628. {
  629. goto Cleanup;
  630. }
  631. dbgprintf("These are the local IP addresses (server-bindings)\n");
  632. if(g_fDebug)
  633. PrintIPArray(pipServerBindings);
  634. }
  635. // UDP is used (for the intial query) iff exclusive TCP_ONLY is not set
  636. *pfUdp = ((*pdwFlags) != DNS_FLAGS_TCP_ONLY);
  637. dwErr = DsGetConfiguration(pszTargetServer, dwVsid, pipDnsServers,
  638. cMaxServers, &fExternal);
  639. if(dwErr != ERROR_SUCCESS)
  640. {
  641. hr = HRESULT_FROM_WIN32(dwErr);
  642. goto Cleanup;
  643. }
  644. //
  645. // If external DNS servers were configured AND pszServer is an external
  646. // target, then then we have all the information we need. Otherwise we
  647. // need to supply the default DNS servers configured on this machine
  648. //
  649. if(pipDnsServers->cAddrCount > 0 && fExternal)
  650. goto Cleanup;
  651. *pfGlobalList = TRUE;
  652. DnsGetDnsServerList(&pipTempServers);
  653. if(NULL == pipTempServers)
  654. {
  655. errprintf("Unable to get configured DNS servers for this computer\n");
  656. goto Cleanup;
  657. }
  658. if(pipTempServers->cAddrCount <= cMaxServers)
  659. {
  660. CopyMemory(pipDnsServers, pipTempServers,
  661. (1 + pipTempServers->cAddrCount) * sizeof(DWORD));
  662. }
  663. else
  664. {
  665. errprintf("Too many DNS servers are configured on this computer for this"
  666. " tool to handle. The maximum number that can be handled by this"
  667. " tool is %d\n", cMaxServers);
  668. goto Cleanup;
  669. }
  670. dbgprintf("Using the default DNS servers configured for this computer.\n");
  671. if(g_fDebug)
  672. PrintIPArray(pipDnsServers);
  673. Cleanup:
  674. if(pIMeta)
  675. pIMeta->Release();
  676. if(pipTempServers)
  677. DnsApiFree(pipTempServers);
  678. if(fCoInitialized)
  679. CoUninitialize();
  680. return hr;
  681. }
  682. BOOL GetServerBindings(
  683. WCHAR *pwszMultiSzBindings,
  684. PIP_ARRAY pipServerBindings,
  685. DWORD cMaxServerBindings)
  686. {
  687. int lErr = 0;
  688. DWORD cbOutBuffer = 0;
  689. WCHAR *pwszBinding = pwszMultiSzBindings;
  690. WCHAR *pwchEnd = pwszBinding;
  691. char szBinding[256];
  692. int cchWritten = 0;
  693. SOCKET sock;
  694. SOCKADDR_IN *lpSockAddrIn = NULL;
  695. BYTE rgbBuffer[512];
  696. LPSOCKET_ADDRESS_LIST pIpBuffer = (LPSOCKET_ADDRESS_LIST)rgbBuffer;
  697. if(*pwszBinding == L':')
  698. {
  699. // Blank binding string
  700. dbgprintf("Encountered blank server binding string for VSI\n");
  701. sock = socket(AF_INET, SOCK_STREAM, 0);
  702. if(sock == INVALID_SOCKET)
  703. {
  704. errprintf("Unable to create socket for WSAIoctl. The Win32 error"
  705. " is %d\n", WSAGetLastError());
  706. return FALSE;
  707. }
  708. lErr = WSAIoctl(sock, SIO_ADDRESS_LIST_QUERY, NULL, 0,
  709. (PBYTE)(pIpBuffer), sizeof(rgbBuffer), &cbOutBuffer, NULL, NULL);
  710. closesocket(sock);
  711. if(lErr != 0)
  712. {
  713. errprintf("Unable to issue WSAIoctl to get local IP addresses."
  714. " The Win32 error is %d\n", WSAGetLastError());
  715. return FALSE;
  716. }
  717. if(pIpBuffer->iAddressCount > (int)cMaxServerBindings)
  718. {
  719. errprintf("%d IP addresses were returned for the local machine"
  720. " by WSAIoctl. The maximum number that can be accomodated"
  721. " by this tool is %d\n", pIpBuffer->iAddressCount,
  722. cMaxServerBindings);
  723. return FALSE;
  724. }
  725. for(pipServerBindings->cAddrCount = 0;
  726. (int)pipServerBindings->cAddrCount < pIpBuffer->iAddressCount;
  727. pipServerBindings->cAddrCount++)
  728. {
  729. lpSockAddrIn =
  730. (SOCKADDR_IN *)
  731. (pIpBuffer->Address[pipServerBindings->cAddrCount].lpSockaddr);
  732. CopyMemory(
  733. (PVOID)&(pipServerBindings->aipAddrs[pipServerBindings->cAddrCount]),
  734. (PVOID)&(lpSockAddrIn->sin_addr),
  735. sizeof(DWORD));
  736. }
  737. return TRUE;
  738. }
  739. while(TRUE)
  740. {
  741. pwchEnd = wcschr(pwszBinding, L':');
  742. if(pwchEnd == NULL)
  743. {
  744. errprintf("Illegal format for server binding string. The server"
  745. " binding string should be in the format <ipaddress>:<port>."
  746. " Instead, the string is \"%S\"\n", pwszBinding);
  747. return FALSE;
  748. }
  749. *pwchEnd = L'\0';
  750. pwchEnd++;
  751. if(pipServerBindings->cAddrCount > cMaxServerBindings)
  752. {
  753. errprintf("Too many server bindings for VSI. Maximum that can be"
  754. " handled by this tool is %d.\n", cMaxServerBindings);
  755. return FALSE;
  756. }
  757. // Explicit IP in binding string
  758. cchWritten = wcstombs(szBinding, pwszBinding, sizeof(szBinding));
  759. if(cchWritten < 0)
  760. {
  761. errprintf("Failed to conversion of %S from widechar to ASCII\n",
  762. pwszBinding);
  763. return FALSE;
  764. }
  765. pipServerBindings->aipAddrs[pipServerBindings->cAddrCount] =
  766. inet_addr(szBinding);
  767. if(pipServerBindings->aipAddrs[pipServerBindings->cAddrCount] ==
  768. INADDR_NONE)
  769. {
  770. errprintf("Illegal format for binding\n");
  771. return FALSE;
  772. }
  773. pipServerBindings->cAddrCount++;
  774. // Skip to end of string
  775. while(*pwchEnd != L'\0')
  776. pwchEnd++;
  777. // 2 NULL terminations signal end of MULTI_SZ
  778. pwchEnd++;
  779. if(*pwchEnd == L'\0')
  780. return TRUE;
  781. pwszBinding = pwchEnd;
  782. }
  783. return FALSE;
  784. }
  785. //-----------------------------------------------------------------------------
  786. // Description:
  787. // Checks the regkey which is created when Exchange is installed, and uses
  788. // it to determine if Exchange is installed.
  789. // Arguments:
  790. // OUT BOOL *pfBool - Set to TRUE if the regkey exists, FALSE otherwise.
  791. // Returns:
  792. // Win32 Error if something failed.
  793. //-----------------------------------------------------------------------------
  794. DWORD IsExchangeInstalled(BOOL *pfBool)
  795. {
  796. LONG lResult = 0;
  797. HKEY hkExchange;
  798. const char szExchange[] =
  799. "Software\\Microsoft\\Exchange";
  800. lResult = RegOpenKeyEx(
  801. HKEY_LOCAL_MACHINE,
  802. szExchange,
  803. 0,
  804. KEY_READ,
  805. &hkExchange);
  806. if(lResult == ERROR_SUCCESS)
  807. {
  808. dbgprintf("Microsoft Exchange is installed on this machine.\n");
  809. RegCloseKey(hkExchange);
  810. *pfBool = TRUE;
  811. return ERROR_SUCCESS;
  812. }
  813. else if(lResult == ERROR_NOT_FOUND || lResult == ERROR_FILE_NOT_FOUND)
  814. {
  815. dbgprintf("Microsoft Exchange not installed on this machine\n");
  816. *pfBool = FALSE;
  817. return ERROR_SUCCESS;
  818. }
  819. errprintf("Error opening registry key HKLM\\%s, Win32 err - %d\n",
  820. szExchange, lResult);
  821. return lResult;
  822. }
  823. //-----------------------------------------------------------------------------
  824. // Description:
  825. // Connects to a domain controller and reads configuration options for the
  826. // VSI being simulated. In addition it checks if the target-server (which
  827. // is to be resolved) is an Exchange computer that is a member of the
  828. // Exchange Org or not.
  829. //
  830. // Arguments:
  831. // IN char *pszTargetServer - Server to resolve
  832. // IN DWORD dwVsid - VSI to simulate
  833. // OUT PIP_ARRAY pipExternalDnsServers - External DNS servers on VSI if
  834. // any are returned in this caller allocated buffer.
  835. // IN DWORD cMaxServers - Capacity of above buffer.
  836. // OUT PBOOL pfExternal - Set to TRUE when there are external DNS servers
  837. // configured.
  838. //
  839. // Returns:
  840. // ERROR_SUCCESS if configuration was read without problems.
  841. // Win32 error code if there was a problem. Error messages are written to
  842. // stdout for diagnostic purposes.
  843. //-----------------------------------------------------------------------------
  844. DWORD
  845. DsGetConfiguration(
  846. char *pszTargetServer,
  847. DWORD dwVsid,
  848. PIP_ARRAY pipExternalDnsServers,
  849. DWORD cMaxServers,
  850. PBOOL pfExternal)
  851. {
  852. DWORD dwErr = ERROR_NOT_FOUND;
  853. BOOL fRet = FALSE;
  854. PLDAP pldap = NULL;
  855. PLDAPMessage pldapMsgContexts = NULL;
  856. PLDAPMessage pldapMsgSmtpVsi = NULL;
  857. PLDAPMessage pEntry = 0;
  858. char szLocalComputerName[256];
  859. DWORD cchLocalComputerName = sizeof(szLocalComputerName);
  860. // Context attributes to read at the base level - so we know where to base
  861. // the rest of our searches from
  862. char *rgszContextAttrs[] =
  863. { "configurationNamingContext", NULL };
  864. // Attributes we are interested in for the VSI object
  865. char *rgszSmtpVsiAttrs[] =
  866. { "msExchSmtpExternalDNSServers", NULL };
  867. // LDAP result ptrs to store the results of the search
  868. char **rgszConfigurationNamingContext = NULL;
  869. char **rgszSmtpVsiExternalDNSServers = NULL;
  870. char *pszExchangeServerDN = NULL;
  871. char szSmtpVsiDN[256];
  872. char *pchSeparator = NULL;
  873. char *pszIPServer = NULL;
  874. char *pszStringEnd = NULL;
  875. int i = 0;
  876. int cValues = 0;
  877. int cch = 0;
  878. BOOL fInstalled = FALSE;
  879. BOOL fFound = FALSE;
  880. *pfExternal = FALSE;
  881. pipExternalDnsServers->cAddrCount = 0;
  882. dwErr = IsExchangeInstalled(&fInstalled);
  883. if(ERROR_SUCCESS != dwErr || !fInstalled)
  884. return dwErr;
  885. dbgprintf("Querying domain controller for configuration.\n");
  886. pldap = BindToDC();
  887. if(!pldap)
  888. {
  889. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  890. goto Cleanup;
  891. }
  892. dwErr = ldap_search_s(
  893. pldap, // ldap binding
  894. "", // base DN
  895. LDAP_SCOPE_BASE, // scope
  896. "(objectClass=*)", // filter
  897. rgszContextAttrs, // attributes we want to read
  898. FALSE, // FALSE means read value
  899. &pldapMsgContexts); // return results here
  900. if(dwErr != LDAP_SUCCESS)
  901. {
  902. errprintf("Error encountered during LDAP search. LDAP err - %d.\n", dwErr);
  903. goto Cleanup;
  904. }
  905. pEntry = ldap_first_entry(pldap, pldapMsgContexts);
  906. if(pEntry == NULL)
  907. {
  908. dwErr = ERROR_INVALID_DATA;
  909. errprintf("Base object not found on domain controller!\n");
  910. goto Cleanup;
  911. }
  912. rgszConfigurationNamingContext = ldap_get_values(pldap, pEntry, rgszContextAttrs[0]);
  913. if(rgszConfigurationNamingContext == NULL)
  914. {
  915. dwErr = ERROR_INVALID_DATA;
  916. errprintf("configurationNamingContext attribute not set on base object of"
  917. " domain controller.\n");
  918. goto Cleanup;
  919. }
  920. if((cValues = ldap_count_values(rgszConfigurationNamingContext)) == 1)
  921. {
  922. dbgprintf("configurationNamingContext is \"%s\"\n", rgszConfigurationNamingContext[0]);
  923. dbgprintf("This will be used as the Base DN for all directory searches.\n");
  924. }
  925. else
  926. {
  927. dwErr = ERROR_INVALID_DATA;
  928. errprintf("Unexpected error reading configurationNamingContext. Expected"
  929. " a single string value, instead there were %d values set\n",
  930. cValues);
  931. goto Cleanup;
  932. }
  933. // See if the target server is an Exchange Server in the Org
  934. dbgprintf("Checking if the target server %s is an Exchange server\n",
  935. pszTargetServer);
  936. dwErr = DsFindExchangeServer(pldap, rgszConfigurationNamingContext[0],
  937. pszTargetServer, NULL, &fFound);
  938. //
  939. // If it is in the Org, nothing more to do - we just use the default DNS
  940. // servers configured for the box to do the resolution
  941. //
  942. if(dwErr == LDAP_SUCCESS && fFound)
  943. {
  944. msgprintf("%s is in the Exchange Org. Global DNS servers will be used.\n",
  945. pszTargetServer);
  946. *pfExternal = FALSE;
  947. goto Cleanup;
  948. }
  949. //
  950. // On the other hand, if the target is not an Exchange computer in the org,
  951. // we need to lookup the VSI object on the local computer and check if it
  952. // is configured with external DNS servers
  953. //
  954. *pfExternal = TRUE;
  955. msgprintf("%s is an external server (not in the Exchange Org).\n", pszTargetServer);
  956. cchLocalComputerName = sizeof(szLocalComputerName);
  957. fRet = GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified,
  958. szLocalComputerName, &cchLocalComputerName);
  959. if(!fRet)
  960. {
  961. dwErr = GetLastError();
  962. errprintf("Unable to retrieve local computer DNS name, Win32 err - %d.\n",
  963. dwErr);
  964. goto Cleanup;
  965. }
  966. dbgprintf("Checking on DC if the VSI being simulated is configured with"
  967. " external DNS servers.\n");
  968. // Find the Exchange Server container object for the local computer
  969. dwErr = DsFindExchangeServer(pldap, rgszConfigurationNamingContext[0],
  970. szLocalComputerName, &pszExchangeServerDN, &fFound);
  971. if(!fFound || !pszExchangeServerDN)
  972. {
  973. errprintf("This server \"%s\" was not found in the DS. Make sure you are"
  974. " running this tool on an Exchange server in the Organization\n");
  975. dwErr = ERROR_INVALID_DATA;
  976. goto Cleanup;
  977. }
  978. // Construct the DN of the VSI for the server we found. This is fixed relative
  979. // to the Exchange Server DN
  980. cch = _snprintf(szSmtpVsiDN, sizeof(szSmtpVsiDN),
  981. "CN=%d,CN=SMTP,CN=Protocols,%s", dwVsid, pszExchangeServerDN);
  982. if(cch < 0)
  983. {
  984. errprintf("Unable to construct SMTP virtual server's DN. The DN is too"
  985. " long for this tool to handle\n");
  986. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  987. goto Cleanup;
  988. }
  989. dbgprintf("DN for the virtual server is \"%s\"\n", szSmtpVsiDN);
  990. // Get the DNS servers attribute for the VSI
  991. dwErr = ldap_search_s(
  992. pldap, // ldap binding
  993. szSmtpVsiDN, // base DN
  994. LDAP_SCOPE_SUBTREE, // scope
  995. "(objectClass=*)", // filter
  996. NULL, //rgszSmtpVsiAttrs, // attributes we want to read
  997. FALSE, // FALSE means read value
  998. &pldapMsgSmtpVsi); // return results here
  999. if(dwErr == LDAP_NO_SUCH_OBJECT)
  1000. {
  1001. errprintf("No object exists for SMTP virtual server #%d on GC for %s\n",
  1002. dwVsid, szLocalComputerName);
  1003. goto Cleanup;
  1004. }
  1005. if(dwErr != LDAP_SUCCESS)
  1006. {
  1007. errprintf("Search for SMTP virtual server object failed, LDAP err - %d\n",
  1008. dwErr);
  1009. goto Cleanup;
  1010. }
  1011. pEntry = ldap_first_entry(pldap, pldapMsgSmtpVsi);
  1012. if(pEntry == NULL)
  1013. {
  1014. errprintf("SMTP virtual server #%d for server %s was not found in the DS\n",
  1015. dwVsid, szLocalComputerName);
  1016. dwErr = ERROR_INVALID_DATA;
  1017. goto Cleanup;
  1018. }
  1019. rgszSmtpVsiExternalDNSServers = ldap_get_values(pldap, pEntry,
  1020. rgszSmtpVsiAttrs[0]);
  1021. if(rgszSmtpVsiExternalDNSServers == NULL)
  1022. {
  1023. dbgprintf("The attribute msExchSmtpExternalDNSServers was not found on"
  1024. " the SMTP virtual server being simulated.\n");
  1025. msgprintf("No external DNS servers on VSI. Using global DNS servers.\n");
  1026. dwErr = ERROR_SUCCESS;
  1027. goto Cleanup;
  1028. }
  1029. // This is a string of comma separated IP addresses
  1030. if((cValues != ldap_count_values(rgszSmtpVsiExternalDNSServers)) == 1)
  1031. {
  1032. errprintf("Unexpected error reading msExchSmtpExternalDNSServers,"
  1033. " cValues - %d\n", cValues);
  1034. dwErr = ERROR_INVALID_DATA;
  1035. goto Cleanup;
  1036. }
  1037. dbgprintf("msExchSmtpExternalDNSServers: %s\n", rgszSmtpVsiExternalDNSServers[0]);
  1038. pszIPServer = rgszSmtpVsiExternalDNSServers[0];
  1039. pszStringEnd = rgszSmtpVsiExternalDNSServers[0] +
  1040. lstrlen(rgszSmtpVsiExternalDNSServers[0]);
  1041. i = 0;
  1042. pipExternalDnsServers->cAddrCount = 0;
  1043. while(pszIPServer < pszStringEnd && *pszIPServer != '\0')
  1044. {
  1045. pchSeparator = strchr(pszIPServer, ',');
  1046. if(pchSeparator != NULL) // last IP address
  1047. *pchSeparator = '\0';
  1048. if(i > (int)cMaxServers)
  1049. {
  1050. errprintf("Too many DNS servers configured in registry. The maximum"
  1051. " that this tool can handle is %d\n", cMaxServers);
  1052. dwErr = ERROR_INVALID_DATA;
  1053. goto Cleanup;
  1054. }
  1055. pipExternalDnsServers->aipAddrs[i] = inet_addr(pszIPServer);
  1056. if(pipExternalDnsServers->aipAddrs[i] == INADDR_NONE)
  1057. {
  1058. errprintf("The attribute msExchSmtpExternalDNSServers is in an"
  1059. " invalid format. Expected a comma separated list of IP"
  1060. " addresses in dotted decimal notation.\n");
  1061. goto Cleanup;
  1062. }
  1063. pipExternalDnsServers->cAddrCount++;
  1064. if(pchSeparator == NULL) // last IP address
  1065. break;
  1066. // There was a comma, advance to just after it
  1067. pszIPServer = pchSeparator + 1;
  1068. i++;
  1069. }
  1070. if(pipExternalDnsServers->cAddrCount == 0)
  1071. {
  1072. errprintf("No IP addresses could be constructed from"
  1073. " msExchSmtpExternalDNSServers\n");
  1074. }
  1075. else
  1076. {
  1077. msgprintf("Using external DNS servers:\n");
  1078. SetMsgColor();
  1079. PrintIPArray(pipExternalDnsServers);
  1080. SetNormalColor();
  1081. }
  1082. dwErr = ERROR_SUCCESS;
  1083. Cleanup:
  1084. if(pszExchangeServerDN)
  1085. delete [] pszExchangeServerDN;
  1086. if(rgszSmtpVsiExternalDNSServers)
  1087. ldap_value_free(rgszSmtpVsiExternalDNSServers);
  1088. if(pldapMsgSmtpVsi)
  1089. ldap_msgfree(pldapMsgSmtpVsi);
  1090. if(rgszConfigurationNamingContext)
  1091. ldap_value_free(rgszConfigurationNamingContext);
  1092. if(pldapMsgContexts)
  1093. ldap_msgfree(pldapMsgContexts);
  1094. if(pldap)
  1095. ldap_unbind(pldap);
  1096. return dwErr;
  1097. }
  1098. //-----------------------------------------------------------------------------
  1099. // Description:
  1100. // Locates a domain controller for the local machine and opens an LDAP
  1101. // connection to it.
  1102. // Arguments:
  1103. // None.
  1104. // Returns:
  1105. // LDAP* which can be used for LDAP queries
  1106. //-----------------------------------------------------------------------------
  1107. PLDAP BindToDC()
  1108. {
  1109. DWORD dwErr = LDAP_SUCCESS;
  1110. PDOMAIN_CONTROLLER_INFO pdci = NULL;
  1111. char *pszDomainController = NULL;
  1112. PLDAP pldap = NULL;
  1113. dwErr = DsGetDcName(
  1114. NULL, // Computer name
  1115. NULL, // Domain name
  1116. NULL, // Domain GUID,
  1117. NULL, // Sitename
  1118. DS_DIRECTORY_SERVICE_REQUIRED |
  1119. DS_RETURN_DNS_NAME,
  1120. &pdci);
  1121. if(dwErr != ERROR_SUCCESS)
  1122. {
  1123. errprintf("Error getting domain controller FQDN, Win32 err - %d\n", dwErr);
  1124. goto Cleanup;
  1125. }
  1126. pszDomainController = pdci->DomainControllerName;
  1127. while(*pszDomainController == '\\')
  1128. pszDomainController++;
  1129. dbgprintf("The domain controller server which will be used for reading"
  1130. " configuration data is %s\n", pszDomainController);
  1131. dbgprintf("Connecting to %s over port %d\n", pszDomainController, LDAP_PORT);
  1132. pldap = ldap_open(pszDomainController, LDAP_PORT);
  1133. if(pldap == NULL)
  1134. {
  1135. dwErr = LdapGetLastError();
  1136. errprintf("Unable to initialize an LDAP session to the domain controller"
  1137. " server %s, LDAP err - %d\n", pszDomainController, dwErr);
  1138. goto Cleanup;
  1139. }
  1140. dwErr = ldap_bind_s(pldap, NULL, NULL, LDAP_AUTH_SSPI);
  1141. if(dwErr != LDAP_SUCCESS)
  1142. {
  1143. errprintf("Unable to authenticate to the domain controller server %s. Make"
  1144. " sure you are running this tool with appropriate credentials,"
  1145. " LDAP err - %d\n", pszDomainController, dwErr);
  1146. goto Cleanup;
  1147. }
  1148. Cleanup:
  1149. if(pdci)
  1150. NetApiBufferFree((PVOID)pdci);
  1151. return pldap;
  1152. }
  1153. //-----------------------------------------------------------------------------
  1154. // Description:
  1155. // Checks if a given FQDN is the name of an Exchange server in the org.
  1156. //
  1157. // Arguments:
  1158. // IN PLDAP pldap - Open LDAP session to domain controller.
  1159. // IN LPSTR szBaseDN - Base DN to search from
  1160. // IN LPSTR szServerName - Servername to search for
  1161. // OUT LPSTR *ppszServerDN - If a non-NULL char** is passed in, the DN
  1162. // of the server (if found) is returned to this. The buffer must be
  1163. // freed using delete [].
  1164. // OUT BOOL *pfFound - Set to TRUE if the server is found.
  1165. //
  1166. // Returns:
  1167. // ERROR_SUCCESS if configuration was read without problems.
  1168. // Win32 error code if there was a problem. Error messages are written to
  1169. // stdout for diagnostic purposes.
  1170. //-----------------------------------------------------------------------------
  1171. DWORD DsFindExchangeServer(
  1172. PLDAP pldap,
  1173. LPSTR szBaseDN,
  1174. LPSTR szServerName,
  1175. LPSTR *ppszServerDN,
  1176. BOOL *pfFound)
  1177. {
  1178. int i = 0;
  1179. int cch = 0;
  1180. int cValues = 0;
  1181. DWORD dwErr = LDAP_SUCCESS;
  1182. PLDAPMessage pldapMsgExchangeServer = NULL;
  1183. PLDAPMessage pEntry = NULL;
  1184. char *rgszExchangeServerAttrs[] = { "distinguishedName", "networkAddress", NULL };
  1185. char **rgszExchangeServerDN = NULL;
  1186. char **rgszExchangeServerNetworkName = NULL;
  1187. char szExchangeServerFilter[256];
  1188. char szSearchNetworkName[256];
  1189. //
  1190. // The Exchange Server object has a multivalued attribute, "networkAddress"
  1191. // that enumerates all the various names by which the Exchange Server is
  1192. // identified such as NetBIOS, DNS etc. We are only interested in the fully
  1193. // qualified domain name. This is set on the attribute as the string
  1194. // "ncacn_ip_tcp:" prefixed to the server's FQDN.
  1195. //
  1196. szExchangeServerFilter[sizeof(szExchangeServerFilter) - 1] = '\0';
  1197. cch = _snprintf(
  1198. szExchangeServerFilter,
  1199. sizeof(szExchangeServerFilter) - 1,
  1200. "(&(networkAddress=ncacn_ip_tcp:%s)(objectClass=msExchExchangeServer))",
  1201. szServerName);
  1202. if(cch < 0)
  1203. {
  1204. errprintf("The servername %s is too long for this tool to handle.\n",
  1205. szServerName);
  1206. dwErr = ERROR_INVALID_DATA;
  1207. goto Cleanup;
  1208. }
  1209. dbgprintf("Searching for an Exchange Server object for %s on the domain"
  1210. " controller\n", szServerName);
  1211. dwErr = ldap_search_s(
  1212. pldap,
  1213. szBaseDN,
  1214. LDAP_SCOPE_SUBTREE,
  1215. szExchangeServerFilter,
  1216. rgszExchangeServerAttrs,
  1217. FALSE,
  1218. &pldapMsgExchangeServer);
  1219. if(dwErr == LDAP_NO_SUCH_OBJECT)
  1220. {
  1221. dbgprintf("No Exchange Server object found for %s on domain controller,"
  1222. " LDAP err - LDAP_NO_SUCH_OBJECT\n", szServerName);
  1223. dwErr = ERROR_SUCCESS;
  1224. goto Cleanup;
  1225. }
  1226. if(dwErr != LDAP_SUCCESS)
  1227. {
  1228. errprintf("LDAP search failed, LDAP err %d\n", dwErr);
  1229. goto Cleanup;
  1230. }
  1231. pEntry = ldap_first_entry(pldap, pldapMsgExchangeServer);
  1232. if(pEntry == NULL)
  1233. {
  1234. dbgprintf("No Exchange Server object found for %s on domain controller,\n",
  1235. szServerName);
  1236. dwErr = ERROR_SUCCESS;
  1237. goto Cleanup;
  1238. }
  1239. dbgprintf("LDAP search returned some results, examining them.\n");
  1240. // Loop through the Exchange server objects
  1241. while(pEntry)
  1242. {
  1243. dbgprintf("Examining next object for attributes we are interested in.\n");
  1244. // Get the Exchange server-DN
  1245. rgszExchangeServerDN = ldap_get_values(
  1246. pldap,
  1247. pEntry,
  1248. rgszExchangeServerAttrs[0]);
  1249. if(rgszExchangeServerDN == NULL)
  1250. {
  1251. errprintf("Unexpected error reading the distinguishedName attribute"
  1252. " on the Exchange Server object. The attribute was not set"
  1253. " on the object. This is a required attribute.\n");
  1254. dwErr = ERROR_INVALID_DATA;
  1255. goto Cleanup;
  1256. }
  1257. else if((cValues = ldap_count_values(rgszExchangeServerDN)) != 1)
  1258. {
  1259. errprintf("Unexpected error reading the distinguishedName attribute"
  1260. " on the Exchange Server object. The attribute is supposed to"
  1261. " have a single string value, instead %d values were"
  1262. " returned.\n", cValues);
  1263. dwErr = ERROR_INVALID_DATA;
  1264. goto Cleanup;
  1265. }
  1266. else
  1267. {
  1268. dbgprintf("Successfully read the distinguishedName attribute on the"
  1269. " Exchange Server object. The value of the attribute is %s\n",
  1270. rgszExchangeServerDN[0]);
  1271. }
  1272. // Get the Exchange server network name
  1273. rgszExchangeServerNetworkName = ldap_get_values(
  1274. pldap,
  1275. pEntry,
  1276. rgszExchangeServerAttrs[1]);
  1277. if(!rgszExchangeServerNetworkName)
  1278. {
  1279. errprintf("The networkName attribute was not set on the Exchange"
  1280. " Server object. This is a required attribute. The DN of the"
  1281. " problematic object is %s\n", rgszExchangeServerDN[0]);
  1282. dwErr = ERROR_INVALID_DATA;
  1283. goto Cleanup;
  1284. }
  1285. // This is a multi-valued string attribute
  1286. cch = _snprintf(szSearchNetworkName, sizeof(szSearchNetworkName),
  1287. "ncacn_ip_tcp:%s", szServerName);
  1288. if(cch < 0)
  1289. {
  1290. errprintf("Exchange server name too long for this tool to handle\n");
  1291. dwErr = ERROR_INVALID_DATA;
  1292. goto Cleanup;
  1293. }
  1294. cValues = ldap_count_values(rgszExchangeServerNetworkName);
  1295. dbgprintf("The search returned the following %d values for the"
  1296. " networkName attribute for the Exchange Server object for %s\n",
  1297. cValues, szServerName);
  1298. dbgprintf("Attempting to match the TCP/IP networkName of the Exchange"
  1299. " Server object returned from the domain controller against the FQDN"
  1300. " we are searching for\n");
  1301. for(i = 0; i < cValues; i++)
  1302. {
  1303. dbgprintf("%d> networkName: %s", i, rgszExchangeServerNetworkName[i]);
  1304. if(!_stricmp(rgszExchangeServerNetworkName[i], szSearchNetworkName))
  1305. {
  1306. // This is an internal server
  1307. dbgprintf("...match succeeded\n");
  1308. dbgprintf("%s is an Exchange Server in the Org.\n",
  1309. szServerName);
  1310. *pfFound = TRUE;
  1311. if(ppszServerDN != NULL)
  1312. {
  1313. *ppszServerDN =
  1314. new char[lstrlen(rgszExchangeServerDN[0]) + 1];
  1315. if(*ppszServerDN == NULL)
  1316. {
  1317. errprintf("Out of memory allocating space for Exchange"
  1318. " Server object DN\n");
  1319. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  1320. goto Cleanup;
  1321. }
  1322. lstrcpy(*ppszServerDN, rgszExchangeServerDN[0]);
  1323. }
  1324. dwErr = ERROR_SUCCESS;
  1325. goto Cleanup;
  1326. }
  1327. dbgprintf("...match failed\n");
  1328. }
  1329. dbgprintf("No networkName on this object matched the server we are "
  1330. " searching for. Checking for more objects returned by search.\n");
  1331. pEntry = ldap_next_entry(pldap, pEntry);
  1332. }
  1333. dbgprintf("Done examining all objects returned by search. No match found.\n");
  1334. dwErr = ERROR_SUCCESS;
  1335. Cleanup:
  1336. if(rgszExchangeServerNetworkName)
  1337. ldap_value_free(rgszExchangeServerNetworkName);
  1338. if(rgszExchangeServerDN)
  1339. ldap_value_free(rgszExchangeServerDN);
  1340. if(pldapMsgExchangeServer)
  1341. ldap_msgfree(pldapMsgExchangeServer);
  1342. return dwErr;
  1343. }
  1344. //-----------------------------------------------------------------------------
  1345. // Description:
  1346. // Destructor for async DNS class. It merely signals when the async
  1347. // resolve has finished. Since this object is deleted by completing ATQ
  1348. // threads on success, we need an explicit way of telling the caller
  1349. // when the resolve has finished.
  1350. //-----------------------------------------------------------------------------
  1351. CAsyncTestDns::~CAsyncTestDns()
  1352. {
  1353. if(m_hCompletion != INVALID_HANDLE_VALUE)
  1354. {
  1355. SetEvent(m_hCompletion);
  1356. if(m_fMxLoopBack)
  1357. SetProgramStatus(DNSDIAG_LOOPBACK);
  1358. }
  1359. }
  1360. //-----------------------------------------------------------------------------
  1361. // Description:
  1362. // Virtual method that is called by the async DNS base class when a query
  1363. // needs to be retried. This function creates a new DNS object and spins
  1364. // off a repeat of the previous async query. The difference is only that
  1365. // the DNS serverlist has probably undergone some state changes with some
  1366. // servers being marked down or fUdp is different from the original query.
  1367. //
  1368. // Arguments:
  1369. // IN BOOL fUdp - What protocol to use for the retry query
  1370. //
  1371. // Returns:
  1372. // TRUE on success
  1373. // FALSE if something failed. Diagnostic messages are printed.
  1374. //-----------------------------------------------------------------------------
  1375. BOOL CAsyncTestDns::RetryAsyncDnsQuery(BOOL fUdp)
  1376. {
  1377. DWORD dwStatus = ERROR_SUCCESS;
  1378. CAsyncTestDns *pAsyncRetryDns = NULL;
  1379. if(GetDnsList()->GetUpServerCount() == 0)
  1380. {
  1381. errprintf("No working DNS servers to retry query with.\n");
  1382. return FALSE;
  1383. }
  1384. dbgprintf("There are %d DNS servers marked as working. Trying the next"
  1385. " one\n", GetDnsList()->GetUpServerCount());
  1386. pAsyncRetryDns = new CAsyncTestDns(m_FQDNToDrop, m_fGlobalList,
  1387. m_hCompletion);
  1388. if(!pAsyncRetryDns)
  1389. {
  1390. errprintf("Unable to create new query. Out of memory.\n");
  1391. return FALSE;
  1392. }
  1393. dwStatus = pAsyncRetryDns->Dns_QueryLib(
  1394. m_HostName,
  1395. DNS_TYPE_MX,
  1396. m_dwFlags,
  1397. fUdp,
  1398. GetDnsList(),
  1399. m_fGlobalList);
  1400. if(dwStatus == ERROR_SUCCESS)
  1401. {
  1402. // New query object will flag completion event
  1403. m_hCompletion = INVALID_HANDLE_VALUE;
  1404. return TRUE;
  1405. }
  1406. errprintf("DNS query failed. The Win32 error is %d.\n", dwStatus);
  1407. delete pAsyncRetryDns;
  1408. return FALSE;
  1409. }
  1410. //-----------------------------------------------------------------------------
  1411. // Description:
  1412. // This is a virtual function declared in the base CAsyncMxDns object.
  1413. // When the MX resolution is finished, this virtual function is invoked
  1414. // so that the user can do custom app-specific processing. In the case
  1415. // of SMTP this consists of spinning off an async connect to the IP
  1416. // addresses reported in m_AuxList. In this diagnostic application
  1417. // we merely display the results, and if results were not found (an
  1418. // error status is passed in), then we print the error message.
  1419. //
  1420. // In this app, we also set m_hCompletion to signal that the resolve
  1421. // is done. The main thread waiting for us in WaitForQueryCompletion
  1422. // will then exit.
  1423. //
  1424. // Arguments:
  1425. // IN DWORD status - DNS error code from resolve
  1426. //
  1427. // Notes:
  1428. // Results are available in m_AuxList
  1429. //-----------------------------------------------------------------------------
  1430. void CAsyncTestDns::HandleCompletedData(DNS_STATUS status)
  1431. {
  1432. PLIST_ENTRY pListHead = NULL;
  1433. PLIST_ENTRY pListTail = NULL;
  1434. PLIST_ENTRY pListCurrent = NULL;
  1435. LPSTR pszIpAddr = NULL;
  1436. DWORD i = 0;
  1437. PMXIPLIST_ENTRY pMxEntry = NULL;
  1438. BOOL fFoundIpAddresses = FALSE;
  1439. if(status == ERROR_NOT_FOUND)
  1440. {
  1441. SetProgramStatus(DNSDIAG_NON_EXISTENT);
  1442. goto Exit;
  1443. }
  1444. else if(!m_AuxList || m_AuxList->NumRecords == 0 || m_AuxList->DnsArray[0] == NULL)
  1445. {
  1446. SetProgramStatus(DNSDIAG_NOT_FOUND);
  1447. errprintf("The target server could not be resolved to IP addresses!\n");
  1448. msgprintf("If the VSI/domain is configured with a fallback"
  1449. " smarthost delivery will be attempted to that smarthost.\n");
  1450. goto Exit;
  1451. }
  1452. msgprintf("\nTarget hostnames and IP addresses\n");
  1453. msgprintf("---------------------------------\n");
  1454. for(i = 0; i < m_AuxList->NumRecords && m_AuxList->DnsArray[i] != NULL; i++)
  1455. {
  1456. pListTail = &(m_AuxList->DnsArray[i]->IpListHead);
  1457. pListHead = m_AuxList->DnsArray[i]->IpListHead.Flink;
  1458. pListCurrent = pListHead;
  1459. msgprintf("HostName: \"%s\"\n", m_AuxList->DnsArray[i]->DnsName);
  1460. if(pListCurrent == pListTail)
  1461. errprintf("\tNo IP addresses for this name!\n");
  1462. while(pListCurrent != pListTail)
  1463. {
  1464. // Atleast 1 IP address was found
  1465. fFoundIpAddresses = TRUE;
  1466. pMxEntry = CONTAINING_RECORD(pListCurrent, MXIPLIST_ENTRY, ListEntry);
  1467. pszIpAddr = iptostring(pMxEntry->IpAddress);
  1468. if(pszIpAddr == NULL)
  1469. {
  1470. errprintf("\tUnexpected error. Failed to read IP address, going on to next.\n");
  1471. pListCurrent = pListCurrent->Flink;
  1472. continue;
  1473. }
  1474. msgprintf("\t%s\n", pszIpAddr);
  1475. pListCurrent = pListCurrent->Flink;
  1476. };
  1477. }
  1478. if(fFoundIpAddresses)
  1479. SetProgramStatus(DNSDIAG_RESOLVED);
  1480. else
  1481. SetProgramStatus(DNSDIAG_NOT_FOUND);
  1482. Exit:
  1483. return;
  1484. }
  1485. //-----------------------------------------------------------------------------
  1486. // Description:
  1487. // If the -v option is being used to simulate a VSI, this virtual function
  1488. // checks if dwIp is one of the IP addresses in the VSI bindings for the
  1489. // VS being simulated. g_pipBindings is initialized at startup of this
  1490. // app from the metabase.
  1491. // Arguments:
  1492. // IN DWORD dwIp - IP address to check
  1493. // Returns:
  1494. // TRUE is dwIp is a local-binding
  1495. // FALSE if not
  1496. //-----------------------------------------------------------------------------
  1497. BOOL CAsyncTestDns::IsAddressMine(DWORD dwIp)
  1498. {
  1499. DWORD i = 0;
  1500. if(g_pipBindings->cAddrCount == 0)
  1501. return FALSE;
  1502. for(i = 0; i < g_pipBindings->cAddrCount; i++)
  1503. {
  1504. if(g_pipBindings->aipAddrs[i] == dwIp)
  1505. return TRUE;
  1506. }
  1507. return FALSE;
  1508. }
  1509. //-----------------------------------------------------------------------------
  1510. // Description:
  1511. // Various output functions. These print informational, debugging and error
  1512. // messages in various colors depending on the current "mode" as set in
  1513. // the global variable g_fDebug.
  1514. //
  1515. // The CDnsLogToFile is instantiated is a global variable. The DNS library
  1516. // checks to see if there is a non-NULL CDnsLogToFile* and if it is
  1517. // present the messages are directed to this object.
  1518. //-----------------------------------------------------------------------------
  1519. void CDnsLogToFile::DnsPrintfMsg(char *szFormat, ...)
  1520. {
  1521. va_list argptr;
  1522. SetMsgColor();
  1523. va_start(argptr, szFormat);
  1524. vprintf(szFormat, argptr);
  1525. va_end(argptr);
  1526. SetNormalColor();
  1527. }
  1528. void CDnsLogToFile::DnsPrintfErr(char *szFormat, ...)
  1529. {
  1530. va_list argptr;
  1531. SetErrColor();
  1532. va_start(argptr, szFormat);
  1533. vprintf(szFormat, argptr);
  1534. va_end(argptr);
  1535. SetNormalColor();
  1536. }
  1537. void CDnsLogToFile::DnsPrintfDbg(char *szFormat, ...)
  1538. {
  1539. va_list argptr;
  1540. if(!g_fDebug)
  1541. return;
  1542. va_start(argptr, szFormat);
  1543. vprintf(szFormat, argptr);
  1544. va_end(argptr);
  1545. }
  1546. void CDnsLogToFile::DnsLogAsyncQuery(
  1547. char *pszQuestionName,
  1548. WORD wQuestionType,
  1549. DWORD dwFlags,
  1550. BOOL fUdp,
  1551. CDnsServerList *pDnsServerList)
  1552. {
  1553. char szFlags[32];
  1554. GetSmtpFlags(dwFlags, szFlags, sizeof(szFlags));
  1555. SetMsgColor();
  1556. printf("Created Async Query:\n");
  1557. printf("--------------------\n");
  1558. printf("\tQNAME = %s\n", pszQuestionName);
  1559. printf("\tType = %s (0x%x)\n", QueryType(wQuestionType), wQuestionType);
  1560. printf("\tFlags = %s (0x%x)\n", szFlags, dwFlags);
  1561. printf("\tProtocol = %s\n", fUdp ? "UDP" : "TCP");
  1562. printf("\tDNS Servers: (DNS cache will not be used)\n");
  1563. DnsLogServerList(pDnsServerList);
  1564. printf("\n");
  1565. SetNormalColor();
  1566. }
  1567. void CDnsLogToFile::DnsLogApiQuery(
  1568. char *pszQuestionName,
  1569. WORD wQuestionType,
  1570. DWORD dwApiFlags,
  1571. BOOL fGlobal,
  1572. PIP_ARRAY pipServers)
  1573. {
  1574. char szFlags[32];
  1575. GetDnsFlags(dwApiFlags, szFlags, sizeof(szFlags));
  1576. SetMsgColor();
  1577. printf("Querying via DNSAPI:\n");
  1578. printf("--------------------\n");
  1579. printf("\tQNAME = %s\n", pszQuestionName);
  1580. printf("\tType = %s (0x%x)\n", QueryType(wQuestionType), wQuestionType);
  1581. printf("\tFlags = %s, (0x%x)\n", szFlags, dwApiFlags);
  1582. printf("\tProtocol = Default UDP, TCP on truncation\n");
  1583. printf("\tServers: ");
  1584. if(fGlobal)
  1585. {
  1586. printf("(DNS cache will be used)\n");
  1587. }
  1588. else
  1589. {
  1590. printf("(DNS cache will not be used)\n");
  1591. }
  1592. if(pipServers)
  1593. PrintIPArray(pipServers, "\t");
  1594. else
  1595. printf("\tDefault DNS servers on box.\n");
  1596. printf("\n");
  1597. if(fGlobal == FALSE)
  1598. SetNormalColor();
  1599. }
  1600. void CDnsLogToFile::DnsLogResponse(
  1601. DWORD dwStatus,
  1602. PDNS_RECORD pDnsRecordList,
  1603. PBYTE pbMsg,
  1604. DWORD wLength)
  1605. {
  1606. PDNS_RECORD pDnsRecord = pDnsRecordList;
  1607. SetMsgColor();
  1608. printf("Received DNS Response:\n");
  1609. printf("----------------------\n");
  1610. switch(dwStatus)
  1611. {
  1612. case ERROR_SUCCESS:
  1613. printf("\tError: %d\n", dwStatus);
  1614. printf("\tDescription: Success\n");
  1615. break;
  1616. case DNS_INFO_NO_RECORDS:
  1617. printf("\tError: %d\n", dwStatus);
  1618. printf("\tDescription: No records could be located for this name\n");
  1619. break;
  1620. case DNS_ERROR_RCODE_NAME_ERROR:
  1621. printf("\tError: %d\n", dwStatus);
  1622. printf("\tDescription: No records exist for this name.\n");
  1623. break;
  1624. default:
  1625. printf("\tError: %d\n", dwStatus);
  1626. printf("\tDescription: Not available.\n");
  1627. break;
  1628. }
  1629. if(pDnsRecord)
  1630. {
  1631. printf("\tThese records were received:\n");
  1632. PrintRecordList(pDnsRecord, "\t");
  1633. printf("\n");
  1634. }
  1635. SetNormalColor();
  1636. }
  1637. void CDnsLogToFile::DnsLogServerList(CDnsServerList *pDnsServerList)
  1638. {
  1639. LPSTR pszAddress = NULL;
  1640. PIP_ARRAY pipArray = NULL;
  1641. if(!pDnsServerList->CopyList(&pipArray))
  1642. {
  1643. printf("Error, out of memory printing serverlist\n");
  1644. return;
  1645. }
  1646. for(DWORD i = 0; i < pDnsServerList->GetCount(); i++)
  1647. {
  1648. pszAddress = iptostring(pipArray->aipAddrs[i]);
  1649. printf("\t%s\n", pszAddress);
  1650. }
  1651. delete pipArray;
  1652. }
  1653. void PrintRecordList(PDNS_RECORD pDnsRecordList, char *pszPrefix)
  1654. {
  1655. PDNS_RECORD pDnsRecord = pDnsRecordList;
  1656. while(pDnsRecord)
  1657. {
  1658. PrintRecord(pDnsRecord, pszPrefix);
  1659. pDnsRecord = pDnsRecord->pNext;
  1660. }
  1661. }
  1662. void PrintRecord(PDNS_RECORD pDnsRecord, char *pszPrefix)
  1663. {
  1664. LPSTR pszAddress = NULL;
  1665. switch(pDnsRecord->wType)
  1666. {
  1667. case DNS_TYPE_MX:
  1668. printf(
  1669. "%s%s MX %d %s\n",
  1670. pszPrefix,
  1671. pDnsRecord->nameOwner,
  1672. pDnsRecord->Data.MX.wPreference,
  1673. pDnsRecord->Data.MX.nameExchange);
  1674. break;
  1675. case DNS_TYPE_CNAME:
  1676. printf(
  1677. "%s%s CNAME %s\n",
  1678. pszPrefix,
  1679. pDnsRecord->nameOwner,
  1680. pDnsRecord->Data.CNAME.nameHost);
  1681. break;
  1682. case DNS_TYPE_A:
  1683. pszAddress = iptostring(pDnsRecord->Data.A.ipAddress);
  1684. printf(
  1685. "%s%s A %s\n",
  1686. pszPrefix,
  1687. pDnsRecord->nameOwner,
  1688. pszAddress);
  1689. break;
  1690. case DNS_TYPE_SOA:
  1691. printf("%s%s SOA (SOA records are not used by us)\n",
  1692. pszPrefix,
  1693. pDnsRecord->nameOwner);
  1694. break;
  1695. default:
  1696. printf("%s%s (Record type = %d) Unknown record type\n",
  1697. pszPrefix,
  1698. pDnsRecord->nameOwner,
  1699. pDnsRecord->wType);
  1700. break;
  1701. }
  1702. }
  1703. void msgprintf(char *szFormat, ...)
  1704. {
  1705. va_list argptr;
  1706. SetMsgColor();
  1707. va_start(argptr, szFormat);
  1708. vprintf(szFormat, argptr);
  1709. va_end(argptr);
  1710. SetNormalColor();
  1711. }
  1712. void errprintf(char *szFormat, ...)
  1713. {
  1714. va_list argptr;
  1715. SetErrColor();
  1716. va_start(argptr, szFormat);
  1717. vprintf(szFormat, argptr);
  1718. va_end(argptr);
  1719. SetNormalColor();
  1720. }
  1721. void dbgprintf(char *szFormat, ...)
  1722. {
  1723. va_list argptr;
  1724. if(!g_fDebug)
  1725. return;
  1726. va_start(argptr, szFormat);
  1727. vprintf(szFormat, argptr);
  1728. va_end(argptr);
  1729. }
  1730. void SetMsgColor()
  1731. {
  1732. if(g_fDebug)
  1733. {
  1734. SetConsoleTextAttribute(g_hConsole,
  1735. FOREGROUND_GREEN | FOREGROUND_INTENSITY);
  1736. }
  1737. }
  1738. void SetErrColor()
  1739. {
  1740. if(g_fDebug)
  1741. {
  1742. SetConsoleTextAttribute(g_hConsole,
  1743. FOREGROUND_RED | FOREGROUND_INTENSITY);
  1744. }
  1745. }
  1746. void SetNormalColor()
  1747. {
  1748. if(g_fDebug)
  1749. {
  1750. SetConsoleTextAttribute(g_hConsole,
  1751. FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
  1752. }
  1753. }