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.

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