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.

422 lines
14 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. asynccon.cxx
  5. Abstract:
  6. Author:
  7. --*/
  8. #define INCL_INETSRV_INCS
  9. #include "smtpinc.h"
  10. #include "remoteq.hxx"
  11. #include <asynccon.hxx>
  12. #include <dnsreci.h>
  13. #include <cdns.h>
  14. #include "smtpdns.hxx"
  15. #include "asyncmx.hxx"
  16. #include "smtpmsg.h"
  17. extern BOOL QueueCallBackFunction(PVOID ThisPtr, BOOLEAN fTimedOut);
  18. extern void DeleteDnsRec(PSMTPDNS_RECS pDnsRec);
  19. extern BOOL GetIpAddressFromDns(char * HostName, PSMTPDNS_RECS pDnsRec, DWORD Index);
  20. CPool CAsyncSmtpDns::Pool(SMTP_ASYNCMX_SIGNATURE);
  21. CAsyncSmtpDns::CAsyncSmtpDns(SMTP_SERVER_INSTANCE * pServiceInstance,
  22. ISMTPConnection *pSmtpConnection)
  23. {
  24. TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::CAsyncSmtpDns");
  25. DebugTrace((LPARAM) this, "Creating CAsyncSmtpDns object = 0x%08x", this);
  26. m_Signature = SMTP_ASYNCMX_SIGNATURE;
  27. m_DomainOptions = 0;
  28. m_fConnectToSmartHost = FALSE;
  29. m_pServiceInstance = pServiceInstance;
  30. m_pISMTPConnection = pSmtpConnection;
  31. m_pDNS_RESOLVER_RECORD = NULL;
  32. m_fInitCalled = FALSE;
  33. m_pszSSLVerificationName = NULL;
  34. pServiceInstance->InsertAsyncDnsObject(this);
  35. }
  36. BOOL CAsyncSmtpDns::Init (LPSTR pszSSLVerificationName)
  37. {
  38. BOOL fRet = FALSE;
  39. TraceFunctEnterEx ((LPARAM) this, "CAsyncSmtpDns::Init");
  40. m_fInitCalled = TRUE;
  41. if (pszSSLVerificationName) {
  42. m_pszSSLVerificationName = new char [lstrlen(pszSSLVerificationName) + 1];
  43. if (!m_pszSSLVerificationName)
  44. goto Exit;
  45. lstrcpy (m_pszSSLVerificationName, pszSSLVerificationName);
  46. }
  47. fRet = TRUE;
  48. Exit:
  49. TraceFunctLeaveEx ((LPARAM) this);
  50. return fRet;
  51. }
  52. CAsyncSmtpDns::~CAsyncSmtpDns()
  53. {
  54. DWORD dwAck = 0;
  55. TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::~CAsyncSmtpDns");
  56. DebugTrace((LPARAM) this, "Destructing CAsyncSmtpDns object = 0x%08x", this);
  57. _ASSERT (m_fInitCalled && "Init not called on CAsyncSmtpDns");
  58. //
  59. // If we did not succeed, we need to ack the connection here (m_dwDiagnostic holds
  60. // the error code to use). On the other hand, if we succeeded, then HandleCompletedData
  61. // must have kicked off an async connection to the server SMTP, and the ISMTPConnection
  62. // will be acked by the "async connect" code -- we don't need to do anything. The
  63. // m_pISMTPConnection may also be legally set to NULL (see HandleCompletedData).
  64. //
  65. if(m_dwDiagnostic != ERROR_SUCCESS && m_pISMTPConnection)
  66. {
  67. if(AQUEUE_E_AUTHORITATIVE_HOST_NOT_FOUND == m_dwDiagnostic)
  68. dwAck = CONNECTION_STATUS_FAILED_NDR_UNDELIVERED;
  69. else
  70. dwAck = CONNECTION_STATUS_FAILED;
  71. DebugTrace((LPARAM) this, "Connection status: %d, Failure: %d", dwAck, m_dwDiagnostic);
  72. m_pISMTPConnection->AckConnection(dwAck);
  73. m_pISMTPConnection->SetDiagnosticInfo(m_dwDiagnostic, NULL, NULL);
  74. m_pISMTPConnection->Release();
  75. m_pISMTPConnection = NULL;
  76. }
  77. if(m_pDNS_RESOLVER_RECORD != NULL)
  78. {
  79. DebugTrace((LPARAM) this, "Deleting DNS_RESOLVER_RECORD in Async SMTP obj");
  80. delete m_pDNS_RESOLVER_RECORD;
  81. m_pDNS_RESOLVER_RECORD = NULL;
  82. }
  83. DBG_CODE(else DebugTrace((LPARAM) this, "No DNS_RESOLVER_RECORD set for Async SMTP obj"));
  84. if(m_pszSSLVerificationName)
  85. delete [] m_pszSSLVerificationName;
  86. m_pServiceInstance->RemoveAsyncDnsObject(this);
  87. TraceFunctLeaveEx((LPARAM) this);
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Description:
  91. // Given a pDnsRec (array of host IP pairs) and an index into it, this
  92. // tries to resolve the host at the Index position. It is assumed that
  93. // the caller (GetMissingIpAddresses) has checked that the host at that
  94. // index lacks an IP address.
  95. // Arguments:
  96. // IN PSMTPDNS_RECS pDnsRec --- Array of (host, IP) pairs.
  97. // IN DWORD Index --- Index of host in pDnsRec to set IP for.
  98. // Returns:
  99. // TRUE --- Success IP was filled in for host.
  100. // FALSE --- Either the host was not resolved from DNS or an error
  101. // occurred (like "out of memory").
  102. //-----------------------------------------------------------------------------
  103. BOOL CAsyncSmtpDns::GetIpFromDns(PSMTPDNS_RECS pDnsRec, DWORD Index)
  104. {
  105. struct hostent *hp = NULL;
  106. MXIPLIST_ENTRY * pEntry = NULL;
  107. BOOL fReturn = FALSE;
  108. TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::GetIpFromDns");
  109. if(m_pServiceInstance->GetNameResolution() == RESOLUTION_UNCACHEDDNS)
  110. {
  111. fReturn = GetIpAddressFromDns(pDnsRec->DnsArray[Index]->DnsName, pDnsRec, Index);
  112. TraceFunctLeaveEx((LPARAM) this);
  113. return fReturn;
  114. }
  115. hp = gethostbyname (pDnsRec->DnsArray[Index]->DnsName);
  116. if(hp != NULL)
  117. {
  118. fReturn = TRUE;
  119. for (DWORD Loop = 0; !m_pServiceInstance->IsShuttingDown() && (hp->h_addr_list[Loop] != NULL); Loop++)
  120. {
  121. pEntry = new MXIPLIST_ENTRY;
  122. if(pEntry != NULL)
  123. {
  124. pDnsRec->DnsArray[Index]->NumEntries++;
  125. CopyMemory(&pEntry->IpAddress, hp->h_addr_list[Loop], 4);
  126. InsertTailList(&pDnsRec->DnsArray[Index]->IpListHead, &pEntry->ListEntry);
  127. }
  128. else
  129. {
  130. fReturn = FALSE;
  131. ErrorTrace((LPARAM) this, "Not enough memory");
  132. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  133. break;
  134. }
  135. }
  136. }
  137. else
  138. {
  139. ErrorTrace((LPARAM) this, "gethostbyname failed on %s", pDnsRec->DnsArray[Index]->DnsName);
  140. SetLastError(ERROR_NO_MORE_ITEMS);
  141. }
  142. TraceFunctLeaveEx((LPARAM) this);
  143. return fReturn;
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Description:
  147. // This runs through the list of hosts (MX hosts, or if no MX records were
  148. // returned, the single target host) and verifies that they all have been
  149. // resolved to IP addresses. If any have been found that do not have IP
  150. // addresses, it will call GetIpFromDns to resolve it.
  151. // Arguments:
  152. // IN PSMTPDNS_RECS pDnsRec -- Object containing Host-IP pairs. Hosts
  153. // without and IP are filled in.
  154. // Returns:
  155. // TRUE -- Success, all hosts have IP addresses.
  156. // FALSE -- Unable to resolve all hosts to IP addresses, or some internal
  157. // error occurred (like "out of memory" or "shutdown in progress".
  158. //-----------------------------------------------------------------------------
  159. BOOL CAsyncSmtpDns::GetMissingIpAddresses(PSMTPDNS_RECS pDnsRec)
  160. {
  161. DWORD Count = 0;
  162. DWORD Error = 0;
  163. BOOL fSucceededOnce = FALSE;
  164. if(pDnsRec == NULL)
  165. {
  166. return FALSE;
  167. }
  168. while(!m_pServiceInstance->IsShuttingDown() && pDnsRec->DnsArray[Count] != NULL)
  169. {
  170. if(IsListEmpty(&pDnsRec->DnsArray[Count]->IpListHead))
  171. {
  172. SetLastError(NO_ERROR);
  173. if(!GetIpFromDns(pDnsRec, Count))
  174. {
  175. Error = GetLastError();
  176. if(Error != ERROR_NO_MORE_ITEMS)
  177. {
  178. return FALSE;
  179. }
  180. }
  181. else
  182. {
  183. fSucceededOnce = TRUE;
  184. }
  185. }
  186. else
  187. {
  188. fSucceededOnce = TRUE;
  189. }
  190. Count++;
  191. }
  192. return ( fSucceededOnce );
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Description:
  196. // HandleCompletedData is called when the DNS resolve is finished. It
  197. // does the final processing after DNS is finished, and sets the
  198. // m_dwDiagnostic flag appropriately. It does one of three things based
  199. // on the DnsStatus and m_AuxList:
  200. //
  201. // (1) If the resolve was successful, it kicks off a connection to the
  202. // server and set the m_dwDiagnostic to ERROR_SUCCESS.
  203. // (2) If the resolve failed authoritatively, it set the m_dwDiagnostic
  204. // to NDR the messages (after checking for a smarthost) ==
  205. // AQUEUE_E_AUTHORITATIVE_HOST_NOT_FOUND.
  206. // (3) If the resolve failed (from dwDnsStatus and m_AuxList) or if
  207. // something fails during HandleCompletedData, the m_dwDiagnostic is
  208. // not modified (it remains initialized to AQUEUE_E_DNS_FAILURE, the
  209. // default error code).
  210. //
  211. // m_dwDiagnostic is examined in ~CAsyncSmtpDns.
  212. // Arguments:
  213. // DNS_STATUS dwDnsStatus - Status code from DnsParseMessage
  214. // Returns:
  215. // Nothing.
  216. //-----------------------------------------------------------------------------
  217. void CAsyncSmtpDns::HandleCompletedData(DNS_STATUS dwDnsStatus)
  218. {
  219. BOOL fRet = FALSE;
  220. PSMTPDNS_RECS TempList = NULL;
  221. CAsyncMx * pAsyncCon = NULL;
  222. MXPARAMS Params;
  223. TempList = m_AuxList;
  224. //
  225. // The DNS lookup failed authoritatively. The messages will be NDR'ed unless there
  226. // is a smarthost configured. If there is a smarthost, we will kick off a resolve
  227. // for it.
  228. //
  229. if(ERROR_NOT_FOUND == dwDnsStatus)
  230. {
  231. if(m_fConnectToSmartHost)
  232. {
  233. char szSmartHost[MAX_PATH+1];
  234. m_pServiceInstance->GetSmartHost(szSmartHost);
  235. ((REMOTE_QUEUE *)m_pServiceInstance->QueryRemoteQObj())->StartAsyncConnect(szSmartHost,
  236. m_pISMTPConnection, m_DomainOptions, FALSE);
  237. //Do not release this ISMTPConnection object! We passed it on to
  238. //StartAsyncConnect so that it can try to associate this object with
  239. //a connection with the smart host. We set it to null here so that we
  240. //will not release it or ack it in the destructor of this object.
  241. m_pISMTPConnection = NULL;
  242. m_dwDiagnostic = ERROR_SUCCESS;
  243. return;
  244. } else {
  245. //No smart host, messages will be NDR'ed. Return value is meaningless.
  246. m_dwDiagnostic = AQUEUE_E_AUTHORITATIVE_HOST_NOT_FOUND;
  247. return;
  248. }
  249. }
  250. //Successful DNS lookup.
  251. if(m_AuxList)
  252. {
  253. m_AuxList = NULL;
  254. //
  255. // Make a last ditch effort to fill in the IP addresses for any hosts
  256. // that are still unresolved.
  257. //
  258. if( !GetMissingIpAddresses(TempList) )
  259. {
  260. m_dwDiagnostic = AQUEUE_E_HOST_NOT_FOUND;
  261. DeleteDnsRec(TempList);
  262. return;
  263. }
  264. Params.HostName = "";
  265. Params.PortNum = m_pServiceInstance->GetRemoteSmtpPort();
  266. Params.TimeOut = INFINITE;
  267. Params.CallBack = QueueCallBackFunction;
  268. Params.pISMTPConnection = m_pISMTPConnection;
  269. Params.pInstance = m_pServiceInstance;
  270. Params.pDnsRec = TempList;
  271. Params.pDNS_RESOLVER_RECORD = m_pDNS_RESOLVER_RECORD;
  272. pAsyncCon = new CAsyncMx (&Params);
  273. if(pAsyncCon != NULL)
  274. {
  275. // Abdicate responsibility for deleting/releasing the dns resolver record
  276. m_pDNS_RESOLVER_RECORD = NULL;
  277. // Outbound SSL: Set name against which server cert. should matched
  278. fRet = pAsyncCon->Init(m_pszSSLVerificationName);
  279. if(!fRet)
  280. {
  281. delete pAsyncCon;
  282. goto Exit;
  283. }
  284. if(!m_fConnectToSmartHost)
  285. {
  286. pAsyncCon->SetTriedOnFailHost();
  287. }
  288. pAsyncCon->SetDomainOptions(m_DomainOptions);
  289. fRet = pAsyncCon->InitializeAsyncConnect();
  290. if(!fRet)
  291. {
  292. delete pAsyncCon;
  293. }
  294. else
  295. {
  296. m_dwDiagnostic = ERROR_SUCCESS;
  297. }
  298. }
  299. else
  300. {
  301. DeleteDnsRec(TempList);
  302. }
  303. }
  304. Exit:
  305. return;
  306. }
  307. //------------------------------------------------------------------------------
  308. // Description:
  309. // Simple wrapper function for DnsQueryAsync. This is a virtual function
  310. // called from CAsyncDns but implemented in CAsyncSmtpDns. In order to retry
  311. // a DNS query we need all the parameters of the old query. These are members
  312. // of CAsyncSmtpDns. Thus the virtual function based implementation.
  313. //
  314. // Arguments:
  315. // BOOL fUdp -- Use UDP as transport for retry query?
  316. //
  317. // Returns:
  318. // TRUE on success. In this situation the ISMTPConnection ack (and release of
  319. // pDNS_RESOLVER_RECORD) is handled by the new CAsyncSmtpDns object created
  320. // by DnsQueryAsync. The diagnostic code of this object is cleared.
  321. //
  322. // FALSE on error. In this case, the cleanup for ISMTPConnection and
  323. // pDNS_RESOLVER_RECORD must be done by "this" CAsyncSmtpDns. The
  324. // diagnostic code is not touched.
  325. //------------------------------------------------------------------------------
  326. BOOL CAsyncSmtpDns::RetryAsyncDnsQuery(BOOL fUdp)
  327. {
  328. TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::RetryAsyncDnsQuery");
  329. BOOL fRet = FALSE;
  330. //
  331. // If we do not have a connection object, then the requery attempt
  332. // is doomed to fail. This can happen when we disconnect and
  333. // ATQ calls our completion function with ERROR_OPERATION_ABORTED
  334. // If we don't have a connection object, there is no way to
  335. // ack the connection or get messages to send.
  336. //
  337. if (!m_pISMTPConnection) {
  338. DebugTrace((LPARAM) this,
  339. "RetryAsyncDnsQuery called without connection object - aborting");
  340. //should be cleared by same code path
  341. _ASSERT(!m_pDNS_RESOLVER_RECORD);
  342. fRet = FALSE; //there is nothing to Ack.
  343. goto Exit;
  344. }
  345. fRet = DnsQueryAsync(
  346. m_pServiceInstance,
  347. m_HostName,
  348. m_FQDNToDrop,
  349. m_pISMTPConnection,
  350. m_dwFlags,
  351. m_DomainOptions,
  352. m_fConnectToSmartHost,
  353. m_pDNS_RESOLVER_RECORD,
  354. m_pszSSLVerificationName,
  355. fUdp);
  356. if(fRet) {
  357. m_pDNS_RESOLVER_RECORD = NULL;
  358. m_pISMTPConnection = NULL;
  359. m_dwDiagnostic = ERROR_SUCCESS;
  360. }
  361. Exit:
  362. TraceFunctLeave();
  363. return fRet;
  364. }