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.

681 lines
21 KiB

  1. #include "dnsincs.h"
  2. #include <stdlib.h>
  3. extern void DeleteDnsRec(PSMTPDNS_RECS pDnsRec);
  4. CAsyncMxDns::CAsyncMxDns(char *MyFQDN)
  5. {
  6. lstrcpyn(m_FQDNToDrop, MyFQDN, sizeof(m_FQDNToDrop));
  7. m_fUsingMx = TRUE;
  8. m_Index = 0;
  9. m_LocalPref = 256;
  10. m_SeenLocal = FALSE;
  11. m_AuxList = NULL;
  12. m_fMxLoopBack = FALSE;
  13. ZeroMemory (m_Weight, sizeof(m_Weight));
  14. ZeroMemory (m_Prefer, sizeof(m_Prefer));
  15. }
  16. //-----------------------------------------------------------------------------
  17. // Description:
  18. // Given a pDnsRec (array of host IP pairs) and an index into it, this
  19. // tries to resolve the host at the Index position. It is assumed that
  20. // the caller (GetMissingIpAddresses) has checked that the host at that
  21. // index lacks an IP address.
  22. // Arguments:
  23. // IN PSMTPDNS_RECS pDnsRec --- Array of (host, IP) pairs.
  24. // IN DWORD Index --- Index of host in pDnsRec to set IP for.
  25. // Returns:
  26. // TRUE --- Success IP was filled in for host.
  27. // FALSE --- Either the host was not resolved from DNS or an error
  28. // occurred (like "out of memory").
  29. //-----------------------------------------------------------------------------
  30. BOOL CAsyncMxDns::GetIpFromDns(PSMTPDNS_RECS pDnsRec, DWORD Index)
  31. {
  32. MXIPLIST_ENTRY * pEntry = NULL;
  33. BOOL fReturn = FALSE;
  34. DWORD dwStatus = ERROR_SUCCESS;
  35. DWORD rgdwIpAddresses[SMTP_MAX_DNS_ENTRIES];
  36. DWORD cIpAddresses = SMTP_MAX_DNS_ENTRIES;
  37. PIP_ARRAY pipDnsList = NULL;
  38. TraceFunctEnterEx((LPARAM) this, "CAsyncMxDns::GetIpFromDns");
  39. fReturn = GetDnsIpArrayCopy(&pipDnsList);
  40. if(!fReturn)
  41. {
  42. ErrorTrace((LPARAM) this, "Unable to get DNS server list copy");
  43. TraceFunctLeaveEx((LPARAM) this);
  44. return FALSE;
  45. }
  46. dwStatus = ResolveHost(
  47. pDnsRec->DnsArray[Index]->DnsName,
  48. pipDnsList,
  49. DNS_QUERY_STANDARD,
  50. rgdwIpAddresses,
  51. &cIpAddresses);
  52. if(dwStatus == ERROR_SUCCESS)
  53. {
  54. fReturn = TRUE;
  55. for (DWORD Loop = 0; !IsShuttingDown() && Loop < cIpAddresses; Loop++)
  56. {
  57. pEntry = new MXIPLIST_ENTRY;
  58. if(pEntry != NULL)
  59. {
  60. pDnsRec->DnsArray[Index]->NumEntries++;
  61. CopyMemory(&pEntry->IpAddress, &rgdwIpAddresses[Loop], 4);
  62. InsertTailList(&pDnsRec->DnsArray[Index]->IpListHead, &pEntry->ListEntry);
  63. }
  64. else
  65. {
  66. fReturn = FALSE;
  67. ErrorTrace((LPARAM) this, "Not enough memory");
  68. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  69. break;
  70. }
  71. }
  72. }
  73. else
  74. {
  75. ErrorTrace((LPARAM) this, "gethostbyname failed on %s", pDnsRec->DnsArray[Index]->DnsName);
  76. SetLastError(ERROR_NO_MORE_ITEMS);
  77. }
  78. ReleaseDnsIpArray(pipDnsList);
  79. TraceFunctLeaveEx((LPARAM) this);
  80. return fReturn;
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Description:
  84. // This runs through the list of hosts (MX hosts, or if no MX records were
  85. // returned, the single target host) and verifies that they all have been
  86. // resolved to IP addresses. If any have been found that do not have IP
  87. // addresses, it will call GetIpFromDns to resolve it.
  88. // Arguments:
  89. // IN PSMTPDNS_RECS pDnsRec -- Object containing Host-IP pairs. Hosts
  90. // without and IP are filled in.
  91. // Returns:
  92. // TRUE -- Success, all hosts have IP addresses.
  93. // FALSE -- Unable to resolve all hosts to IP addresses, or some internal
  94. // error occurred (like "out of memory" or "shutdown in progress".
  95. //-----------------------------------------------------------------------------
  96. BOOL CAsyncMxDns::GetMissingIpAddresses(PSMTPDNS_RECS pDnsRec)
  97. {
  98. DWORD Count = 0;
  99. DWORD Error = 0;
  100. BOOL fSucceededOnce = FALSE;
  101. if(pDnsRec == NULL)
  102. {
  103. return FALSE;
  104. }
  105. while(!IsShuttingDown() && pDnsRec->DnsArray[Count] != NULL)
  106. {
  107. if(IsListEmpty(&pDnsRec->DnsArray[Count]->IpListHead))
  108. {
  109. SetLastError(NO_ERROR);
  110. if(!GetIpFromDns(pDnsRec, Count))
  111. {
  112. Error = GetLastError();
  113. if(Error != ERROR_NO_MORE_ITEMS)
  114. {
  115. return FALSE;
  116. }
  117. }
  118. else
  119. {
  120. fSucceededOnce = TRUE;
  121. }
  122. }
  123. else
  124. {
  125. fSucceededOnce = TRUE;
  126. }
  127. Count++;
  128. }
  129. return ( fSucceededOnce );
  130. }
  131. int MxRand(char * host)
  132. {
  133. int hfunc = 0;
  134. unsigned int seed = 0;;
  135. seed = rand() & 0xffff;
  136. hfunc = seed;
  137. while (*host != '\0')
  138. {
  139. int c = *host++;
  140. if (isascii((UCHAR)c) && isupper((UCHAR)c))
  141. c = tolower(c);
  142. hfunc = ((hfunc << 1) ^ c) % 2003;
  143. }
  144. hfunc &= 0xff;
  145. return hfunc;
  146. }
  147. BOOL CAsyncMxDns::CheckList(void)
  148. {
  149. MXIPLIST_ENTRY * pEntry = NULL;
  150. BOOL fRet = TRUE;
  151. DWORD dwStatus = ERROR_SUCCESS;
  152. DWORD rgdwIpAddresses[SMTP_MAX_DNS_ENTRIES];
  153. DWORD cIpAddresses = SMTP_MAX_DNS_ENTRIES;
  154. PIP_ARRAY pipDnsList = NULL;
  155. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::CheckList");
  156. if(m_Index == 0)
  157. {
  158. DebugTrace((LPARAM) this, "m_Index == 0 in CheckList");
  159. m_fUsingMx = FALSE;
  160. DeleteDnsRec(m_AuxList);
  161. m_AuxList = new SMTPDNS_RECS;
  162. if(m_AuxList == NULL)
  163. {
  164. ErrorTrace((LPARAM) this, "m_AuxList = new SMTPDNS_RECS failed");
  165. TraceFunctLeaveEx((LPARAM)this);
  166. return FALSE;
  167. }
  168. ZeroMemory(m_AuxList, sizeof(SMTPDNS_RECS));
  169. m_AuxList->NumRecords = 1;
  170. m_AuxList->DnsArray[0] = new MX_NAMES;
  171. if(m_AuxList->DnsArray[0] == NULL)
  172. {
  173. ErrorTrace((LPARAM) this, "m_AuxList->DnsArray[0] = new MX_NAMES failed");
  174. TraceFunctLeaveEx((LPARAM)this);
  175. return FALSE;
  176. }
  177. m_AuxList->DnsArray[0]->NumEntries = 0;
  178. InitializeListHead(&m_AuxList->DnsArray[0]->IpListHead);
  179. lstrcpyn(m_AuxList->DnsArray[0]->DnsName, m_HostName,
  180. sizeof(m_AuxList->DnsArray[m_Index]->DnsName));
  181. fRet = GetDnsIpArrayCopy(&pipDnsList);
  182. if(!fRet)
  183. {
  184. ErrorTrace((LPARAM) this, "Unable to get DNS server list copy");
  185. TraceFunctLeaveEx((LPARAM) this);
  186. return FALSE;
  187. }
  188. dwStatus = ResolveHost(
  189. m_HostName,
  190. pipDnsList,
  191. DNS_QUERY_STANDARD,
  192. rgdwIpAddresses,
  193. &cIpAddresses);
  194. if(dwStatus == ERROR_SUCCESS && cIpAddresses)
  195. {
  196. for (DWORD Loop = 0; Loop < cIpAddresses; Loop++)
  197. {
  198. pEntry = new MXIPLIST_ENTRY;
  199. if(pEntry != NULL)
  200. {
  201. m_AuxList->DnsArray[0]->NumEntries++;
  202. CopyMemory(&pEntry->IpAddress, &rgdwIpAddresses[Loop], 4);
  203. InsertTailList(&m_AuxList->DnsArray[0]->IpListHead, &pEntry->ListEntry);
  204. }
  205. else
  206. {
  207. fRet = FALSE;
  208. ErrorTrace((LPARAM) this, "pEntry = new MXIPLIST_ENTRY failed in CheckList");
  209. break;
  210. }
  211. }
  212. }
  213. else
  214. {
  215. fRet = FALSE;
  216. }
  217. ReleaseDnsIpArray(pipDnsList);
  218. }
  219. TraceFunctLeaveEx((LPARAM)this);
  220. return fRet;
  221. }
  222. BOOL CAsyncMxDns::SortMxList(void)
  223. {
  224. BOOL fRet = TRUE;
  225. /* sort the records */
  226. for (DWORD i = 0; i < m_Index; i++)
  227. {
  228. for (DWORD j = i + 1; j < m_Index; j++)
  229. {
  230. if (m_Prefer[i] > m_Prefer[j] ||
  231. (m_Prefer[i] == m_Prefer[j] && m_Weight[i] > m_Weight[j]))
  232. {
  233. DWORD temp;
  234. MX_NAMES *temp1;
  235. temp = m_Prefer[i];
  236. m_Prefer[i] = m_Prefer[j];
  237. m_Prefer[j] = temp;
  238. temp1 = m_AuxList->DnsArray[i];
  239. m_AuxList->DnsArray[i] = m_AuxList->DnsArray[j];
  240. m_AuxList->DnsArray[j] = temp1;
  241. temp = m_Weight[i];
  242. m_Weight[i] = m_Weight[j];
  243. m_Weight[j] = temp;
  244. }
  245. }
  246. if (m_SeenLocal && m_Prefer[i] >= m_LocalPref)
  247. {
  248. /* truncate higher preference part of list */
  249. m_Index = i;
  250. }
  251. }
  252. m_AuxList->NumRecords = m_Index;
  253. if(!CheckList())
  254. {
  255. DeleteDnsRec(m_AuxList);
  256. m_AuxList = NULL;
  257. fRet = FALSE;
  258. }
  259. return fRet;
  260. }
  261. void CAsyncMxDns::ProcessMxRecord(PDNS_RECORD pnewRR)
  262. {
  263. DWORD Len = 0;
  264. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::ProcessMxRecord");
  265. //
  266. // Leave room for NULL-termination of array
  267. //
  268. if(m_Index >= SMTP_MAX_DNS_ENTRIES-1)
  269. {
  270. DebugTrace((LPARAM) this, "SMTP_MAX_DNS_ENTRIES reached for %s", m_HostName);
  271. TraceFunctLeaveEx((LPARAM)this);
  272. return;
  273. }
  274. if((pnewRR->wType == DNS_TYPE_MX) && pnewRR->Data.MX.nameExchange)
  275. {
  276. Len = lstrlen(pnewRR->Data.MX.nameExchange);
  277. if(pnewRR->Data.MX.nameExchange[Len - 1] == '.')
  278. {
  279. pnewRR->Data.MX.nameExchange[Len - 1] = '\0';
  280. }
  281. DebugTrace((LPARAM) this, "Received MX rec %s with priority %d for %s", pnewRR->Data.MX.nameExchange, pnewRR->Data.MX.wPreference, m_HostName);
  282. if(lstrcmpi(pnewRR->Data.MX.nameExchange, m_FQDNToDrop))
  283. {
  284. m_AuxList->DnsArray[m_Index] = new MX_NAMES;
  285. if(m_AuxList->DnsArray[m_Index])
  286. {
  287. m_AuxList->DnsArray[m_Index]->NumEntries = 0;;
  288. InitializeListHead(&m_AuxList->DnsArray[m_Index]->IpListHead);
  289. lstrcpyn(m_AuxList->DnsArray[m_Index]->DnsName,pnewRR->Data.MX.nameExchange, sizeof(m_AuxList->DnsArray[m_Index]->DnsName));
  290. m_Weight[m_Index] = MxRand (m_AuxList->DnsArray[m_Index]->DnsName);
  291. m_Prefer[m_Index] = pnewRR->Data.MX.wPreference;
  292. m_Index++;
  293. }
  294. else
  295. {
  296. DebugTrace((LPARAM) this, "Out of memory allocating MX_NAMES for %s", m_HostName);
  297. }
  298. }
  299. else
  300. {
  301. if (!m_SeenLocal || pnewRR->Data.MX.wPreference < m_LocalPref)
  302. m_LocalPref = pnewRR->Data.MX.wPreference;
  303. m_SeenLocal = TRUE;
  304. }
  305. }
  306. else if(pnewRR->wType == DNS_TYPE_A)
  307. {
  308. MXIPLIST_ENTRY * pEntry = NULL;
  309. for(DWORD i = 0; i < m_Index; i++)
  310. {
  311. if(lstrcmpi(pnewRR->nameOwner, m_AuxList->DnsArray[i]->DnsName) == 0)
  312. {
  313. pEntry = new MXIPLIST_ENTRY;
  314. if(pEntry != NULL)
  315. {
  316. m_AuxList->DnsArray[i]->NumEntries++;;
  317. pEntry->IpAddress = pnewRR->Data.A.ipAddress;
  318. InsertTailList(&m_AuxList->DnsArray[i]->IpListHead, &pEntry->ListEntry);
  319. }
  320. break;
  321. }
  322. }
  323. }
  324. TraceFunctLeaveEx((LPARAM)this);
  325. }
  326. void CAsyncMxDns::ProcessARecord(PDNS_RECORD pnewRR)
  327. {
  328. MXIPLIST_ENTRY * pEntry = NULL;
  329. if(pnewRR->wType == DNS_TYPE_A)
  330. {
  331. pEntry = new MXIPLIST_ENTRY;
  332. if(pEntry != NULL)
  333. {
  334. pEntry->IpAddress = pnewRR->Data.A.ipAddress;
  335. InsertTailList(&m_AuxList->DnsArray[0]->IpListHead, &pEntry->ListEntry);
  336. }
  337. }
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Description:
  341. // Checks to see if any of the IP addresses returned by DNS belong to this
  342. // machine. This is a common configuration when there are backup mail
  343. // spoolers. To avoid mail looping, we should delete all MX records that
  344. // are less preferred than the record containing the local IP address.
  345. //
  346. // Arguments:
  347. // None.
  348. // Returns:
  349. // TRUE if no loopback
  350. // FALSE if loopback detected
  351. //-----------------------------------------------------------------------------
  352. BOOL CAsyncMxDns::CheckMxLoopback()
  353. {
  354. ULONG i = 0;
  355. ULONG cLocalIndex = 0;
  356. BOOL fSeenLocal = TRUE;
  357. DWORD dwIpAddress = INADDR_NONE;
  358. DWORD dwLocalPref = 256;
  359. PLIST_ENTRY pListHead = NULL;
  360. PLIST_ENTRY pListTail = NULL;
  361. PLIST_ENTRY pListCurrent = NULL;
  362. PMXIPLIST_ENTRY pMxIpListEntry = NULL;
  363. TraceFunctEnterEx((LPARAM)this, "CAsyncMxDns::CheckMxLoopback");
  364. if(!m_AuxList)
  365. {
  366. TraceFunctLeaveEx((LPARAM)this);
  367. return TRUE;
  368. }
  369. //
  370. // m_AuxList is a sorted list of MX records. Scan through it searching
  371. // for an MX record with a local-IP address. cLocalIndex is set to the
  372. // index, within m_AuxList, of this record.
  373. //
  374. while(m_AuxList->DnsArray[cLocalIndex] != NULL)
  375. {
  376. pListTail = &(m_AuxList->DnsArray[cLocalIndex]->IpListHead);
  377. pListHead = m_AuxList->DnsArray[cLocalIndex]->IpListHead.Flink;
  378. pListCurrent = pListHead;
  379. while(pListCurrent != pListTail)
  380. {
  381. pMxIpListEntry = CONTAINING_RECORD(pListCurrent, MXIPLIST_ENTRY, ListEntry);
  382. dwIpAddress = pMxIpListEntry->IpAddress;
  383. if(IsAddressMine(dwIpAddress))
  384. {
  385. DNS_PRINTF_MSG("Local host's IP is one of the target IPs.\n");
  386. DNS_PRINTF_MSG("Discarding all equally or less-preferred IP addresses.\n");
  387. DebugTrace((LPARAM)this, "Local record found in MX list, name=%s, pref=%d, ip=%08x",
  388. m_AuxList->DnsArray[cLocalIndex]->DnsName,
  389. m_Prefer[cLocalIndex],
  390. dwIpAddress);
  391. // All records with preference > m_Prefer[cLocalIndex] should be deleted. Since
  392. // m_AuxList is sorted by preference, we need to delete everthing with index >
  393. // cLocalIndex. However since there may be some records with preference == local-
  394. // preference, which occur before cLocalIndex, we walk backwards till we find
  395. // the first record with preference = m_Prefer[cLocalIndex].
  396. dwLocalPref = m_Prefer[cLocalIndex];
  397. while(cLocalIndex > 0 && dwLocalPref == m_Prefer[cLocalIndex])
  398. cLocalIndex--;
  399. if(dwLocalPref != m_Prefer[cLocalIndex])
  400. cLocalIndex++;
  401. fSeenLocal = TRUE;
  402. // All records > cLocalIndex are even less preferred than this one,
  403. // (since m_AuxList already sorted) and will be deleted.
  404. goto END_SEARCH;
  405. }
  406. pListCurrent = pListCurrent->Flink;
  407. }
  408. cLocalIndex++;
  409. }
  410. END_SEARCH:
  411. //
  412. // If a local-IP address was found, delete all less-preferred records
  413. //
  414. if(fSeenLocal)
  415. {
  416. DebugTrace((LPARAM)this,
  417. "Deleting all MX records with lower preference than %d", m_Prefer[cLocalIndex]);
  418. for(i = cLocalIndex; m_AuxList->DnsArray[i] != NULL; i++)
  419. {
  420. if(!m_AuxList->DnsArray[i]->DnsName[0])
  421. continue;
  422. while(!IsListEmpty(&(m_AuxList->DnsArray[i]->IpListHead)))
  423. {
  424. pListCurrent = RemoveHeadList(&(m_AuxList->DnsArray[i]->IpListHead));
  425. pMxIpListEntry = CONTAINING_RECORD(pListCurrent, MXIPLIST_ENTRY, ListEntry);
  426. delete pMxIpListEntry;
  427. }
  428. delete m_AuxList->DnsArray[i];
  429. m_AuxList->DnsArray[i] = NULL;
  430. }
  431. m_AuxList->NumRecords = cLocalIndex;
  432. // No records left
  433. if(m_AuxList->NumRecords == 0)
  434. {
  435. DNS_PRINTF_ERR("DNS configuration error (loopback), messages will be NDRed.\n");
  436. DNS_PRINTF_ERR("Local host's IP address is the most preferred MX record.\n");
  437. ErrorTrace((LPARAM)this, "Possible misconfiguration: most preferred MX record is loopback");
  438. _ASSERT(m_AuxList->pMailMsgObj == NULL);
  439. delete m_AuxList;
  440. m_AuxList = NULL;
  441. return FALSE;
  442. }
  443. }
  444. TraceFunctLeaveEx((LPARAM)this);
  445. return TRUE;
  446. }
  447. void CAsyncMxDns::DnsProcessReply(
  448. DWORD status,
  449. PDNS_RECORD pRecordList)
  450. {
  451. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::DnsParseMessage");
  452. PDNS_RECORD pTmp = NULL;
  453. m_SeenLocal = FALSE;
  454. m_LocalPref = 256;
  455. m_AuxList = new SMTPDNS_RECS;
  456. if(!m_AuxList)
  457. {
  458. return;
  459. }
  460. ZeroMemory(m_AuxList, sizeof(SMTPDNS_RECS));
  461. //
  462. // Due to Raid #122555 m_fUsingMx is always TRUE in this function
  463. // - hence we will always go a GetHostByName() if there is no MX
  464. // record. It would be better Perf if we did a A record lookup.
  465. //
  466. DebugTrace((LPARAM) this, "Parsed DNS record for %s. status = 0x%08x", m_HostName, status);
  467. switch(status)
  468. {
  469. case ERROR_SUCCESS:
  470. //
  471. // Got the DNS record we want.
  472. //
  473. DNS_PRINTF_MSG("Processing MX/A records in reply.\n");
  474. DebugTrace((LPARAM) this, "Success: DNS record parsed");
  475. pTmp = pRecordList;
  476. while( pTmp )
  477. {
  478. if( m_fUsingMx )
  479. {
  480. ProcessMxRecord( pTmp );
  481. }
  482. else
  483. {
  484. ProcessARecord( pTmp );
  485. }
  486. pTmp = pTmp->pNext;
  487. }
  488. if(m_fUsingMx)
  489. {
  490. //
  491. // SortMxList sorts the MX records by preference and calls
  492. // gethostbyname() to resolve A records for Mail Exchangers
  493. // if needed (when the A records are not returned in the
  494. // supplementary info).
  495. //
  496. DNS_PRINTF_MSG("Sorting MX records by priority.\n");
  497. if(SortMxList())
  498. {
  499. status = ERROR_SUCCESS;
  500. DebugTrace((LPARAM) this, "SortMxList() succeeded.");
  501. }
  502. else
  503. {
  504. status = ERROR_RETRY;
  505. ErrorTrace((LPARAM) this, "SortMxList() failed. Message will stay queued.");
  506. }
  507. }
  508. break;
  509. case DNS_ERROR_RCODE_NAME_ERROR:
  510. // Fall through to using gethostbyname()
  511. case DNS_INFO_NO_RECORDS:
  512. // Non authoritative host not found.
  513. // Fall through to using gethostbyname()
  514. default:
  515. DebugTrace((LPARAM) this, "Error in query: status = 0x%08x.", status);
  516. //
  517. // Use gethostbyname to resolve the hostname:
  518. // One issue with our approach is that sometimes we will NDR the message
  519. // on non-permanent errors, "like WINS server down", when gethostbyname
  520. // fails. However, there's no way around it --- gethostbyname doesn't
  521. // report errors in a reliable manner, so it's not possible to distinguish
  522. // between permanent and temporary errors.
  523. //
  524. if (!CheckList ()) {
  525. if(status == DNS_ERROR_RCODE_NAME_ERROR) {
  526. DNS_PRINTF_ERR("Host does not exist in DNS. Messages will be NDRed.\n");
  527. ErrorTrace((LPARAM) this, "Authoritative error");
  528. status = ERROR_NOT_FOUND;
  529. } else {
  530. DNS_PRINTF_ERR("Host could not be resolved. Messages will be retried later.\n");
  531. ErrorTrace((LPARAM) this, "Retryable error");
  532. status = ERROR_RETRY;
  533. }
  534. } else {
  535. DebugTrace ((LPARAM) this, "Successfully resolved using gethostbyname");
  536. status = ERROR_SUCCESS;
  537. }
  538. break;
  539. }
  540. //
  541. // Make a last ditch effort to fill in the IP addresses for any hosts
  542. // that are still unresolved.
  543. //
  544. if(m_AuxList && status == ERROR_SUCCESS)
  545. {
  546. if(!GetMissingIpAddresses(m_AuxList))
  547. {
  548. DeleteDnsRec(m_AuxList);
  549. m_AuxList = NULL;
  550. status = ERROR_RETRY;
  551. goto Exit;
  552. }
  553. if(!CheckMxLoopback())
  554. {
  555. m_fMxLoopBack = TRUE;
  556. DeleteDnsRec(m_AuxList);
  557. m_AuxList = NULL;
  558. TraceFunctLeaveEx((LPARAM) this);
  559. return;
  560. }
  561. }
  562. //
  563. // End of resolve: HandleCompleted data examines the DnsStatus and results, and sets up
  564. // member variables of CAsyncMxDns to either NDR messages, connect to the remote host
  565. // or ack this queue for retry when the object is deleted.
  566. //
  567. Exit:
  568. HandleCompletedData(status);
  569. return;
  570. }