Source code of Windows XP (NT5)
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.

1503 lines
40 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. send.c
  5. Abstract:
  6. Domain Name System (DNS) Library
  7. Send response routines.
  8. Author:
  9. Jim Gilroy (jamesg) October, 1996
  10. Revision History:
  11. --*/
  12. #define INCL_INETSRV_INCS
  13. #include "smtpinc.h"
  14. #include "remoteq.hxx"
  15. #include "dnsreci.h"
  16. #include <dnsapi.h>
  17. #include "cdns.h"
  18. #include <time.h>
  19. extern CTcpRegIpList g_TcpRegIpList;
  20. WORD gwTransactionId = 1;
  21. VOID
  22. DnsCompletion(
  23. PVOID pvContext,
  24. DWORD cbWritten,
  25. DWORD dwCompletionStatus,
  26. OVERLAPPED * lpo
  27. )
  28. {
  29. BOOL WasProcessed = TRUE;
  30. CAsyncDns *pCC = (CAsyncDns *) pvContext;
  31. _ASSERT(pCC);
  32. _ASSERT(pCC->IsValid());
  33. //
  34. // if we could not process a command, or we were
  35. // told to destroy this object, close the connection.
  36. //
  37. WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, lpo);
  38. }
  39. void DeleteDnsRec(PSMTPDNS_RECS pDnsRec)
  40. {
  41. DWORD Loop = 0;
  42. PLIST_ENTRY pEntry = NULL;
  43. PMXIPLIST_ENTRY pQEntry = NULL;
  44. if(pDnsRec == NULL)
  45. {
  46. return;
  47. }
  48. while (pDnsRec->DnsArray[Loop] != NULL)
  49. {
  50. if(pDnsRec->DnsArray[Loop]->DnsName[0])
  51. {
  52. while(!IsListEmpty(&pDnsRec->DnsArray[Loop]->IpListHead))
  53. {
  54. pEntry = RemoveHeadList (&pDnsRec->DnsArray[Loop]->IpListHead);
  55. pQEntry = CONTAINING_RECORD( pEntry, MXIPLIST_ENTRY, ListEntry);
  56. delete pQEntry;
  57. }
  58. delete pDnsRec->DnsArray[Loop];
  59. }
  60. Loop++;
  61. }
  62. if(pDnsRec)
  63. {
  64. delete pDnsRec;
  65. pDnsRec = NULL;
  66. }
  67. }
  68. CAsyncDns::CAsyncDns(void)
  69. {
  70. m_signature = DNS_CONNECTION_SIGNATURE_VALID; // signature on object for sanity check
  71. m_cPendingIoCount = 0;
  72. m_cThreadCount = 0;
  73. m_cbReceived = 0;
  74. m_BytesToRead = 0;
  75. m_dwIpServer = 0;
  76. m_dwFlags = 0;
  77. m_Index = 0;
  78. m_LocalPref = 256;
  79. m_fUdp = TRUE;
  80. m_fUsingMx = TRUE;
  81. m_FirstRead = TRUE;
  82. //
  83. // By default we fail (AQUEUE_E_DNS_FAILURE) is the generic failure code. On success
  84. // this is set to ERROR_SUCCESS. We may also set this to a more specific error code
  85. // at the point of failure.
  86. //
  87. m_dwDiagnostic = AQUEUE_E_DNS_FAILURE;
  88. m_pMsgRecv = NULL;
  89. m_pMsgRecvBuf = NULL;
  90. m_pMsgSend = NULL;
  91. m_pMsgSendBuf = NULL;
  92. m_cbSendBufSize = 0;
  93. m_ppRecord = NULL;
  94. m_ppResponseRecords = NULL;
  95. m_pAtqContext = NULL;
  96. m_FQDNToDrop[0] = '\0';
  97. m_HostName [0] = '\0';
  98. m_SeenLocal = FALSE;
  99. ZeroMemory (m_Weight, sizeof(m_Weight));
  100. ZeroMemory (m_Prefer, sizeof(m_Prefer));
  101. }
  102. CAsyncDns::~CAsyncDns(void)
  103. {
  104. PATQ_CONTEXT pAtqContext = NULL;
  105. //_ASSERT(m_cThreadCount == 0);
  106. if(m_pMsgSend)
  107. {
  108. delete [] m_pMsgSendBuf;
  109. m_pMsgSend = NULL;
  110. m_pMsgSendBuf = NULL;
  111. }
  112. if(m_pMsgRecv)
  113. {
  114. delete [] m_pMsgRecvBuf;
  115. m_pMsgRecv = NULL;
  116. m_pMsgRecvBuf = NULL;
  117. }
  118. //release the context from Atq
  119. pAtqContext = (PATQ_CONTEXT)InterlockedExchangePointer( (PVOID *)&m_pAtqContext, NULL);
  120. if ( pAtqContext != NULL )
  121. {
  122. AtqFreeContext( pAtqContext, TRUE );
  123. }
  124. DeleteDnsRec(m_AuxList);
  125. m_signature = DNS_CONNECTION_SIGNATURE_FREE; // signature on object for sanity check
  126. }
  127. BOOL CAsyncDns::ReadFile(
  128. IN LPVOID pBuffer,
  129. IN DWORD cbSize /* = MAX_READ_BUFF_SIZE */
  130. )
  131. {
  132. BOOL fRet = TRUE;
  133. _ASSERT(pBuffer != NULL);
  134. _ASSERT(cbSize > 0);
  135. ZeroMemory(&m_ReadOverlapped, sizeof(m_ReadOverlapped));
  136. m_ReadOverlapped.LastIoState = DNS_READIO;
  137. IncPendingIoCount();
  138. fRet = AtqReadFile(m_pAtqContext, // Atq context
  139. pBuffer, // Buffer
  140. cbSize, // BytesToRead
  141. (OVERLAPPED *)&m_ReadOverlapped) ;
  142. if(!fRet)
  143. {
  144. DisconnectClient();
  145. DecPendingIoCount();
  146. }
  147. return fRet;
  148. }
  149. BOOL CAsyncDns::WriteFile(
  150. IN LPVOID pBuffer,
  151. IN DWORD cbSize /* = MAX_READ_BUFF_SIZE */
  152. )
  153. {
  154. BOOL fRet = TRUE;
  155. _ASSERT(pBuffer != NULL);
  156. _ASSERT(cbSize > 0);
  157. ZeroMemory(&m_WriteOverlapped, sizeof(m_WriteOverlapped));
  158. m_WriteOverlapped.LastIoState = DNS_WRITEIO;
  159. IncPendingIoCount();
  160. fRet = AtqWriteFile(m_pAtqContext, // Atq context
  161. pBuffer, // Buffer
  162. cbSize, // BytesToRead
  163. (OVERLAPPED *) &m_WriteOverlapped) ;
  164. if(!fRet)
  165. {
  166. DisconnectClient();
  167. DecPendingIoCount();
  168. }
  169. return fRet;
  170. }
  171. DNS_STATUS
  172. CAsyncDns::SendPacket(void)
  173. {
  174. return 0;
  175. }
  176. //
  177. // Public send routines
  178. //
  179. DNS_STATUS
  180. CAsyncDns::Dns_Send(
  181. )
  182. /*++
  183. Routine Description:
  184. Send a DNS packet.
  185. This is the generic send routine used for ANY send of a DNS message.
  186. It assumes nothing about the message type, but does assume:
  187. - pCurrent points at byte following end of desired data
  188. - RR count bytes are in HOST byte order
  189. Arguments:
  190. pMsg - message info for message to send
  191. Return Value:
  192. TRUE if successful.
  193. FALSE on send error.
  194. --*/
  195. {
  196. INT err = 0;
  197. BOOL fRet = TRUE;
  198. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::Dns_Send");
  199. DebugTrace((LPARAM) this, "Sending DNS request for %s", m_HostName);
  200. fRet = WriteFile(m_pMsgSendBuf, (DWORD) m_cbSendBufSize);
  201. if(!fRet)
  202. {
  203. err = GetLastError();
  204. }
  205. return( (DNS_STATUS)err );
  206. } // Dns_Send
  207. //-----------------------------------------------------------------------------------
  208. // Description:
  209. // Kicks off an async query to DNS.
  210. //
  211. // Arguments:
  212. // IN pszQuestionName - Name to query for.
  213. //
  214. // IN wQuestionType - Record type to query for.
  215. //
  216. // IN dwFlags - DNS configuration flags for SMTP. Currently these dictate
  217. // what transport is used to talk to DNS (TCP/UDP). They are:
  218. //
  219. // DNS_FLAGS_NONE - Use UDP initially. If that fails, or if the
  220. // reply is truncated requery using TCP.
  221. //
  222. // DNS_FLAGS_TCP_ONLY - Use TCP only.
  223. //
  224. // DNS_FLAGS_UDP_ONLY - Use UDP only.
  225. //
  226. // IN MyFQDN - FQDN of this machine (for MX record sorting)
  227. //
  228. // IN fUdp - Should UDP or TCP be used for this query? When dwFlags is
  229. // DNS_FLAGS_NONE the initial query is UDP, and the retry query, if the
  230. // response was truncated, is TCP. Depending on whether we're retrying
  231. // this flag should be set appropriately by the caller.
  232. //
  233. // Returns:
  234. // ERROR_SUCCESS if an async query was pended
  235. // Win32 error if an error occurred and an async query was not pended. All
  236. // errors from this function are retryable (as opposed NDR'ing the message)
  237. // so the message is re-queued if an error occurred.
  238. //-----------------------------------------------------------------------------------
  239. DNS_STATUS
  240. CAsyncDns::Dns_QueryLib(
  241. IN DNS_NAME pszQuestionName,
  242. IN WORD wQuestionType,
  243. IN DWORD dwFlags,
  244. IN char *MyFQDN,
  245. IN BOOL fUdp)
  246. {
  247. DNS_STATUS status = ERROR_NOT_ENOUGH_MEMORY;
  248. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::Dns_QueryLib");
  249. m_dwFlags = dwFlags;
  250. m_fUdp = fUdp;
  251. m_AuxList = new SMTPDNS_RECS;
  252. if(m_AuxList == NULL)
  253. {
  254. TraceFunctLeaveEx((LPARAM) this);
  255. return (DNS_STATUS) ERROR_NOT_ENOUGH_MEMORY;
  256. }
  257. ZeroMemory(m_AuxList, sizeof(SMTPDNS_RECS));
  258. lstrcpyn(m_FQDNToDrop, MyFQDN, sizeof(m_FQDNToDrop));
  259. lstrcpyn(m_HostName, pszQuestionName, sizeof(m_HostName));
  260. MultiByteToWideChar( CP_ACP, 0, pszQuestionName, -1, m_wszHostName, MAX_PATH );
  261. //
  262. // build send packet
  263. //
  264. m_pMsgSendBuf = new BYTE[DNS_TCP_DEFAULT_PACKET_LENGTH ];
  265. if( NULL == m_pMsgSendBuf )
  266. {
  267. TraceFunctLeaveEx((LPARAM) this);
  268. return (DNS_STATUS) ERROR_NOT_ENOUGH_MEMORY;
  269. }
  270. DWORD dwBufSize = DNS_TCP_DEFAULT_PACKET_LENGTH ;
  271. if( !m_fUdp )
  272. {
  273. m_pMsgSend = (PDNS_MESSAGE_BUFFER)(m_pMsgSendBuf+2);
  274. dwBufSize -= 2;
  275. }
  276. else
  277. {
  278. m_pMsgSend = (PDNS_MESSAGE_BUFFER)(m_pMsgSendBuf);
  279. }
  280. if( !DnsWriteQuestionToBuffer_UTF8 ( m_pMsgSend,
  281. &dwBufSize,
  282. pszQuestionName,
  283. wQuestionType,
  284. gwTransactionId++,
  285. !( dwFlags & DNS_QUERY_NO_RECURSION ) ) )
  286. {
  287. ErrorTrace((LPARAM) this, "Unable to create DNS query for %s", pszQuestionName);
  288. TraceFunctLeaveEx((LPARAM) this);
  289. return ERROR_NOT_ENOUGH_MEMORY;
  290. }
  291. m_cbSendBufSize = (WORD) dwBufSize;
  292. if( !m_fUdp )
  293. {
  294. *((u_short*)m_pMsgSendBuf) = htons((WORD)dwBufSize );
  295. m_cbSendBufSize += 2;
  296. }
  297. if (m_pMsgSend)
  298. {
  299. status = DnsSendRecord();
  300. }
  301. else
  302. {
  303. status = ERROR_INVALID_NAME;
  304. }
  305. TraceFunctLeaveEx((LPARAM) this);
  306. return status;
  307. }
  308. void CAsyncDns::DisconnectClient(void)
  309. {
  310. SOCKET hSocket;
  311. hSocket = (SOCKET)InterlockedExchangePointer( (PVOID *)&m_DnsSocket, (PVOID) INVALID_SOCKET );
  312. if ( hSocket != INVALID_SOCKET )
  313. {
  314. if ( QueryAtqContext() != NULL )
  315. {
  316. AtqCloseSocket(QueryAtqContext() , TRUE);
  317. }
  318. }
  319. }
  320. //
  321. // TCP routines
  322. //
  323. DNS_STATUS
  324. CAsyncDns::Dns_OpenTcpConnectionAndSend()
  325. /*++
  326. Routine Description:
  327. Connect via TCP or UDP to a DNS server. The server list is held
  328. in a global variable read from the registry.
  329. Arguments:
  330. None
  331. Return Value:
  332. ERROR_SUCCESS on success
  333. Win32 error on failure
  334. --*/
  335. {
  336. INT err = 0;
  337. DWORD dwErrServList = ERROR_SUCCESS;
  338. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::Dns_OpenTcpConnectionAndSend");
  339. //
  340. // setup a TCP socket
  341. // - INADDR_ANY -- let stack select source IP
  342. //
  343. if(!m_fUdp)
  344. {
  345. m_DnsSocket = Dns_CreateSocket(SOCK_STREAM);
  346. BOOL fRet = FALSE;
  347. int err;
  348. //Alway enable linger so sockets that connect to the server.
  349. //This will send a hard close to the server which will cause
  350. //the servers TCP/IP socket table to be flushed very early.
  351. //We should see very few, if any, sockets in the TIME_WAIT
  352. //state
  353. struct linger Linger;
  354. Linger.l_onoff = 1;
  355. Linger.l_linger = 0;
  356. err = setsockopt(m_DnsSocket, SOL_SOCKET, SO_LINGER, (const char FAR *)&Linger, sizeof(Linger));
  357. }
  358. else
  359. {
  360. m_DnsSocket = Dns_CreateSocket(SOCK_DGRAM);
  361. }
  362. if ( m_DnsSocket == INVALID_SOCKET )
  363. {
  364. err = WSAGetLastError();
  365. if ( !err )
  366. {
  367. err = WSAENOTSOCK;
  368. }
  369. ErrorTrace((LPARAM) this, "Received error %d opening a socket to DNS server", err);
  370. return( err );
  371. }
  372. m_RemoteAddress.sin_family = AF_INET;
  373. m_RemoteAddress.sin_port = DNS_PORT_NET_ORDER;
  374. //
  375. // Get a DNS server from the set of servers for this machine and connect
  376. // to it. The g_TcpRegIpList has logic to keep track of the state of DNS
  377. // servers (UP or DOWN) and logic to retry DOWN DNS servers.
  378. //
  379. dwErrServList = g_TcpRegIpList.GetIp(&m_dwIpServer);
  380. while(ERROR_SUCCESS == dwErrServList)
  381. {
  382. m_RemoteAddress.sin_addr.s_addr = m_dwIpServer;
  383. err = connect(m_DnsSocket, (struct sockaddr *) &m_RemoteAddress, sizeof(SOCKADDR_IN));
  384. if ( !err )
  385. {
  386. break;
  387. }
  388. else
  389. {
  390. MarkDown(m_dwIpServer, err, m_fUdp);
  391. dwErrServList = g_TcpRegIpList.GetIp(&m_dwIpServer);
  392. continue;
  393. }
  394. }
  395. if(DNS_ERROR_NO_DNS_SERVERS == dwErrServList || ERROR_RETRY == dwErrServList)
  396. {
  397. //
  398. // Log Event and set diagnostic: No DNS servers available.
  399. //
  400. err = DNS_ERROR_NO_DNS_SERVERS;
  401. m_dwDiagnostic = AQUEUE_E_NO_DNS_SERVERS;
  402. SmtpLogEventSimple(SMTP_NO_DNS_SERVERS, DNS_ERROR_NO_DNS_SERVERS);
  403. ErrorTrace((LPARAM) this, "No DNS servers. Error - %d", dwErrServList);
  404. return err;
  405. }
  406. _ASSERT(ERROR_SUCCESS == dwErrServList);
  407. //
  408. // We have a connection to DNS
  409. //
  410. if(ERROR_SUCCESS == err)
  411. {
  412. //
  413. // NOTE: We've set the timeout to a hardcoded value of 1 minute. This might
  414. // seem excessively large for DNS, however, since the resolution of the ATQ
  415. // timer is 1 minute that's the minimum anyway. Might as well make it apparent.
  416. //
  417. // Re-associate the handle to the ATQ
  418. // Call ATQ to associate the handle
  419. if (!AtqAddAsyncHandle(
  420. &m_pAtqContext,
  421. NULL,
  422. (LPVOID) this,
  423. DnsCompletion,
  424. 60, // Timeout == 60 seconds
  425. (HANDLE) m_DnsSocket))
  426. {
  427. return GetLastError();
  428. }
  429. //
  430. // send desired packet
  431. //
  432. err = Dns_Send();
  433. }
  434. else
  435. {
  436. if(m_DnsSocket != INVALID_SOCKET)
  437. {
  438. closesocket(m_DnsSocket);
  439. m_DnsSocket = INVALID_SOCKET;
  440. }
  441. }
  442. return( (DNS_STATUS)err );
  443. } // Dns_OpenTcpConnectionAndSend
  444. //------------------------------------------------------------------------------
  445. // Description:
  446. // Failed to connect to dwIpServer. Mark the server as DOWN, log
  447. // an event and write traces. This is a simple wrapper for
  448. // CTcpRegIpList::MarkDown.
  449. // Arguments:
  450. // DWORD dwIpServer - IP of server to which we failed to connect
  451. // DWORD dwErr - Win32 error if any
  452. // BOOL fUdp - Which transport was being used when the failure occurred.
  453. //------------------------------------------------------------------------------
  454. void CAsyncDns::MarkDown(DWORD dwIpServer, DWORD dwErr, BOOL fUdp)
  455. {
  456. const CHAR *pszServerIp = NULL;
  457. const CHAR *pszProtocol = NULL;
  458. const CHAR *apszSubStrings[2];
  459. in_addr inAddrIpServer;
  460. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::MarkDown");
  461. CopyMemory(&inAddrIpServer, &dwIpServer, sizeof(DWORD));
  462. pszServerIp = inet_ntoa(inAddrIpServer);
  463. if(NULL != pszServerIp)
  464. {
  465. pszProtocol = fUdp ? "UDP" : "TCP";
  466. apszSubStrings[0] = pszServerIp;
  467. apszSubStrings[1] = pszProtocol;
  468. SmtpLogEvent(SMTP_DNS_SERVER_DOWN, 2, apszSubStrings, dwErr);
  469. }
  470. ErrorTrace((LPARAM) this, "Received error %d connecting to DNS server %d.%d.%d.%d over %s",
  471. dwErr, ((PBYTE)&dwIpServer)[0], ((PBYTE)&dwIpServer)[1],
  472. ((PBYTE)&dwIpServer)[2], ((PBYTE)&dwIpServer)[3],
  473. fUdp ? "UDP" : "TCP");
  474. g_TcpRegIpList.MarkDown(dwIpServer);
  475. TraceFunctLeaveEx((LPARAM)this);
  476. }
  477. BOOL CAsyncDns::ProcessReadIO(IN DWORD InputBufferLen,
  478. IN DWORD dwCompletionStatus,
  479. IN OUT OVERLAPPED * lpo)
  480. {
  481. BOOL fRet = TRUE;
  482. DWORD DataSize = 0;
  483. DNS_STATUS DnsStatus = 0;
  484. TraceFunctEnterEx((LPARAM) this, "BOOL CAsyncDns::ProcessReadIO");
  485. //add up the number of bytes we received thus far
  486. m_cbReceived += InputBufferLen;
  487. //
  488. // read atleast 2 bytes
  489. //
  490. if(!m_fUdp && m_FirstRead && ( m_cbReceived < 2 ) )
  491. {
  492. fRet = ReadFile(&m_pMsgRecvBuf[m_cbReceived],DNS_TCP_DEFAULT_PACKET_LENGTH-1 );
  493. return fRet;
  494. }
  495. //
  496. // get the size of the message
  497. //
  498. if(!m_fUdp && m_FirstRead && (m_cbReceived >= 2))
  499. {
  500. DataSize = ntohs(*(u_short *)m_pMsgRecvBuf);
  501. //
  502. // add 2 bytes for the field which specifies the length of data
  503. //
  504. m_BytesToRead = DataSize + 2;
  505. m_FirstRead = FALSE;
  506. }
  507. //
  508. // pend another read if we have n't read enough
  509. //
  510. if(!m_fUdp && (m_cbReceived < m_BytesToRead))
  511. {
  512. DWORD cbMoreToRead = m_BytesToRead - m_cbReceived;
  513. fRet = ReadFile(&m_pMsgRecvBuf[m_cbReceived], cbMoreToRead);
  514. }
  515. else
  516. {
  517. if( !m_fUdp )
  518. {
  519. //
  520. // message length is 2 bytes less to take care of the msg length
  521. // field.
  522. //
  523. //m_pMsgRecv->MessageLength = (WORD) m_cbReceived - 2;
  524. m_pMsgRecv = (PDNS_MESSAGE_BUFFER)(m_pMsgRecvBuf+2);
  525. }
  526. else
  527. {
  528. //m_pMsgRecv->MessageLength = (WORD) m_cbReceived;
  529. m_pMsgRecv = (PDNS_MESSAGE_BUFFER)m_pMsgRecvBuf;
  530. }
  531. SWAP_COUNT_BYTES(&m_pMsgRecv->MessageHead);
  532. //
  533. // We queried over UDP and the reply from DNS was truncated because the response
  534. // was longer than the UDP packet size. We requery DNS using TCP unless SMTP is
  535. // configured to use UDP only. RetryAsyncDnsQuery sets the members of this CAsyncDns
  536. // object appropriately depending on whether if fails or succeeds. After calling
  537. // RetryAsyncDnsQuery, this object must be deleted.
  538. //
  539. if(m_fUdp && !(m_dwFlags & DNS_FLAGS_UDP_ONLY) && m_pMsgRecv->MessageHead.Truncation)
  540. {
  541. _ASSERT(!(m_dwFlags & DNS_FLAGS_TCP_ONLY) && "Shouldn't have truncated reply over TCP");
  542. DebugTrace((LPARAM) this, "Truncated reply - reissuing query using TCP");
  543. RetryAsyncDnsQuery(FALSE); // FALSE == Do not use UDP
  544. goto Exit;
  545. }
  546. DnsStatus = DnsParseMessage( m_pMsgRecv,
  547. (WORD)( m_fUdp ? ( m_cbReceived ) : ( m_cbReceived - 2 )),
  548. &m_ppRecord);
  549. //
  550. // End of resolve: HandleCompleted data examines the DnsStatus and results, and sets up
  551. // member variables of CAsyncSmtpDns to either NDR messages, connect to the remote host
  552. // or ack this queue for retry when the object is deleted.
  553. //
  554. HandleCompletedData(DnsStatus);
  555. }
  556. Exit:
  557. TraceFunctLeaveEx((LPARAM) this);
  558. return fRet;
  559. }
  560. BOOL CAsyncDns::ProcessClient (IN DWORD InputBufferLen,
  561. IN DWORD dwCompletionStatus,
  562. IN OUT OVERLAPPED * lpo)
  563. {
  564. BOOL RetStatus = FALSE;
  565. DWORD dwIp = 0;
  566. BOOL fRetryQuery = FALSE;
  567. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::ProcessClient()");
  568. IncThreadCount();
  569. //if lpo == NULL, then we timed out. Send an appropriate message
  570. //then close the connection
  571. if( (lpo == NULL) && (dwCompletionStatus == ERROR_SEM_TIMEOUT))
  572. {
  573. fRetryQuery = TRUE;
  574. //
  575. // fake a pending IO as we'll dec the overall count in the
  576. // exit processing of this routine needs to happen before
  577. // DisconnectClient else completing threads could tear us down
  578. //
  579. IncPendingIoCount();
  580. DebugTrace( (LPARAM)this, "Async DNS client timed out");
  581. DisconnectClient();
  582. }
  583. else if((InputBufferLen == 0) || (dwCompletionStatus != NO_ERROR))
  584. {
  585. fRetryQuery = TRUE;
  586. DebugTrace((LPARAM) this, "CAsyncDns::ProcessClient: InputBufferLen = %d dwCompletionStatus = %d - Closing connection", InputBufferLen, dwCompletionStatus);
  587. DisconnectClient();
  588. }
  589. else if (lpo == (OVERLAPPED *) &m_ReadOverlapped)
  590. {
  591. //A client based async IO completed
  592. RetStatus = ProcessReadIO(InputBufferLen, dwCompletionStatus, lpo);
  593. }
  594. else if(lpo == (OVERLAPPED *) &m_WriteOverlapped)
  595. {
  596. RetStatus = ReadFile(m_pMsgRecvBuf, DNS_TCP_DEFAULT_PACKET_LENGTH);
  597. if(!RetStatus)
  598. {
  599. ErrorTrace((LPARAM) this, "ReadFile failed");
  600. fRetryQuery = TRUE;
  601. }
  602. }
  603. if(fRetryQuery)
  604. {
  605. MarkDown(m_dwIpServer, dwCompletionStatus, m_fUdp);
  606. if(g_TcpRegIpList.GetIp(&dwIp) != ERROR_SUCCESS)
  607. {
  608. m_dwDiagnostic = AQUEUE_E_NO_DNS_SERVERS;
  609. SmtpLogEventSimple(SMTP_NO_DNS_SERVERS, DNS_ERROR_NO_DNS_SERVERS);
  610. ErrorTrace((LPARAM) this, "No DNS servers");
  611. } else {
  612. RetryAsyncDnsQuery(m_fUdp); // This sets m_dwDiagnostic
  613. }
  614. }
  615. DebugTrace((LPARAM)this,"ASYNC DNS - Pending IOs: %d", m_cPendingIoCount);
  616. // Do NOT Touch the member variables past this POINT!
  617. // This object may be deleted!
  618. //
  619. // decrement the overall pending IO count for this session
  620. // tracing and ASSERTs if we're going down.
  621. //
  622. DecThreadCount();
  623. if (DecPendingIoCount() == 0)
  624. {
  625. DisconnectClient();
  626. DebugTrace((LPARAM)this,"ASYNC DNS - Pending IOs: %d", m_cPendingIoCount);
  627. DebugTrace((LPARAM)this,"ASYNC DNS - Thread count: %d", m_cThreadCount);
  628. delete this;
  629. }
  630. return TRUE;
  631. }
  632. int MxRand(char * host)
  633. {
  634. int hfunc = 0;
  635. unsigned int seed = 0;;
  636. seed = rand() & 0xffff;
  637. hfunc = seed;
  638. while (*host != '\0')
  639. {
  640. int c = *host++;
  641. if (isascii((UCHAR)c) && isupper((UCHAR)c))
  642. c = tolower(c);
  643. hfunc = ((hfunc << 1) ^ c) % 2003;
  644. }
  645. hfunc &= 0xff;
  646. return hfunc;
  647. }
  648. BOOL CAsyncDns::CheckList(void)
  649. {
  650. MXIPLIST_ENTRY * pEntry = NULL;
  651. struct hostent *hp = NULL;
  652. BOOL fRet = TRUE;
  653. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::CheckList");
  654. if(m_Index == 0)
  655. {
  656. DebugTrace((LPARAM) this, "m_Index == 0 in CheckList");
  657. m_fUsingMx = FALSE;
  658. m_cbReceived = 0;
  659. m_BytesToRead = 0;
  660. m_FirstRead = TRUE;
  661. DeleteDnsRec(m_AuxList);
  662. m_AuxList = new SMTPDNS_RECS;
  663. if(m_AuxList == NULL)
  664. {
  665. ErrorTrace((LPARAM) this, "m_AuxList = new SMTPDNS_RECS failed");
  666. TraceFunctLeaveEx((LPARAM)this);
  667. return FALSE;
  668. }
  669. ZeroMemory(m_AuxList, sizeof(SMTPDNS_RECS));
  670. m_AuxList->NumRecords = 1;
  671. m_AuxList->DnsArray[0] = new MX_NAMES;
  672. if(m_AuxList->DnsArray[0] == NULL)
  673. {
  674. ErrorTrace((LPARAM) this, "m_AuxList->DnsArray[0] = new MX_NAMES failed");
  675. TraceFunctLeaveEx((LPARAM)this);
  676. return FALSE;
  677. }
  678. m_AuxList->DnsArray[0]->NumEntries = 0;
  679. InitializeListHead(&m_AuxList->DnsArray[0]->IpListHead);
  680. lstrcpyn(m_AuxList->DnsArray[0]->DnsName, m_HostName, sizeof(m_AuxList->DnsArray[m_Index]->DnsName));
  681. hp = gethostbyname (m_HostName);
  682. if(hp != NULL)
  683. {
  684. for (DWORD Loop = 0; (hp->h_addr_list[Loop] != NULL); Loop++)
  685. {
  686. pEntry = new MXIPLIST_ENTRY;
  687. if(pEntry != NULL)
  688. {
  689. m_AuxList->DnsArray[0]->NumEntries++;
  690. CopyMemory(&pEntry->IpAddress, hp->h_addr_list[Loop], 4);
  691. InsertTailList(&m_AuxList->DnsArray[0]->IpListHead, &pEntry->ListEntry);
  692. }
  693. else
  694. {
  695. fRet = FALSE;
  696. ErrorTrace((LPARAM) this, "pEntry = new MXIPLIST_ENTRY failed in CheckList");
  697. break;
  698. }
  699. }
  700. }
  701. else
  702. {
  703. fRet = FALSE;
  704. }
  705. }
  706. TraceFunctLeaveEx((LPARAM)this);
  707. return fRet;
  708. }
  709. BOOL CAsyncDns::SortMxList(void)
  710. {
  711. BOOL fRet = TRUE;
  712. /* sort the records */
  713. for (DWORD i = 0; i < m_Index; i++)
  714. {
  715. for (DWORD j = i + 1; j < m_Index; j++)
  716. {
  717. if (m_Prefer[i] > m_Prefer[j] ||
  718. (m_Prefer[i] == m_Prefer[j] && m_Weight[i] > m_Weight[j]))
  719. {
  720. DWORD temp;
  721. MX_NAMES *temp1;
  722. temp = m_Prefer[i];
  723. m_Prefer[i] = m_Prefer[j];
  724. m_Prefer[j] = temp;
  725. temp1 = m_AuxList->DnsArray[i];
  726. m_AuxList->DnsArray[i] = m_AuxList->DnsArray[j];
  727. m_AuxList->DnsArray[j] = temp1;
  728. temp = m_Weight[i];
  729. m_Weight[i] = m_Weight[j];
  730. m_Weight[j] = temp;
  731. }
  732. }
  733. if (m_SeenLocal && m_Prefer[i] >= m_LocalPref)
  734. {
  735. /* truncate higher preference part of list */
  736. m_Index = i;
  737. }
  738. }
  739. m_AuxList->NumRecords = m_Index;
  740. if(!CheckList())
  741. {
  742. DeleteDnsRec(m_AuxList);
  743. m_AuxList = NULL;
  744. fRet = FALSE;
  745. }
  746. return fRet;
  747. }
  748. void CAsyncDns::ProcessMxRecord(PDNS_RECORD pnewRR)
  749. {
  750. DWORD Len = 0;
  751. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::ProcessMxRecord");
  752. if(m_Index >= SMTP_MAX_DNS_ENTRIES)
  753. {
  754. DebugTrace((LPARAM) this, "SMTP_MAX_DNS_ENTRIES reached for %s", m_HostName);
  755. TraceFunctLeaveEx((LPARAM)this);
  756. return;
  757. }
  758. if((pnewRR->wType == DNS_TYPE_MX) && pnewRR->Data.MX.nameExchange)
  759. {
  760. Len = lstrlen(pnewRR->Data.MX.nameExchange);
  761. if(pnewRR->Data.MX.nameExchange[Len - 1] == '.')
  762. {
  763. pnewRR->Data.MX.nameExchange[Len - 1] = '\0';
  764. }
  765. DebugTrace((LPARAM) this, "Received MX rec %s with priority %d for %s", pnewRR->Data.MX.nameExchange, pnewRR->Data.MX.wPreference, m_HostName);
  766. if(lstrcmpi(pnewRR->Data.MX.nameExchange, m_FQDNToDrop))
  767. {
  768. m_AuxList->DnsArray[m_Index] = new MX_NAMES;
  769. if(m_AuxList->DnsArray[m_Index])
  770. {
  771. m_AuxList->DnsArray[m_Index]->NumEntries = 0;;
  772. InitializeListHead(&m_AuxList->DnsArray[m_Index]->IpListHead);
  773. lstrcpyn(m_AuxList->DnsArray[m_Index]->DnsName,pnewRR->Data.MX.nameExchange, sizeof(m_AuxList->DnsArray[m_Index]->DnsName));
  774. m_Weight[m_Index] = MxRand (m_AuxList->DnsArray[m_Index]->DnsName);
  775. m_Prefer[m_Index] = pnewRR->Data.MX.wPreference;
  776. m_Index++;
  777. }
  778. else
  779. {
  780. DebugTrace((LPARAM) this, "Out of memory allocating MX_NAMES for %s", m_HostName);
  781. }
  782. }
  783. else
  784. {
  785. if (!m_SeenLocal || pnewRR->Data.MX.wPreference < m_LocalPref)
  786. m_LocalPref = pnewRR->Data.MX.wPreference;
  787. m_SeenLocal = TRUE;
  788. }
  789. }
  790. else if(pnewRR->wType == DNS_TYPE_A)
  791. {
  792. MXIPLIST_ENTRY * pEntry = NULL;
  793. for(DWORD i = 0; i < m_Index; i++)
  794. {
  795. if(lstrcmpi(pnewRR->nameOwner, m_AuxList->DnsArray[i]->DnsName) == 0)
  796. {
  797. pEntry = new MXIPLIST_ENTRY;
  798. if(pEntry != NULL)
  799. {
  800. m_AuxList->DnsArray[i]->NumEntries++;;
  801. pEntry->IpAddress = pnewRR->Data.A.ipAddress;
  802. InsertTailList(&m_AuxList->DnsArray[i]->IpListHead, &pEntry->ListEntry);
  803. }
  804. break;
  805. }
  806. }
  807. }
  808. TraceFunctLeaveEx((LPARAM)this);
  809. }
  810. void CAsyncDns::ProcessARecord(PDNS_RECORD pnewRR)
  811. {
  812. MXIPLIST_ENTRY * pEntry = NULL;
  813. if(pnewRR->wType == DNS_TYPE_A)
  814. {
  815. pEntry = new MXIPLIST_ENTRY;
  816. if(pEntry != NULL)
  817. {
  818. pEntry->IpAddress = pnewRR->Data.A.ipAddress;
  819. InsertTailList(&m_AuxList->DnsArray[0]->IpListHead, &pEntry->ListEntry);
  820. }
  821. }
  822. }
  823. DNS_STATUS CAsyncDns::DnsParseMessage(
  824. IN PDNS_MESSAGE_BUFFER pMsg,
  825. IN WORD wMessageLength,
  826. OUT PDNS_RECORD * ppRecord)
  827. {
  828. TraceFunctEnterEx((LPARAM) this, "CAsyncDns::DnsParseMessage");
  829. PDNS_RECORD pTmp = NULL;
  830. m_SeenLocal = FALSE;
  831. m_LocalPref = 256;
  832. DNS_STATUS status = DnsExtractRecordsFromMessage_UTF8( pMsg, wMessageLength, ppRecord );
  833. //
  834. // Due to Raid #122555 m_fUsingMx is always TRUE in this function
  835. // - hence we will always go a GetHostByName() if there is no MX
  836. // record. It would be better Perf if we did a A record lookup.
  837. //
  838. DebugTrace((LPARAM) this, "Parsed DNS record for %s. status = 0x%08x", m_HostName, status);
  839. switch(status)
  840. {
  841. case ERROR_SUCCESS:
  842. //
  843. // Got the DNS record we want.
  844. //
  845. DebugTrace((LPARAM) this, "Success: DNS record parsed");
  846. pTmp = *ppRecord;
  847. while( pTmp )
  848. {
  849. if( m_fUsingMx )
  850. {
  851. ProcessMxRecord( pTmp );
  852. }
  853. else
  854. {
  855. ProcessARecord( pTmp );
  856. }
  857. pTmp = pTmp->pNext;
  858. }
  859. if(m_fUsingMx)
  860. {
  861. //
  862. // SortMxList sorts the MX records by preference and calls
  863. // gethostbyname() to resolve A records for Mail Exchangers
  864. // if needed (when the A records are not returned in the
  865. // supplementary info).
  866. //
  867. if(SortMxList())
  868. {
  869. status = ERROR_SUCCESS;
  870. DebugTrace((LPARAM) this, "SortMxList() succeeded.");
  871. }
  872. else
  873. {
  874. status = ERROR_RETRY;
  875. ErrorTrace((LPARAM) this, "SortMxList() failed. Message will stay queued.");
  876. }
  877. }
  878. break;
  879. case DNS_ERROR_RCODE_NAME_ERROR:
  880. // Fall through to using gethostbyname()
  881. case DNS_INFO_NO_RECORDS:
  882. // Non authoritative host not found.
  883. // Fall through to using gethostbyname()
  884. default:
  885. DebugTrace((LPARAM) this, "Error in query: status = 0x%08x.", status);
  886. //
  887. // Use gethostbyname to resolve the hostname:
  888. // One issue with our approach is that sometimes we will NDR the message
  889. // on non-permanent errors, "like WINS server down", when gethostbyname
  890. // fails. However, there's no way around it --- gethostbyname doesn't
  891. // report errors in a reliable manner, so it's not possible to distinguish
  892. // between permanent and temporary errors.
  893. //
  894. if (!CheckList ()) {
  895. if(status == DNS_ERROR_RCODE_NAME_ERROR) {
  896. ErrorTrace((LPARAM) this, "Authoritative error");
  897. status = ERROR_NOT_FOUND;
  898. } else {
  899. ErrorTrace((LPARAM) this, "Retryable error");
  900. status = ERROR_RETRY;
  901. }
  902. } else {
  903. DebugTrace ((LPARAM) this, "Successfully resolved using gethostbyname");
  904. status = ERROR_SUCCESS;
  905. }
  906. break;
  907. }
  908. DnsRecordListFree( *ppRecord, TRUE );
  909. return( status );
  910. }
  911. DNS_STATUS
  912. CAsyncDns::DnsSendRecord()
  913. /*++
  914. Routine Description:
  915. Send message, receive response.
  916. Arguments:
  917. aipDnsServers -- specific DNS servers to query;
  918. OPTIONAL, if specified overrides normal list associated with machine
  919. Return Value:
  920. ERROR_SUCCESS if successful.
  921. Error code on failure.
  922. --*/
  923. {
  924. DNS_STATUS status = 0;
  925. m_pMsgRecvBuf = (BYTE*) new BYTE[DNS_TCP_DEFAULT_PACKET_LENGTH];
  926. if(m_pMsgRecvBuf == NULL)
  927. {
  928. return( DNS_ERROR_NO_MEMORY );
  929. }
  930. status = Dns_OpenTcpConnectionAndSend();
  931. return( status );
  932. }
  933. SOCKET
  934. CAsyncDns::Dns_CreateSocket(
  935. IN INT SockType
  936. )
  937. /*++
  938. Routine Description:
  939. Create socket.
  940. Arguments:
  941. SockType -- SOCK_DGRAM or SOCK_STREAM
  942. Return Value:
  943. socket if successful.
  944. Otherwise INVALID_SOCKET.
  945. --*/
  946. {
  947. SOCKET s;
  948. //
  949. // create socket
  950. //
  951. s = socket( AF_INET, SockType, 0 );
  952. if ( s == INVALID_SOCKET )
  953. {
  954. return INVALID_SOCKET;
  955. }
  956. return s;
  957. }
  958. //-----------------------------------------------------------------------------
  959. // Description:
  960. // Constructor and Destructor for class to maintain a list of IP addresses
  961. // (for DNS servers) and their state (UP or DOWN). The IP addresses are
  962. // held in an IP_ARRAY, and the user must set m_DeleteFunc to deallocate
  963. // the memory.
  964. //-----------------------------------------------------------------------------
  965. CTcpRegIpList::CTcpRegIpList()
  966. {
  967. m_IpListPtr = NULL;
  968. //
  969. // Shortcut to quickly figure out how many servers are down. This keeps track
  970. // of how many servers are marked up currently. Used in ResetServersIfNeeded
  971. // primarily to avoid checking the state of all servers in the usual case when
  972. // all servers are up.
  973. //
  974. m_cUpServers = 0;
  975. m_prgdwFailureTick = NULL;
  976. m_prgfServerUp = NULL;
  977. m_dwSig = TCP_REG_LIST_SIGNATURE;
  978. }
  979. CTcpRegIpList::~CTcpRegIpList()
  980. {
  981. _ASSERT(m_DeleteFunc);
  982. if(m_DeleteFunc && m_IpListPtr)
  983. m_DeleteFunc(m_IpListPtr);
  984. if(m_prgdwFailureTick)
  985. delete [] m_prgdwFailureTick;
  986. if(m_prgfServerUp)
  987. delete [] m_prgfServerUp;
  988. m_IpListPtr = NULL;
  989. m_prgdwFailureTick = NULL;
  990. m_prgfServerUp = NULL;
  991. }
  992. //-----------------------------------------------------------------------------
  993. // Description:
  994. // Initializes or updates the IP address list. If this fails due to out
  995. // of memory, there's precious little we can do. So we don't return anything
  996. // and just delete the server IP list.
  997. // Arguments:
  998. // IpPtr - Ptr to IP_ARRAY of servers, this can be NULL in which case
  999. // we assume that there are no servers. On shutdown, the SMTP code
  1000. // calls this with NULL.
  1001. //-----------------------------------------------------------------------------
  1002. void CTcpRegIpList::Update(PIP_ARRAY IpPtr)
  1003. {
  1004. BOOL fFatalError = FALSE;
  1005. TraceFunctEnterEx((LPARAM) this, "CTcpRegIpList::Update");
  1006. m_sl.ExclusiveLock();
  1007. _ASSERT(m_DeleteFunc);
  1008. if(m_IpListPtr && m_DeleteFunc)
  1009. m_DeleteFunc(m_IpListPtr);
  1010. if(m_prgdwFailureTick) {
  1011. delete [] m_prgdwFailureTick;
  1012. m_prgdwFailureTick = NULL;
  1013. }
  1014. if(m_prgfServerUp) {
  1015. delete [] m_prgfServerUp;
  1016. m_prgfServerUp = NULL;
  1017. }
  1018. // Note: this can be NULL
  1019. m_IpListPtr = IpPtr;
  1020. if(IpPtr == NULL) {
  1021. m_cUpServers = 0;
  1022. goto Exit;
  1023. }
  1024. m_cUpServers = IpPtr->cAddrCount;
  1025. m_prgdwFailureTick = new DWORD[m_cUpServers];
  1026. m_prgfServerUp = new BOOL[m_cUpServers];
  1027. if(!m_prgdwFailureTick || !m_prgfServerUp) {
  1028. ErrorTrace((LPARAM) this, "Failed to read DNS server list - out of memory");
  1029. fFatalError = TRUE;
  1030. goto Exit;
  1031. }
  1032. for(int i = 0; i < m_cUpServers; i++) {
  1033. m_prgdwFailureTick[i] = 0;
  1034. m_prgfServerUp[i] = TRUE;
  1035. }
  1036. Exit:
  1037. if(fFatalError) {
  1038. if(m_prgfServerUp) {
  1039. delete [] m_prgfServerUp;
  1040. m_prgfServerUp = NULL;
  1041. }
  1042. if(m_prgdwFailureTick) {
  1043. delete [] m_prgdwFailureTick;
  1044. m_prgdwFailureTick = NULL;
  1045. }
  1046. if(m_IpListPtr && m_DeleteFunc) {
  1047. m_DeleteFunc(m_IpListPtr);
  1048. m_IpListPtr = NULL;
  1049. }
  1050. m_cUpServers = 0;
  1051. }
  1052. m_sl.ExclusiveUnlock();
  1053. TraceFunctLeaveEx((LPARAM) this);
  1054. }
  1055. //-----------------------------------------------------------------------------
  1056. // Description:
  1057. // Return the IP address of a server known to be UP. This function also
  1058. // checks to see if any servers currently marked DOWN should be reset to
  1059. // the UP state again (based on a retry interval).
  1060. // Arguments:
  1061. // DWORD *pdwIpServer - Sets the DWORD pointed to, to the IP address of
  1062. // a server in the UP state.
  1063. // Returns:
  1064. // ERROR_SUCCESS - If a DNS server in the UP state was found
  1065. // ERROR_RETRY - If all DNS servers are currently down
  1066. // DNS_ERROR_NO_DNS_SERVERS - If no DNS servers are configured
  1067. //-----------------------------------------------------------------------------
  1068. DWORD CTcpRegIpList::GetIp(DWORD *pdwIpServer)
  1069. {
  1070. DWORD dwErr = ERROR_SUCCESS;
  1071. int iServer = 0;
  1072. _ASSERT(pdwIpServer != NULL);
  1073. *pdwIpServer = INADDR_NONE;
  1074. // Check if any servers were down and bring them up if needed
  1075. ResetServersIfNeeded();
  1076. m_sl.ShareLock();
  1077. if(m_IpListPtr == NULL || m_IpListPtr->cAddrCount == 0) {
  1078. dwErr = DNS_ERROR_NO_DNS_SERVERS;
  1079. goto Exit;
  1080. }
  1081. if(m_cUpServers == 0) {
  1082. dwErr = ERROR_RETRY;
  1083. goto Exit;
  1084. }
  1085. for(iServer = 0; iServer < (int)m_IpListPtr->cAddrCount; iServer++) {
  1086. if(m_prgfServerUp[iServer])
  1087. break;
  1088. }
  1089. if(m_prgfServerUp[iServer])
  1090. *pdwIpServer = m_IpListPtr->aipAddrs[iServer];
  1091. else
  1092. dwErr = ERROR_RETRY;
  1093. Exit:
  1094. m_sl.ShareUnlock();
  1095. return dwErr;
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Description:
  1099. // Marks a server in the list as down and sets the next retry time for
  1100. // that server. The next retry time is calculated modulo MAX_TICK_COUNT.
  1101. // Arguments:
  1102. // dwIp -- IP address of server to mark as DOWN
  1103. //-----------------------------------------------------------------------------
  1104. void CTcpRegIpList::MarkDown(DWORD dwIp)
  1105. {
  1106. int iServer = 0;
  1107. m_sl.ExclusiveLock();
  1108. if(m_IpListPtr == NULL || m_IpListPtr->cAddrCount == 0 || m_cUpServers == 0)
  1109. goto Exit;
  1110. // Find the server to mark as down among all the UP servers
  1111. for(iServer = 0; iServer < (int)m_IpListPtr->cAddrCount; iServer++) {
  1112. if(m_IpListPtr->aipAddrs[iServer] == dwIp)
  1113. break;
  1114. }
  1115. if(iServer < (int)m_IpListPtr->cAddrCount && m_prgfServerUp[iServer]) {
  1116. m_prgfServerUp[iServer] = FALSE;
  1117. _ASSERT(m_cUpServers > 0);
  1118. m_cUpServers--;
  1119. m_prgdwFailureTick[iServer] = GetTickCount();
  1120. }
  1121. Exit:
  1122. m_sl.ExclusiveUnlock();
  1123. return;
  1124. }
  1125. //-----------------------------------------------------------------------------
  1126. // Description:
  1127. // Checks if any servers are DOWN, and if the retry time has expired for
  1128. // those servers. If so those servers will be brought up.
  1129. //-----------------------------------------------------------------------------
  1130. void CTcpRegIpList::ResetServersIfNeeded()
  1131. {
  1132. int iServer = 0;
  1133. DWORD dwElapsedTicks = 0;
  1134. DWORD dwCurrentTick = 0;
  1135. //
  1136. // Quick check - if all servers are up (usual case) or there are no configured
  1137. // servers, there's nothing for us to do.
  1138. //
  1139. m_sl.ShareLock();
  1140. if(m_IpListPtr == NULL || m_IpListPtr->cAddrCount == 0 || m_cUpServers == m_IpListPtr->cAddrCount) {
  1141. m_sl.ShareUnlock();
  1142. return;
  1143. }
  1144. m_sl.ShareUnlock();
  1145. // Some servers are down... figure out which need to be brought up
  1146. m_sl.ExclusiveLock();
  1147. // Re-check that no one modified the list while we didn't have the sharelock
  1148. if(m_IpListPtr == NULL || m_IpListPtr->cAddrCount == 0 || m_cUpServers == m_IpListPtr->cAddrCount) {
  1149. m_sl.ExclusiveUnlock();
  1150. return;
  1151. }
  1152. dwCurrentTick = GetTickCount();
  1153. for(iServer = 0; iServer < (int)m_IpListPtr->cAddrCount; iServer++) {
  1154. if(m_prgfServerUp[iServer])
  1155. continue;
  1156. //
  1157. // Note: This also takes care of the special case where dwCurrentTick occurs
  1158. // after the wraparound and m_prgdwFailureTick occurs before the wraparound.
  1159. // This is because, in that case, the elapsed time is:
  1160. //
  1161. // time since wraparound + time before wraparound that failure occurred - 1
  1162. // (-1 is because it's 0 time to transition from MAX_TICK_VALUE to 0)
  1163. //
  1164. // = dwCurrentTick + (MAX_TICK_VALUE - m_prgdwFailureTick[iServer]) - 1
  1165. //
  1166. // Since MAX_TICK_VALUE == -1
  1167. //
  1168. // = dwCurrentTick + (-1 - m_prgdwFailureTick[iServer]) - 1
  1169. // = dwCurrentTick - m_prgdwFailureTick[iServer]
  1170. //
  1171. dwElapsedTicks = dwCurrentTick - m_prgdwFailureTick[iServer];
  1172. #define TICKS_TILL_RETRY 10 * 60 * 1000 // 10 minutes
  1173. if(dwElapsedTicks > TICKS_TILL_RETRY) {
  1174. m_prgfServerUp[iServer] = TRUE;
  1175. m_prgdwFailureTick[iServer] = 0;
  1176. m_cUpServers++;
  1177. _ASSERT(m_cUpServers <= (int)m_IpListPtr->cAddrCount);
  1178. }
  1179. }
  1180. m_sl.ExclusiveUnlock();
  1181. }