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.

2395 lines
78 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name :
  4. remoteq.cxx
  5. Abstract:
  6. Implements a derivation of the generic queue
  7. for internet mail delivery
  8. Author:
  9. Rohan Phillips ( Rohanp ) 24-JAN-1995
  10. Project:
  11. SMTP Server DLL
  12. Functions Exported:
  13. Revision History:
  14. --*/
  15. /************************************************************
  16. * Include Headers
  17. ************************************************************/
  18. #define INCL_INETSRV_INCS
  19. #include "smtpinc.h"
  20. #include "dropdir.hxx"
  21. #include "remoteq.hxx"
  22. #include "smtpout.hxx"
  23. #include <cdns.h>
  24. #include "smtpdns.hxx"
  25. #define INVALID_RCPT_IDX_VALUE 0xFFFFFFFF
  26. extern char * MyStrChr(char *Line, unsigned char Val, DWORD LineSize);
  27. extern void DeleteDnsRec(PSMTPDNS_RECS pDnsRec);
  28. extern CTcpRegIpList g_TcpRegIpList;
  29. ///////////////////////////////////////////////////////////////////////////
  30. #if 0
  31. REMOTE_QUEUE::REMOTE_QUEUE(SMTP_SERVER_INSTANCE * pSmtpInst)
  32. : PERSIST_QUEUE(pSmtpInst)
  33. {
  34. }
  35. #endif
  36. ///////////////////////////////////////////////////////////////////////////
  37. DWORD g_dwFileCounter = 0;
  38. #define MIN(a,b) ( (a) > (b) ? (b) : (a) )
  39. ///////////////////////////////////////////////////////////////////////////
  40. /*++
  41. Name :
  42. REMOTE_QUEUE::ProcessQueueEvents
  43. Description:
  44. This function takes a pointer to a QUEUE_ENTRY,
  45. which contains all the information needed to
  46. deliver local mail, and delivers the mail to the
  47. remote site.
  48. Arguments:
  49. a pointer to a QUEUE_ENTRY class
  50. Returns:
  51. --*/
  52. BOOL REMOTE_QUEUE::ProcessQueueEvents(ISMTPConnection *pISMTPConnection)
  53. {
  54. DWORD Error = 0;
  55. char * FileName = NULL;
  56. DWORD IpAddress = 0;
  57. DWORD TransmitOptions = 0;
  58. HRESULT hr = S_OK;
  59. BOOL AsyncConnectStarted = FALSE;
  60. DomainInfo DomainParams;
  61. char * ConnectedDomain = NULL;
  62. TraceFunctEnterEx((LPARAM) this, "REMOTE_QUEUE::ProcessQueueEvents(PQUEUE_ENTRY pEntry)");
  63. _ASSERT(pISMTPConnection != NULL);
  64. ZeroMemory (&DomainParams, sizeof(DomainParams));
  65. if(pISMTPConnection == NULL)
  66. {
  67. TraceFunctLeaveEx((LPARAM)this);
  68. return FALSE;
  69. }
  70. //leave quickly if we are shutting down
  71. if(GetParentInst()->IsShuttingDown()
  72. || (GetParentInst()->QueryServerState( ) == MD_SERVER_STATE_STOPPED)
  73. || (GetParentInst()->QueryServerState( ) == MD_SERVER_STATE_INVALID))
  74. {
  75. HandleFailedConnection(pISMTPConnection);
  76. TraceFunctLeaveEx((LPARAM)this);
  77. return FALSE;
  78. }
  79. ZeroMemory(&DomainParams, sizeof(DomainParams));
  80. DomainParams.cbVersion = sizeof(DomainParams);
  81. hr = pISMTPConnection->GetDomainInfo(&DomainParams);
  82. if(!FAILED(hr))
  83. {
  84. if(DomainParams.dwDomainInfoFlags & DOMAIN_INFO_LOCAL_DROP)
  85. {
  86. AsyncCopyMailToDropDir(pISMTPConnection, DomainParams.szDropDirectory, GetParentInst());
  87. pISMTPConnection->Release();
  88. TraceFunctLeaveEx((LPARAM)this);
  89. return TRUE;
  90. }
  91. ConnectedDomain = DomainParams.szDomainName;
  92. if(DomainParams.szSmartHostDomainName != NULL)
  93. {
  94. ConnectedDomain = DomainParams.szSmartHostDomainName;
  95. }
  96. AsyncConnectStarted = StartAsyncConnect( (const char *)ConnectedDomain,
  97. pISMTPConnection,
  98. DomainParams.dwDomainInfoFlags,
  99. GetParentInst()->UseSmartHostAfterFail());
  100. if(AsyncConnectStarted)
  101. {
  102. TraceFunctLeaveEx((LPARAM) this);
  103. return TRUE;
  104. }
  105. }
  106. HandleFailedConnection(pISMTPConnection);
  107. TraceFunctLeaveEx((LPARAM) this);
  108. return FALSE;
  109. }
  110. ///////////////////////////////////////////////////////////////////////////
  111. /*++
  112. Name :
  113. void HandleFailedConnection (PMAIL_ENTRY MailQEntry)
  114. Description:
  115. This function takes a pointer to a PMAIL_ENTRY,
  116. and either places it in the retry queue or the
  117. bad mail directory.
  118. Arguments:
  119. a pointer to a PMAIL_ENTRY class
  120. dwConnectionStatus... the status passed back to AQ
  121. dwConnectedIPAddress IP Adress string (passed back to AQ)
  122. 11/11/98 - MikeSwa Added dwConnectionStatus
  123. 11/29/2001 - MikeSwa Added dwConnectedIPAddress
  124. --*/
  125. void REMOTE_QUEUE::HandleFailedConnection (ISMTPConnection *pISMTPConnection,
  126. DWORD dwConnectionStatus,
  127. DWORD dwConnectedIPAddress)
  128. {
  129. HRESULT hrConnectionFailure = AQUEUE_E_HOST_NOT_RESPONDING;
  130. if(pISMTPConnection != NULL)
  131. {
  132. //We know the connection failed... lets add some additional diagnostic
  133. //information
  134. if (CONNECTION_STATUS_FAILED_LOOPBACK == dwConnectionStatus)
  135. hrConnectionFailure = AQUEUE_E_LOOPBACK_DETECTED;
  136. pISMTPConnection->SetDiagnosticInfo(hrConnectionFailure, NULL, NULL);
  137. pISMTPConnection->AckConnection(dwConnectionStatus);
  138. if (dwConnectedIPAddress)
  139. ReportConnectedIPAddress(pISMTPConnection, dwConnectedIPAddress);
  140. pISMTPConnection->Release();
  141. }
  142. }
  143. ///////////////////////////////////////////////////////////////////////////
  144. /*++
  145. Name :
  146. void ReportConnectedIPAddress
  147. Description:
  148. Reports IP address that the protocol attempted to connect to back
  149. to Queuing.
  150. Arguments:
  151. ISMTPConnection AQ connection object
  152. dwConnectedIPAddress IP Adress string (passed back to AQ)
  153. 11/29/2001 - MikeSwa Created
  154. --*/
  155. void REMOTE_QUEUE::ReportConnectedIPAddress(ISMTPConnection *pISMTPConnection,
  156. DWORD dwConnectedIPAddress)
  157. {
  158. CHAR szConnectedIPAddress[46] = "";
  159. DWORD IPAddress = dwConnectedIPAddress;
  160. CMailMsgPropertyBag SessionPropertyBag;
  161. if (!IPAddress || !pISMTPConnection)
  162. return;
  163. //
  164. // Convert connected IP address to string form
  165. //
  166. szConnectedIPAddress[sizeof(szConnectedIPAddress) - 1] = '\0';
  167. InetNtoa(*(struct in_addr *) &IPAddress,
  168. (CHAR *) &szConnectedIPAddress);
  169. _ASSERT(strlen(szConnectedIPAddress) < sizeof(szConnectedIPAddress));
  170. if (szConnectedIPAddress[0])
  171. {
  172. SessionPropertyBag.PutStringA(ISESSION_PID_REMOTE_IP_ADDRESS,
  173. szConnectedIPAddress);
  174. SMTP_CONNOUT::PromoteSessionPropertiesToAQ(
  175. (IUnknown *) ((IMailMsgPropertyBag *) &SessionPropertyBag),
  176. pISMTPConnection);
  177. }
  178. }
  179. ///////////////////////////////////////////////////////////////////////////
  180. DWORD QueueDeleteFunction(PVOID ThisPtr)
  181. {
  182. CAsyncMx * ThisQueue = (CAsyncMx *) ThisPtr;
  183. if(ThisQueue)
  184. {
  185. delete ThisQueue;
  186. }
  187. return 0;
  188. }
  189. ///////////////////////////////////////////////////////////////////////////
  190. //
  191. // QueueCallBackFunction :
  192. //
  193. //Return TRUE - when we want the asyncmx object to be kept around
  194. //Return FALSE - to delete the object when the thread exits
  195. //
  196. BOOL QueueCallBackFunction(PVOID ThisPtr, BOOLEAN fTimedOut)
  197. {
  198. CAsyncMx * ThisQueue = (CAsyncMx *) ThisPtr;
  199. REMOTE_QUEUE * pRemoteQ = NULL;
  200. BOOL fSuccessfullConnect = TRUE;
  201. BOOL fAtqConnect = TRUE;
  202. char * NextMxPtr = NULL;
  203. SMTPDNS_RECS * pDnsRec = NULL;
  204. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD = NULL;
  205. char szSmartHost[MAX_PATH + 1];
  206. char Scratch[256];
  207. DWORD Error = 0;
  208. DWORD dwFailedConnectionStatus = CONNECTION_STATUS_FAILED;
  209. int NumRecords = 0;
  210. DWORD IpAddress = 0;
  211. TraceFunctEnterEx((LPARAM) ThisQueue, "QueueCallBackFunction");
  212. Scratch[0] = '\0';
  213. if(ThisQueue->GetParentInst()->IsShuttingDown())
  214. {
  215. //takes care of the case where we are shutting down, but
  216. //a successful connection came in at the same time
  217. ThisQueue->SetCloseSocketFlag(TRUE);
  218. ThisQueue->CloseAsyncSocket();
  219. ThisQueue->AckMessage();
  220. pRemoteQ = (REMOTE_QUEUE *) ThisQueue->GetParentInst()->QueryRemoteQObj();
  221. _ASSERT(pRemoteQ != NULL);
  222. pRemoteQ->HandleFailedConnection(ThisQueue->GetSmtpConnectionObj());
  223. TraceFunctLeaveEx((LPARAM) ThisPtr);
  224. return FALSE;
  225. }
  226. if (!ThisQueue->GetDnsRec())
  227. {
  228. //See bug X5:120720 - This means that another thread has called back
  229. //on this object. The call to OnConnect below will AV. We do not
  230. //have a repro scenario for this, and see if only once every few
  231. //months. MilanS has recommended this non-intrusive check to
  232. //add additional protection against a double callback
  233. _ASSERT(0 && "Multiple threads calling back on CAsyncMx");
  234. //Return TRUE because first thread will handle deleting object
  235. return TRUE;
  236. }
  237. //get a pointer to the remote queue
  238. pRemoteQ = (REMOTE_QUEUE *) ThisQueue->GetParentInst()->QueryRemoteQObj();
  239. _ASSERT(pRemoteQ != NULL);
  240. IpAddress = ThisQueue->GetConnectedIpAddress();
  241. InetNtoa(*(struct in_addr *) &IpAddress, Scratch);
  242. //See if the connect was successful
  243. fSuccessfullConnect = ThisQueue->AsyncConnectSuccessfull();
  244. //Call record the state of this connection
  245. ThisQueue->OnConnect(fSuccessfullConnect);
  246. //Test the connect value
  247. if(fSuccessfullConnect)
  248. {
  249. DebugTrace((LPARAM)ThisQueue, "QueueCallBack called with successful connect!");
  250. pDnsRec = ThisQueue->GetDnsRec();
  251. pDNS_RESOLVER_RECORD = ThisQueue->GetDnsResolverRecord();
  252. ThisQueue->SetDnsRecToNull();
  253. ThisQueue->SetDnsResolverRecord(NULL);
  254. fAtqConnect = pRemoteQ->MakeATQConnection( pDnsRec,
  255. ThisQueue->GetSockethandle(),
  256. ThisQueue->GetConnectedIpAddress(),
  257. ThisQueue->GetSmtpConnectionObj(),
  258. ThisQueue->GetDomainOptions(),
  259. ThisQueue->GetSSLVerificationName(),
  260. pDNS_RESOLVER_RECORD);
  261. if(!fAtqConnect)
  262. {
  263. ErrorTrace((LPARAM)ThisQueue, "FAILED pRemoteQ->MakeATQConnection!!!");
  264. ThisQueue->AckMessage();
  265. if (ThisQueue->WasLoopback())
  266. dwFailedConnectionStatus = CONNECTION_STATUS_FAILED_LOOPBACK;
  267. pRemoteQ->HandleFailedConnection(ThisQueue->GetSmtpConnectionObj(),
  268. dwFailedConnectionStatus,
  269. ThisQueue->GetConnectedIpAddress());
  270. }
  271. TraceFunctLeaveEx((LPARAM) ThisPtr);
  272. return FALSE;
  273. } else if (ThisQueue->WasLoopback()) {
  274. dwFailedConnectionStatus = CONNECTION_STATUS_FAILED_LOOPBACK;
  275. ThisQueue->GetSmtpConnectionObj()->SetDiagnosticInfo(AQUEUE_E_LOOPBACK_DETECTED, NULL, NULL);
  276. //
  277. // Make sure we have the IP address for diagnostic purposes
  278. //
  279. pRemoteQ->ReportConnectedIPAddress(ThisQueue->GetSmtpConnectionObj(),
  280. ThisQueue->GetConnectedIpAddress());
  281. }
  282. ErrorTrace((LPARAM)ThisPtr,"connection to %s failed", Scratch);
  283. BUMP_COUNTER (ThisQueue->GetParentInst(), NumConnOutRefused);
  284. IpAddress = ThisQueue->GetNextIpAddress();
  285. //
  286. // A connect will cause QueueCallBack to be called again through a
  287. // completion event posted on FD_CONNECT. On each call to ConnectToHost
  288. // we try a new IP address till all the IP addresses for this MX host
  289. // are exhausted.
  290. //
  291. if((IpAddress != INADDR_NONE) && ThisQueue->ConnectToHost(IpAddress))
  292. {
  293. DebugTrace((LPARAM)ThisQueue, "Connecting to MX host: %08x", IpAddress);
  294. TraceFunctLeaveEx((LPARAM) ThisPtr);
  295. return TRUE;
  296. }
  297. ThisQueue->CloseAsyncSocket();
  298. DebugTrace((LPARAM) ThisQueue, "Ran out of IP addresses for MX host");
  299. //
  300. // If we failed to connect to any of the IP addresses for the (current)
  301. // MX host, try connecting to the next MX host for this destination.
  302. //
  303. while(!ThisQueue->GetParentInst()->IsShuttingDown())
  304. {
  305. if(ThisQueue->ConnectToNextMxHost())
  306. {
  307. DebugTrace((LPARAM)ThisQueue, "Trying ConnectToNextMxHost");
  308. TraceFunctLeaveEx((LPARAM) ThisPtr);
  309. return TRUE;
  310. }
  311. else
  312. {
  313. Error = GetLastError();
  314. ThisQueue->CloseAsyncSocket();
  315. if(Error == ERROR_NO_MORE_ITEMS)
  316. {
  317. DebugTrace((LPARAM)ThisQueue, "Failed ConnectToNextMxHost with ERROR_NO_MORE_ITEMS");
  318. }
  319. else
  320. {
  321. ErrorTrace((LPARAM)ThisQueue, "Failed ConnectToNextMxHost: Host not responding");
  322. ThisQueue->GetSmtpConnectionObj()->SetDiagnosticInfo(AQUEUE_E_HOST_NOT_RESPONDING, NULL, NULL);
  323. }
  324. break;
  325. }
  326. }
  327. ThisQueue->CloseAsyncSocket();
  328. DebugTrace((LPARAM)ThisQueue, "Ran out of MX hosts for destination. Trying new destination.");
  329. //
  330. // If we failed to connect to any MX host for the destination server
  331. // we need to try an alternate destination. ConnectToNextResolverHost
  332. // uses the DNS_RESOLVER_RECORD (member of CAsyncMx *ThisQueue) to get
  333. // the name of an alternative host and resolve it (get the MX records
  334. // for it).
  335. //
  336. while(!ThisQueue->GetParentInst()->IsShuttingDown())
  337. {
  338. if(pRemoteQ->ConnectToNextResolverHost( ThisQueue ))
  339. {
  340. TraceFunctLeaveEx((LPARAM) ThisPtr);
  341. return TRUE;
  342. }
  343. else
  344. {
  345. Error = GetLastError();
  346. ThisQueue->CloseAsyncSocket();
  347. if(Error == ERROR_NO_MORE_ITEMS)
  348. {
  349. DebugTrace((LPARAM)ThisQueue, "Failed ConnectToNextResolverHost : ERROR_NO_MORE_ITEMS");
  350. }
  351. else
  352. {
  353. ErrorTrace((LPARAM)ThisQueue, "Failed ConnectToNextResolverHost. Error = %08x", Error);
  354. ThisQueue->GetSmtpConnectionObj()->SetDiagnosticInfo(AQUEUE_E_DNS_FAILURE, NULL, NULL);
  355. }
  356. break;
  357. }
  358. }
  359. ThisQueue->CloseAsyncSocket();
  360. //So we have tried all we could for the real destination
  361. //check if we have a fallback smarthost and that we have not already tried it
  362. if( pRemoteQ->GetParentInst()->UseSmartHostAfterFail() &&
  363. pRemoteQ->GetParentInst()->GetSmartHost(szSmartHost) &&
  364. !ThisQueue->GetTriedOnFailHost())
  365. {
  366. if(pRemoteQ->StartAsyncConnect(szSmartHost, ThisQueue->GetSmtpConnectionObj(), ThisQueue->GetDomainOptions(), FALSE))
  367. {
  368. TraceFunctLeaveEx((LPARAM) ThisPtr);
  369. return TRUE;
  370. }
  371. }
  372. ThisQueue->AckMessage();
  373. pRemoteQ->HandleFailedConnection(ThisQueue->GetSmtpConnectionObj(),
  374. dwFailedConnectionStatus,
  375. ThisQueue->GetConnectedIpAddress());
  376. TraceFunctLeaveEx((LPARAM) ThisPtr);
  377. return FALSE;
  378. }
  379. ///////////////////////////////////////////////////////////////////////////
  380. PSMTPDNS_RECS GetDnsRecordsFromHostFile(const char * HostName)
  381. {
  382. PSMTPDNS_RECS pDnsRec = NULL;
  383. MXIPLIST_ENTRY * pEntry = NULL;
  384. struct hostent *hp = NULL;
  385. BOOL fRet = TRUE;
  386. DWORD Error = 0;
  387. TraceFunctEnterEx((LPARAM) NULL, "GetDnsRecordsFromHostFile");
  388. DebugTrace((LPARAM)NULL,"Using gethostbyname for hostname resolution - %s", HostName);
  389. hp = gethostbyname (HostName);
  390. if(hp == NULL)
  391. {
  392. Error = WSAGetLastError();
  393. ErrorTrace((LPARAM)NULL,"struct hostent *hp is NULL for %s - %x", HostName, Error);
  394. TraceFunctLeaveEx((LPARAM) NULL);
  395. return NULL;
  396. }
  397. pDnsRec = new SMTPDNS_RECS;
  398. if(pDnsRec == NULL)
  399. {
  400. ErrorTrace((LPARAM)NULL,"pDnsRec = new SMTPDNS_RECS failed for %s", HostName);
  401. TraceFunctLeaveEx((LPARAM) NULL);
  402. return NULL;
  403. }
  404. ZeroMemory(pDnsRec, sizeof(SMTPDNS_RECS));
  405. pDnsRec->DnsArray[0] = new MX_NAMES;
  406. if(pDnsRec->DnsArray[0] == NULL)
  407. {
  408. ErrorTrace((LPARAM)NULL,"new MX_NAMES failed for %s", HostName);
  409. delete pDnsRec;
  410. TraceFunctLeaveEx((LPARAM) NULL);
  411. return NULL;
  412. }
  413. pDnsRec->NumRecords = 1;
  414. pDnsRec->DnsArray[0]->NumEntries = 0;
  415. InitializeListHead(&pDnsRec->DnsArray[0]->IpListHead);
  416. lstrcpyn(pDnsRec->DnsArray[0]->DnsName, HostName, sizeof(pDnsRec->DnsArray[0]->DnsName));
  417. for (DWORD Loop = 0; (hp->h_addr_list[Loop] != NULL); Loop++)
  418. {
  419. pEntry = new MXIPLIST_ENTRY;
  420. if(pEntry != NULL)
  421. {
  422. pDnsRec->DnsArray[0]->NumEntries++;
  423. CopyMemory(&pEntry->IpAddress, hp->h_addr_list[Loop], 4);
  424. InsertTailList(&pDnsRec->DnsArray[0]->IpListHead, &pEntry->ListEntry);
  425. }
  426. else
  427. {
  428. ErrorTrace((LPARAM)NULL,"new MXIPLIST_ENTRY failed for %s", HostName);
  429. fRet = FALSE;
  430. break;
  431. }
  432. }
  433. if(fRet)
  434. {
  435. return pDnsRec;
  436. }
  437. else
  438. {
  439. DeleteDnsRec(pDnsRec);
  440. pDnsRec = NULL;
  441. }
  442. TraceFunctLeaveEx((LPARAM) NULL);
  443. return pDnsRec;
  444. }
  445. ///////////////////////////////////////////////////////////////////////////
  446. PSMTPDNS_RECS GetDnsRecordsFromLiteral(const char * HostName)
  447. {
  448. PSMTPDNS_RECS pDnsRec = NULL;
  449. MXIPLIST_ENTRY * pEntry = NULL;
  450. BOOL fRet = TRUE;
  451. DWORD Error = 0;
  452. unsigned long InetAddr = 0;
  453. char * pEndIp = NULL;
  454. char * pRealHost = NULL;
  455. char OldChar = '\0';
  456. TraceFunctEnterEx((LPARAM) NULL, "GetDnsRecordsFromLiteral");
  457. pRealHost = (char *) HostName;
  458. //see if this is a domain literal
  459. if(pRealHost[0] == '[')
  460. {
  461. pEndIp = strchr(pRealHost, ']');
  462. if(pEndIp == NULL)
  463. {
  464. ErrorTrace((LPARAM)NULL,"Didn't find ] in literal for %s", HostName);
  465. TraceFunctLeaveEx((LPARAM) NULL);
  466. return NULL;
  467. }
  468. //save the old character
  469. OldChar = *pEndIp;
  470. //null terminate the string
  471. *pEndIp = '\0';
  472. pRealHost++;
  473. //Is this an ip address
  474. InetAddr = inet_addr( (char *) pRealHost );
  475. }
  476. //put back the old character
  477. if (pEndIp)
  478. *pEndIp = OldChar;
  479. if((InetAddr == INADDR_NONE) || (InetAddr == 0))
  480. {
  481. ErrorTrace((LPARAM)NULL,"InetAddr is invalid for %s", HostName);
  482. return NULL;
  483. }
  484. pDnsRec = new SMTPDNS_RECS;
  485. if(pDnsRec == NULL)
  486. {
  487. ErrorTrace((LPARAM)NULL,"new SMTPDNS_RECS2 failed for", HostName);
  488. TraceFunctLeaveEx((LPARAM) NULL);
  489. return NULL;
  490. }
  491. ZeroMemory(pDnsRec, sizeof(SMTPDNS_RECS));
  492. pDnsRec->DnsArray[0] = new MX_NAMES;
  493. if(pDnsRec->DnsArray[0] == NULL)
  494. {
  495. ErrorTrace((LPARAM)NULL,"new MX_NAMES2 failed for %s", HostName);
  496. delete pDnsRec;
  497. TraceFunctLeaveEx((LPARAM) NULL);
  498. return NULL;
  499. }
  500. pEntry = new MXIPLIST_ENTRY;
  501. if(pEntry == NULL)
  502. {
  503. ErrorTrace((LPARAM)NULL,"MXIPLIST_ENTRY2 failed for %s", HostName);
  504. DeleteDnsRec(pDnsRec);
  505. TraceFunctLeaveEx((LPARAM) NULL);
  506. return NULL;
  507. }
  508. pDnsRec->NumRecords = 1;
  509. pDnsRec->DnsArray[0]->NumEntries = 1;
  510. pEntry->IpAddress = InetAddr;
  511. InitializeListHead(&pDnsRec->DnsArray[0]->IpListHead);
  512. lstrcpyn(pDnsRec->DnsArray[0]->DnsName, HostName, sizeof(pDnsRec->DnsArray[0]->DnsName));
  513. InsertTailList(&pDnsRec->DnsArray[0]->IpListHead, &pEntry->ListEntry);
  514. TraceFunctLeaveEx((LPARAM) NULL);
  515. return pDnsRec;
  516. }
  517. ///////////////////////////////////////////////////////////////////////////
  518. PSMTPDNS_RECS GetDnsRecordsFromResolverInfo(const char * HostName, DWORD dwAddr)
  519. {
  520. PSMTPDNS_RECS pDnsRec = NULL;
  521. MXIPLIST_ENTRY * pEntry = NULL;
  522. TraceFunctEnterEx((LPARAM) NULL, "GetDnsRecordsFromResolverInfo");
  523. pDnsRec = new SMTPDNS_RECS;
  524. if(pDnsRec == NULL)
  525. {
  526. ErrorTrace((LPARAM)NULL,"new SMTPDNS_RECS2 failed for", HostName);
  527. TraceFunctLeaveEx((LPARAM) NULL);
  528. return NULL;
  529. }
  530. ZeroMemory(pDnsRec, sizeof(SMTPDNS_RECS));
  531. pDnsRec->DnsArray[0] = new MX_NAMES;
  532. if(pDnsRec->DnsArray[0] == NULL)
  533. {
  534. ErrorTrace((LPARAM)NULL,"new MX_NAMES2 failed for %s", HostName);
  535. delete pDnsRec;
  536. TraceFunctLeaveEx((LPARAM) NULL);
  537. return NULL;
  538. }
  539. pEntry = new MXIPLIST_ENTRY;
  540. if(pEntry == NULL)
  541. {
  542. ErrorTrace((LPARAM)NULL,"MXIPLIST_ENTRY2 failed for %s", HostName);
  543. DeleteDnsRec(pDnsRec);
  544. TraceFunctLeaveEx((LPARAM) NULL);
  545. return NULL;
  546. }
  547. pDnsRec->NumRecords = 1;
  548. pDnsRec->DnsArray[0]->NumEntries = 1;
  549. pEntry->IpAddress = dwAddr;
  550. InitializeListHead(&pDnsRec->DnsArray[0]->IpListHead);
  551. lstrcpyn(pDnsRec->DnsArray[0]->DnsName, HostName, sizeof(pDnsRec->DnsArray[0]->DnsName));
  552. InsertTailList(&pDnsRec->DnsArray[0]->IpListHead, &pEntry->ListEntry);
  553. TraceFunctLeaveEx((LPARAM) NULL);
  554. return pDnsRec;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Decription:
  558. // This function is called when we have exhausted all the MX hosts for a
  559. // particular destination server. The only option is to see if there are
  560. // any alternative destinations to which the mail may be forwarded. A list
  561. // of alternative hosts is maintained in the DNS resolver record (if it is
  562. // available. So we need to kick off a resolve for the next alternate host
  563. // if it exists.
  564. // Arguments:
  565. // Returns:
  566. // History:
  567. //-----------------------------------------------------------------------------
  568. BOOL REMOTE_QUEUE::ConnectToNextResolverHost( CAsyncMx * pThisQ )
  569. {
  570. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD;
  571. BOOL fRet;
  572. // We embedded the resolver record into the MX records object
  573. pDNS_RESOLVER_RECORD = pThisQ->GetDnsResolverRecord();
  574. // We will handle deletion of pDNS_RESOLVER_RECORD from now on
  575. pThisQ->SetDnsResolverRecord(NULL);
  576. // No alternate hosts.
  577. if(pDNS_RESOLVER_RECORD == NULL || pDNS_RESOLVER_RECORD->GetDnsResolverRecord() == NULL)
  578. {
  579. if(pDNS_RESOLVER_RECORD)
  580. delete pDNS_RESOLVER_RECORD;
  581. return( FALSE );
  582. }
  583. char MyFQDNName[MAX_PATH + 1];
  584. GetParentInst()->LockGenCrit();
  585. lstrcpyn(MyFQDNName,GetParentInst()->GetFQDomainName(),MAX_PATH);
  586. GetParentInst()->UnLockGenCrit();
  587. CrashOnInvalidSMTPConn(pThisQ->GetSmtpConnectionObj());
  588. fRet = ConnectToResolverHost( pThisQ->GetSSLVerificationName(),
  589. MyFQDNName,
  590. pThisQ->GetSmtpConnectionObj(),
  591. pThisQ->GetDomainOptions(),
  592. FALSE,
  593. pDNS_RESOLVER_RECORD,
  594. pThisQ->GetDnsRec() );
  595. return fRet;
  596. }
  597. //-----------------------------------------------------------------------------
  598. // Description:
  599. // Basically this function tries to resolve one of the hosts in the DNS
  600. // resolver record (supplied by the sink) so that SMTP can send to that.
  601. // To do this it is called repeatedly, and each time around it tries a
  602. // different host till it succeeds in resolving it. If no DNS resolver
  603. // record is available (NULL) we try to resolve the HostName directly.
  604. // If DNS resolution is not needed --- ie if the resolution information
  605. // is available locally or from the resolver record, then this function
  606. // will kick off an async connect to the first MX host.
  607. // Arguments:
  608. // [IN] const char * HostName - Hostname to resolve
  609. // [IN] LPSTR MyFQDNName -
  610. // [IN] ISMTPConnection *pISMTPConnection -
  611. // [IN] DWORD DomainOptions - Bitmask of options
  612. // [IN] BOOL fUseSmartHostAfterFail -
  613. // [IN] DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD - This object must always
  614. // be associated with the current remote queue, till we succeed in
  615. // connecting to the remote SMTP server. If this function fails,
  616. // the DNS_RESOLVER_RECORD will be deleted. The caller no longer
  617. // has responsibility for this after calling this function.
  618. // [IN] PSMTP_DNSRECS pDnsRec - This is NULL unless the call to Connect...
  619. // is a "failover call". It encapsulates the parameters for the failed
  620. // message in such a case. These parameters are copied when an async
  621. // connection/DNS-query are kicked off and will be acked by the async
  622. // connect/DNS-query code. If this function succeeds, these parameters
  623. // should not be acked by the caller and they are set to NULL by this
  624. // function, within the pDnsRetryRec.
  625. // Returns:
  626. // TRUE on success.
  627. // FALSE on all errors.
  628. // History:
  629. // GPulla modified.
  630. //-----------------------------------------------------------------------------
  631. BOOL REMOTE_QUEUE::ConnectToResolverHost( const char * HostName,
  632. LPSTR MyFQDNName,
  633. ISMTPConnection *pISMTPConnection,
  634. DWORD DomainOptions,
  635. BOOL fUseSmartHostAfterFail,
  636. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD,
  637. PSMTPDNS_RECS pDnsRetryRec )
  638. {
  639. DWORD dwAddr = 0;
  640. PSMTPDNS_RECS pDnsRec = NULL;
  641. BOOL fRet = FALSE;
  642. BOOL fUseDns = TRUE;
  643. BOOL fIsLiteral = FALSE;
  644. DWORD dwDnsFlags = 0;
  645. LPSTR pszRealHostName = NULL;
  646. BOOL fFreeHostName = FALSE;
  647. HRESULT hr = S_OK;
  648. LPSTR pszSSLVerificationName = NULL;
  649. BOOL fSSLSubjectDisabled = FALSE;
  650. TraceFunctEnterEx((LPARAM)this, "ConnectToResolverHost (const char * HostName)");
  651. DebugTrace((LPARAM) this,"Finding MX records for %s with options %x", HostName, DomainOptions);
  652. if (HostName)
  653. {
  654. fIsLiteral = (HostName[0] == '[');
  655. }
  656. if(GetParentInst()->UseGetHostByName() || fIsLiteral)
  657. {
  658. fUseDns = FALSE;
  659. DebugTrace((LPARAM) this,"Not using DNS for resolution - literal, or GHBN");
  660. }
  661. else if(g_TcpRegIpList.GetCount() == 0)
  662. {
  663. fUseDns = FALSE;
  664. DebugTrace((LPARAM) this,"Not using DNS for resolution - No DNS servers");
  665. }
  666. if (!(DomainOptions | MUST_DO_TLS) || !GetParentInst()->RequiresSSLCertVerifySubject())
  667. fSSLSubjectDisabled = TRUE;
  668. dwDnsFlags = GetParentInst()->GetDnsFlags();
  669. pszRealHostName = (LPSTR)HostName;
  670. //
  671. // If there is a DNS resolver record from the DNS sink, retrieve the (next)
  672. // possible destination host from it for resolution.
  673. //
  674. if( pDNS_RESOLVER_RECORD && pDNS_RESOLVER_RECORD->GetDnsResolverRecord() )
  675. {
  676. DWORD dwAddr = 0;
  677. LPSTR pszTmpHostName = NULL;
  678. DebugTrace((LPARAM) this, "Trying to get next host from DNS resolver record");
  679. if( FAILED( hr = pDNS_RESOLVER_RECORD->HrGetNextDestinationHost(&pszTmpHostName, &dwAddr) ) )
  680. {
  681. if( HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) != hr )
  682. {
  683. ErrorTrace((LPARAM) this,"m_pIDnsResolverRecord->GetItem() failed hr = 0x%x failed", hr);
  684. }
  685. else
  686. {
  687. DebugTrace((LPARAM) this, "Tried all possible destination hosts. Failing ConnectToResolverHost.\n");
  688. SetLastError( ERROR_NO_MORE_ITEMS );
  689. }
  690. fRet = FALSE;
  691. goto Exit;
  692. }
  693. else
  694. {
  695. fFreeHostName = TRUE;
  696. pszRealHostName = pszTmpHostName;
  697. DebugTrace((LPARAM) this, "ConnectToResolverHost trying destination host : %s", pszRealHostName);
  698. if( dwAddr != 0 )
  699. {
  700. //
  701. // this means we don't have to call DNS or GetHostByName to get the Ip addres
  702. //
  703. DebugTrace((LPARAM) this, "DNS records available, not calling DNS");
  704. pDnsRec = GetDnsRecordsFromResolverInfo( pszRealHostName, dwAddr );
  705. if(pDnsRec)
  706. {
  707. //
  708. // This causes SMTP to successively try an connect to each of the MX hosts
  709. // for the destination host in turn till a connection succeeds. pDnsRec has
  710. // the MX hosts info.
  711. //
  712. DebugTrace((LPARAM) this, "Initializing async connect to MX hosts...");
  713. if(pDnsRetryRec)
  714. {
  715. pDnsRec->pMailMsgObj = pDnsRetryRec->pMailMsgObj;
  716. pDnsRec->pAdvQContext = pDnsRetryRec->pAdvQContext;
  717. pDnsRec->pRcptIdxList = pDnsRetryRec->pRcptIdxList;
  718. pDnsRec->dwNumRcpts = pDnsRetryRec->dwNumRcpts;
  719. }
  720. if (fSSLSubjectDisabled) {
  721. pszSSLVerificationName = NULL;
  722. } else if (ERROR_SUCCESS == DnsValidateName (HostName, DnsNameDomain)) {
  723. DebugTrace ((LPARAM) this, "%s is a DNS name", HostName);
  724. pszSSLVerificationName = (LPSTR) HostName;
  725. } else {
  726. DebugTrace ((LPARAM) this, "%s is not a DNS name", HostName);
  727. pszSSLVerificationName = pszTmpHostName;
  728. }
  729. //
  730. // if HostName is not a DNS name --- its a special name (like a
  731. // GUID for an Exchange connector, and we need to use pszTmpHostName,
  732. // the name returned by DNS sink as the SSL verification Name.
  733. //
  734. // if HostName is a DNS name --- the the DNS sink resolved it to an
  735. // IP and pszTmpHostName was obtained by DNS indirection (MX record
  736. // or CNAME record). This is insecure and cannot be used for SSL subject
  737. // verification, so we use HostName instead.
  738. //
  739. fRet = BeginInitializeAsyncConnect(
  740. pDnsRec,
  741. pISMTPConnection,
  742. DomainOptions,
  743. pDNS_RESOLVER_RECORD,
  744. pszSSLVerificationName);
  745. pDNS_RESOLVER_RECORD = NULL; // Passed on delete responsibillity to BeginInitializeAsyncConnect
  746. if(!fRet)
  747. {
  748. ErrorTrace((LPARAM) this, "Failed BeginInitializeAsyncConnect.");
  749. DeleteDnsRec(pDnsRec);
  750. }
  751. }
  752. CoTaskMemFree( pszRealHostName );
  753. pszRealHostName = NULL;
  754. goto Exit;
  755. }
  756. //
  757. // else go thru DNS or gethostbyname to get the address
  758. //
  759. DebugTrace((LPARAM) this, "Querying DNS");
  760. }
  761. }
  762. else
  763. {
  764. _ASSERT( HostName );
  765. }
  766. if(fUseDns)
  767. {
  768. DebugTrace((LPARAM) this, "ConnectToResolverHost querying DNS to resolve host: %s", pszRealHostName);
  769. if (fSSLSubjectDisabled) {
  770. pszSSLVerificationName = NULL;
  771. } else {
  772. pszSSLVerificationName = (LPSTR) pszRealHostName;
  773. }
  774. //
  775. // Using DNS to resolve destination host.
  776. //
  777. fRet = BeginInitializeAsyncDnsQuery( pszRealHostName,
  778. MyFQDNName,
  779. pISMTPConnection,
  780. dwDnsFlags,
  781. DomainOptions,
  782. fUseSmartHostAfterFail,
  783. pDNS_RESOLVER_RECORD,
  784. pszSSLVerificationName,
  785. pDnsRetryRec );
  786. pDNS_RESOLVER_RECORD = NULL; // Passed on delete responsibility to BeginInitializeAsyncDnsQuery
  787. DebugTrace((LPARAM) this, "BeginInitializeAsyncDnsQuery returned %s", fRet ? "TRUE" : "FALSE");
  788. }
  789. else
  790. {
  791. if(!fIsLiteral)
  792. pDnsRec = GetDnsRecordsFromHostFile(HostName);
  793. else
  794. pDnsRec = GetDnsRecordsFromLiteral(HostName);
  795. if(pDnsRec)
  796. {
  797. DebugTrace((LPARAM) this, "ConnectToResolverHost resolved %s locally", pszRealHostName);
  798. //
  799. // The target host (before DNS) to which this server is connecting to... used by SSL
  800. // If this is a literal, pass in NULL, as there is no hostname
  801. //
  802. pszSSLVerificationName = NULL;
  803. if (fSSLSubjectDisabled) {
  804. pszSSLVerificationName = NULL;
  805. } else if (fIsLiteral) {
  806. pszSSLVerificationName = NULL;
  807. } else {
  808. pszSSLVerificationName = (LPSTR) HostName;
  809. }
  810. if(pDnsRetryRec)
  811. {
  812. pDnsRec->pMailMsgObj = pDnsRetryRec->pMailMsgObj;
  813. pDnsRec->pAdvQContext = pDnsRetryRec->pAdvQContext;
  814. pDnsRec->pRcptIdxList = pDnsRetryRec->pRcptIdxList;
  815. pDnsRec->dwNumRcpts = pDnsRetryRec->dwNumRcpts;
  816. }
  817. fRet = BeginInitializeAsyncConnect(
  818. pDnsRec,
  819. pISMTPConnection,
  820. DomainOptions,
  821. pDNS_RESOLVER_RECORD,
  822. pszSSLVerificationName);
  823. pDNS_RESOLVER_RECORD = NULL; // Passed on delete responsibility to BeginInitializeAsyncConnect
  824. if(!fRet)
  825. {
  826. ErrorTrace((LPARAM) this, "Failed BeginInitializeAsyncConnect");
  827. DeleteDnsRec(pDnsRec);
  828. }
  829. }
  830. }
  831. if( fFreeHostName )
  832. {
  833. CoTaskMemFree( pszRealHostName );
  834. }
  835. Exit:
  836. if(pDNS_RESOLVER_RECORD) // Non NULL => delete responsibility is still this function's
  837. delete pDNS_RESOLVER_RECORD;
  838. if(fRet && pDnsRetryRec && pDnsRetryRec->pMailMsgObj)
  839. {
  840. //
  841. // Defensive: make sure caller doesn't mistakenly ack a failover message
  842. // if this function has succeeded
  843. //
  844. pDnsRetryRec->pMailMsgObj = NULL;
  845. pDnsRetryRec->pAdvQContext = NULL;
  846. pDnsRetryRec->pRcptIdxList = NULL;
  847. pDnsRetryRec->dwNumRcpts = 0;
  848. }
  849. TraceFunctLeaveEx((LPARAM)this);
  850. return fRet;
  851. }
  852. ///////////////////////////////////////////////////////////////////////////
  853. /*++
  854. Name :
  855. StartAsyncConnect
  856. Description:
  857. This function either does a straight gethostbyname(),
  858. or performs an MX record lookup to get the hostname/ip
  859. address to connect to.
  860. Arguments:
  861. char * SmartHost - This is either an IP address,
  862. or a hostname
  863. Returns:
  864. TRUE if an Async connection was started
  865. FALSE otherwise
  866. --*/
  867. BOOL REMOTE_QUEUE::StartAsyncConnect(
  868. const char *HostName,
  869. ISMTPConnection *pISMTPConnection,
  870. DWORD DomainOptions,
  871. BOOL fUseSmartHostAfterFail )
  872. {
  873. DWORD dwVirtualServerId = 0;
  874. HRESULT hr = S_OK;
  875. char MyFQDNName[MAX_PATH + 1];
  876. BOOL fRet;
  877. IDnsResolverRecord *pIDnsResolverRecord = NULL;
  878. IDnsStatus *pIDnsStatus = NULL;
  879. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD = NULL;
  880. DNS_SERVER_INFO *pDnsServerInfo = NULL;
  881. CTcpRegIpList *pTcpRegIpList = NULL;
  882. TraceFunctEnterEx((LPARAM)this, "StartAysncConnect (const char * HostName)");
  883. CrashOnInvalidSMTPConn(pISMTPConnection);
  884. DebugTrace((LPARAM) this,"Finding MX records for %s with options %x", HostName, DomainOptions);
  885. GetParentInst()->LockGenCrit();
  886. lstrcpyn(MyFQDNName,GetParentInst()->GetFQDomainName(),MAX_PATH);
  887. dwVirtualServerId = GetParentInst()->QueryInstanceId();
  888. GetParentInst()->UnLockGenCrit();
  889. //
  890. // The DNS resolver event is fired every time we connect to a domain.
  891. // It enables a sink to return information that modifies how we resolve
  892. // or connect to the target host "HostName".
  893. //
  894. // Two pieces of information may be returned by the DNS sink:
  895. //
  896. // (1) pDnsServerInfo - Designed for use on a multihomed server which
  897. // acts as a bridge relaying mail between different networks. The
  898. // DNS sink determines if HostName is on a different network than
  899. // the "default" network for SMTP. Target hosts in the default
  900. // network are resolved using the default DNS servers on this box.
  901. // The DNS sink may return an alternate set of DNS servers for hosts
  902. // on a different network in pDnsServerInfo.
  903. //
  904. // (2) pIDnsResolverRecord - This is a list of <hostname, ipaddress>
  905. // pairs. The DNS sink may return this to indicate that mail
  906. // directed to "HostName" should instead be delivered to the
  907. // hosts in pIDnsResolverRecord. If multiple records are returned
  908. // in pIDnsResolverRecord we will use them for failover. The
  909. // "ipaddress" portion may be missing for a given record in
  910. // pIDnsResolverRecord, in which case, the "hostname" must be
  911. // resolved by SMTP using the appropriate DNS servers.
  912. //
  913. GetParentInst()->TriggerDnsResolverEvent( (LPSTR)HostName,
  914. MyFQDNName,
  915. dwVirtualServerId,
  916. &pDnsServerInfo,
  917. &pIDnsResolverRecord );
  918. if(pDnsServerInfo)
  919. {
  920. //
  921. // Since we use our connection history with a DNS server for the
  922. // purposes of our DNS server state tracking logic (CTcpRegIpList)
  923. // we must remember the serverlist returned from pDnsServerInfo
  924. // and tabulate the successes/failures for them. This means that
  925. // the list is kept stored in SMTP_SERVER_INSTANCE and we constantly
  926. // update the connection history as we try resolves against the
  927. // servers.
  928. //
  929. // Since a DNS sink may return different lists of DNS servers for
  930. // different networks, we need to differentiate between when the
  931. // serverlist is being returned for a different network and the
  932. // serverlist for a certain network has itself changed. For this
  933. // reason, pDnsServerInfo also includes a GUID that uniquely
  934. // identifies the network associated with the serverlist.
  935. //
  936. pTcpRegIpList = GetParentInst()->UpdateDnsServerInfo(pDnsServerInfo);
  937. if(!pTcpRegIpList)
  938. {
  939. ErrorTrace((LPARAM)this, "Unable to update server info");
  940. fRet = FALSE;
  941. goto Exit;
  942. }
  943. pDNS_RESOLVER_RECORD = new DNS_RESOLVER_RECORD;
  944. if(!pDNS_RESOLVER_RECORD)
  945. {
  946. ErrorTrace((LPARAM)this, "Out of memory creating DNS_RESOLVER_RECORD");
  947. goto Exit;
  948. }
  949. pDNS_RESOLVER_RECORD->SetDnsList(pTcpRegIpList);
  950. }
  951. if(pIDnsResolverRecord)
  952. {
  953. //
  954. // IDnsStatus is an optional interface exposed by the DNS resolver record
  955. // object that contains additional information if something failed in the
  956. // DNS resolver. If resolution fails, we use it to check if the failure is
  957. // authoritative, and therefore we should NDR the messages.
  958. //
  959. hr = pIDnsResolverRecord->QueryInterface(IID_IDnsStatus, (PVOID *) &pIDnsStatus);
  960. if(SUCCEEDED(hr))
  961. {
  962. DWORD dwAck = 0;
  963. DWORD dwDiagnostic = 0;
  964. hr = pIDnsStatus->GetDnsStatus();
  965. if(HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR) == hr)
  966. {
  967. dwDiagnostic = AQUEUE_E_AUTHORITATIVE_HOST_NOT_FOUND;
  968. dwAck = CONNECTION_STATUS_FAILED_NDR_UNDELIVERED;
  969. }
  970. else if(FAILED(hr))
  971. {
  972. dwDiagnostic = AQUEUE_E_HOST_NOT_FOUND;
  973. dwAck = CONNECTION_STATUS_FAILED;
  974. }
  975. if(FAILED(hr))
  976. {
  977. pISMTPConnection->SetDiagnosticInfo(dwDiagnostic, NULL, NULL);
  978. pISMTPConnection->AckConnection(dwAck);
  979. pISMTPConnection->Release();
  980. fRet = TRUE;
  981. pIDnsStatus->Release();
  982. pIDnsResolverRecord->Release();
  983. goto Exit;
  984. }
  985. pIDnsStatus->Release();
  986. }
  987. DebugTrace((LPARAM) this, "DNS resolver sink returned pIDnsResolverRecord");
  988. if(!pDNS_RESOLVER_RECORD)
  989. pDNS_RESOLVER_RECORD = new DNS_RESOLVER_RECORD;
  990. if(!pDNS_RESOLVER_RECORD )
  991. {
  992. ErrorTrace((LPARAM) this, "Cannot allocate pDNS_RESOLVER_RECORD. Out of memory.");
  993. pIDnsResolverRecord->Release();
  994. fRet = FALSE;
  995. goto Exit;
  996. }
  997. pDNS_RESOLVER_RECORD->SetDnsResolverRecord(pIDnsResolverRecord);
  998. }
  999. fRet = ConnectToResolverHost( HostName,
  1000. MyFQDNName,
  1001. pISMTPConnection,
  1002. DomainOptions,
  1003. fUseSmartHostAfterFail,
  1004. pDNS_RESOLVER_RECORD,
  1005. NULL );
  1006. pDNS_RESOLVER_RECORD = NULL;
  1007. Exit:
  1008. if(pDNS_RESOLVER_RECORD)
  1009. delete pDNS_RESOLVER_RECORD;
  1010. if(pDnsServerInfo)
  1011. CoTaskMemFree(pDnsServerInfo);
  1012. TraceFunctLeaveEx((LPARAM)this);
  1013. return fRet;
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Description:
  1017. // Wrapper for DnsQueryAsync (exists only to BUMP REMOTE_QUEUE counters).
  1018. // Arguments:
  1019. // These are simply passed in to DnsQueryAsync... see documentation of
  1020. // DnsQueryAsync for details.
  1021. // Returns:
  1022. // TRUE if query was successfully started.
  1023. // FALSE on errors.
  1024. // History:
  1025. // GPulla modified.
  1026. //-----------------------------------------------------------------------------
  1027. BOOL REMOTE_QUEUE::BeginInitializeAsyncDnsQuery( LPSTR pszHostName,
  1028. LPSTR pszFQDN,
  1029. ISMTPConnection *pISMTPConnection,
  1030. DWORD dwDnsFlags,
  1031. DWORD DomainOptions,
  1032. BOOL fUseSmartHostAfterFail,
  1033. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD,
  1034. const char * pszSSLVerificationName,
  1035. PSMTPDNS_RECS pDnsRetryRec )
  1036. {
  1037. BOOL fRet = FALSE;
  1038. DWORD fUdp = TRUE;
  1039. RETRYPARAMS retryParams;
  1040. RETRYPARAMS *pRetryParams = NULL;
  1041. TraceFunctEnterEx((LPARAM) this, "REMOTE_QUEUE::BeginInitializeAsyncDnsQuery");
  1042. if(dwDnsFlags & DNS_FLAGS_TCP_ONLY)
  1043. fUdp = FALSE;
  1044. if(pDnsRetryRec) {
  1045. retryParams.m_pIMsg = (IMailMsgProperties *) pDnsRetryRec->pMailMsgObj;
  1046. retryParams.m_pAdvQContext = pDnsRetryRec->pAdvQContext;
  1047. retryParams.m_pRcptIdxList = (PDWORD) pDnsRetryRec->pRcptIdxList;
  1048. retryParams.m_dwNumRcpts = pDnsRetryRec->dwNumRcpts;
  1049. pRetryParams = &retryParams;
  1050. }
  1051. fRet = DnsQueryAsync(
  1052. GetParentInst(),
  1053. pszHostName,
  1054. pszFQDN,
  1055. pISMTPConnection,
  1056. dwDnsFlags,
  1057. DomainOptions,
  1058. fUseSmartHostAfterFail,
  1059. pDNS_RESOLVER_RECORD,
  1060. pszSSLVerificationName,
  1061. pRetryParams,
  1062. fUdp);
  1063. if(fRet)
  1064. BUMP_COUNTER(GetParentInst(), NumDnsQueries);
  1065. TraceFunctLeaveEx((LPARAM) this);
  1066. return fRet;
  1067. }
  1068. //-----------------------------------------------------------------------------
  1069. // Description:
  1070. // Kicks off an async query to DNS to resolve pszHostName (ie get the MX
  1071. // records for it). When the query is complete, and the MX records have
  1072. // been retrieved, the completion thread will try to connect to the MX
  1073. // hosts by posting a callback to QueueCallBackFunction().
  1074. // Arguments:
  1075. // [IN] pServiceInstance - PTR to ISMTPServerInstance for this queue
  1076. //
  1077. // [IN] pszHostName - Host we're trying to lookup (this is copied over)
  1078. //
  1079. // [IN] pszFQDN - My FQDN (this is copied over)
  1080. //
  1081. // [IN] pISMTPConnection - Connection to ACK, get messages from. After
  1082. // this is passed in, this funtion will handle acking and releasing it
  1083. // if TRUE is returned. If FALSE is returned, the pISMTPConnection
  1084. // is not touched and the caller must ACK and release it.
  1085. //
  1086. // [IN] dwDnsFlags - DNS configuration flags.
  1087. //
  1088. // [IN] DomainOptions - Use SSL, Verify SSL cert, etc. Various outbound options.
  1089. //
  1090. // [IN] fUseSmartHostAfterFail - DUH
  1091. //
  1092. // [IN] DNS_RESOLVER_RECORD * - Set of possible next hop destinations
  1093. // returned by the DNS sink (may be NULL). If we fail DNS resolution
  1094. // for pszHostName (which is the first next hop in pDNS_RESOLVER_RECORD),
  1095. // the others are tried in turn. After this is passed in... this function
  1096. // handles deleting it (irrespective of whether TRUE or FALSE is returned).
  1097. //
  1098. // [IN] pszSSLVerificationName - Target host which we are trying to
  1099. // resolve/connect. Could be NULL if there isn't a target host (such as
  1100. // in the case of a literal IP address) (this is copied over).
  1101. //
  1102. // [IN] pRetryParams - On a failover path (i.e. if a temporary error occurred
  1103. // while delivering to an SMTP target, we will attempt delivery to an
  1104. // alternate host (like an alternate MX host). The failed message
  1105. // parameters are encapsulated in this.
  1106. //
  1107. // [IN] fUdp - Issue query over UDP or TCP?
  1108. //-----------------------------------------------------------------------------
  1109. BOOL DnsQueryAsync(
  1110. SMTP_SERVER_INSTANCE *pServiceInstance,
  1111. LPSTR pszHostName,
  1112. LPSTR pszFQDN,
  1113. ISMTPConnection *pISMTPConnection,
  1114. DWORD dwDnsFlags,
  1115. DWORD DomainOptions,
  1116. BOOL fUseSmartHostAfterFail,
  1117. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD,
  1118. const char * pszSSLVerificationName,
  1119. RETRYPARAMS *pRetryParams,
  1120. BOOL fUdp)
  1121. {
  1122. PSMTPDNS_RECS pDnsRec = NULL;
  1123. DWORD dwStatus = ERROR_SUCCESS;
  1124. BOOL fRet = FALSE;
  1125. CTcpRegIpList *pDnsList = &g_TcpRegIpList;
  1126. BOOL fGlobalDnsList = TRUE;
  1127. TraceFunctEnterEx((LPARAM)NULL, "REMOTE_QUEUE::BeginInitializeAsyncDnsQuery");
  1128. CAsyncSmtpDns *pAsyncDns = new CAsyncSmtpDns(pServiceInstance, pISMTPConnection,
  1129. pRetryParams, pszFQDN);
  1130. if(!pAsyncDns)
  1131. {
  1132. DebugTrace((LPARAM) NULL, "Unable to allocate CAsyncSmtpDns object. Out of Memory");
  1133. goto Exit;
  1134. }
  1135. //
  1136. // From now on pAsyncDns will handle the deletion of pDNS_RESOLVER_RECORD and
  1137. // the pISmtpConnection ack... initiation of the async DNS query has succeeded
  1138. // as far as the caller is concerned.
  1139. //
  1140. fRet = TRUE;
  1141. pAsyncDns->SetDnsResolverRecord(pDNS_RESOLVER_RECORD);
  1142. //
  1143. // If the DNS sink specified an alternate set of servers to be used, set them
  1144. // on the async DNS object.
  1145. //
  1146. if(pDNS_RESOLVER_RECORD && pDNS_RESOLVER_RECORD->GetDnsList())
  1147. {
  1148. DebugTrace((LPARAM)NULL, "Using sink returned DNS list for %s", pszHostName);
  1149. pDnsList = pDNS_RESOLVER_RECORD->GetDnsList();
  1150. fGlobalDnsList = FALSE;
  1151. }
  1152. pDNS_RESOLVER_RECORD = NULL; // Passed on delete responsibility to pAsyncDns object
  1153. pISMTPConnection = NULL; // Passed on delete responsibility to pAsyncDns object
  1154. pAsyncDns->SetDomainOptions(DomainOptions);
  1155. pAsyncDns->SetSmartHostOption(fUseSmartHostAfterFail);
  1156. if (!pAsyncDns->Init((LPSTR) pszSSLVerificationName))
  1157. {
  1158. delete pAsyncDns;
  1159. goto Exit;
  1160. }
  1161. DebugTrace((LPARAM) NULL, "Issuing DNS query for pAsyncDns = 0x%08x", pAsyncDns);
  1162. dwStatus = pAsyncDns->Dns_QueryLib(
  1163. pszHostName,
  1164. DNS_TYPE_MX,
  1165. dwDnsFlags,
  1166. fUdp,
  1167. pDnsList,
  1168. fGlobalDnsList);
  1169. if(dwStatus != ERROR_SUCCESS)
  1170. {
  1171. ErrorTrace((LPARAM)NULL, "Failed to issue DNS query for pAsyncDns = 0x%08x", pAsyncDns);
  1172. delete pAsyncDns;
  1173. }
  1174. else
  1175. {
  1176. DebugTrace((LPARAM) NULL, "DNS query outstanding on object pAsyncDns = 0x%08x", pAsyncDns);
  1177. }
  1178. Exit:
  1179. if(pDNS_RESOLVER_RECORD) // Non NULL => delete responsibility is still this function's
  1180. delete pDNS_RESOLVER_RECORD;
  1181. TraceFunctLeaveEx((LPARAM)NULL);
  1182. return( fRet );
  1183. }
  1184. //-----------------------------------------------------------------------------
  1185. // Description:
  1186. // This function kicks off a connection to the first of the MX hosts
  1187. // (from pDnsRec). It calls InitializeAsyncConnect() which will call
  1188. // back to QueueCallbackFunction() immediately.
  1189. // Arguments:
  1190. // Returns:
  1191. // History:
  1192. //-----------------------------------------------------------------------------
  1193. BOOL REMOTE_QUEUE::BeginInitializeAsyncConnect( PSMTPDNS_RECS pDnsRec,
  1194. ISMTPConnection *pISMTPConnection,
  1195. DWORD DomainOptions,
  1196. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD,
  1197. const char *pszSSLVerificationName )
  1198. {
  1199. MXPARAMS Params;
  1200. BOOL fRet = FALSE;
  1201. CHAR szPostDnsSmartHost[IP_ADDRESS_STRING_LENGTH + 3];
  1202. TraceFunctEnterEx((LPARAM)this, "REMOTE_QUEUE::BeginInitializeAsyncConnect");
  1203. //
  1204. // The post-DNS smart host is useful for testing DNS. It allows us
  1205. // to exercise the DNS resolution codepath and yet send to a smarthost.
  1206. // If a smarthost is specified, we will allocate a new TempList struct
  1207. // and fill it in with the IP address.
  1208. //
  1209. if(GetParentInst()->GetPostDnsSmartHost(
  1210. szPostDnsSmartHost, sizeof(szPostDnsSmartHost)))
  1211. {
  1212. DeleteDnsRec(pDnsRec);
  1213. // Note: Literal IP must be enclosed in brackets: []
  1214. pDnsRec = GetDnsRecordsFromLiteral(szPostDnsSmartHost);
  1215. if(!pDnsRec)
  1216. {
  1217. ErrorTrace((LPARAM) this, "Can't convert %s to IP", szPostDnsSmartHost);
  1218. TraceFunctLeaveEx((LPARAM) this);
  1219. return FALSE;
  1220. }
  1221. }
  1222. Params.HostName = pDnsRec->DnsArray[0]->DnsName;
  1223. Params.PortNum = GetParentInst()->GetRemoteSmtpPort();
  1224. Params.TimeOut = INFINITE;
  1225. Params.CallBack = QueueCallBackFunction;
  1226. Params.pISMTPConnection = pISMTPConnection;
  1227. Params.pInstance = GetParentInst();
  1228. Params.pDnsRec = pDnsRec;
  1229. Params.pDNS_RESOLVER_RECORD = pDNS_RESOLVER_RECORD;
  1230. CrashOnInvalidSMTPConn(pISMTPConnection);
  1231. CAsyncMx* pAsyncIo = new CAsyncMx (&Params);
  1232. if(pAsyncIo)
  1233. {
  1234. pDNS_RESOLVER_RECORD = NULL; // Passed on delete responsibility to pAsyncIo
  1235. pAsyncIo->SetDomainOptions(DomainOptions);
  1236. if(!pAsyncIo->Init((LPSTR) pszSSLVerificationName))
  1237. {
  1238. delete pAsyncIo;
  1239. }
  1240. else if(!pAsyncIo->InitializeAsyncConnect())
  1241. {
  1242. delete pAsyncIo;
  1243. fRet = FALSE;
  1244. }
  1245. else
  1246. {
  1247. fRet = TRUE;
  1248. }
  1249. }
  1250. else
  1251. {
  1252. fRet = FALSE;
  1253. }
  1254. if(pDNS_RESOLVER_RECORD) // Non NULL => delete responsibility is still this function's
  1255. delete pDNS_RESOLVER_RECORD;
  1256. TraceFunctLeaveEx((LPARAM)this);
  1257. return fRet;
  1258. }
  1259. ///////////////////////////////////////////////////////////////////////////
  1260. BOOL REMOTE_QUEUE::ReStartAsyncConnections(
  1261. SMTPDNS_RECS * pDnsRec,
  1262. ISMTPConnection * pISMTPConnection,
  1263. DWORD DomainOptions,
  1264. LPSTR pszSSLVerificationName,
  1265. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD )
  1266. {
  1267. CAsyncMx * pAsyncIo = NULL;
  1268. MXPARAMS Params;
  1269. BOOL fRet = FALSE;
  1270. char szMyFQDNName[MAX_PATH + 1];
  1271. TraceFunctEnterEx((LPARAM)this, "ReStartAsyncConnections");
  1272. //
  1273. // If we're out of MX records to connect to, check to see if there are
  1274. // alternate hosts (returned by the DNS sink in pDNS_RESOLVER_RECORD)
  1275. // Kick off a DNS resolve/connect to the next alternate host.
  1276. //
  1277. if(pDnsRec->StartRecord >= pDnsRec->NumRecords)
  1278. {
  1279. _ASSERT(pDNS_RESOLVER_RECORD && pDNS_RESOLVER_RECORD->GetDnsResolverRecord()
  1280. && "Need more MX info or alternate hosts from DNS sink");
  1281. GetParentInst()->LockGenCrit();
  1282. lstrcpyn(szMyFQDNName,GetParentInst()->GetFQDomainName(),MAX_PATH);
  1283. GetParentInst()->UnLockGenCrit();
  1284. fRet = ConnectToResolverHost(
  1285. pszSSLVerificationName,
  1286. szMyFQDNName,
  1287. pISMTPConnection,
  1288. DomainOptions,
  1289. GetParentInst()->UseSmartHostAfterFail(),
  1290. pDNS_RESOLVER_RECORD,
  1291. pDnsRec);
  1292. if(!fRet)
  1293. ErrorTrace((LPARAM) this, "Failed connect to DNS sink returned host");
  1294. pDNS_RESOLVER_RECORD = NULL;
  1295. if(pDnsRec)
  1296. DeleteDnsRec(pDnsRec);
  1297. goto Exit;
  1298. }
  1299. //
  1300. // Connect to the next MX record in pDnsRec
  1301. //
  1302. Params.HostName = pDnsRec->DnsArray[pDnsRec->StartRecord]->DnsName;
  1303. Params.PortNum = GetParentInst()->GetRemoteSmtpPort();
  1304. Params.TimeOut = INFINITE;
  1305. Params.CallBack = QueueCallBackFunction;
  1306. Params.pISMTPConnection = pISMTPConnection;
  1307. Params.pInstance = GetParentInst();
  1308. Params.pDnsRec = pDnsRec;
  1309. Params.pDNS_RESOLVER_RECORD = pDNS_RESOLVER_RECORD;
  1310. pAsyncIo = new CAsyncMx (&Params);
  1311. if(pAsyncIo)
  1312. {
  1313. pDNS_RESOLVER_RECORD = NULL; // Passed on delete responsibility to pAsyncIo
  1314. if (!pAsyncIo->Init(pszSSLVerificationName))
  1315. {
  1316. ErrorTrace ((LPARAM) this, "pAsyncIo->Init() failed");
  1317. delete pAsyncIo;
  1318. goto Exit;
  1319. }
  1320. pAsyncIo->SetDomainOptions(DomainOptions);
  1321. if(!pAsyncIo->InitializeAsyncConnect())
  1322. {
  1323. ErrorTrace((LPARAM) this,"pAsyncIo->InitializeAsyncConnect()for %s failed", Params.HostName);
  1324. delete pAsyncIo;
  1325. }
  1326. else
  1327. {
  1328. fRet = TRUE;
  1329. }
  1330. }
  1331. Exit:
  1332. if(pDNS_RESOLVER_RECORD) // Non NULL => delete responsibility is still this function's
  1333. delete pDNS_RESOLVER_RECORD;
  1334. TraceFunctLeaveEx((LPARAM)this);
  1335. return fRet;
  1336. }
  1337. ///////////////////////////////////////////////////////////////////////////
  1338. BOOL REMOTE_QUEUE::MakeATQConnection(
  1339. SMTPDNS_RECS* pDnsRec,
  1340. SOCKET Socket,
  1341. DWORD IpAddress,
  1342. ISMTPConnection* pISMTPConnection,
  1343. DWORD Options,
  1344. LPSTR pszSSLVerificationName,
  1345. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD)
  1346. {
  1347. sockaddr_in AddrRemote;
  1348. SMTP_CONNOUT * SmtpConn = NULL;
  1349. DWORD Error = 0;
  1350. TraceFunctEnterEx((LPARAM) this, "REMOTE_QUEUE::MakeATQConnection");
  1351. _ASSERT (Socket != INVALID_SOCKET);
  1352. _ASSERT (GetParentInst() != NULL);
  1353. //_ASSERT (IpAddress != 0);
  1354. if(IpAddress == 0)
  1355. {
  1356. if(Socket != INVALID_SOCKET)
  1357. closesocket(Socket);
  1358. TraceFunctLeaveEx((LPARAM) this);
  1359. return FALSE;
  1360. }
  1361. //set the remote IP address we connected to
  1362. AddrRemote.sin_addr.s_addr = IpAddress;
  1363. //create an outbound connection
  1364. SmtpConn = SMTP_CONNOUT::CreateSmtpConnection(
  1365. GetParentInst(),
  1366. Socket,
  1367. (SOCKADDR_IN *)&AddrRemote,
  1368. (SOCKADDR_IN *)&AddrRemote,
  1369. NULL,
  1370. NULL,
  1371. 0,
  1372. Options,
  1373. pszSSLVerificationName,
  1374. pDNS_RESOLVER_RECORD);
  1375. if(SmtpConn == NULL)
  1376. {
  1377. Error = GetLastError();
  1378. pISMTPConnection->SetDiagnosticInfo(HRESULT_FROM_WIN32(Error), NULL, NULL);
  1379. closesocket(Socket);
  1380. DeleteDnsRec(pDnsRec);
  1381. delete pDNS_RESOLVER_RECORD;
  1382. FatalTrace((LPARAM) this, "SMTP_CONNOUT::CreateSmtpConnection failed, error =%i", Error);
  1383. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1384. TraceFunctLeaveEx((LPARAM) this);
  1385. return FALSE;
  1386. }
  1387. SmtpConn->SetDnsRec(pDnsRec);
  1388. DebugTrace((LPARAM) this, "alloced SMTPOUT pointer %X", SmtpConn);
  1389. //add this connection object to the ATQ infrastructure
  1390. if(!SmtpConn->AddToAtqHandles((HANDLE)Socket, NULL, GetParentInst()->GetRemoteTimeOut(), InternetCompletion))
  1391. {
  1392. Error = GetLastError();
  1393. pISMTPConnection->SetDiagnosticInfo(HRESULT_FROM_WIN32(Error), NULL, NULL);
  1394. closesocket(Socket);
  1395. FatalTrace((LPARAM) this, "SmtpConn->AddToAtqHandles failed, error =%d", Error);
  1396. SmtpConn->SetConnectionStatus(CONNECTION_STATUS_FAILED);
  1397. delete SmtpConn;
  1398. SmtpConn = NULL;
  1399. SetLastError(Error);
  1400. TraceFunctLeaveEx((LPARAM) this);
  1401. return FALSE;
  1402. }
  1403. //insert the outbound connection object into
  1404. //our list of outbound conection objects
  1405. if(!GetParentInst()->InsertNewOutboundConnection(SmtpConn))
  1406. {
  1407. Error = GetLastError();
  1408. pISMTPConnection->SetDiagnosticInfo(HRESULT_FROM_WIN32(Error), NULL, NULL);
  1409. FatalTrace((LPARAM) this, "GetParentInst()->InsertNewOutboundConnection failed, error =%d", Error);
  1410. SmtpConn->DisconnectClient();
  1411. SmtpConn->SetConnectionStatus(CONNECTION_STATUS_FAILED);
  1412. delete SmtpConn;
  1413. SmtpConn = NULL;
  1414. SetLastError(Error);
  1415. TraceFunctLeaveEx((LPARAM) this);
  1416. return FALSE;
  1417. }
  1418. SmtpConn->SetCurrentObject(pISMTPConnection);
  1419. //start session will pend a read to pick
  1420. //up the servers signon banner
  1421. if(!SmtpConn->StartSession())
  1422. {
  1423. //get the error
  1424. Error = GetLastError();
  1425. //SmtpConn->SetCurrentObjectToNull();
  1426. FatalTrace((LPARAM) this, "SmtpConn->StartSession failed, error =%d", Error);
  1427. SmtpConn->DisconnectClient();
  1428. GetParentInst()->RemoveOutboundConnection(SmtpConn);
  1429. //An empty queue at this point is really not an error
  1430. if (ERROR_EMPTY == Error)
  1431. SmtpConn->SetConnectionStatus(CONNECTION_STATUS_OK);
  1432. else
  1433. SmtpConn->SetConnectionStatus(CONNECTION_STATUS_FAILED);
  1434. delete SmtpConn;
  1435. SmtpConn = NULL;
  1436. SetLastError (Error);
  1437. //TraceFunctLeaveEx((LPARAM) this);
  1438. //return FALSE;
  1439. }
  1440. TraceFunctLeaveEx((LPARAM) this);
  1441. return TRUE;
  1442. }
  1443. ///////////////////////////////////////////////////////////////////////////
  1444. #define PRIVATE_OPTIMAL_BUFFER_SIZE 64 * 1024
  1445. #define PRIVATE_LINE_BUFFER_SIZE 1024
  1446. ///////////////////////////////////////////////////////////////////////////
  1447. static BOOL CopyMessage(PFIO_CONTEXT hSrcFile, HANDLE hDstFile, HANDLE dwEventHandle)
  1448. {
  1449. CHAR acBuffer[PRIVATE_OPTIMAL_BUFFER_SIZE];
  1450. DWORD dwBytesRead;
  1451. DWORD dwBytesWritten;
  1452. DWORD dwTotalBytes = 0;
  1453. CHAR acCrLfDotCrLf[5] = { '\r', '\n', '.', '\r', '\n' };
  1454. CHAR acLastBytes[5] = { '\0', '\0', '\0', '\0', '\0' };
  1455. FH_OVERLAPPED Ov;
  1456. BOOL fResult = TRUE;
  1457. DWORD err = 0;
  1458. ZeroMemory (&Ov, sizeof(Ov));
  1459. Ov.hEvent = (HANDLE) ((ULONG_PTR) dwEventHandle | 1);
  1460. // Copies from the current file pointer to the end of hSrcFile
  1461. // and appends to the current file pointer of hDstFile.
  1462. _ASSERT(hSrcFile != NULL);
  1463. _ASSERT(hDstFile != INVALID_HANDLE_VALUE);
  1464. do
  1465. {
  1466. fResult = FIOReadFile(hSrcFile, acBuffer,
  1467. PRIVATE_OPTIMAL_BUFFER_SIZE,
  1468. &Ov);
  1469. // if this returned TRUE then we want to go down the path which calls
  1470. // GetOverlappedResult just so that we can get dwBytesRead.
  1471. if (fResult) err = ERROR_IO_PENDING;
  1472. else err = GetLastError();
  1473. if(err == ERROR_IO_PENDING)
  1474. {
  1475. if(GetOverlappedResult(dwEventHandle, (OVERLAPPED *) &Ov, &dwBytesRead, INFINITE))
  1476. {
  1477. Ov.Offset += dwBytesRead;
  1478. ResetEvent(dwEventHandle);
  1479. }
  1480. else
  1481. {
  1482. return FALSE;
  1483. }
  1484. } else {
  1485. //SmtpLogEventEx(SMTP_EVENT_CANNOT_WRITE_FILE, MailF
  1486. SetLastError (err); //preserve the last error
  1487. if(err == ERROR_HANDLE_EOF)
  1488. return TRUE;
  1489. else
  1490. return FALSE;
  1491. }
  1492. if (dwBytesRead)
  1493. {
  1494. if (!WriteFile(hDstFile, acBuffer,
  1495. dwBytesRead,
  1496. &dwBytesWritten,
  1497. NULL))
  1498. return(FALSE);
  1499. // See if read equals written
  1500. if (dwBytesRead != dwBytesWritten)
  1501. return(FALSE);
  1502. }
  1503. else
  1504. {
  1505. dwBytesWritten = 0;
  1506. }
  1507. if (dwBytesWritten)
  1508. {
  1509. dwTotalBytes += dwBytesWritten;
  1510. // Save the last two bytes ever written
  1511. if (dwBytesWritten > 4)
  1512. {
  1513. CopyMemory(acLastBytes, &acBuffer[dwBytesWritten-5], 5);
  1514. }
  1515. else
  1516. {
  1517. MoveMemory(acLastBytes, &acLastBytes[dwBytesWritten], 5-dwBytesWritten);
  1518. CopyMemory(&acLastBytes[5-dwBytesWritten], acBuffer, dwBytesWritten);
  1519. }
  1520. }
  1521. } while (dwBytesRead);
  1522. // Now, see if the file ends with a CRLF, if not, add it
  1523. if ((dwTotalBytes > 1) && memcmp(&acLastBytes[3], &acCrLfDotCrLf[3], 2))
  1524. {
  1525. // Add the trailing CRLF
  1526. if (!WriteFile(hDstFile, acCrLfDotCrLf,
  1527. 2,
  1528. &dwBytesWritten,
  1529. NULL))
  1530. {
  1531. return(FALSE);
  1532. }
  1533. if (dwBytesWritten != 2)
  1534. {
  1535. return(FALSE);
  1536. }
  1537. dwTotalBytes+=2;
  1538. }
  1539. //If file ends with CRLF.CRLF, remove the trailing CRLF.CRLF
  1540. //R.P - On 1/12/98 we decided to remove the CRLF.CRLF because
  1541. //of a bug/feature in IMAP. POP3 will add the CRLF.CRLF when
  1542. //retrieving the mail.
  1543. if ((dwTotalBytes > 4) && !memcmp(acLastBytes, acCrLfDotCrLf, 5))
  1544. {
  1545. // Remove the trailing CRLF.CRLF
  1546. if ((SetFilePointer(hDstFile, -5, NULL, FILE_CURRENT) == 0xffffffff) ||
  1547. !SetEndOfFile(hDstFile))
  1548. {
  1549. return(FALSE);
  1550. }
  1551. }
  1552. else
  1553. {
  1554. // Remove the trailing CRLF
  1555. if ((SetFilePointer(hDstFile, -2, NULL, FILE_CURRENT) == 0xffffffff) ||
  1556. !SetEndOfFile(hDstFile))
  1557. {
  1558. return(FALSE);
  1559. }
  1560. }
  1561. return(TRUE);
  1562. }
  1563. ///////////////////////////////////////////////////////////////////////////
  1564. BOOL CreateXHeaders(
  1565. IMailMsgProperties *pIMsg,
  1566. IMailMsgRecipients *pIMsgRecips ,
  1567. DWORD cRcpts,
  1568. DWORD *rgRcptIndex,
  1569. HANDLE hDrop)
  1570. {
  1571. TraceFunctEnter("CreateXHeaders");
  1572. #define X_SENDER_HEADER "x-sender: "
  1573. #define X_RECEIVER_HEADER "x-receiver: "
  1574. #define X_HEADER_EOLN "\r\n"
  1575. #define MAX_HEADER_SIZE (sizeof(X_RECEIVER_HEADER))
  1576. BOOL fRet = FALSE;
  1577. HRESULT hr;
  1578. DWORD i, cBytes;
  1579. BOOL fContinue = TRUE;
  1580. char szBuffer[
  1581. MAX_HEADER_SIZE +
  1582. MAX_INTERNET_NAME +
  1583. 1 + 2 + 1]; // Closing ">", CRLF, and NULL
  1584. strcpy( szBuffer, X_SENDER_HEADER );
  1585. hr = pIMsg->GetStringA(
  1586. IMMPID_MP_SENDER_ADDRESS_SMTP,
  1587. MAX_INTERNET_NAME,
  1588. &szBuffer[ sizeof(X_SENDER_HEADER) - 1] );
  1589. if(SUCCEEDED(hr))
  1590. {
  1591. strcat(szBuffer, X_HEADER_EOLN);
  1592. if (!WriteFile(hDrop, szBuffer, strlen(szBuffer), &cBytes, NULL) ) {
  1593. ErrorTrace(0, "Error %d writing x-sender line %s",
  1594. GetLastError(), szBuffer);
  1595. goto Cleanup;
  1596. } else {
  1597. _ASSERT( cBytes == strlen(szBuffer) );
  1598. }
  1599. } else {
  1600. DebugTrace(0, "Could not get Sender Address %x", hr);
  1601. SetLastError( ERROR_INVALID_DATA );
  1602. goto Cleanup;
  1603. }
  1604. strcpy( szBuffer, X_RECEIVER_HEADER );
  1605. for (i = 0; i < cRcpts && fContinue; i++)
  1606. {
  1607. DWORD dwRecipientFlags = 0;
  1608. hr = pIMsgRecips->GetDWORD(rgRcptIndex[i], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  1609. if( SUCCEEDED( hr ) )
  1610. {
  1611. if( RP_HANDLED != ( dwRecipientFlags & RP_HANDLED ) )
  1612. {
  1613. hr = pIMsgRecips->GetStringA(
  1614. rgRcptIndex[i],
  1615. IMMPID_RP_ADDRESS_SMTP,
  1616. MAX_INTERNET_NAME,
  1617. &szBuffer[ sizeof(X_RECEIVER_HEADER) - 1 ]);
  1618. if (SUCCEEDED(hr)) {
  1619. strcat(szBuffer, X_HEADER_EOLN);
  1620. if (!WriteFile(hDrop, szBuffer, strlen(szBuffer), &cBytes, NULL)) {
  1621. ErrorTrace(0, "Error %d writing recipient x-header %s",
  1622. GetLastError(), szBuffer);
  1623. fContinue = FALSE;
  1624. }
  1625. }
  1626. else
  1627. {
  1628. SetLastError( ERROR_INVALID_DATA );
  1629. fContinue = FALSE;
  1630. }
  1631. }
  1632. }
  1633. }
  1634. // If we got all recipients without error, we were successful
  1635. if (i == cRcpts)
  1636. fRet = TRUE;
  1637. Cleanup:
  1638. TraceFunctLeave();
  1639. return( fRet );
  1640. }
  1641. ///////////////////////////////////////////////////////////////////////////
  1642. HANDLE REMOTE_QUEUE::CreateDropFile(const char * DropDir, char * szDropFile)
  1643. {
  1644. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  1645. DWORD dwStrLen;
  1646. FILETIME ftTime;
  1647. DWORD Error = 0;
  1648. TraceFunctEnterEx((LPARAM)this, "REMOTE_QUEUE::CreateDropFile");
  1649. dwStrLen = lstrlen(DropDir);
  1650. lstrcpy(szDropFile, DropDir);
  1651. do
  1652. {
  1653. GetSystemTimeAsFileTime(&ftTime);
  1654. wsprintf(&szDropFile[dwStrLen],
  1655. "%08x%08x%08x%s",
  1656. ftTime.dwLowDateTime,
  1657. ftTime.dwHighDateTime,
  1658. InterlockedIncrement((PLONG)&g_dwFileCounter),
  1659. ".eml");
  1660. FileHandle = CreateFile(szDropFile, GENERIC_READ | GENERIC_WRITE,
  1661. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW,
  1662. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  1663. NULL);
  1664. if (FileHandle != INVALID_HANDLE_VALUE)
  1665. break;
  1666. if((Error = GetLastError()) != ERROR_FILE_EXISTS)
  1667. {
  1668. TraceFunctLeaveEx((LPARAM)this);
  1669. return(INVALID_HANDLE_VALUE);
  1670. }
  1671. } while( (FileHandle == INVALID_HANDLE_VALUE) && !GetParentInst()->IsShuttingDown());
  1672. return FileHandle;
  1673. }
  1674. ///////////////////////////////////////////////////////////////////////////
  1675. /*++
  1676. Name :
  1677. CopyMailToDropDir()
  1678. Description:
  1679. This function copies a spooled file to the drop directory
  1680. The drop file will be of the same name as the spooled file.
  1681. This funciton translates the sender and recipient informaiton
  1682. from the mail envelope into x-headers in the drop file.
  1683. Both the message file and the stream file are assumed to be
  1684. opened upstream.
  1685. Arguments:
  1686. PMAIL_ENTRY lpMailEntry - Queue entry of the spooled file
  1687. Returns:
  1688. TRUE if the message was written successfully to the drop dir.
  1689. FALSE in all other cases.
  1690. --*/
  1691. BOOL REMOTE_QUEUE::CopyMailToDropDir(ISMTPConnection *pISMTPConnection, const char * DropDirectory)
  1692. {
  1693. DWORD dwError = NO_ERROR;
  1694. DWORD dwBytesWritten = 0;
  1695. DWORD NumRcpts = 0;
  1696. HANDLE hDrop = INVALID_HANDLE_VALUE;
  1697. PFIO_CONTEXT hMail = NULL;
  1698. HRESULT hr = S_OK;
  1699. PVOID AdvContext = NULL;
  1700. DWORD *RcptIndexList = NULL;
  1701. IMailMsgProperties * pIMsg = NULL;
  1702. IMailMsgBind * pBindInterface = NULL;
  1703. BOOL fRet = FALSE;
  1704. MessageAck MsgAck;
  1705. HANDLE hFileReadEvent = NULL;
  1706. IMailMsgRecipients *pIMsgRecips = NULL;
  1707. char szDropFile[MAX_PATH +1];
  1708. TraceFunctEnterEx(NULL, "CopyMailToDropDir");
  1709. hFileReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1710. if(hFileReadEvent == INVALID_HANDLE_VALUE)
  1711. {
  1712. ErrorTrace((LPARAM) this, "CreateEvent() failed for FileWriteFileEvent");
  1713. goto ErrorExit;
  1714. }
  1715. while (!GetParentInst()->IsShuttingDown())
  1716. {
  1717. pBindInterface = NULL;
  1718. AdvContext = NULL;
  1719. RcptIndexList = NULL;
  1720. pIMsg = NULL;
  1721. fRet = FALSE;
  1722. hr = pISMTPConnection->GetNextMessage(&pIMsg, (DWORD **)&AdvContext, &NumRcpts, &RcptIndexList);
  1723. if(FAILED(hr))
  1724. {
  1725. fRet = TRUE;
  1726. break;
  1727. }
  1728. if( NumRcpts == 0 )
  1729. {
  1730. fRet = TRUE;
  1731. goto ErrorExit;
  1732. }
  1733. hr = pIMsg->QueryInterface( IID_IMailMsgRecipients, (PVOID *) &pIMsgRecips);
  1734. if( FAILED( hr ) )
  1735. {
  1736. goto ErrorExit;
  1737. }
  1738. if( CheckIfAllRcptsHandled( pIMsgRecips, RcptIndexList, NumRcpts ) )
  1739. {
  1740. fRet = TRUE;
  1741. goto ErrorExit;
  1742. }
  1743. hr = pIMsg->QueryInterface(IID_IMailMsgBind, (void **)&pBindInterface);
  1744. if(FAILED(hr))
  1745. {
  1746. goto ErrorExit;
  1747. }
  1748. hr = pBindInterface->GetBinding(&hMail, NULL);
  1749. if(FAILED(hr))
  1750. {
  1751. goto ErrorExit;
  1752. }
  1753. DebugTrace((LPARAM)NULL, "Dropping file to: %s", DropDirectory);
  1754. hDrop = CreateDropFile(DropDirectory, szDropFile);
  1755. if (hDrop == INVALID_HANDLE_VALUE)
  1756. {
  1757. dwError = GetLastError();
  1758. ErrorTrace(NULL, "Unable to create drop directory (%s) : %u",
  1759. DropDirectory,
  1760. dwError);
  1761. SetLastError(dwError);
  1762. goto ErrorExit;
  1763. }
  1764. // Output the x-headers
  1765. if (!CreateXHeaders(pIMsg, pIMsgRecips, NumRcpts, RcptIndexList, hDrop)) {
  1766. dwError = GetLastError();
  1767. ErrorTrace(NULL, "Error %d while creating x-headers", dwError);
  1768. goto ErrorExit;
  1769. }
  1770. ResetEvent(hFileReadEvent);
  1771. // Copy the mail file over
  1772. if (!CopyMessage(hMail, hDrop, hFileReadEvent))
  1773. {
  1774. dwError = GetLastError();
  1775. ErrorTrace(NULL, "Unable to copy mail file into drop directory : %u",
  1776. dwError);
  1777. goto ErrorExit;
  1778. }
  1779. if( FAILED( hr = SetAllRcptsHandled( pIMsgRecips, RcptIndexList, NumRcpts ) ) )
  1780. {
  1781. goto ErrorExit;
  1782. }
  1783. fRet = TRUE;
  1784. ErrorExit:
  1785. if( pIMsgRecips )
  1786. {
  1787. pIMsgRecips->Release();
  1788. pIMsgRecips = NULL;
  1789. }
  1790. if(pBindInterface)
  1791. {
  1792. pBindInterface->ReleaseContext();
  1793. pBindInterface->Release();
  1794. pBindInterface = NULL;
  1795. }
  1796. MsgAck.pIMailMsgProperties = pIMsg;
  1797. MsgAck.pvMsgContext = (DWORD *) AdvContext;
  1798. if(fRet)
  1799. {
  1800. MsgAck.dwMsgStatus = MESSAGE_STATUS_ALL_DELIVERED;
  1801. }
  1802. else
  1803. {
  1804. MsgAck.dwMsgStatus = MESSAGE_STATUS_RETRY_ALL;
  1805. }
  1806. MsgAck.dwStatusCode = 0;
  1807. pISMTPConnection->AckMessage(&MsgAck);
  1808. pIMsg->Release();
  1809. pIMsg = NULL;
  1810. if(hDrop != INVALID_HANDLE_VALUE)
  1811. {
  1812. _VERIFY(CloseHandle(hDrop));
  1813. hDrop = INVALID_HANDLE_VALUE;
  1814. }
  1815. if(fRet)
  1816. BUMP_COUNTER(GetParentInst(), DirectoryDrops);
  1817. }
  1818. if(fRet)
  1819. {
  1820. pISMTPConnection->AckConnection(CONNECTION_STATUS_OK);
  1821. }
  1822. else
  1823. {
  1824. DeleteFile(szDropFile);
  1825. SetLastError(dwError);
  1826. pISMTPConnection->AckConnection(CONNECTION_STATUS_FAILED);
  1827. }
  1828. if(hFileReadEvent != NULL)
  1829. {
  1830. CloseHandle(hFileReadEvent);
  1831. }
  1832. TraceFunctLeave();
  1833. return(fRet);
  1834. }
  1835. //////////////////////////////////////////////////////////////////////////////
  1836. BOOL REMOTE_QUEUE::CheckIfAllRcptsHandled(
  1837. IMailMsgRecipients *pIMsgRecips,
  1838. DWORD *RcptIndexList,
  1839. DWORD NumRcpts )
  1840. {
  1841. BOOL fRet = TRUE;
  1842. for( DWORD i = 0; i < NumRcpts; i++ )
  1843. {
  1844. if (RcptIndexList[i] != INVALID_RCPT_IDX_VALUE)
  1845. {
  1846. DWORD dwRecipientFlags = 0;
  1847. HRESULT hr = pIMsgRecips->GetDWORD(RcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  1848. if (FAILED(hr))
  1849. {
  1850. fRet = FALSE;
  1851. break;
  1852. }
  1853. if( RP_HANDLED != ( dwRecipientFlags & RP_HANDLED ) )
  1854. {
  1855. fRet = FALSE;
  1856. break;
  1857. }
  1858. }
  1859. }
  1860. return( fRet );
  1861. }
  1862. //////////////////////////////////////////////////////////////////////////////
  1863. HRESULT REMOTE_QUEUE::SetAllRcptsHandled(
  1864. IMailMsgRecipients *pIMsgRecips,
  1865. DWORD *RcptIndexList,
  1866. DWORD NumRcpts )
  1867. {
  1868. HRESULT hr = S_OK;
  1869. for( DWORD i = 0; i < NumRcpts; i++ )
  1870. {
  1871. if (RcptIndexList[i] != INVALID_RCPT_IDX_VALUE)
  1872. {
  1873. DWORD dwRecipientFlags = 0;
  1874. hr = pIMsgRecips->GetDWORD(RcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  1875. if (FAILED(hr))
  1876. {
  1877. break;
  1878. }
  1879. if( RP_HANDLED != ( dwRecipientFlags & RP_HANDLED ) )
  1880. {
  1881. dwRecipientFlags |= RP_DELIVERED;
  1882. hr = pIMsgRecips->PutDWORD(RcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,dwRecipientFlags);
  1883. if (FAILED(hr))
  1884. {
  1885. break;
  1886. }
  1887. }
  1888. }
  1889. }
  1890. return( hr );
  1891. }
  1892. /*++
  1893. ABSTRACT:
  1894. This function Creates a CDropDir object which is associated with a MailMsg object.
  1895. The job of the CDropDir object is to asynchronously write the Mail to the drop dir.
  1896. Called by
  1897. ProcessQueueEvents()
  1898. CDropDir::~CDropDir().
  1899. --*/
  1900. BOOL AsyncCopyMailToDropDir(
  1901. ISMTPConnection *pISMTPConnection,
  1902. const char * DropDirectory,
  1903. SMTP_SERVER_INSTANCE *pParentInst
  1904. )
  1905. {
  1906. PVOID AdvContext = NULL;
  1907. IMailMsgProperties *pIMsg = NULL;
  1908. DWORD NumRcpts = 0;
  1909. DWORD *RcptIndexList = NULL;
  1910. CDropDir *pDropDir = NULL;
  1911. BOOL fRet = TRUE;
  1912. HRESULT hr = S_OK;
  1913. TraceFunctEnterEx(NULL, "newCopyMailToDropDir");
  1914. if (!pParentInst->IsShuttingDown())
  1915. {
  1916. AdvContext = NULL;
  1917. fRet = FALSE;
  1918. hr = pISMTPConnection->GetNextMessage(&pIMsg, (DWORD **)&AdvContext, &NumRcpts, &RcptIndexList);
  1919. if(FAILED(hr))
  1920. {
  1921. fRet = TRUE;
  1922. goto Exit;
  1923. }
  1924. pDropDir = new CDropDir();
  1925. if( NULL == pDropDir )
  1926. {
  1927. fRet = FALSE;
  1928. goto Exit;
  1929. }
  1930. if( FAILED( hr = pDropDir->CopyMailToDropDir( pISMTPConnection,
  1931. DropDirectory,
  1932. pIMsg,
  1933. AdvContext,
  1934. NumRcpts,
  1935. RcptIndexList,
  1936. pParentInst) ) )
  1937. {
  1938. fRet = FALSE;
  1939. goto Exit;
  1940. }
  1941. SAFE_RELEASE(pDropDir);
  1942. SAFE_RELEASE(pIMsg );
  1943. fRet = TRUE;
  1944. }
  1945. Exit:
  1946. SAFE_RELEASE(pDropDir);
  1947. SAFE_RELEASE(pIMsg );
  1948. TraceFunctLeave();
  1949. return(fRet);
  1950. }