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.

5721 lines
191 KiB

  1. /*++
  2. Copyright (c) 1994-2002 Microsoft Corporation
  3. Module Name :
  4. smtpout.cxx
  5. Abstract:
  6. This module defines the functions for derived class of connections
  7. for Internet Services ( class SMTP_CONNOUT)
  8. Author:
  9. Rohan Phillips ( Rohanp ) 02-Feb-1996
  10. Project:
  11. SMTP Server DLL
  12. Functions Exported:
  13. SMTP_CONNOUT::~SMTP_CONNOUT()
  14. BOOL SMTP_CONNOUT::ProcessClient( IN DWORD cbWritten,
  15. IN DWORD dwCompletionStatus,
  16. IN BOOL fIOCompletion)
  17. BOOL SMTP_CONNOUT::StartupSession( VOID)
  18. Revision History:
  19. --*/
  20. /************************************************************
  21. * Include Headers
  22. ************************************************************/
  23. #define INCL_INETSRV_INCS
  24. #include "smtpinc.h"
  25. #include "remoteq.hxx"
  26. #include "smtpmsg.h"
  27. //
  28. // ATL includes
  29. //
  30. #define _ATL_NO_DEBUG_CRT
  31. #define _ASSERTE _ASSERT
  32. #define _WINDLL
  33. #include "atlbase.h"
  34. extern CComModule _Module;
  35. #include "atlcom.h"
  36. #undef _WINDLL
  37. //
  38. // SEO includes
  39. //
  40. #include "seo.h"
  41. #include "seolib.h"
  42. #include <memory.h>
  43. #include "smtpcli.hxx"
  44. #include "smtpout.hxx"
  45. #include <smtpevent.h>
  46. #include <smtpguid.h>
  47. //
  48. // Dispatcher implementation
  49. //
  50. #include "pe_dispi.hxx"
  51. int strcasecmp(char *s1, char *s2);
  52. int strncasecmp(char *s1, char *s2, int n);
  53. extern CHAR g_VersionString[];
  54. static char * IsLineCompleteBW(IN OUT char * pchRecvd, IN DWORD cbRecvd, IN DWORD cbMaxRecvBuffer);
  55. #define SMTP_DUMMY_FAILURE (0x1000 | SMTP_ACTION_ABORTED_CODE)
  56. #define SMTPOUT_CONTENT_FILE_IO_TIMEOUT 2*60*1000
  57. static const char * TO_MANY_RCPTS_ERROR = "552 Too many recipients";
  58. #define KNOWN_AUTH_FLAGS ((DWORD)(DOMAIN_INFO_USE_NTLM | DOMAIN_INFO_USE_PLAINTEXT | DOMAIN_INFO_USE_DPA \
  59. | DOMAIN_INFO_USE_KERBEROS))
  60. #define INVALID_RCPT_IDX_VALUE 0xFFFFFFFF
  61. // provide memory for static declared in SMTP_CONNOUT
  62. CPool SMTP_CONNOUT::Pool(CLIENT_CONNECTION_SIGNATURE_VALID);
  63. //
  64. // Statics for outbound protocol events
  65. //
  66. CInboundDispatcherClassFactory g_cfInbound;
  67. COutboundDispatcherClassFactory g_cfOutbound;
  68. CResponseDispatcherClassFactory g_cfResponse;
  69. /************************************************************
  70. * Functions
  71. ************************************************************/
  72. #define MAX_LOG_ERROR_LEN (500)
  73. extern void DeleteDnsRec(PSMTPDNS_RECS pDnsRec);
  74. VOID
  75. SmtpCompletion(
  76. PVOID pvContext,
  77. DWORD cbWritten,
  78. DWORD dwCompletionStatus,
  79. OVERLAPPED * lpo
  80. );
  81. /*++
  82. Name :
  83. InternetCompletion
  84. Description:
  85. Handles a completed I/O for outbound connections.
  86. Arguments:
  87. pvContext: the context pointer specified in the initial IO
  88. cbWritten: the number of bytes sent
  89. dwCompletionStatus: the status of the completion (usually NO_ERROR)
  90. lpo: the overlapped structure associated with the IO
  91. Returns:
  92. nothing.
  93. --*/
  94. VOID InternetCompletion(PVOID pvContext, DWORD cbWritten,
  95. DWORD dwCompletionStatus, OVERLAPPED * lpo)
  96. {
  97. BOOL WasProcessed;
  98. SMTP_CONNOUT *pCC = (SMTP_CONNOUT *) pvContext;
  99. _ASSERT(pCC);
  100. _ASSERT(pCC->IsValid());
  101. _ASSERT(pCC->QuerySmtpInstance() != NULL);
  102. TraceFunctEnterEx((LPARAM) pCC, "InternetCompletion");
  103. // if we could not process a command, or we were
  104. // told to destroy this object, close the connection.
  105. WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, lpo);
  106. if (!WasProcessed) {
  107. pCC->DisconnectClient();
  108. pCC->QuerySmtpInstance()->RemoveOutboundConnection(pCC);
  109. delete pCC;
  110. pCC = NULL;
  111. }
  112. //TraceFunctLeaveEx((LPARAM)pCC);
  113. }
  114. VOID FIOInternetCompletion(PFIO_CONTEXT pFIOContext,
  115. PFH_OVERLAPPED lpo,
  116. DWORD cbWritten,
  117. DWORD dwCompletionStatus)
  118. {
  119. BOOL WasProcessed;
  120. SMTP_CONNOUT *pCC = (SMTP_CONNOUT *) (((SERVEREVENT_OVERLAPPED *) lpo)->ThisPtr);
  121. _ASSERT(pCC);
  122. _ASSERT(pCC->IsValid());
  123. _ASSERT(pCC->QuerySmtpInstance() != NULL);
  124. TraceFunctEnterEx((LPARAM) pCC, "InternetCompletion");
  125. // if we could not process a command, or we were
  126. // told to destroy this object, close the connection.
  127. WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, (OVERLAPPED *) lpo);
  128. if (!WasProcessed) {
  129. pCC->DisconnectClient();
  130. pCC->QuerySmtpInstance()->RemoveOutboundConnection(pCC);
  131. delete pCC;
  132. pCC = NULL;
  133. }
  134. //TraceFunctLeaveEx((LPARAM)pCC);
  135. }
  136. void SMTP_CONNOUT::ProtocolLogCommand(LPSTR pszCommand,
  137. DWORD cParameters,
  138. LPCSTR pszIpAddress,
  139. FORMAT_SMTP_MESSAGE_LOGLEVEL eLogLevel)
  140. {
  141. char *pszCR = NULL, *pszParameters = NULL, *pszSpace = NULL;
  142. DWORD cBytesSent;
  143. if (eLogLevel == FSM_LOG_NONE) return;
  144. if (pszCR = strchr(pszCommand, '\r')) *pszCR = 0;
  145. if (pszSpace = strchr(pszCommand, ' ')) {
  146. *pszSpace = 0;
  147. pszParameters = (eLogLevel == FSM_LOG_ALL) ? pszSpace + 1 : NULL;
  148. }
  149. cBytesSent = strlen(pszCommand);
  150. LogRemoteDeliveryTransaction(
  151. pszCommand,
  152. NULL,
  153. pszParameters,
  154. pszIpAddress,
  155. 0,
  156. 0,
  157. cBytesSent,
  158. 0,
  159. FALSE);
  160. if (pszCR) *pszCR = '\r';
  161. if (pszSpace) *pszSpace = ' ';
  162. }
  163. void SMTP_CONNOUT::ProtocolLogResponse(LPSTR pszResponse,
  164. DWORD cParameters,
  165. LPCSTR pszIpAddress)
  166. {
  167. char *pszCR = NULL;
  168. DWORD cBytesReceived;
  169. if (pszCR = strchr(pszResponse, '\r')) *pszCR = 0;
  170. cBytesReceived = strlen(pszResponse);
  171. LogRemoteDeliveryTransaction(
  172. NULL,
  173. NULL,
  174. pszResponse,
  175. pszIpAddress,
  176. 0,
  177. 0,
  178. cBytesReceived,
  179. 0,
  180. TRUE);
  181. if (pszCR) *pszCR = '\r';
  182. }
  183. void SMTP_CONNOUT::LogRemoteDeliveryTransaction(
  184. LPCSTR pszOperation,
  185. LPCSTR pszTarget,
  186. LPCSTR pszParameters,
  187. LPCSTR pszIpAddress,
  188. DWORD dwWin32Error,
  189. DWORD dwServiceSpecificStatus,
  190. DWORD dwBytesSent,
  191. DWORD dwBytesReceived,
  192. BOOL fResponse
  193. )
  194. {
  195. INETLOG_INFORMATION translog;
  196. DWORD dwLog;
  197. LPSTR lpNull = "";
  198. DWORD cchError = MAX_LOG_ERROR_LEN;
  199. char VersionString[] = "SMTP";
  200. char szClientUserNameCommand[] = "OutboundConnectionCommand";
  201. char szClientUserNameResponse[] = "OutboundConnectionResponse";
  202. //Buffers to prevent overwrite by IIS logging
  203. //which does evil things like change '<sp>' to '+'
  204. // 6/23/99 - MikeSwa
  205. char szOperationBuffer[20] = ""; //This is the protocol verb
  206. char szTargetBuffer[20] = ""; //Currently unused by all callers
  207. char szParametersBuffer[1024] = ""; //Data portion of buffer information
  208. ZeroMemory(&translog, sizeof(translog));
  209. if (pszParameters == NULL)
  210. pszParameters = lpNull;
  211. if (pszIpAddress == NULL)
  212. pszIpAddress = lpNull;
  213. translog.pszVersion = VersionString;
  214. translog.msTimeForProcessing = QueryProcessingTime();;
  215. if (fResponse) {
  216. translog.pszClientUserName = szClientUserNameResponse;
  217. } else {
  218. translog.pszClientUserName = szClientUserNameCommand;
  219. }
  220. translog.pszClientHostName = (LPSTR)pszIpAddress;
  221. translog.cbClientHostName = lstrlen(pszIpAddress);
  222. //Make sure we log the correct port number
  223. if (IsSecure()) {
  224. translog.dwPort = QuerySmtpInstance()->GetRemoteSmtpSecurePort();
  225. } else {
  226. translog.dwPort = QuerySmtpInstance()->GetRemoteSmtpPort();
  227. }
  228. //Copy buffers
  229. if (pszOperation) {
  230. lstrcpyn(szOperationBuffer, pszOperation, sizeof(szOperationBuffer)-sizeof(CHAR));
  231. translog.pszOperation = szOperationBuffer;
  232. translog.cbOperation = lstrlen(szOperationBuffer);
  233. } else {
  234. translog.pszOperation = "";
  235. translog.cbOperation = 0;
  236. }
  237. if (pszTarget) {
  238. lstrcpyn(szTargetBuffer, pszTarget, sizeof(szTargetBuffer)-sizeof(CHAR));
  239. translog.pszTarget = szTargetBuffer;
  240. translog.cbTarget = lstrlen(szTargetBuffer);
  241. } else {
  242. translog.pszTarget = "";
  243. translog.cbTarget = 0;
  244. }
  245. if (pszParameters) {
  246. lstrcpyn(szParametersBuffer, pszParameters, sizeof(szParametersBuffer)-sizeof(CHAR));
  247. translog.pszParameters = szParametersBuffer;
  248. } else {
  249. translog.pszParameters = "";
  250. }
  251. //Detect if usage drastically changes... but don't check parameters, because
  252. //we don't care about logging more than 1K per command
  253. _ASSERT(sizeof(szOperationBuffer) > lstrlen(pszOperation));
  254. _ASSERT(sizeof(szTargetBuffer) > lstrlen(pszTarget));
  255. translog.dwBytesSent = dwBytesSent;
  256. translog.dwBytesRecvd = dwBytesReceived;
  257. translog.dwWin32Status = dwWin32Error;
  258. translog.dwProtocolStatus = dwServiceSpecificStatus;
  259. dwLog = QuerySmtpInstance()->m_Logging.LogInformation( &translog);
  260. }
  261. /*++
  262. Name:
  263. SMTP_CONNOUT::SMTP_CONNOUT
  264. Constructs a new SMTP connection object for the client
  265. connection given the client connection socket and socket
  266. address. This constructor is private. Only the Static
  267. member funtion, declared below, can call it.
  268. Arguments:
  269. sClient socket for communicating with client
  270. psockAddrRemote pointer to address of the remote client
  271. ( the value should be copied).
  272. psockAddrLocal pointer to address for the local card through
  273. which the client came in.
  274. pAtqContext pointer to ATQ Context used for AcceptEx'ed conn.
  275. pvInitialRequest pointer to void buffer containing the initial request
  276. cbInitialData count of bytes of data read initially.
  277. --*/
  278. SMTP_CONNOUT::SMTP_CONNOUT(
  279. IN PSMTP_SERVER_INSTANCE pInstance,
  280. IN SOCKET sClient,
  281. IN const SOCKADDR_IN * psockAddrRemote,
  282. IN const SOCKADDR_IN * psockAddrLocal /* = NULL */ ,
  283. IN PATQ_CONTEXT pAtqContext /* = NULL */ ,
  284. IN PVOID pvInitialRequest/* = NULL*/ ,
  285. IN DWORD cbInitialData /* = 0 */
  286. )
  287. : m_encryptCtx( TRUE ),
  288. m_securityCtx(pInstance,
  289. TCPAUTH_CLIENT| TCPAUTH_UUENCODE,
  290. ((PSMTP_SERVER_INSTANCE)pInstance)->QueryAuthentication()),
  291. CLIENT_CONNECTION ( sClient, psockAddrRemote,
  292. psockAddrRemote, pAtqContext,
  293. pvInitialRequest, cbInitialData )
  294. {
  295. _ASSERT(pInstance != NULL);
  296. m_cActiveThreads = 0;
  297. m_cPendingIoCount = 0;
  298. m_MsgOptions = 0;
  299. m_AuthToUse = 0;
  300. m_pInstance = pInstance;
  301. m_UsingSSL = FALSE;
  302. m_fCanTurn = TRUE;
  303. m_pIMsg = NULL;
  304. m_pIMsgRecips = NULL;
  305. m_pISMTPConnection = NULL;
  306. m_AdvContext = NULL;
  307. m_pDnsRec = NULL;
  308. m_EhloSent = FALSE;
  309. pInstance->IncConnOutObjs();
  310. //
  311. // By default, we use the smallish receive buffer inherited from
  312. // the base CLIENT_CONNECTION object and a smallish output buffer defined in
  313. // SMTP_CONNOUT
  314. //
  315. m_precvBuffer = m_recvBuffer;
  316. m_cbMaxRecvBuffer = sizeof(m_recvBuffer);
  317. m_pOutputBuffer = m_OutputBuffer;
  318. m_cbMaxOutputBuffer = sizeof(m_OutputBuffer);
  319. m_pmszTurnList = NULL;
  320. m_szCurrentTURNDomain = NULL;
  321. m_IMsgDotStuffedFileHandle = NULL;
  322. m_ConnectedDomain [0] = '\0';
  323. m_pBindInterface = NULL;
  324. m_SeoOverlapped.ThisPtr = (PVOID) this;
  325. m_SeoOverlapped.pfnCompletion = InternetCompletion;
  326. //initialize this error in case the connection gets
  327. //broken early.
  328. m_Error = ERROR_BROKEN_PIPE;
  329. m_fNeedRelayedDSN = FALSE;
  330. m_fHadHardError = FALSE;
  331. m_fHadTempError = FALSE;
  332. m_fHadSuccessfulDelivery = FALSE;
  333. //
  334. // Protocol Events
  335. //
  336. m_fNativeHandlerFired = FALSE;
  337. m_pOutboundDispatcher = NULL;
  338. m_pResponseDispatcher = NULL;
  339. //
  340. // Diagnostic Information
  341. //
  342. m_hrDiagnosticError = S_OK;
  343. m_szDiagnosticVerb = NULL;
  344. m_szDiagnosticResponse[0]= '\0';
  345. m_szCurrentETRNDomain = NULL;
  346. m_pszSSLVerificationName = NULL;
  347. m_pDNS_RESOLVER_RECORD = NULL;
  348. m_pProviderPackagesInfo = NULL;
  349. }
  350. /*++
  351. Name :
  352. SMTP_CONNOUT::~SMTP_CONNOUT (void)
  353. Description:
  354. Destructor for outbound connection object.
  355. This routine checks to see if there was a
  356. current mail object that was being processed
  357. before this destructor was called and does
  358. whatever is necessary to clean up its' memory.
  359. Then it checks the mailbag and cleans up any
  360. mail objects it finds in there.
  361. Arguments:
  362. none
  363. Returns:
  364. none
  365. --*/
  366. SMTP_CONNOUT::~SMTP_CONNOUT (void)
  367. {
  368. PSMTP_SERVER_INSTANCE pInstance = NULL;
  369. HRESULT hrDiagnostic = S_OK;
  370. char * pTempBuffer = NULL;
  371. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::~SMTP_CONNOUT (void)");
  372. //We need to call our cleanup function... so that the ATQ context will be
  373. //freed. We do this first, because a message ack or connection ack may
  374. //trigger DSN generation, which may take long enough to cause the context
  375. //to time out and complete on us (causing one thread to AV).
  376. Cleanup();
  377. //catch all message ack call
  378. //NK** : Need to substitute it with an HRESULT based on the actual internal error
  379. //mikeswa - 9/11/98 - Add check recips to flags
  380. HandleCompletedMailObj(MESSAGE_STATUS_RETRY_ALL | MESSAGE_STATUS_CHECK_RECIPS, "451 Remote host dropped connection", 0);
  381. //if we were doing a TLS transmission that got interrupted, we need to
  382. //destroy the AtqContext we created for reading from the mail file.
  383. //FreeAtqFileContext();
  384. if (m_pISMTPConnection) {
  385. //Ack the connection
  386. m_pISMTPConnection->AckConnection((eConnectionStatus)m_dwConnectionStatus);
  387. PromoteSessionPropertiesToAQ();
  388. if (FAILED(m_hrDiagnosticError))
  389. {
  390. m_pISMTPConnection->SetDiagnosticInfo(m_hrDiagnosticError,
  391. m_szDiagnosticVerb, m_szDiagnosticResponse);
  392. }
  393. else if (CONNECTION_STATUS_OK != m_dwConnectionStatus)
  394. {
  395. //Report appropriate diagnostic information if we don't have specific failures
  396. switch (m_dwConnectionStatus)
  397. {
  398. case CONNECTION_STATUS_DROPPED:
  399. hrDiagnostic = AQUEUE_E_CONNECTION_DROPPED;
  400. break;
  401. case CONNECTION_STATUS_FAILED:
  402. hrDiagnostic = AQUEUE_E_CONNECTION_FAILED;
  403. break;
  404. default:
  405. hrDiagnostic = E_FAIL;
  406. }
  407. m_pISMTPConnection->SetDiagnosticInfo(hrDiagnostic, NULL, NULL);
  408. }
  409. m_pISMTPConnection->Release();
  410. m_pISMTPConnection = NULL;
  411. }
  412. //If we had a TURN list free it up
  413. if (m_pmszTurnList) {
  414. delete m_pmszTurnList;
  415. m_pmszTurnList = NULL;
  416. m_szCurrentTURNDomain = NULL;
  417. }
  418. pInstance = (PSMTP_SERVER_INSTANCE ) InterlockedExchangePointer((PVOID *) &m_pInstance, (PVOID) NULL);
  419. if (pInstance != NULL) {
  420. pInstance->DecConnOutObjs();
  421. }
  422. pTempBuffer = (char *) InterlockedExchangePointer((PVOID *) &m_precvBuffer, (PVOID) &m_recvBuffer[0]);
  423. if (pTempBuffer != m_recvBuffer) {
  424. delete [] pTempBuffer;
  425. }
  426. pTempBuffer = (char *) InterlockedExchangePointer((PVOID *) &m_pOutputBuffer, (PVOID) &m_OutputBuffer[0]);
  427. if (pTempBuffer != m_OutputBuffer) {
  428. delete [] pTempBuffer;
  429. }
  430. // Protocol events: Release the dispatchers
  431. if (m_pOutboundDispatcher)
  432. m_pOutboundDispatcher->Release();
  433. if (m_pResponseDispatcher)
  434. m_pResponseDispatcher->Release();
  435. if (m_pDnsRec) {
  436. DeleteDnsRec(m_pDnsRec);
  437. m_pDnsRec = NULL;
  438. }
  439. if (m_pDNS_RESOLVER_RECORD) {
  440. delete m_pDNS_RESOLVER_RECORD;
  441. m_pDNS_RESOLVER_RECORD = NULL;
  442. }
  443. if (m_pszSSLVerificationName)
  444. delete [] m_pszSSLVerificationName;
  445. if (m_pProviderPackagesInfo)
  446. m_pProviderPackagesInfo->Release();
  447. if (m_szDiagnosticVerb)
  448. free(m_szDiagnosticVerb);
  449. DebugTrace((LPARAM) this,"%X was deleted", this);
  450. TraceFunctLeaveEx((LPARAM)this);
  451. }
  452. /*++
  453. Name :
  454. SMTP_CONNOUT::DisconnectClient(DWORD dwErrorCode)
  455. Description:
  456. Disconnects from the remote server. It first calls
  457. CLIENT_CONNECTION::DisconnectClient, and then shuts down any mail-file
  458. read handles we may be pending reads on.
  459. Arguments:
  460. dwErrorCode -- Passed through to CLIENT_CONNECTION::Disconnect
  461. Returns:
  462. nothing
  463. --*/
  464. VOID SMTP_CONNOUT::DisconnectClient(DWORD dwErrorCode)
  465. {
  466. TraceFunctEnter("SMTP_CONNOUT::DisconnectClient");
  467. if (m_DoCleanup)
  468. CLIENT_CONNECTION::DisconnectClient();
  469. }
  470. /*++
  471. Name :
  472. SMTP_CONNOUT::HandleCompletedMailObj
  473. Description:
  474. This routinr gets called after the mail file
  475. has been sent. It will either requeue the object
  476. in the outbound queue, retry queue, BadMail,etc.
  477. Arguments:
  478. none
  479. Returns:
  480. none
  481. --*/
  482. void SMTP_CONNOUT::HandleCompletedMailObj(DWORD MsgStatus, char * szExtendedStatus, DWORD cbExtendedStatus)
  483. {
  484. MessageAck MsgAck;
  485. HRESULT hr = S_OK;
  486. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::HandleCompletedMailObj");
  487. _ASSERT(IsValid());
  488. if (m_pISMTPConnection) {
  489. if (m_pIMsgRecips) {
  490. //Uncheck all marked recipients if the connection has been dropped
  491. if (((m_dwConnectionStatus != CONNECTION_STATUS_OK) || (szExtendedStatus[0] != SMTP_COMPLETE_SUCCESS)) &&
  492. m_NumRcptSentSaved) {
  493. UnMarkHandledRcpts();
  494. }
  495. m_pIMsgRecips->Release();
  496. m_pIMsgRecips = NULL;
  497. }
  498. if (m_pIMsg) {
  499. MsgAck.pvMsgContext = (DWORD *) m_AdvContext;
  500. MsgAck.pIMailMsgProperties = m_pIMsg;
  501. if ( (MsgStatus & MESSAGE_STATUS_RETRY_ALL) ||
  502. (MsgStatus & MESSAGE_STATUS_NDR_ALL)) {
  503. //DebugTrace((LPARAM) this,"CompObj:file %s going to %s was retryed", FileName, m_ConnectedDomain);
  504. } else {
  505. //DebugTrace((LPARAM) this,"CompObj:file %s going to %s was delivered", FileName, m_ConnectedDomain);
  506. }
  507. MsgAck.dwMsgStatus = MsgStatus;
  508. MsgAck.dwStatusCode = 0;
  509. //We will have an extended status string to go along with the Status code
  510. //in case of some major failure that makes us fail the complete message
  511. if (MsgStatus & MESSAGE_STATUS_EXTENDED_STATUS_CODES ) {
  512. MsgAck.cbExtendedStatus = cbExtendedStatus;
  513. MsgAck.szExtendedStatus = szExtendedStatus;
  514. }
  515. if (m_pBindInterface) {
  516. m_pBindInterface->ReleaseContext();
  517. m_pBindInterface->Release();
  518. m_pBindInterface = NULL;
  519. if( NULL != m_IMsgDotStuffedFileHandle )
  520. {
  521. ReleaseContext( m_IMsgDotStuffedFileHandle );
  522. m_IMsgDotStuffedFileHandle = NULL;
  523. }
  524. }
  525. //
  526. // Do Message Tracking
  527. //
  528. MSG_TRACK_INFO msgTrackInfo;
  529. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  530. msgTrackInfo.dwEventId = MTE_END_OUTBOUND_TRANSFER;
  531. msgTrackInfo.pszPartnerName = m_ConnectedDomain;
  532. if( MsgStatus & MESSAGE_STATUS_RETRY_ALL )
  533. {
  534. msgTrackInfo.dwRcptReportStatus = MP_STATUS_RETRY;
  535. }
  536. else if( MsgStatus & MESSAGE_STATUS_NDR_ALL )
  537. {
  538. msgTrackInfo.dwEventId = MTE_NDR_ALL;
  539. msgTrackInfo.pszPartnerName = NULL;
  540. msgTrackInfo.dwRcptReportStatus = MP_STATUS_ABORT_DELIVERY;
  541. }
  542. m_pInstance->WriteLog( &msgTrackInfo, m_pIMsg, NULL, NULL );
  543. m_pISMTPConnection->AckMessage(&MsgAck);
  544. m_pIMsg->Release();
  545. m_pIMsg = NULL;
  546. }
  547. }
  548. TraceFunctLeaveEx((LPARAM) this);
  549. }
  550. /*++
  551. Name :
  552. SMTP_CONNOUT::UnMarkHandledRcpts
  553. Description:
  554. When we send out recipients we assumptively mark the recipients as
  555. delivered or failed based on the responses. Later if it turns out that we
  556. could never completely send the message. we need to rset the status of
  557. successful recipients. However, if we have a hard per-recipient error,
  558. we should leave the error code intact (otherwise the sender may receive
  559. a DELAY DSN with a 500-level status code).
  560. Arguments:
  561. none
  562. Returns:
  563. none
  564. --*/
  565. BOOL SMTP_CONNOUT::UnMarkHandledRcpts(void)
  566. {
  567. DWORD i;
  568. HRESULT hr = S_OK;
  569. DWORD dwRecipientFlags;
  570. DWORD dwRcptsSaved = m_NumRcptSentSaved;
  571. //
  572. // It is possible for this to be called after HandleCompletedMailObj (after
  573. // a 421 response to a DATA command for example). We should bail if we
  574. // do not have a mailmsg ptr.
  575. //
  576. if (!m_pIMsgRecips)
  577. return (TRUE);
  578. for (i = m_FirstAddressinCurrentMail; (i < m_NumRcpts) && dwRcptsSaved;i++) {
  579. //Get to the next rcpt that we send out this time
  580. if (m_RcptIndexList[i] != INVALID_RCPT_IDX_VALUE) {
  581. //
  582. // The ideal way to handle this situation is to use the
  583. // RP_VOLATILE_FLAGS_MASK bits in the recipient flags a tmp
  584. // storage and then "commit" the handled bit after a successful
  585. // connection. Given how mailmsg works, this is not a problem
  586. // - While we are processing the message... it is not possible
  587. // for it to be saved to disk until we are done with it.
  588. // - If we can write a property once before committing... we
  589. // can always rewrite a property of the same size (since
  590. // the required portion of the property stream is already
  591. // in memory.
  592. // I have added the ASSERT(SUCEEDED(hr)) below
  593. // - mikeswa 9/11/98 (updated 10/05/2000)
  594. dwRecipientFlags = 0;
  595. hr = m_pIMsgRecips->GetDWORD(m_RcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  596. if (FAILED(hr)) {
  597. //Problemmo
  598. SetLastError(ERROR_OUTOFMEMORY);
  599. return (FALSE);
  600. }
  601. //Check to see if we marked it as delivered... and unmark it if we did
  602. if (RP_DELIVERED == (dwRecipientFlags & RP_DELIVERED)) {
  603. dwRecipientFlags &= ~RP_DELIVERED;
  604. hr = m_pIMsgRecips->PutDWORD(m_RcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,dwRecipientFlags);
  605. if (FAILED(hr)) {
  606. //
  607. // We need to understand how this can fail... mailmsg
  608. // is designed so this should not happen.
  609. //
  610. ASSERT(FALSE && "Potential loss of recipient");
  611. SetLastError(ERROR_OUTOFMEMORY);
  612. return (FALSE);
  613. }
  614. }
  615. dwRcptsSaved--;
  616. }
  617. }
  618. return (TRUE);
  619. }
  620. /*++
  621. Name :
  622. SMTP_CONNOUT::InitializeObject
  623. Description:
  624. Initializes all member variables and pre-allocates
  625. a mail context class
  626. Arguments:
  627. Options - SSL etc.
  628. pszSSLVerificationName - Subject name to look for in server certificate
  629. if using SSL
  630. Returns:
  631. TRUE if memory can be allocated.
  632. FALSE if no memory can be allocated
  633. --*/
  634. BOOL SMTP_CONNOUT::InitializeObject (
  635. DWORD Options,
  636. LPSTR pszSSLVerificationName,
  637. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD)
  638. {
  639. BOOL fRet = TRUE;
  640. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::InitializeObject");
  641. m_szCurrentETRNDomain = NULL;
  642. m_cbReceived = 0;
  643. m_cbParsable = 0;
  644. m_OutputBufferSize = 0;
  645. m_NumRcptSent = 0;
  646. m_FirstAddressinCurrentMail = 0;
  647. m_NumRcptSentSaved = 0;
  648. m_SizeOptionSize = 0;
  649. m_Flags = 0;
  650. m_NumFailedAddrs = 0;
  651. m_cActiveThreads = 0;
  652. m_cPendingIoCount = 0;
  653. m_FileSize = 0;
  654. m_NextAddress = 0;
  655. m_FirstPipelinedAddress = 0;
  656. m_First552Address = -1;
  657. m_NextState = NULL;
  658. m_HeloSent = FALSE;
  659. m_EhloFailed = FALSE;
  660. m_FirstRcpt = FALSE;
  661. m_SendAgain = FALSE;
  662. m_Active = TRUE;
  663. m_HaveDataResponse = FALSE;
  664. m_SecurePort = FALSE;
  665. m_fNegotiatingSSL = FALSE;
  666. m_MsgOptions = Options;
  667. m_dwConnectionStatus = CONNECTION_STATUS_OK;
  668. m_fUseBDAT = FALSE;
  669. m_fNeedRelayedDSN = FALSE;
  670. m_fHadHardError = FALSE;
  671. m_fHadTempError = FALSE;
  672. m_fHadSuccessfulDelivery = FALSE;
  673. m_pDNS_RESOLVER_RECORD = pDNS_RESOLVER_RECORD;
  674. if (Options & KNOWN_AUTH_FLAGS) {
  675. m_pInstance->LockGenCrit();
  676. // Initialize Security Context
  677. //
  678. m_pProviderPackagesInfo = m_pInstance->GetAddRefdProviderPackagesInfo();
  679. if(!m_pProviderPackagesInfo)
  680. {
  681. fRet = FALSE;
  682. m_pInstance->UnLockGenCrit();
  683. goto Exit;
  684. }
  685. if (!m_securityCtx.SetInstanceAuthPackageNames(
  686. m_pProviderPackagesInfo->GetProviderPackagesCount(),
  687. m_pProviderPackagesInfo->GetProviderNames(),
  688. m_pProviderPackagesInfo->GetProviderPackages())) {
  689. m_Error = GetLastError();
  690. ErrorTrace((LPARAM)this, "SetInstanceAuthPackageNames FAILED <Err=%u>",
  691. m_Error);
  692. fRet = FALSE;
  693. m_pInstance->UnLockGenCrit();
  694. goto Exit;
  695. }
  696. //
  697. // We want to set up the Cleartext authentication package
  698. // for this connection based on the instance configuration.
  699. // To enable MBS CTA,
  700. // MD_SMTP_CLEARTEXT_AUTH_PROVIDER must be set to the package name.
  701. // To disable it, the md value must be set to "".
  702. //
  703. m_securityCtx.SetCleartextPackageName(
  704. m_pInstance->GetCleartextAuthPackage(),
  705. m_pInstance->GetMembershipBroker());
  706. if (*m_pInstance->GetCleartextAuthPackage() == '\0' ||
  707. *m_pInstance->GetMembershipBroker() == '\0') {
  708. m_fUseMbsCta = FALSE;
  709. } else {
  710. m_fUseMbsCta = TRUE;
  711. }
  712. m_pInstance->UnLockGenCrit();
  713. }
  714. m_pmszTurnList = NULL;
  715. m_szCurrentTURNDomain = NULL;
  716. m_UsingSSL = (Options & DOMAIN_INFO_USE_SSL);
  717. m_TlsState = (Options & DOMAIN_INFO_USE_SSL) ? MUST_DO_TLS : DONT_DO_TLS;
  718. m_TransmitTailBuffer[0] = '.';
  719. m_TransmitTailBuffer[1] = '\r';
  720. m_TransmitTailBuffer[2] = '\n';
  721. m_TransmitBuffers.Head = NULL;
  722. m_TransmitBuffers.HeadLength = 0;
  723. m_TransmitBuffers.Tail = m_TransmitTailBuffer;
  724. m_TransmitBuffers.TailLength = 3;
  725. //
  726. // Protocol events: get the response dispatcher for the session
  727. //
  728. m_pIEventRouter = m_pInstance->GetRouter();
  729. if (m_pIEventRouter) {
  730. HRESULT hr = m_pIEventRouter->GetDispatcherByClassFactory(
  731. CLSID_CResponseDispatcher,
  732. &g_cfResponse,
  733. CATID_SMTP_ON_SERVER_RESPONSE,
  734. IID_ISmtpServerResponseDispatcher,
  735. (IUnknown **)&m_pResponseDispatcher);
  736. if (!SUCCEEDED(hr)) {
  737. // If we fail, we don't process protocol events
  738. m_pResponseDispatcher = NULL;
  739. ErrorTrace((LPARAM) this,
  740. "Unable to get response dispatcher from router (%08x)", hr);
  741. }
  742. }
  743. if (pszSSLVerificationName) {
  744. m_pszSSLVerificationName = new char [lstrlen (pszSSLVerificationName) + 1];
  745. if (!m_pszSSLVerificationName) {
  746. fRet = FALSE;
  747. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  748. goto Exit;
  749. }
  750. lstrcpy (m_pszSSLVerificationName, pszSSLVerificationName);
  751. }
  752. StartProcessingTimer();
  753. Exit:
  754. TraceFunctLeaveEx((LPARAM) this);
  755. return fRet;
  756. }
  757. BOOL SMTP_CONNOUT::GoToWaitForConnectResponseState(void)
  758. {
  759. BOOL fRet = TRUE;
  760. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::GoToWaitForConnectResponseState( void)");
  761. SetNextState (&SMTP_CONNOUT::WaitForConnectResponse);
  762. m_Error = NO_ERROR;
  763. m_LastClientIo = SMTP_CONNOUT::READIO;
  764. IncPendingIoCount();
  765. fRet = ReadFile(QueryMRcvBuffer(), m_cbMaxRecvBuffer);
  766. if (!fRet) {
  767. m_Error = GetLastError();
  768. DebugTrace((LPARAM) this, "SMTP_CONNOUT::WaitForConnectResponseState - ReadFile failed with error %d", m_Error);
  769. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  770. DisconnectClient();
  771. DecPendingIoCount();
  772. SetLastError(m_Error);
  773. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  774. fRet = FALSE;
  775. }
  776. return fRet;
  777. }
  778. BOOL SMTP_CONNOUT::GetNextTURNConnection(void)
  779. {
  780. HRESULT hr = S_OK;
  781. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::GetNextTURNConnection( void)");
  782. //We are on a TURNed connection and need to start the queue for next
  783. //domain in the turn list if it exists
  784. //Before getting the next connection release the current one.
  785. //
  786. if (m_pISMTPConnection) {
  787. //Ack the last connection
  788. m_dwConnectionStatus = CONNECTION_STATUS_OK;
  789. m_pISMTPConnection->AckConnection((eConnectionStatus)m_dwConnectionStatus);
  790. PromoteSessionPropertiesToAQ();
  791. m_pISMTPConnection->Release();
  792. m_pISMTPConnection = NULL;
  793. }
  794. m_szCurrentTURNDomain = m_pmszTurnList->Next( m_szCurrentTURNDomain );
  795. while (m_szCurrentTURNDomain && !QuerySmtpInstance()->IsShuttingDown()) {
  796. //We have another domain to start
  797. hr = QuerySmtpInstance()->GetConnManPtr()->GetNamedConnection(lstrlen(m_szCurrentTURNDomain), (CHAR*)m_szCurrentTURNDomain, &m_pISMTPConnection);
  798. if (FAILED(hr)) {
  799. //Something bad happened on this call
  800. ErrorTrace((LPARAM) this, "StartSession - SMTP_ERROR_PROCESSING_CODE, GetNamedConnection failed %d",hr);
  801. TraceFunctLeaveEx((LPARAM) this);
  802. return FALSE;
  803. }
  804. //If the link corresponding to this domain does not exist in AQ, we get a NULL
  805. //ISMTPConnection at this point
  806. if (m_pISMTPConnection)
  807. break;
  808. else {
  809. m_szCurrentTURNDomain = m_pmszTurnList->Next( m_szCurrentTURNDomain );
  810. continue;
  811. }
  812. }
  813. TraceFunctLeaveEx((LPARAM) this);
  814. return TRUE;
  815. }
  816. /*++
  817. Name :
  818. SMTP_CONNOUT::StartSession
  819. Description:
  820. Starts up a session for new client.
  821. starts off a receive request from client.
  822. Arguments:
  823. Returns:
  824. TRUE if everything is O.K
  825. FALSE if a write or a pended read failed
  826. --*/
  827. BOOL SMTP_CONNOUT::StartSession( void)
  828. {
  829. HRESULT hr = S_OK;
  830. BOOL fRet = TRUE;
  831. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::StartSession( void)");
  832. _ASSERT(IsValid());
  833. //We do not do s restart if the connection is a tunr connection
  834. if (!m_pmszTurnList || !m_szCurrentTURNDomain) {
  835. _ASSERT(m_pDnsRec);
  836. if(!m_pDnsRec)
  837. {
  838. ErrorTrace((LPARAM)this, "Unexpected NULL DnsRec");
  839. TraceFunctLeaveEx((LPARAM)this);
  840. return FALSE;
  841. }
  842. if (m_pDnsRec->pMailMsgObj) {
  843. fRet = ReStartSession();
  844. TraceFunctLeaveEx((LPARAM) this);
  845. return fRet;
  846. }
  847. }
  848. //
  849. // We are either not doing SSL or are done establishing an SSL session.
  850. // Lets do the real work of starting a session with a remote SMTP server.
  851. //
  852. m_IMsgFileHandle = NULL;
  853. m_IMsgDotStuffedFileHandle = NULL;
  854. //get the next object to send
  855. //This is in loop because - we might have to pickup a new connection in case
  856. //we are handling a TURN list
  857. while (1) {
  858. // we can't call into GetNextMessage if we are on a TURN-only
  859. // connection. if we did and a message queued up between the
  860. // last time we were in StartSession and now then we would
  861. // get back into the waitforconnect state, which would be really
  862. // bad. so if we see the m_Flags set to TURN_ONLY_OPTION then we
  863. // know that this is an empty TURN and we just pretend that there
  864. // is no message to pick up.
  865. if (!(m_Flags & TURN_ONLY_OPTION)) {
  866. hr = m_pISMTPConnection->GetNextMessage(&m_pIMsg, (DWORD **) &m_AdvContext, &m_NumRcpts, &m_RcptIndexList);
  867. } else {
  868. m_pIMsg = NULL;
  869. hr = HRESULT_FROM_WIN32(ERROR_EMPTY);
  870. }
  871. if(FAILED(hr) || (m_pIMsg == NULL))
  872. {
  873. m_fCanTurn = FALSE;
  874. if (m_pmszTurnList && m_szCurrentTURNDomain) {
  875. //We have valid TURN list - Try and get the connetion for next domain to TURN
  876. if (GetNextTURNConnection()) {
  877. //We loop back if we got a valid connection. Otherwise we drop thru
  878. if (m_pISMTPConnection)
  879. continue;
  880. } else { //some error happened
  881. TraceFunctLeaveEx((LPARAM) this);
  882. return FALSE;
  883. }
  884. }
  885. if (m_MsgOptions & DOMAIN_INFO_SEND_TURN) {
  886. if (m_HeloSent || m_EhloSent) {
  887. // we will fall into this if we have already sent
  888. // the helo
  889. FormatSmtpMessage(FSM_LOG_ALL, "TURN\r\n");
  890. m_cbReceived = 0;
  891. m_cbParsable = 0;
  892. m_pmszTurnList = NULL;
  893. m_szCurrentTURNDomain = NULL;
  894. SendSmtpResponse();
  895. SetNextState (&SMTP_CONNOUT::DoTURNCommand);
  896. TraceFunctLeaveEx((LPARAM) this);
  897. return TRUE;
  898. } else {
  899. // we fall into this if we are sending TURN on an
  900. // otherwise empty connection. At this point we have
  901. // not yet sent EHLO, so it is not safe to send TURN.
  902. m_Flags |= TURN_ONLY_OPTION;
  903. return GoToWaitForConnectResponseState();
  904. }
  905. } else if ((m_MsgOptions & DOMAIN_INFO_SEND_ETRN) &&
  906. (m_NextState == NULL) &&
  907. !IsOptionSet(ETRN_SENT)) {
  908. m_Flags |= ETRN_ONLY_OPTION;
  909. return GoToWaitForConnectResponseState();
  910. } else if (!(m_EhloSent)) {
  911. // This is an empty connection
  912. m_MsgOptions |= EMPTY_CONNECTION_OPTION;
  913. return GoToWaitForConnectResponseState();
  914. } else {
  915. // 1/11/99 - MikeSwa
  916. //There just happened to be no mail at this time. Could
  917. //have been serviced by another connection, or just link
  918. //state detection
  919. if (HRESULT_FROM_WIN32(ERROR_EMPTY) == hr)
  920. SetLastError(ERROR_EMPTY); //AQueue does not setlast error
  921. else
  922. {
  923. SetDiagnosticInfo(hr, NULL, NULL);
  924. }
  925. DebugTrace((LPARAM) this,"Mailbag empty - returning FALSE");
  926. TraceFunctLeaveEx((LPARAM)this);
  927. return FALSE;
  928. }
  929. }
  930. else
  931. {
  932. //
  933. // The actual file may have been deleted from the queue. If so, we
  934. // need to ack the message and get the next one.
  935. //
  936. hr = m_pIMsg->QueryInterface(IID_IMailMsgBind, (void **)&m_pBindInterface);
  937. if (FAILED(hr))
  938. {
  939. MessageAck MsgAck;
  940. ErrorTrace((LPARAM)this, "Unable to Queryinterface message, going on to next one.");
  941. m_IMsgFileHandle = NULL;
  942. MsgAck.pvMsgContext = (DWORD *) m_AdvContext;
  943. MsgAck.pIMailMsgProperties = m_pIMsg;
  944. MsgAck.dwMsgStatus = MESSAGE_STATUS_RETRY;
  945. MsgAck.dwStatusCode = 0;
  946. m_pISMTPConnection->AckMessage(&MsgAck);
  947. SetDiagnosticInfo(AQUEUE_E_BIND_ERROR, NULL, NULL);
  948. m_pIMsg->Release();
  949. m_pIMsg = NULL;
  950. continue;
  951. }
  952. hr = m_pBindInterface->GetBinding(&m_IMsgFileHandle, NULL);
  953. if (SUCCEEDED(hr))
  954. {
  955. MSG_TRACK_INFO msgTrackInfo;
  956. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  957. msgTrackInfo.pszServerName = g_ComputerName;
  958. msgTrackInfo.dwEventId = MTE_BEGIN_OUTBOUND_TRANSFER;
  959. m_pInstance->WriteLog( &msgTrackInfo, m_pIMsg, NULL, NULL );
  960. break;
  961. }
  962. else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
  963. {
  964. MessageAck MsgAck;
  965. DebugTrace(NULL,
  966. "Message from queue has been deleted - ignoring it");
  967. m_pBindInterface->Release();
  968. m_IMsgFileHandle = NULL;
  969. MsgAck.pvMsgContext = (DWORD *) m_AdvContext;
  970. MsgAck.pIMailMsgProperties = m_pIMsg;
  971. MsgAck.dwMsgStatus = MESSAGE_STATUS_ALL_DELIVERED;
  972. MsgAck.dwStatusCode = 0;
  973. m_pISMTPConnection->AckMessage(&MsgAck);
  974. m_pIMsg->Release();
  975. m_pIMsg = NULL;
  976. }
  977. else
  978. {
  979. ASSERT(FAILED(hr));
  980. MessageAck MsgAck;
  981. ErrorTrace((LPARAM)this, "Unable to Bind message, going on to next one.");
  982. m_pBindInterface->Release();
  983. m_IMsgFileHandle = NULL;
  984. MsgAck.pvMsgContext = (DWORD *) m_AdvContext;
  985. MsgAck.pIMailMsgProperties = m_pIMsg;
  986. MsgAck.dwMsgStatus = MESSAGE_STATUS_RETRY;
  987. MsgAck.dwStatusCode = 0;
  988. m_pISMTPConnection->AckMessage(&MsgAck);
  989. SetDiagnosticInfo(AQUEUE_E_BIND_ERROR, NULL, NULL);
  990. m_pIMsg->Release();
  991. m_pIMsg = NULL;
  992. continue;
  993. }
  994. }
  995. }
  996. // Bump both the remote and total recipient counters
  997. ADD_COUNTER (QuerySmtpInstance(), NumRcptsRecvdRemote, m_NumRcpts);
  998. ADD_COUNTER (QuerySmtpInstance(), NumRcptsRecvd, m_NumRcpts);
  999. hr = m_pIMsg->QueryInterface(IID_IMailMsgRecipients, (void **) &m_pIMsgRecips);
  1000. if (FAILED(hr)) {
  1001. TraceFunctLeaveEx((LPARAM) this);
  1002. return FALSE;
  1003. }
  1004. m_FirstPipelinedAddress = 0;
  1005. //Nk** I moved this here from PerRcptEvent
  1006. m_NextAddress = 0;
  1007. //if m_NextState is NULL, this is the
  1008. //first time this routine has been called
  1009. //as a result of a connection. If m_NextState
  1010. //is not NULL, this means we just finished
  1011. //sending mail and we are about to send another
  1012. //mail message
  1013. if (m_NextState == NULL) {
  1014. m_Error = NO_ERROR;
  1015. DebugTrace((LPARAM) this,"start session called because of new connection");
  1016. m_FirstPipelinedAddress = 0;
  1017. SetNextState (&SMTP_CONNOUT::WaitForConnectResponse);
  1018. m_LastClientIo = SMTP_CONNOUT::READIO;
  1019. IncPendingIoCount();
  1020. fRet = ReadFile(QueryMRcvBuffer(), m_cbMaxRecvBuffer);
  1021. if (!fRet) {
  1022. m_Error = GetLastError();
  1023. DebugTrace((LPARAM) this, "SMTP_CONNOUT::StartSession - ReadFile failed with error %d", m_Error);
  1024. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1025. DisconnectClient();
  1026. DecPendingIoCount();
  1027. SetLastError(m_Error);
  1028. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1029. }
  1030. } else {
  1031. DebugTrace((LPARAM) this,"start session called because item was found in mailbag");
  1032. m_cbReceived = 0;
  1033. m_cbParsable = 0;
  1034. m_OutputBufferSize = 0;
  1035. m_Error = NO_ERROR;
  1036. m_NumRcptSent = 0;
  1037. m_FirstAddressinCurrentMail = 0;
  1038. m_NumRcptSentSaved = 0;
  1039. m_NumFailedAddrs = 0;
  1040. m_SendAgain = FALSE;
  1041. m_HaveDataResponse = FALSE;
  1042. m_FirstPipelinedAddress = 0;
  1043. m_FirstRcpt = FALSE;
  1044. m_TransmitTailBuffer[0] = '.';
  1045. m_TransmitTailBuffer[1] = '\r';
  1046. m_TransmitTailBuffer[2] = '\n';
  1047. m_TransmitBuffers.Head = NULL;
  1048. m_TransmitBuffers.HeadLength = 0;
  1049. m_TransmitBuffers.Tail = m_TransmitTailBuffer;
  1050. m_TransmitBuffers.TailLength = 3;
  1051. //send a reset
  1052. m_fNativeHandlerFired = FALSE;
  1053. m_RsetReasonCode = BETWEEN_MSG;
  1054. fRet = DoRSETCommand(NULL, 0, 0);
  1055. if (!fRet) {
  1056. m_Error = GetLastError();
  1057. DebugTrace((LPARAM) this,"reset command failed in StartSession");
  1058. TraceFunctLeaveEx((LPARAM) this);
  1059. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1060. return FALSE;
  1061. }
  1062. // WaitForRSETResponse is smart and will raise the message
  1063. // start event
  1064. SetNextState (&SMTP_CONNOUT::WaitForRSETResponse);
  1065. }
  1066. TraceFunctLeaveEx((LPARAM) NULL);
  1067. return fRet;
  1068. }
  1069. BOOL SMTP_CONNOUT::DecPendingIoCountEx(void)
  1070. {
  1071. BOOL fRet = FALSE;
  1072. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::DecPendingIoCountEx");
  1073. _ASSERT(IsValid());
  1074. if (InterlockedDecrement( &m_cPendingIoCount ) == 0) {
  1075. DebugTrace((LPARAM) this, "DecPendingIoCountEx deleting Smtp_Connout");
  1076. fRet = TRUE;
  1077. DisconnectClient();
  1078. QuerySmtpInstance()->RemoveOutboundConnection(this);
  1079. delete this;
  1080. }
  1081. TraceFunctLeaveEx((LPARAM) NULL);
  1082. return fRet;
  1083. }
  1084. BOOL SMTP_CONNOUT::ConnectToNextIpAddress(void)
  1085. {
  1086. BOOL fRet = TRUE;
  1087. REMOTE_QUEUE * pRemoteQ = NULL;
  1088. PSMTPDNS_RECS pDnsRec = NULL;
  1089. ISMTPConnection * pISMTPConnection = NULL;
  1090. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD = NULL;
  1091. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::ConnectToNextIpAddress( void)");
  1092. if (m_pmszTurnList && m_szCurrentTURNDomain) {
  1093. ErrorTrace((LPARAM) this, "Failing ConnectToNextIpAddress because of TURN");
  1094. TraceFunctLeaveEx((LPARAM) this);
  1095. return FALSE;
  1096. }
  1097. if (!m_fCanTurn) {
  1098. ErrorTrace((LPARAM) this, "Failing ConnectToNextIpAddress because of m_fCanTurn");
  1099. TraceFunctLeaveEx((LPARAM) this);
  1100. return FALSE;
  1101. }
  1102. if(!SetDnsRecToNextMx() &&
  1103. (!m_pDNS_RESOLVER_RECORD || !m_pDNS_RESOLVER_RECORD->GetDnsResolverRecord())) {
  1104. //
  1105. // Set m_pDnsRec to point to the next MX record to connect to. If that fails (because
  1106. // we have no more MX records for the current host), check to see if we have an
  1107. // m_pDNS_RESOLVER_RECORD with any more possible destination hosts. If there is, we
  1108. // will go on to ReStartAsyncConnections, if not, there is nothing to do but retry.
  1109. //
  1110. TraceFunctLeaveEx((LPARAM) this);
  1111. return FALSE;
  1112. }
  1113. if (m_NumRcptSentSaved) {
  1114. UnMarkHandledRcpts();
  1115. }
  1116. m_pDnsRec->pMailMsgObj = (PVOID) m_pIMsg;
  1117. m_pDnsRec->pAdvQContext = m_AdvContext;
  1118. m_pDnsRec->pRcptIdxList = (PVOID) m_RcptIndexList;
  1119. m_pDnsRec->dwNumRcpts = m_NumRcpts;
  1120. pDnsRec = m_pDnsRec;
  1121. m_pDnsRec = NULL;
  1122. pDNS_RESOLVER_RECORD = m_pDNS_RESOLVER_RECORD;
  1123. m_pDNS_RESOLVER_RECORD = NULL;
  1124. pISMTPConnection = m_pISMTPConnection;
  1125. if (m_pIMsgRecips) {
  1126. m_pIMsgRecips->Release();
  1127. m_pIMsgRecips = NULL;
  1128. }
  1129. if (m_pBindInterface) {
  1130. m_pBindInterface->ReleaseContext();
  1131. m_pBindInterface->Release();
  1132. m_pBindInterface = NULL;
  1133. if( NULL != m_IMsgDotStuffedFileHandle )
  1134. {
  1135. ReleaseContext( m_IMsgDotStuffedFileHandle );
  1136. m_IMsgDotStuffedFileHandle = NULL;
  1137. }
  1138. }
  1139. pRemoteQ = (REMOTE_QUEUE *) QuerySmtpInstance()->QueryRemoteQObj();
  1140. fRet = pRemoteQ->ReStartAsyncConnections(
  1141. pDnsRec,
  1142. pISMTPConnection,
  1143. m_MsgOptions,
  1144. m_pszSSLVerificationName,
  1145. pDNS_RESOLVER_RECORD);
  1146. if (fRet) {
  1147. m_pISMTPConnection = NULL;
  1148. m_pIMsg = NULL;
  1149. m_AdvContext = NULL;
  1150. m_RcptIndexList = NULL;
  1151. m_NumRcpts = 0;
  1152. DebugTrace((LPARAM)this, "RestartAsyncConnections succeeded.");
  1153. //close the socket
  1154. DisconnectClient();
  1155. }
  1156. TraceFunctLeaveEx((LPARAM) this);
  1157. return fRet;
  1158. }
  1159. //-----------------------------------------------------------------------------
  1160. // Description:
  1161. // The m_pDnsRec encapsulates all the MX records for a target host for
  1162. // this outbound connection. SMTP iterates through the MX records trying
  1163. // to deliver to each in turn, till an MX host accepts delivery. The
  1164. // m_pDnsRec contains within it, an index keeping track of which MX record
  1165. // we are currently trying to deliver to. This function increments the
  1166. // index to point to the next MX record (if any).
  1167. // Arguments:
  1168. // None.
  1169. // Returns:
  1170. // TRUE - StartRecord (the index) was successfully pointed to the next
  1171. // MX record.
  1172. // FALSE - No more MX records, we've tried and failed all of them.
  1173. //-----------------------------------------------------------------------------
  1174. BOOL SMTP_CONNOUT::SetDnsRecToNextMx()
  1175. {
  1176. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::SetDnsRecToNextIp");
  1177. if (m_pDnsRec == NULL) {
  1178. ErrorTrace((LPARAM) this, "Failing ConnectToNextIpAddress becuase m_pDnsRec is NULL");
  1179. TraceFunctLeaveEx((LPARAM) this);
  1180. return FALSE;
  1181. }
  1182. if (m_pDnsRec->StartRecord > m_pDnsRec->NumRecords) {
  1183. ErrorTrace((LPARAM) this, "Failing ConnectToNextIpAddress because StartRecord > NumRecords");
  1184. TraceFunctLeaveEx((LPARAM) NULL);
  1185. return FALSE;
  1186. }
  1187. if (m_pDnsRec->StartRecord == m_pDnsRec->NumRecords) {
  1188. if (IsListEmpty(&m_pDnsRec->DnsArray[m_pDnsRec->NumRecords - 1]->IpListHead)) {
  1189. ErrorTrace((LPARAM) this, "Failing ConnectToNextIpAddress because list is empty");
  1190. TraceFunctLeaveEx((LPARAM) NULL);
  1191. return FALSE;
  1192. }
  1193. m_pDnsRec->StartRecord = m_pDnsRec->NumRecords - 1;
  1194. } else if (IsListEmpty(&m_pDnsRec->DnsArray[m_pDnsRec->StartRecord]->IpListHead)) {
  1195. m_pDnsRec->StartRecord++;
  1196. if (m_pDnsRec->StartRecord > m_pDnsRec->NumRecords) {
  1197. TraceFunctLeaveEx((LPARAM) NULL);
  1198. return FALSE;
  1199. }
  1200. if (m_pDnsRec->StartRecord == m_pDnsRec->NumRecords) {
  1201. if (IsListEmpty(&m_pDnsRec->DnsArray[m_pDnsRec->NumRecords - 1]->IpListHead)) {
  1202. TraceFunctLeaveEx((LPARAM) NULL);
  1203. return FALSE;
  1204. }
  1205. m_pDnsRec->StartRecord = m_pDnsRec->NumRecords - 1;
  1206. }
  1207. }
  1208. TraceFunctLeaveEx((LPARAM) this);
  1209. return TRUE;
  1210. }
  1211. BOOL SMTP_CONNOUT::ReStartSession(void)
  1212. {
  1213. BOOL fRet = TRUE;
  1214. HRESULT hr = S_OK;
  1215. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::ReStartSession( void)");
  1216. DebugTrace((LPARAM) this,"restart session called because of new connection");
  1217. m_cbReceived = 0;
  1218. m_cbParsable = 0;
  1219. m_OutputBufferSize = 0;
  1220. m_Error = NO_ERROR;
  1221. m_NumRcptSent = 0;
  1222. m_NumRcptSentSaved = 0;
  1223. m_NumFailedAddrs = 0;
  1224. m_SendAgain = FALSE;
  1225. m_HaveDataResponse = FALSE;
  1226. m_FirstPipelinedAddress = 0;
  1227. m_FirstRcpt = FALSE;
  1228. m_TransmitTailBuffer[0] = '.';
  1229. m_TransmitTailBuffer[1] = '\r';
  1230. m_TransmitTailBuffer[2] = '\n';
  1231. m_TransmitBuffers.Head = NULL;
  1232. m_TransmitBuffers.HeadLength = 0;
  1233. m_TransmitBuffers.Tail = m_TransmitTailBuffer;
  1234. m_TransmitBuffers.TailLength = 3;
  1235. SetNextState (&SMTP_CONNOUT::WaitForConnectResponse);
  1236. m_pIMsg = (IMailMsgProperties *) m_pDnsRec->pMailMsgObj;
  1237. m_AdvContext = m_pDnsRec->pAdvQContext;
  1238. m_RcptIndexList = (DWORD *) m_pDnsRec->pRcptIdxList;
  1239. m_NumRcpts = m_pDnsRec->dwNumRcpts;
  1240. m_pDnsRec->pMailMsgObj = NULL;
  1241. m_pDnsRec->pAdvQContext = NULL;
  1242. m_pDnsRec->pRcptIdxList = NULL;
  1243. m_pDnsRec->dwNumRcpts = 0;
  1244. // Bump both the remote and total recipient counters
  1245. ADD_COUNTER (QuerySmtpInstance(), NumRcptsRecvdRemote, m_NumRcpts);
  1246. ADD_COUNTER (QuerySmtpInstance(), NumRcptsRecvd, m_NumRcpts);
  1247. hr = m_pIMsg->QueryInterface(IID_IMailMsgRecipients, (void **) &m_pIMsgRecips);
  1248. if (FAILED(hr)) {
  1249. TraceFunctLeaveEx((LPARAM) this);
  1250. return FALSE;
  1251. }
  1252. hr = m_pIMsg->QueryInterface(IID_IMailMsgBind, (void **)&m_pBindInterface);
  1253. if (FAILED(hr)) {
  1254. TraceFunctLeaveEx((LPARAM)this);
  1255. return FALSE;
  1256. }
  1257. hr = m_pBindInterface->GetBinding(&m_IMsgFileHandle, NULL);
  1258. if(FAILED(hr))
  1259. {
  1260. TraceFunctLeaveEx((LPARAM)this);
  1261. return FALSE;
  1262. }
  1263. DWORD fFoundEmbeddedCrlfDot = FALSE;
  1264. DWORD fScanned = FALSE;
  1265. m_LastClientIo = SMTP_CONNOUT::READIO;
  1266. IncPendingIoCount();
  1267. fRet = ReadFile(QueryMRcvBuffer(), m_cbMaxRecvBuffer);
  1268. if (!fRet) {
  1269. m_Error = GetLastError();
  1270. DebugTrace((LPARAM) this, "SMTP_CONNOUT::StartSession - ReadFile failed with error %d", m_Error);
  1271. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1272. DisconnectClient();
  1273. DecPendingIoCount();
  1274. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1275. SetLastError(m_Error);
  1276. }
  1277. TraceFunctLeaveEx((LPARAM)this);
  1278. return fRet;
  1279. }
  1280. /*++
  1281. Name :
  1282. SMTP_CONNOUT::CreateSmtpConnection
  1283. Description:
  1284. This is the static member function than is the only
  1285. entity that is allowed to create an SMTP_CONNOUT
  1286. class. This class cannot be allocated on the stack.
  1287. Arguments:
  1288. sClient socket for communicating with client
  1289. psockAddrRemote pointer to address of the remote client
  1290. ( the value should be copied).
  1291. psockAddrLocal pointer to address for the local card through
  1292. which the client came in.
  1293. pAtqContext pointer to ATQ Context used for AcceptEx'ed conn.
  1294. pvInitialRequest pointer to void buffer containing the initial request
  1295. cbInitialData count of bytes of data read initially.
  1296. fUseSSL Indiates whether the connection is to use SSL
  1297. Returns:
  1298. A pointer to an SMTP_CONNOUT class or NULL
  1299. --*/
  1300. SMTP_CONNOUT * SMTP_CONNOUT::CreateSmtpConnection (
  1301. IN PSMTP_SERVER_INSTANCE pInstance,
  1302. IN SOCKET sClient,
  1303. IN const SOCKADDR_IN * psockAddrRemote,
  1304. IN const SOCKADDR_IN * psockAddrLocal /* = NULL */ ,
  1305. IN PATQ_CONTEXT pAtqContext /* = NULL */ ,
  1306. IN PVOID pTurnList/* = NULL*/ ,
  1307. IN DWORD cbInitialData /* = 0 */,
  1308. IN DWORD Options /* = 0 */,
  1309. IN LPSTR pszSSLVerificationName,
  1310. IN DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD)
  1311. {
  1312. SMTP_CONNOUT * pSmtpObj;
  1313. TraceFunctEnter("SMTP_CONNOUT::CreateSmtpConnection");
  1314. pSmtpObj = new SMTP_CONNOUT (pInstance, sClient, psockAddrRemote, psockAddrLocal, pAtqContext,
  1315. pTurnList, cbInitialData);
  1316. if (pSmtpObj == NULL) {
  1317. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  1318. FatalTrace(NULL, "new SMTP_CONNOUT failed (err=%d)", GetLastError());
  1319. TraceFunctLeave();
  1320. return NULL;
  1321. }
  1322. if (!pSmtpObj->InitializeObject(Options, pszSSLVerificationName, pDNS_RESOLVER_RECORD)) {
  1323. ErrorTrace(NULL, "InitializeObject failed (err=%d)", GetLastError());
  1324. delete pSmtpObj;
  1325. TraceFunctLeave();
  1326. return NULL;
  1327. }
  1328. if (pTurnList) {
  1329. //Set the TURN domainlist
  1330. pSmtpObj->SetTurnList((PTURN_DOMAIN_LIST)pTurnList);
  1331. }
  1332. TraceFunctLeave();
  1333. return pSmtpObj;
  1334. }
  1335. /*++
  1336. Name :
  1337. SMTP_CONNOUT::SendSmtpResponse
  1338. Description:
  1339. This function sends data that was queued in the internal
  1340. m_pOutputBuffer buffer
  1341. Arguments:
  1342. SyncSend - Flag that signifies sync or async send
  1343. Returns:
  1344. TRUE is the string was sent. False otherwise
  1345. --*/
  1346. BOOL SMTP_CONNOUT::SendSmtpResponse(BOOL SyncSend)
  1347. {
  1348. BOOL RetStatus = TRUE;
  1349. DWORD cbMessage = m_OutputBufferSize;
  1350. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::SendSmtpResponse");
  1351. //if m_OutputBufferSize > 0that means there is
  1352. //something in the buffer, therefore, we will send it.
  1353. if (m_OutputBufferSize) {
  1354. //
  1355. // If we are using SSL, encrypt the output buffer now. Note that
  1356. // FormatSmtpMsg already left header space for the seal header.
  1357. //
  1358. if (m_SecurePort) {
  1359. char *Buffer = &m_pOutputBuffer[m_encryptCtx.GetSealHeaderSize()];
  1360. RetStatus = m_encryptCtx.SealMessage(
  1361. (UCHAR *) Buffer,
  1362. m_OutputBufferSize,
  1363. (UCHAR *) m_pOutputBuffer,
  1364. &cbMessage);
  1365. if (!RetStatus)
  1366. {
  1367. ErrorTrace ((LPARAM)this, "Sealmessage failed");
  1368. SetLastError(AQUEUE_E_SSL_ERROR);
  1369. }
  1370. }
  1371. if (RetStatus) {
  1372. RetStatus = CLIENT_CONNECTION::WriteFile(m_pOutputBuffer, cbMessage);
  1373. }
  1374. if (RetStatus) {
  1375. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesSentTotal, m_OutputBufferSize);
  1376. } else {
  1377. DebugTrace((LPARAM) this, "WriteFile failed with error %d", GetLastError());
  1378. }
  1379. m_OutputBufferSize = 0;
  1380. }
  1381. TraceFunctLeaveEx((LPARAM) this);
  1382. return ( RetStatus );
  1383. }
  1384. /*++
  1385. Name :
  1386. SMTP_CONNOUT::FormatSmtpMessage( IN const char * Format, ...)
  1387. Description:
  1388. This function operates likes sprintf, printf, etc. It
  1389. just places it's data in the output buffer.
  1390. Arguments:
  1391. Format - Data to place in the buffer
  1392. Returns:
  1393. --*/
  1394. BOOL SMTP_CONNOUT::FormatSmtpMessage( FORMAT_SMTP_MESSAGE_LOGLEVEL eLogLevel, IN const char * Format, ...)
  1395. {
  1396. int BytesWritten;
  1397. va_list arglist;
  1398. char *Buffer;
  1399. DWORD AvailableBytes;
  1400. DWORD HeaderOffset = (m_SecurePort ? m_encryptCtx.GetSealHeaderSize() : 0);
  1401. DWORD SealOverhead = (m_SecurePort ?
  1402. (m_encryptCtx.GetSealHeaderSize() +
  1403. m_encryptCtx.GetSealTrailerSize()) : 0);
  1404. Buffer = &m_pOutputBuffer[m_OutputBufferSize + HeaderOffset];
  1405. AvailableBytes = m_cbMaxOutputBuffer - m_OutputBufferSize - SealOverhead;
  1406. //if BytesWritten is < 0, that means there is no space
  1407. //left in the buffer. Therefore, we flush any pending
  1408. //responses to make space. Then we try to place the
  1409. //information in the buffer again. It should never
  1410. //fail this time.
  1411. va_start (arglist, Format);
  1412. BytesWritten = _vsnprintf (Buffer, AvailableBytes, Format, arglist);
  1413. if (BytesWritten < 0) {
  1414. //flush any pending response
  1415. SendSmtpResponse();
  1416. _ASSERT (m_OutputBufferSize == 0);
  1417. Buffer = &m_pOutputBuffer[HeaderOffset];
  1418. AvailableBytes = m_cbMaxOutputBuffer - SealOverhead;
  1419. BytesWritten = _vsnprintf (Buffer, AvailableBytes, Format, arglist);
  1420. _ASSERT (BytesWritten > 0);
  1421. }
  1422. va_end(arglist);
  1423. // log this transaction
  1424. ProtocolLogCommand(Buffer, BytesWritten, QueryClientHostName(), eLogLevel);
  1425. m_OutputBufferSize += (DWORD) BytesWritten;
  1426. //m_OutputBufferSize += vsprintf (&m_OutputBuffer[m_OutputBufferSize], Format, arglist);
  1427. return TRUE;
  1428. }
  1429. /*++
  1430. Name :
  1431. SMTP_CONNOUT::FormatBinaryBlob(IN PBYTE pbBlob, IN DWORD cbSize)
  1432. Description:
  1433. Places pbBlob of size cbSize into buffer
  1434. Arguments:
  1435. pbBlob - blob to place in the buffer
  1436. cbSize - blob size
  1437. Returns:
  1438. BOOL
  1439. --*/
  1440. BOOL SMTP_CONNOUT::FormatBinaryBlob( IN PBYTE pbBlob, IN DWORD cbSize)
  1441. {
  1442. char *Buffer;
  1443. DWORD AvailableBytes;
  1444. TraceQuietEnter( "SMTP_CONNOUT::FormatBinaryBlob");
  1445. DWORD HeaderOffset = ( m_SecurePort ? m_encryptCtx.GetSealHeaderSize() : 0);
  1446. DWORD SealOverhead = ( m_SecurePort ?
  1447. ( m_encryptCtx.GetSealHeaderSize() +
  1448. m_encryptCtx.GetSealTrailerSize()) : 0);
  1449. Buffer = &m_pOutputBuffer[ m_OutputBufferSize + HeaderOffset];
  1450. AvailableBytes = m_cbMaxOutputBuffer - m_OutputBufferSize - SealOverhead;
  1451. while ( AvailableBytes < cbSize) {
  1452. memcpy( Buffer, pbBlob, AvailableBytes);
  1453. pbBlob += AvailableBytes;
  1454. cbSize -= AvailableBytes;
  1455. m_OutputBufferSize += AvailableBytes;
  1456. SendSmtpResponse();
  1457. _ASSERT ( m_OutputBufferSize == 0);
  1458. Buffer = &m_pOutputBuffer[ HeaderOffset];
  1459. AvailableBytes = m_cbMaxOutputBuffer - SealOverhead;
  1460. }
  1461. memcpy( Buffer, pbBlob, cbSize);
  1462. m_OutputBufferSize += cbSize;
  1463. return TRUE;
  1464. }
  1465. /*++
  1466. Name :
  1467. SMTP_CONNOUT::ProcessWriteIO
  1468. Description:
  1469. Handles an async write completion event.
  1470. Arguments:
  1471. InputBufferLen - Number of bytes that was written
  1472. dwCompletionStatus -Holds error code from ATQ, if any
  1473. lpo - Pointer to overlapped structure
  1474. Returns:
  1475. TRUE if the object should continue to survive
  1476. FALSE if the object should be deleted
  1477. --*/
  1478. BOOL SMTP_CONNOUT::ProcessWriteIO ( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  1479. {
  1480. CBuffer* pBuffer;
  1481. TraceQuietEnter("SMTP_CONNOUT::ProcessWriteIO");
  1482. _ASSERT(IsValid());
  1483. _ASSERT(lpo);
  1484. pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer;
  1485. //
  1486. // check for partial completions or errors
  1487. //
  1488. if ( pBuffer->GetSize() != InputBufferLen || dwCompletionStatus != NO_ERROR ) {
  1489. ErrorTrace( (LPARAM)this,
  1490. "WriteFile error: %d, bytes %d, expected: %d, lpo: 0x%08X",
  1491. dwCompletionStatus,
  1492. InputBufferLen,
  1493. pBuffer->GetSize(),
  1494. lpo );
  1495. m_Error = dwCompletionStatus;
  1496. SetDiagnosticInfo(HRESULT_FROM_WIN32(dwCompletionStatus), NULL, NULL);
  1497. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1498. DisconnectClient();
  1499. return ( FALSE );
  1500. } else {
  1501. DebugTrace( (LPARAM)this,
  1502. "WriteFile complete. bytes %d, lpo: 0x%08X",
  1503. InputBufferLen, lpo );
  1504. }
  1505. //
  1506. // free up IO buffer
  1507. //
  1508. delete pBuffer;
  1509. //
  1510. // increment only after write completes
  1511. //
  1512. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesSentMsg, InputBufferLen);
  1513. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesSentTotal, InputBufferLen);
  1514. DebugTrace( (LPARAM)this, "m_bComplete: %s",
  1515. m_bComplete ? "TRUE" : "FALSE" );
  1516. return ( TRUE );
  1517. }
  1518. /*++
  1519. Name :
  1520. SMTP_CONNOUT::ProcessTransmitFileIO
  1521. Description:
  1522. processes the return from TransmitFile.
  1523. Right not it just posts a read.
  1524. Arguments:
  1525. Returns:
  1526. TRUE
  1527. --*/
  1528. BOOL SMTP_CONNOUT::ProcessTransmitFileIO ( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  1529. {
  1530. BOOL fRet = TRUE;
  1531. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::ProcessTransmitFileIO");
  1532. _ASSERT(IsValid());
  1533. //we need to set this outside of the if statement,
  1534. //because we will always have a read pended
  1535. m_LastClientIo = SMTP_CONNOUT::READIO;
  1536. if (dwCompletionStatus != NO_ERROR) {
  1537. m_Error = dwCompletionStatus;
  1538. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1539. DebugTrace((LPARAM) this, "TranmitFile in ProcessTransmitFileIO failed with error %d !!!!", dwCompletionStatus);
  1540. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1541. DisconnectClient();
  1542. TraceFunctLeave();
  1543. return FALSE;
  1544. }
  1545. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesSentMsg, InputBufferLen);
  1546. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesSentTotal, InputBufferLen);
  1547. //pend an IO to pickup the "250 XXXX queued for delivery response"
  1548. IncPendingIoCount();
  1549. fRet = ReadFile(QueryMRcvBuffer(), m_cbMaxRecvBuffer);
  1550. if (!fRet) {
  1551. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1552. m_Error = GetLastError();
  1553. DebugTrace((LPARAM) this, "ReadFile in ProcessTransmitFileIO failed with error %d !!!!", m_Error);
  1554. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1555. DisconnectClient();
  1556. DecPendingIoCount();
  1557. }
  1558. TraceFunctLeave();
  1559. return fRet;
  1560. }
  1561. /*++
  1562. Name :
  1563. SMTP_CONNOUT::WaitForConnectResponse
  1564. Description:
  1565. This function gets called when the SMTP
  1566. server sends it's opening response
  1567. Arguments:
  1568. InputLine - Buffer containing opening response
  1569. ParameterSize - Size of opening response
  1570. Returns:
  1571. TRUE if Object is not to be destroyed
  1572. FALSE if Object is to be destroyed
  1573. --*/
  1574. BOOL SMTP_CONNOUT::WaitForConnectResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  1575. {
  1576. char * pszSearch = NULL;
  1577. DWORD IntermediateSize = 0;
  1578. BOOL IsNextLine;
  1579. DWORD Error;
  1580. HRESULT hr = S_OK;
  1581. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::WaitForConnectResponse");
  1582. //get rid of all continuation lines
  1583. while ((pszSearch = IsLineComplete(InputLine, ParameterSize))) {
  1584. *pszSearch = '\0';
  1585. //calculate the length of the line, with the CRLF
  1586. IntermediateSize = (DWORD)((pszSearch - InputLine) + 2);
  1587. //
  1588. // The response is not RFC-compliant. It lacks the 3 digit status - drop
  1589. // connection. We need to do the minimum possible amount of processing of
  1590. // the input from this point on. Many SMTP_CONNOUT functions make assumptions
  1591. // about the well-formedness of the input, and will have problems if an
  1592. // RFC-violating InputLine is passed into them. For example DoCompletedMessage
  1593. // assumes that InputLine[0] is a digit at the very least. The safe thing to
  1594. // do is to disconnect and delete this object.
  1595. //
  1596. if(IntermediateSize < 5 || !isdigit((UCHAR)InputLine[0]) ||
  1597. !isdigit((UCHAR)InputLine[1]) || !isdigit((UCHAR)InputLine[2]))
  1598. {
  1599. ErrorTrace((LPARAM)this, "Received non RFC compliant response %s", InputLine);
  1600. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, NULL, NULL);
  1601. TraceFunctLeaveEx((LPARAM)this);
  1602. return FALSE;
  1603. }
  1604. //check to see if there is a continuation line
  1605. if(IntermediateSize >= 5)
  1606. IsNextLine = (InputLine[3] == '-');
  1607. else
  1608. IsNextLine = FALSE;
  1609. ShrinkBuffer(
  1610. pszSearch + 2,
  1611. ParameterSize - IntermediateSize + UndecryptedTailSize);
  1612. ParameterSize -= IntermediateSize;
  1613. }
  1614. //If ParameterSize is > 0 but pszSearch == NULL, this means
  1615. //there is data in the buffer, but it does not have a CRLF
  1616. //to delimit it, therefore, we need more data to continue.
  1617. //set m_cbReceived equal to where in our input buffer we
  1618. //should pend another read and then return TRUE to pend
  1619. //that read.
  1620. if (ParameterSize != 0) {
  1621. //if we need more data, move the remaining data to the
  1622. //front of the buffer
  1623. MoveMemory(
  1624. (void *)QueryMRcvBuffer(),
  1625. InputLine,
  1626. ParameterSize + UndecryptedTailSize);
  1627. m_cbParsable = ParameterSize;
  1628. m_cbReceived = ParameterSize + UndecryptedTailSize;
  1629. return (TRUE);
  1630. } else if (IsNextLine) {
  1631. m_cbParsable = 0;
  1632. m_cbReceived = UndecryptedTailSize;
  1633. return (TRUE);
  1634. } else {
  1635. BOOL fResult = TRUE;
  1636. //we got a line that is not a continuation line.
  1637. // Process the connect response
  1638. switch (InputLine [0]) {
  1639. case SMTP_COMPLETE_FAILURE:
  1640. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, NULL, NULL);
  1641. DisconnectClient();
  1642. InputLine [3] = '\0';
  1643. Error = atoi (InputLine);
  1644. InputLine [3] = ' ';
  1645. SaveToErrorFile(InputLine, ParameterSize);
  1646. SaveToErrorFile("\r\n", 2);
  1647. fResult = FALSE;
  1648. break;
  1649. case SMTP_COMPLETE_SUCCESS: {
  1650. //If the domain has been specified to use only HELO then we
  1651. //fake it such that EHLO has been already sentand failed
  1652. if (m_MsgOptions & DOMAIN_INFO_USE_HELO )
  1653. m_EhloFailed = TRUE;
  1654. // copy the domain name from the ehlo banner
  1655. strncpy(m_ConnectedDomain, &InputLine[4], sizeof(m_ConnectedDomain)-1);
  1656. // trunc at the first space
  1657. char *pszSpace = strchr(m_ConnectedDomain, ' ');
  1658. if (pszSpace) *pszSpace = 0;
  1659. fResult = DoSessionStartEvent(InputLine, ParameterSize, UndecryptedTailSize);
  1660. break;
  1661. }
  1662. default:
  1663. ErrorTrace( (LPARAM) this,
  1664. "SMTP_CONNOUT::WaitForConnectResponse executing quit command err = %c%c%c",
  1665. InputLine [0],
  1666. InputLine [1],
  1667. InputLine [2]);
  1668. //
  1669. // Set the error code for DoCompletedMessage to use. We want DoCompletedMessage
  1670. // to issue the QUIT command, so this must be SMTP_SERVICE_UNAVAILABLE_CODE which
  1671. // is a fatal error. Specifically, we do *NOT* want DoCompletedMessage to issue RSET
  1672. // and try to call StartSession again for errors in the connect response.
  1673. //
  1674. m_ResponseContext.m_dwSmtpStatus = SMTP_SERVICE_UNAVAILABLE_CODE;
  1675. hr = m_ResponseContext.m_cabResponse.Append(
  1676. (PCHAR)SMTP_SRV_UNAVAIL_STR,
  1677. strlen(SMTP_SRV_UNAVAIL_STR),
  1678. NULL);
  1679. if (FAILED(hr))
  1680. {
  1681. //
  1682. // This is probably an out of memory error, just kill the session.
  1683. // We cannot call DoCompletedMessage which does extra stuff like
  1684. // failover the connection to an alternate host since DoCompletedMessage
  1685. // relies on the m_cabResponse being set.
  1686. //
  1687. ErrorTrace((LPARAM) this,
  1688. "Unable to append Input line to Response Context", hr);
  1689. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, NULL, NULL);
  1690. return FALSE;
  1691. }
  1692. fResult = DoCompletedMessage(InputLine, ParameterSize, UndecryptedTailSize);
  1693. break;
  1694. }
  1695. TraceFunctLeaveEx((LPARAM)this);
  1696. return fResult;
  1697. }
  1698. }
  1699. /*++
  1700. Name :
  1701. SMTP_CONNOUT::ProcessReadIO
  1702. Description:
  1703. This function gets a buffer from ATQ, parses the buffer to
  1704. find out what command the client sent, then executes that
  1705. command.
  1706. Arguments:
  1707. InputBufferLen - Number of bytes that was written
  1708. dwCompletionStatus -Holds error code from ATQ, if any
  1709. lpo - Pointer to overlapped structure
  1710. Returns:
  1711. TRUE if the connection should stay open.
  1712. FALSE if this object should be deleted.
  1713. --*/
  1714. BOOL SMTP_CONNOUT::ProcessReadIO ( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  1715. {
  1716. BOOL fReturn = TRUE;
  1717. char * InputLine = NULL;
  1718. char * pszSearch = NULL;
  1719. DWORD IntermediateSize = 0;
  1720. DWORD UndecryptedTailSize = 0;
  1721. PMFI PreviousState = NULL;
  1722. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::ProcessReadIO");
  1723. //make sure the next state is not NULL
  1724. _ASSERT(m_NextState != NULL || m_fNegotiatingSSL);
  1725. _ASSERT(IsValid());
  1726. //get a pointer to our buffer
  1727. InputLine = (char *) QueryMRcvBuffer();
  1728. //add up the number of bytes we received thus far
  1729. m_cbReceived += InputBufferLen;
  1730. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesRcvdTotal, InputBufferLen);
  1731. //if we are in the middle of negotiating a SSL session, handle it now.
  1732. if (m_fNegotiatingSSL) {
  1733. fReturn = DoSSLNegotiation(QueryMRcvBuffer(),InputBufferLen, 0);
  1734. if (!fReturn) {
  1735. m_Error = GetLastError();
  1736. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1737. DisconnectClient();
  1738. }
  1739. return ( fReturn );
  1740. }
  1741. //if we are using the secure port, decrypt the input buffer now.
  1742. if (m_SecurePort) {
  1743. fReturn = DecryptInputBuffer();
  1744. if (!fReturn) {
  1745. m_Error = GetLastError();
  1746. SetDiagnosticInfo(AQUEUE_E_SSL_ERROR, NULL, NULL);
  1747. return ( fReturn );
  1748. }
  1749. } else {
  1750. m_cbParsable = m_cbReceived;
  1751. }
  1752. //we only process lines that have CRLFs at the end, so if
  1753. //we don't find the CRLF pair, pend another read. Note
  1754. //that we start at the end of the buffer looking for the
  1755. //CRLF pair. We just need to know if atleast one line
  1756. //has a CRLF to continue.
  1757. if ((pszSearch = IsLineCompleteBW(InputLine,m_cbParsable, m_cbMaxRecvBuffer)) == NULL) {
  1758. if (m_cbReceived >= m_cbMaxRecvBuffer)
  1759. m_cbReceived = 0;
  1760. IncPendingIoCount();
  1761. fReturn = ReadFile(QueryMRcvBuffer() + m_cbReceived, m_cbMaxRecvBuffer - m_cbReceived);
  1762. if (!fReturn) {
  1763. m_Error = GetLastError();
  1764. DebugTrace((LPARAM) this, "SMTP_CONNOUT::ProcessReadIO - ReadFile # 1 failed with error %d", m_Error);
  1765. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1766. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1767. DisconnectClient();
  1768. DecPendingIoCount();
  1769. }
  1770. TraceFunctLeaveEx((LPARAM)this);
  1771. return fReturn;
  1772. }
  1773. //save the number of bytes that we received,
  1774. //so we can pass it to the function call below.
  1775. //we set m_cbReceived = 0, because other functions
  1776. //will set it the offset in the buffer where we
  1777. //should pend the next read.
  1778. IntermediateSize = m_cbParsable;
  1779. UndecryptedTailSize = m_cbReceived - m_cbParsable;
  1780. m_cbParsable = 0;
  1781. m_cbReceived = 0;
  1782. //save the previous state
  1783. PreviousState = m_NextState;
  1784. ProtocolLogResponse(InputLine, IntermediateSize, QueryClientHostName());
  1785. //execute the next state
  1786. fReturn = (this->*m_NextState)(InputLine, IntermediateSize, UndecryptedTailSize);
  1787. if (fReturn) {
  1788. //if we are in STARTTLS state, don't pend a read since
  1789. //DoSSLNegotiation would have pended a read
  1790. if ((m_NextState == &SMTP_CONNOUT::DoSTARTTLSCommand) &&
  1791. (m_TlsState == SSL_NEG)) {
  1792. DebugTrace((LPARAM) this, "SMTP_CONNOUT::ProcessReadIO - leaving because we are in middle of SSL negotiation");
  1793. TraceFunctLeaveEx((LPARAM)this);
  1794. return fReturn;
  1795. }
  1796. //do't pend a read if the previous state was
  1797. //DoDataCommandEx. We want either the transmitfile
  1798. //or the read to fail. Not both. Later, we will fix
  1799. //it right for both to complete concurrently.
  1800. if (PreviousState == &SMTP_CONNOUT::DoDATACommandEx) {
  1801. DebugTrace((LPARAM) this, "SMTP_CONNOUT::ProcessReadIO - leaving because we did a transmitfile");
  1802. TraceFunctLeaveEx((LPARAM)this);
  1803. return fReturn;
  1804. } else if (m_fUseBDAT) {
  1805. //We also don't want to pend a read if we did BDAT processing
  1806. //It is a special case, because of the fact that DoBDATCommand synchronously
  1807. //calls DoDATACommandEx without pending a read. So we never come thru here and get
  1808. //chance to set PreviousState = DoDATACommandEx
  1809. //So I have to hack it
  1810. if ((m_NextState == &SMTP_CONNOUT::DoContentResponse) ||
  1811. (m_SendAgain && (m_NextState == &SMTP_CONNOUT::DoMessageStartEvent))) {
  1812. DebugTrace((LPARAM) this, "SMTP_CONNOUT::ProcessReadIO - leaving because we did a transmitfile");
  1813. TraceFunctLeaveEx((LPARAM)this);
  1814. return fReturn;
  1815. }
  1816. }
  1817. if (m_cbReceived >= m_cbMaxRecvBuffer)
  1818. m_cbReceived = 0;
  1819. IncPendingIoCount();
  1820. fReturn = ReadFile(QueryMRcvBuffer() + m_cbReceived, m_cbMaxRecvBuffer - m_cbReceived);
  1821. if (!fReturn) {
  1822. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1823. m_Error = GetLastError();
  1824. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1825. DisconnectClient();
  1826. DecPendingIoCount();
  1827. DebugTrace((LPARAM) this, "SMTP_CONNOUT::ProcessReadIO - ReadFile # 2 failed with error %d", m_Error);
  1828. }
  1829. }
  1830. TraceFunctLeaveEx((LPARAM)this);
  1831. return fReturn;
  1832. }
  1833. /*++
  1834. Name :
  1835. SMTP_CONNOUT::ProcessFileIO
  1836. Description:
  1837. Handles completion of an async read issued against a message file by
  1838. MessageReadFile
  1839. Arguments:
  1840. cbRead count of bytes read
  1841. dwCompletionStatus Error code for IO operation
  1842. lpo Overlapped structure
  1843. Returns:
  1844. TRUE if connection should stay open
  1845. FALSE if this object should be deleted
  1846. --*/
  1847. BOOL SMTP_CONNOUT::ProcessFileIO(
  1848. IN DWORD BytesRead,
  1849. IN DWORD dwCompletionStatus,
  1850. IN OUT OVERLAPPED *lpo
  1851. )
  1852. {
  1853. CBuffer* pBuffer;
  1854. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNOUT::ProcessFileIO");
  1855. _ASSERT(lpo);
  1856. pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer;
  1857. //
  1858. // check for partial completions or errors
  1859. //
  1860. if ( BytesRead != pBuffer->GetSize() || dwCompletionStatus != NO_ERROR ) {
  1861. ErrorTrace( (LPARAM)this,
  1862. "Message ReadFile error: %d, bytes %d, expected %d",
  1863. dwCompletionStatus,
  1864. BytesRead,
  1865. pBuffer->GetSize() );
  1866. m_Error = dwCompletionStatus;
  1867. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1868. DisconnectClient();
  1869. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_dwConnectionStatus), NULL, NULL);
  1870. return ( FALSE );
  1871. } else {
  1872. DebugTrace( (LPARAM)this,
  1873. "ReadFile complete. bytes %d, lpo: 0x%08X",
  1874. BytesRead, lpo );
  1875. }
  1876. m_bComplete = (m_dwFileOffset + BytesRead) >= m_FileSize;
  1877. m_dwFileOffset += BytesRead;
  1878. //
  1879. // If anything to write to the client
  1880. //
  1881. if ( BytesRead > 0 ) {
  1882. //
  1883. // set buffer specific IO state
  1884. //
  1885. pBuffer->SetIoState( CLIENT_WRITE );
  1886. pBuffer->SetSize( BytesRead );
  1887. ZeroMemory( (void*)&pBuffer->m_Overlapped.SeoOverlapped, sizeof(OVERLAPPED) );
  1888. DebugTrace( (LPARAM)this, "WriteFile 0x%08X, len: %d, LPO: 0x%08X",
  1889. pBuffer->GetData(),
  1890. BytesRead,
  1891. &pBuffer->m_Overlapped.SeoOverlapped.Overlapped );
  1892. //
  1893. // If we're on the SSL port, encrypt the data.
  1894. //
  1895. if ( m_SecurePort ) {
  1896. //
  1897. // Seal the message in place as we've already reserved room
  1898. // for both the header and trailer
  1899. //
  1900. if ( m_encryptCtx.SealMessage( pBuffer->GetData() +
  1901. m_encryptCtx.GetSealHeaderSize(),
  1902. BytesRead,
  1903. pBuffer->GetData(),
  1904. &BytesRead ) == FALSE ) {
  1905. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1906. m_Error = GetLastError();
  1907. ErrorTrace( (LPARAM)this, "SealMessage failed. err: %d", m_Error);
  1908. SetDiagnosticInfo(AQUEUE_E_SSL_ERROR, NULL, NULL);
  1909. DisconnectClient();
  1910. } else {
  1911. //
  1912. // adjust the byte count to include header and trailer
  1913. //
  1914. _ASSERT(BytesRead == pBuffer->GetSize() +
  1915. m_encryptCtx.GetSealHeaderSize() +
  1916. m_encryptCtx.GetSealTrailerSize() );
  1917. pBuffer->SetSize( BytesRead );
  1918. }
  1919. }
  1920. IncPendingIoCount();
  1921. if ( WriteFile( pBuffer->GetData(),
  1922. BytesRead,
  1923. (LPOVERLAPPED)&pBuffer->m_Overlapped ) == FALSE ) {
  1924. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1925. m_Error = GetLastError();
  1926. //
  1927. // reset the bytes read so we drop out
  1928. //
  1929. BytesRead = 0;
  1930. ErrorTrace( (LPARAM)this, "WriteFile failed (err=%d)", m_Error );
  1931. delete pBuffer;
  1932. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1933. //
  1934. // treat as fatal error
  1935. //
  1936. DisconnectClient( );
  1937. //
  1938. // cleanup after write failure
  1939. //
  1940. DecPendingIoCount();
  1941. }
  1942. }
  1943. if ( m_bComplete ) {
  1944. BOOL fRet = TRUE;
  1945. //FreeAtqFileContext();
  1946. //We do not have any trailers to write if we are processing BDAT
  1947. //
  1948. if (!m_fUseBDAT) {
  1949. if (m_SecurePort) {
  1950. //Nimishk : Is this right ***
  1951. m_OutputBufferSize = m_cbMaxOutputBuffer;
  1952. fRet = m_encryptCtx.SealMessage(
  1953. (LPBYTE) m_TransmitBuffers.Tail,
  1954. m_TransmitBuffers.TailLength,
  1955. (LPBYTE) m_pOutputBuffer,
  1956. &m_OutputBufferSize);
  1957. if (fRet)
  1958. fRet = WriteFile(m_pOutputBuffer, m_OutputBufferSize);
  1959. } else {
  1960. fRet = WriteFile(
  1961. (LPVOID)m_TransmitBuffers.Tail,
  1962. m_TransmitBuffers.TailLength );
  1963. }
  1964. }
  1965. m_OutputBufferSize = 0;
  1966. IncPendingIoCount();
  1967. if (fRet) {
  1968. //pend an IO to pickup the "250 XXXX queued for delivery response"
  1969. m_LastClientIo = SMTP_CONNOUT::READIO;
  1970. fRet = ReadFile(QueryMRcvBuffer(), m_cbMaxRecvBuffer);
  1971. }
  1972. if (!fRet) {
  1973. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  1974. m_Error = GetLastError();
  1975. DebugTrace((LPARAM) this, "Error %d sending tail or posting read", m_Error);
  1976. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  1977. DisconnectClient();
  1978. DecPendingIoCount();
  1979. }
  1980. return ( fRet );
  1981. } else {
  1982. return (MessageReadFile());
  1983. }
  1984. }
  1985. /*++
  1986. Name :
  1987. SMTP_CONNOUT::ProcessClient
  1988. Description:
  1989. Main function for this class. Processes the connection based
  1990. on current state of the connection.
  1991. It may invoke or be invoked by ATQ functions.
  1992. Arguments:
  1993. cbWritten count of bytes written
  1994. dwCompletionStatus Error Code for last IO operation
  1995. lpo Overlapped stucture
  1996. Returns:
  1997. TRUE when processing is incomplete.
  1998. FALSE when the connection is completely processed and this
  1999. object may be deleted.
  2000. --*/
  2001. BOOL SMTP_CONNOUT::ProcessClient( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  2002. {
  2003. BOOL RetStatus;
  2004. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::ProcessClient");
  2005. IncThreadCount();
  2006. //if lpo == NULL, then we timed out. Send an appropriate message
  2007. //then close the connection
  2008. if ((lpo == NULL) && (dwCompletionStatus == ERROR_SEM_TIMEOUT)) {
  2009. //
  2010. // fake a pending IO as we'll dec the overall count in the
  2011. // exit processing of this routine needs to happen before
  2012. // DisconnectClient else completing threads could tear us down
  2013. //
  2014. SetDiagnosticInfo(HRESULT_FROM_WIN32(ERROR_SEM_TIMEOUT), NULL, NULL);
  2015. IncPendingIoCount();
  2016. m_Error = dwCompletionStatus;
  2017. DebugTrace((LPARAM) this, "SMTP_CONNOUT::ProcessClient: - Timing out");
  2018. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2019. DisconnectClient();
  2020. } else if ((InputBufferLen == 0) || (dwCompletionStatus != NO_ERROR)) {
  2021. //if InputBufferLen == 0, then the connection was closed.
  2022. if (m_Error == NO_ERROR)
  2023. m_Error = ERROR_BROKEN_PIPE;
  2024. SetDiagnosticInfo(AQUEUE_E_CONNECTION_DROPPED, NULL, NULL);
  2025. //
  2026. // If the lpo points to an IO buffer allocated for SSL IO, delete it
  2027. //
  2028. if (lpo != NULL &&
  2029. lpo != &m_Overlapped &&
  2030. lpo != &QueryAtqContext()->Overlapped) {
  2031. CBuffer* pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer;
  2032. delete pBuffer;
  2033. }
  2034. DebugTrace((LPARAM) this, "SMTP_CONNOUT::ProcessClient: InputBufferLen = %d dwCompletionStatus = %d - Closing connection", InputBufferLen, dwCompletionStatus);
  2035. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2036. DisconnectClient();
  2037. } else if (lpo == &m_Overlapped || lpo == &QueryAtqContext()->Overlapped
  2038. || lpo == (OVERLAPPED *) &m_SeoOverlapped) {
  2039. switch (m_LastClientIo) {
  2040. case SMTP_CONNOUT::READIO:
  2041. RetStatus = ProcessReadIO (InputBufferLen, dwCompletionStatus, lpo);
  2042. break;
  2043. case SMTP_CONNOUT::TRANSFILEIO:
  2044. RetStatus = ProcessTransmitFileIO (InputBufferLen, dwCompletionStatus, lpo);
  2045. break;
  2046. default:
  2047. _ASSERT (FALSE);
  2048. RetStatus = FALSE;
  2049. break;
  2050. }
  2051. } else {
  2052. //
  2053. // This lpo belongs to a CBuffer allocated for message transfers using
  2054. // async reads and writes.
  2055. //
  2056. CBuffer* pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer;
  2057. if (pBuffer->GetIoState() == MESSAGE_READ) {
  2058. RetStatus = ProcessFileIO(InputBufferLen, dwCompletionStatus, lpo);
  2059. } else {
  2060. _ASSERT( pBuffer->GetIoState() == CLIENT_WRITE );
  2061. RetStatus = ProcessWriteIO(InputBufferLen, dwCompletionStatus, lpo);
  2062. }
  2063. }
  2064. DecThreadCount();
  2065. //
  2066. // decrement the overall pending IO count for this session
  2067. // tracing and ASSERTs if we're going down.
  2068. //
  2069. if ( DecPendingIoCount() == 0 ) {
  2070. if (m_dwConnectionStatus == CONNECTION_STATUS_DROPPED) {
  2071. if (!QuerySmtpInstance()->IsShuttingDown())
  2072. ConnectToNextIpAddress();
  2073. }
  2074. DebugTrace( (LPARAM)this,"Num Threads: %d",m_cActiveThreads);
  2075. _ASSERT( m_cActiveThreads == 0 );
  2076. m_Active = FALSE;
  2077. RetStatus = FALSE;
  2078. } else {
  2079. DebugTrace( (LPARAM)this,"SMTP_CONNOUT::ProcessClient Pending IOs: %d",m_cPendingIoCount);
  2080. DebugTrace( (LPARAM)this,"SMTP_CONNOUT::ProcessClient Num Threads: %d",m_cActiveThreads);
  2081. RetStatus = TRUE;
  2082. }
  2083. TraceFunctLeaveEx((LPARAM)this);
  2084. return RetStatus;
  2085. }
  2086. /*++
  2087. Name :
  2088. SMTP_CONNOUT::DoSSLNegotiation
  2089. Description:
  2090. Does the SSL Handshake with a remote SMTP server.
  2091. Arguments:
  2092. Returns:
  2093. TRUE if successful so far
  2094. FALSE if this object should be deleted
  2095. --*/
  2096. BOOL SMTP_CONNOUT::DoSSLNegotiation(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2097. {
  2098. BOOL fRet = TRUE, fMore, fPostRead;
  2099. DWORD dwErr;
  2100. HRESULT hr = S_OK;
  2101. ULONG cbExtra = 0;
  2102. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::DoSSLNegotiations");
  2103. m_OutputBufferSize = m_cbMaxOutputBuffer;
  2104. dwErr = m_encryptCtx.Converse(
  2105. QueryMRcvBuffer(),
  2106. m_cbReceived,
  2107. (LPBYTE) m_pOutputBuffer,
  2108. &m_OutputBufferSize,
  2109. &fMore,
  2110. (LPSTR) QueryLocalHostName(),
  2111. (LPSTR) QueryLocalPortName(),
  2112. (LPVOID) QuerySmtpInstance(),
  2113. QuerySmtpInstance()->QueryInstanceId(),
  2114. &cbExtra
  2115. );
  2116. if (dwErr == NO_ERROR) {
  2117. //
  2118. // reset the receive buffer
  2119. //
  2120. if (cbExtra)
  2121. MoveMemory (QueryMRcvBuffer(), QueryMRcvBuffer() + (m_cbReceived - cbExtra), cbExtra);
  2122. m_cbReceived = cbExtra;
  2123. if (m_OutputBufferSize != 0) {
  2124. // Send the last negotiation blob to the server and start with Encrypting.
  2125. // Reset the output buffer size
  2126. fRet = WriteFile(m_pOutputBuffer, m_OutputBufferSize);
  2127. m_OutputBufferSize = 0;
  2128. }
  2129. if (fMore) {
  2130. fPostRead = TRUE;
  2131. } else {
  2132. m_fNegotiatingSSL = FALSE;
  2133. if (ValidateSSLCertificate ()) {
  2134. m_TlsState = CHANNEL_SECURE;
  2135. //If we negotiated because of STARTTLS, we need to send EHLO and get the
  2136. //options all over again. According to jump_thru_hoops draft
  2137. if (IsOptionSet(STARTTLS_OPTION)) {
  2138. //Do ehlo again - this to get the response
  2139. _ASSERT(m_EhloFailed == FALSE);
  2140. char szInput[25];
  2141. strcpy(szInput,"220 OK");
  2142. fRet = DoSessionStartEvent(szInput, strlen(szInput), 0);
  2143. } else if (!(m_MsgOptions & KNOWN_AUTH_FLAGS)) {
  2144. if (m_MsgOptions & EMPTY_CONNECTION_OPTION) {
  2145. fRet = DoSessionEndEvent(InputLine, ParameterSize, UndecryptedTailSize);
  2146. } else {
  2147. fRet = DoMessageStartEvent(InputLine, ParameterSize, UndecryptedTailSize);
  2148. }
  2149. } else {
  2150. fRet = DoSASLCommand(InputLine, ParameterSize, UndecryptedTailSize);
  2151. }
  2152. fPostRead = fRet;
  2153. } else {
  2154. fPostRead = fRet = FALSE;
  2155. //Fill in the response context buffer so as to generate the right response
  2156. // Get the error code
  2157. m_ResponseContext.m_dwSmtpStatus = SMTP_RESP_BAD_CMD;
  2158. hr = m_ResponseContext.m_cabResponse.Append(
  2159. (char *)SMTP_REMOTE_HOST_REJECTED_SSL,
  2160. strlen(SMTP_REMOTE_HOST_REJECTED_SSL),
  2161. NULL);
  2162. }
  2163. }
  2164. if (fPostRead) {
  2165. //
  2166. // The negotiation is going well so far, but there is still more
  2167. // negotiation to do. So post a read to receive the next leg of
  2168. // the negotiation.
  2169. //
  2170. DebugTrace((LPARAM) this,"StartSession negotiating SSL");
  2171. IncPendingIoCount();
  2172. m_LastClientIo = SMTP_CONNOUT::READIO;
  2173. fRet = ReadFile(QueryMRcvBuffer() + cbExtra, m_cbMaxRecvBuffer - cbExtra);
  2174. if (!fRet) {
  2175. dwErr = GetLastError();
  2176. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSSLNegotiation failed %d", m_Error);
  2177. DecPendingIoCount();
  2178. SetLastError(dwErr);
  2179. SetDiagnosticInfo(HRESULT_FROM_WIN32(dwErr), NULL, NULL);
  2180. //Fill in the response context buffer so as to generate the right response
  2181. // Get the error code
  2182. }
  2183. }
  2184. } else {
  2185. fRet = FALSE;
  2186. SetLastError(ERROR_NO_SECURITY_ON_OBJECT);
  2187. SetDiagnosticInfo(AQUEUE_E_TLS_NOT_SUPPORTED_ERROR, NULL, NULL);
  2188. }
  2189. TraceFunctLeaveEx((LPARAM) this);
  2190. return ( fRet );
  2191. }
  2192. //----------------------------------------------------------------------------------
  2193. // Description:
  2194. // Check to see if the SSL cert is valid:
  2195. // (1) Always check if it has expired.
  2196. // (2) If configured, check if the issuer is trusted.
  2197. // (3) If configured, check if the subject matches who we are sending to. This
  2198. // is not simply the server-fqdn we are connected to. Since the server-fqdn
  2199. // may have been obtained via DNS (through MX or CNAME indirection), and
  2200. // since DNS is an insecure protocol, we cannot trust that for the subject.
  2201. // Instead we will use the domain we were passed in prior to DNS... the
  2202. // one passed into the REMOTE_QUEUE. This domain name is passed into
  2203. // SMTP_CONNOUT when it is created as m_pszSSLVerificationDomain. We will
  2204. // do some wildcard matching as well if the certificate subject has the
  2205. // '*' character in it (see simssl.cpp). m_pszSSLVerificationName may be
  2206. // NULL if there is no target hostname --- such as in the case of a literal
  2207. // IP address. In this case, subject verification is skipped.
  2208. // Returns:
  2209. // TRUE - success, certificate verified
  2210. // FALSE - failure, stick queue into retry
  2211. //----------------------------------------------------------------------------------
  2212. BOOL SMTP_CONNOUT::ValidateSSLCertificate ()
  2213. {
  2214. BOOL fRet = FALSE;
  2215. DWORD dwAQDiagnostic = 0;
  2216. TraceFunctEnterEx ((LPARAM) this, "SMTP_CONNECTION::ValidateCertificate");
  2217. fRet = m_encryptCtx.CheckCertificateExpired();
  2218. if (!fRet) {
  2219. ErrorTrace ((LPARAM) this, "SSL Certificate Expired");
  2220. dwAQDiagnostic = AQUEUE_E_SSL_CERT_EXPIRED;
  2221. goto Exit;
  2222. }
  2223. if (m_pInstance->RequiresSSLCertVerifyIssuer()) {
  2224. DebugTrace ((LPARAM) this, "Verifying certificate issuing authority");
  2225. //
  2226. // Failure in these checks could occur due to temporary conditions (like
  2227. // out of memory). So the AQueue diagnostic is not 100% accurate, but it's
  2228. // OK as we don't NDR message. Anyway we DID fail during cert validation.
  2229. //
  2230. fRet = m_encryptCtx.CheckCertificateTrust();
  2231. if (!fRet) {
  2232. ErrorTrace ((LPARAM) this, "SSL Certificate trust verification failure");
  2233. dwAQDiagnostic = AQUEUE_E_SSL_CERT_ISSUER_UNTRUSTED;
  2234. goto Exit;
  2235. }
  2236. }
  2237. if (!m_pszSSLVerificationName) {
  2238. DebugTrace ((LPARAM) this,
  2239. "Skipping certificate subject validation, no name to validate against");
  2240. goto Exit;
  2241. }
  2242. DebugTrace ((LPARAM) this,
  2243. "Validating certificate subject against: %s", m_pszSSLVerificationName);
  2244. fRet = m_encryptCtx.CheckCertificateSubjectName (m_pszSSLVerificationName);
  2245. if (!fRet) {
  2246. ErrorTrace ((LPARAM) this, "SSL certificate subject verification failure");
  2247. dwAQDiagnostic = AQUEUE_E_SSL_CERT_SUBJECT_MISMATCH;
  2248. }
  2249. Exit:
  2250. if (!fRet && dwAQDiagnostic)
  2251. SetDiagnosticInfo(dwAQDiagnostic, NULL, NULL);
  2252. TraceFunctLeaveEx ((LPARAM) this);
  2253. return fRet;
  2254. }
  2255. /*++
  2256. Name :
  2257. void SMTP_CONNECTION::SkipWord
  2258. Description:
  2259. skips over words in a buffer and
  2260. returns pointer to next word
  2261. Arguments:
  2262. none
  2263. Returns:
  2264. TRUE if the receive buffer was successfully decrypted, FALSE otherwise
  2265. --*/
  2266. BOOL SMTP_CONNOUT::DecryptInputBuffer(void)
  2267. {
  2268. TraceFunctEnterEx( (LPARAM)this, "SMTP_CONNOUT::DecryptInputBuffer");
  2269. DWORD cbExpected;
  2270. DWORD cbReceived;
  2271. DWORD cbParsable;
  2272. DWORD dwError;
  2273. dwError = m_encryptCtx.DecryptInputBuffer(
  2274. (LPBYTE) QueryMRcvBuffer() + m_cbParsable,
  2275. m_cbReceived - m_cbParsable,
  2276. &cbReceived,
  2277. &cbParsable,
  2278. &cbExpected );
  2279. if ( dwError == NO_ERROR ) {
  2280. //
  2281. // new total received size is the residual from last processing
  2282. // and whatever is left in the current decrypted read buffer
  2283. //
  2284. m_cbReceived = m_cbParsable + cbReceived;
  2285. //
  2286. // new total parsable size is the residual from last processing
  2287. // and whatever was decrypted from this read io operation
  2288. //
  2289. m_cbParsable += cbParsable;
  2290. } else {
  2291. //
  2292. // errors from this routine indicate that the stream has been
  2293. // tampered with or we have an internal error
  2294. //
  2295. ErrorTrace( (LPARAM)this,
  2296. "DecryptInputBuffer failed: 0x%08X",
  2297. dwError );
  2298. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2299. DisconnectClient( dwError );
  2300. }
  2301. TraceFunctLeaveEx((LPARAM)this);
  2302. return ( dwError == NO_ERROR );
  2303. }
  2304. BOOL SMTP_CONNOUT::DoSASLNegotiation(char *InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2305. {
  2306. BUFFER BuffOut;
  2307. DWORD BytesRet = 0;
  2308. BOOL fMoreData = FALSE;
  2309. BOOL fRet = TRUE;
  2310. BOOL fReturn = TRUE;
  2311. DomainInfo DomainParams;
  2312. LPSTR szClearTextPassword = NULL;
  2313. HRESULT hr = S_OK;
  2314. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoSASLNegotiation");
  2315. //we need to receive a 354 from the server to go on
  2316. if (InputLine[0] == SMTP_INTERMEDIATE_SUCCESS) {
  2317. if (m_AuthToUse & SMTP_AUTH_NTLM || m_AuthToUse & SMTP_AUTH_KERBEROS) {
  2318. if (m_securityCtx.ClientConverse(
  2319. InputLine + 4, ParameterSize - 4, &BuffOut, &BytesRet,
  2320. &fMoreData, &AuthInfoStruct, m_szAuthPackage,
  2321. NULL, NULL, (PIIS_SERVER_INSTANCE) m_pInstance)) {
  2322. if (BytesRet) {
  2323. FormatSmtpMessage(FSM_LOG_NONE, "%s\r\n", BuffOut.QueryPtr());
  2324. SendSmtpResponse();
  2325. }
  2326. } else {
  2327. DebugTrace((LPARAM) this, "m_securityCtx.Converse failed - %d",
  2328. GetLastError());
  2329. m_Error = ERROR_LOGON_FAILURE;
  2330. //Fill in the response context buffer so as to generate the right response
  2331. // Get the error code
  2332. m_ResponseContext.m_dwSmtpStatus = SMTP_RESP_BAD_CMD;
  2333. hr = m_ResponseContext.m_cabResponse.Append(
  2334. (char *)SMTP_REMOTE_HOST_REJECTED_AUTH,
  2335. strlen(SMTP_REMOTE_HOST_REJECTED_AUTH),
  2336. NULL);
  2337. fRet = FALSE;
  2338. SetDiagnosticInfo(AQUEUE_E_SASL_REJECTED, NULL, NULL);
  2339. }
  2340. } else {
  2341. ZeroMemory (&DomainParams, sizeof(DomainParams));
  2342. DomainParams.cbVersion = sizeof(DomainParams);
  2343. hr = m_pISMTPConnection->GetDomainInfo(&DomainParams);
  2344. if (FAILED(hr)) {
  2345. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2346. DisconnectClient();
  2347. SetLastError(m_Error = ERROR_LOGON_FAILURE);
  2348. SetDiagnosticInfo(AQUEUE_E_SASL_REJECTED, NULL, NULL);
  2349. TraceFunctLeaveEx((LPARAM)this);
  2350. return FALSE;
  2351. }
  2352. szClearTextPassword = DomainParams.szPassword;
  2353. if (!szClearTextPassword) szClearTextPassword = "";
  2354. fReturn = uuencode ((unsigned char *)szClearTextPassword,
  2355. lstrlen(szClearTextPassword), &BuffOut, FALSE);
  2356. if (fReturn) {
  2357. FormatSmtpMessage(FSM_LOG_NONE, "%s\r\n", BuffOut.QueryPtr());
  2358. SendSmtpResponse();
  2359. } else {
  2360. m_Error = GetLastError();
  2361. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  2362. fRet = FALSE;
  2363. }
  2364. }
  2365. } else if (InputLine[0] == SMTP_COMPLETE_SUCCESS) {
  2366. if (m_MsgOptions & EMPTY_CONNECTION_OPTION) {
  2367. fRet = DoSessionEndEvent(InputLine, ParameterSize, UndecryptedTailSize);
  2368. } else {
  2369. fRet = DoMessageStartEvent(InputLine, ParameterSize, UndecryptedTailSize);
  2370. }
  2371. } else {
  2372. fRet = FALSE;
  2373. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2374. m_Error = ERROR_LOGON_FAILURE;
  2375. SetDiagnosticInfo(AQUEUE_E_SASL_REJECTED, NULL, NULL);
  2376. DisconnectClient();
  2377. }
  2378. TraceFunctLeaveEx((LPARAM)this);
  2379. return fRet;
  2380. }
  2381. /*++
  2382. Name :
  2383. SMTP_CONNOUT::DoEHLOCommand
  2384. Description:
  2385. Responds to the SMTP EHLO command
  2386. Arguments:
  2387. Are ignored
  2388. Returns:
  2389. TRUE if the connection should stay open.
  2390. FALSE if this object should be deleted.
  2391. --*/
  2392. BOOL SMTP_CONNOUT::DoEHLOCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2393. {
  2394. BOOL fRet = TRUE;
  2395. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoEHLOCommand");
  2396. m_OutboundContext.m_cabNativeCommand.Reset();
  2397. if (m_EhloFailed)
  2398. {
  2399. //server did not understand EHLO command, so
  2400. //just send the HELO command
  2401. m_HeloSent = TRUE;
  2402. fRet = PE_AppendSmtpMessage("HELO ");
  2403. } else {
  2404. fRet = PE_AppendSmtpMessage("EHLO ");
  2405. }
  2406. if (fRet) {
  2407. QuerySmtpInstance()->LockGenCrit();
  2408. fRet = PE_AppendSmtpMessage(QuerySmtpInstance()->GetFQDomainName());
  2409. QuerySmtpInstance()->UnLockGenCrit();
  2410. }
  2411. if (fRet)
  2412. fRet = PE_AppendSmtpMessage("\r\n");
  2413. m_OutboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  2414. m_EhloSent = TRUE;
  2415. TraceFunctLeaveEx((LPARAM)this);
  2416. return (fRet);
  2417. }
  2418. /*++
  2419. Name :
  2420. SMTP_CONNOUT::DoEHLOResponse
  2421. Description:
  2422. Responds to the SMTP EHLO command
  2423. Arguments:
  2424. Are ignored
  2425. Returns:
  2426. TRUE if the connection should stay open.
  2427. FALSE if this object should be deleted.
  2428. --*/
  2429. BOOL SMTP_CONNOUT::DoEHLOResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2430. {
  2431. BOOL fGotAllOptions;
  2432. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoEHLOResponse");
  2433. char chInputGood = 0;
  2434. BOOL fEndSession = FALSE;
  2435. if ( isdigit( (UCHAR)InputLine[1] ) && isdigit( (UCHAR)InputLine[2] ) && ( ( InputLine[3] == ' ' ) || ( InputLine[3] == '-' ) ) ) {
  2436. chInputGood = InputLine[0];
  2437. } else {
  2438. chInputGood = SMTP_COMPLETE_FAILURE;
  2439. fEndSession = TRUE;
  2440. }
  2441. //get the response
  2442. switch ( chInputGood ) {
  2443. case SMTP_COMPLETE_SUCCESS:
  2444. m_ResponseContext.m_dwResponseStatus = EXPE_SUCCESS;
  2445. fGotAllOptions = GetEhloOptions(
  2446. InputLine,
  2447. ParameterSize,
  2448. UndecryptedTailSize,
  2449. m_HeloSent);
  2450. _ASSERT(fGotAllOptions);
  2451. if (IsOptionSet(ETRN_ONLY_OPTION) && !IsOptionSet(ETRN_OPTION)) {
  2452. //We have no messages to send... only ETRN, but ETRN is not
  2453. //supported by the remote server, we should send quit
  2454. m_ResponseContext.m_dwResponseStatus = EXPE_CHANGE_STATE;
  2455. m_ResponseContext.m_dwNextState = PE_STATE_SESSION_END;
  2456. m_OutboundContext.m_pCurrentCommandContext = NULL;
  2457. }
  2458. break;
  2459. case SMTP_COMPLETE_FAILURE:
  2460. //if the HELO was sent and we got a 55X reply,
  2461. //just quit. This server is hosed.
  2462. if ( m_HeloSent || fEndSession ) {
  2463. DebugTrace((LPARAM) this,
  2464. "SMTP_CONNOUT::DoEHLOCommand executing quit command err from client = %c%c%c",
  2465. InputLine [0],InputLine [1],InputLine [2]);
  2466. m_ResponseContext.m_dwResponseStatus = EXPE_CHANGE_STATE;
  2467. m_ResponseContext.m_dwNextState = PE_STATE_SESSION_END;
  2468. m_OutboundContext.m_pCurrentCommandContext = NULL;
  2469. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, m_HeloSent ? "HELO" : "EHLO", InputLine);
  2470. } else {
  2471. //server did not understand EHLO command, so
  2472. //just send the HELO command
  2473. m_EhloFailed = TRUE;
  2474. m_ResponseContext.m_dwResponseStatus = EXPE_REPEAT_COMMAND;
  2475. }
  2476. break;
  2477. default:
  2478. DebugTrace((LPARAM) this,
  2479. "SMTP_CONNOUT::DoEHLOCommand executing quit command err = %c%c%c",
  2480. InputLine [0],InputLine [1],InputLine [2]);
  2481. m_ResponseContext.m_dwResponseStatus = EXPE_CHANGE_STATE;
  2482. m_ResponseContext.m_dwNextState = PE_STATE_SESSION_END;
  2483. m_OutboundContext.m_pCurrentCommandContext = NULL;
  2484. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, m_HeloSent ? "HELO" : "EHLO", InputLine);
  2485. }
  2486. TraceFunctLeaveEx((LPARAM)this);
  2487. return (TRUE);
  2488. }
  2489. /*++
  2490. Name :
  2491. SMTP_CONNOUT::GetEhloOptions(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2492. Description:
  2493. Parses the response from the EHLO command
  2494. Arguments:
  2495. Line containing the response and line size
  2496. Returns:
  2497. TRUE if it parses all data
  2498. FALSE if it needs more data to continue parsing
  2499. --*/
  2500. BOOL SMTP_CONNOUT::GetEhloOptions(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize, BOOL fIsHelo)
  2501. {
  2502. register char * pszValue = NULL;
  2503. register char * pszSearch = NULL;
  2504. DWORD IntermediateSize = 0;
  2505. BOOL IsNextLine = FALSE;
  2506. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::GetEhloOptions");
  2507. //get each line and parse the commands
  2508. while ((pszSearch = IsLineComplete(InputLine, ParameterSize)) != NULL) {
  2509. //Null terminate the end of the option
  2510. *pszSearch = '\0';
  2511. //check to see if there is a continuation line
  2512. IsNextLine = (InputLine[3] == '-');
  2513. IntermediateSize= (DWORD)((pszSearch - InputLine) + 2); //+2 for CRLF
  2514. // Only process if it is an EHLO command, otherwise, just
  2515. // eat up all the response data.
  2516. if (!fIsHelo) {
  2517. //skip over code and space
  2518. //look for a space in the line.
  2519. //options that have paramters
  2520. //need to have a space after
  2521. //their name
  2522. pszValue = strchr(InputLine + 4, ' ');
  2523. //does the server support the SIZE option ???
  2524. if (strncasecmp(InputLine + 4, (char * )"SIZE", 4) == 0) {
  2525. //yes sireee bob. If the server advertised
  2526. //a maximum file size, get it while we are
  2527. //here.
  2528. m_Flags |= SIZE_OPTION;
  2529. if (pszValue != NULL)
  2530. m_SizeOptionSize = atoi (pszValue);
  2531. } else if (strcasecmp(InputLine + 4, (char * )"PIPELINING") == 0) {
  2532. m_Flags |= PIPELINE_OPTION;
  2533. } else if (strcasecmp(InputLine + 4, (char * )"8bitmime") == 0) {
  2534. m_Flags |= EBITMIME_OPTION;
  2535. } else if (strcasecmp(InputLine + 4, (char * )"dsn") == 0) {
  2536. m_Flags |= DSN_OPTION;
  2537. } else if (strncasecmp(InputLine + 4, (char *)"TLS", 3) == 0) {
  2538. m_Flags |= TLS_OPTION;
  2539. } else if (strncasecmp(InputLine + 4, (char *)"STARTTLS", 8) == 0) {
  2540. m_Flags |= STARTTLS_OPTION;
  2541. } else if (strncasecmp(InputLine + 4, (char *)"ETRN", 4) == 0) {
  2542. m_Flags |= ETRN_OPTION;
  2543. } else if (strcasecmp(InputLine + 4, (char *)"CHUNKING") == 0) {
  2544. m_Flags |= CHUNKING_OPTION;
  2545. } else if (strcasecmp(InputLine + 4, (char *)"BINARYMIME") == 0) {
  2546. m_Flags |= BINMIME_OPTION;
  2547. } else if (strcasecmp(InputLine + 4, (char *)"ENHANCEDSTATUSCODES") == 0) {
  2548. m_Flags |= ENHANCEDSTATUSCODE_OPTION;
  2549. } else if (strncasecmp(InputLine + 4, (char *)"AUTH", 4) == 0) {
  2550. pszValue = strchr(InputLine + 4, '=');
  2551. if (pszValue == NULL)
  2552. pszValue = strchr(InputLine + 4, ' ');
  2553. while (pszValue != NULL) {
  2554. if (strncasecmp(pszValue + 1, (char *)"NTLM", 4) == 0) {
  2555. m_Flags |= AUTH_NTLM;
  2556. } else if (strncasecmp(pszValue + 1, (char *)"LOGIN", 5) == 0) {
  2557. m_Flags |= AUTH_CLEARTEXT;
  2558. } else if (strncasecmp(pszValue + 1, (char *)"DPA", 3) == 0) {
  2559. m_Flags |= AUTH_NTLM;
  2560. } else if (strncasecmp(pszValue + 1, (char *)"GSSAPI", 6) == 0) {
  2561. m_Flags |= AUTH_GSSAPI;
  2562. }
  2563. pszValue = strchr(pszValue + 1, ' ');
  2564. }
  2565. }
  2566. }
  2567. InputLine += IntermediateSize;
  2568. ParameterSize -= IntermediateSize;
  2569. }
  2570. // We don't want to bail out for weird responses, but we want to
  2571. // note such occurrences.
  2572. if ((ParameterSize > 1) ||
  2573. (!ParameterSize) ||
  2574. (*InputLine)) {
  2575. ErrorTrace((LPARAM)this, "Weird response tail <%*s>",
  2576. ParameterSize, InputLine);
  2577. _ASSERT(FALSE);
  2578. }
  2579. TraceFunctLeaveEx((LPARAM)this);
  2580. return TRUE;
  2581. }
  2582. BOOL SMTP_CONNOUT::DoSASLCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2583. {
  2584. BOOL fRet = TRUE;
  2585. BOOL fMoreData = TRUE;
  2586. BUFFER BuffOut;
  2587. DWORD BytesRet = 0;
  2588. DomainInfo DomainParams;
  2589. LPSTR szUserName = NULL;
  2590. LPSTR szPassword = NULL;
  2591. HRESULT hr = S_OK;
  2592. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoSASLCommand");
  2593. ZeroMemory (&DomainParams, sizeof(DomainParams));
  2594. if ((m_MsgOptions & DOMAIN_INFO_USE_NTLM ||
  2595. m_MsgOptions & DOMAIN_INFO_USE_KERBEROS)) {
  2596. if (!IsOptionSet(AUTH_NTLM) && !(IsOptionSet(AUTH_GSSAPI))) {
  2597. //
  2598. // We were told to do secure connection, but the remote server doesn't
  2599. // support any form of login authentication! We have to drop this connection now.
  2600. //
  2601. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2602. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSASLCommand: ERROR! Remote server does not support AUTH!");
  2603. DisconnectClient();
  2604. SetLastError(m_Error = ERROR_NO_SUCH_PACKAGE);
  2605. SetDiagnosticInfo(AQUEUE_E_SASL_REJECTED, "AUTH", InputLine);
  2606. TraceFunctLeaveEx((LPARAM)this);
  2607. return ( FALSE );
  2608. }
  2609. }
  2610. if (m_MsgOptions & DOMAIN_INFO_USE_PLAINTEXT) {
  2611. if (!IsOptionSet(AUTH_CLEARTEXT)) {
  2612. //
  2613. // We were told to do secure connection, but the remote server doesn't
  2614. // support any form of login authentication! We have to drop this connection now.
  2615. //
  2616. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSASLCommand: ERROR! Remote server does not support AUTH!");
  2617. DisconnectClient();
  2618. SetLastError(m_Error = ERROR_NO_SUCH_PACKAGE);
  2619. SetDiagnosticInfo(AQUEUE_E_SASL_REJECTED, "AUTH", InputLine);
  2620. TraceFunctLeaveEx((LPARAM)this);
  2621. return ( FALSE );
  2622. }
  2623. }
  2624. //set the next state
  2625. SetNextState (&SMTP_CONNOUT::DoSASLNegotiation);
  2626. ZeroMemory (&DomainParams, sizeof(DomainParams));
  2627. DomainParams.cbVersion = sizeof(DomainParams);
  2628. hr = m_pISMTPConnection->GetDomainInfo(&DomainParams);
  2629. if (FAILED(hr)) {
  2630. DisconnectClient();
  2631. SetLastError(m_Error = ERROR_LOGON_FAILURE);
  2632. SetDiagnosticInfo(AQUEUE_E_SASL_LOGON_FAILURE, "AUTH", InputLine);
  2633. TraceFunctLeaveEx((LPARAM)this);
  2634. return FALSE;
  2635. }
  2636. szUserName = DomainParams.szUserName;
  2637. szPassword = DomainParams.szPassword;
  2638. //If a username is specified, but NULL password, IIS will attempt
  2639. //anonymous logon. Force "" password so the correct user is
  2640. //used.
  2641. if (szUserName && !szPassword)
  2642. szPassword = "";
  2643. if (m_MsgOptions & DOMAIN_INFO_USE_NTLM ||
  2644. m_MsgOptions & DOMAIN_INFO_USE_KERBEROS) {
  2645. m_szAuthPackage[0] = '\0';
  2646. BOOL fReturn = FALSE;
  2647. char szTarget[MAX_INTERNET_NAME + 1];
  2648. if ((m_MsgOptions & DOMAIN_INFO_USE_KERBEROS) && (IsOptionSet(AUTH_GSSAPI))) {
  2649. m_AuthToUse = SMTP_AUTH_KERBEROS;
  2650. strcpy(m_szAuthPackage,"GSSAPI");
  2651. //
  2652. // For Kerberos, we need to set the target server SPN, for which we
  2653. // pickup info from m_pDnsRec
  2654. //
  2655. MX_NAMES *pmx;
  2656. _ASSERT( m_pDnsRec != NULL );
  2657. _ASSERT( m_pDnsRec->StartRecord < m_pDnsRec->NumRecords );
  2658. pmx = m_pDnsRec->DnsArray[m_pDnsRec->StartRecord];
  2659. _ASSERT( pmx != NULL );
  2660. _ASSERT( pmx->DnsName != NULL );
  2661. m_securityCtx.SetTargetPrincipalName(
  2662. SMTP_SERVICE_PRINCIPAL_PREFIX,pmx->DnsName);
  2663. DebugTrace((LPARAM) this, "Setting Target Server SPN to %s",
  2664. pmx->DnsName);
  2665. } else {
  2666. m_AuthToUse = SMTP_AUTH_NTLM;
  2667. strcpy(m_szAuthPackage,"NTLM");
  2668. }
  2669. DebugTrace((LPARAM) this, "Using NTLM/KERBEROS for user %s",
  2670. DomainParams.szUserName);
  2671. fReturn = m_securityCtx.ClientConverse(
  2672. NULL, 0, &BuffOut, &BytesRet,
  2673. &fMoreData, &AuthInfoStruct,
  2674. m_szAuthPackage,
  2675. (char *) szUserName,
  2676. (char *) szPassword,
  2677. (PIIS_SERVER_INSTANCE) m_pInstance);
  2678. if (fReturn) {
  2679. if (BytesRet) {
  2680. FormatSmtpMessage(FSM_LOG_ALL, "AUTH %s %s\r\n", m_szAuthPackage,
  2681. BuffOut.QueryPtr());
  2682. }
  2683. if (!fMoreData && (m_MsgOptions & DOMAIN_INFO_USE_NTLM)) {
  2684. fRet = FALSE;
  2685. }
  2686. } else {
  2687. m_Error = GetLastError();
  2688. DebugTrace((LPARAM) this, "m_securityCtx.Converse for user %s - %d",
  2689. szUserName, m_Error);
  2690. DisconnectClient();
  2691. m_Error = ERROR_LOGON_FAILURE;
  2692. SetDiagnosticInfo(AQUEUE_E_SASL_LOGON_FAILURE, "AUTH", NULL); //Should not pass param Inputline: encrypted
  2693. fRet = FALSE;
  2694. }
  2695. } else {
  2696. BOOL fReturn = FALSE;
  2697. m_AuthToUse = SMTP_AUTH_CLEARTEXT;
  2698. if (!szUserName) szUserName = "";
  2699. if (!szPassword) szPassword = "";
  2700. DebugTrace((LPARAM) this, "Using ClearText for user %s",
  2701. szUserName);
  2702. fReturn = uuencode ((unsigned char *)szUserName,
  2703. lstrlen(szUserName), &BuffOut, FALSE);
  2704. if (fReturn) {
  2705. FormatSmtpMessage(FSM_LOG_VERB_ONLY, "AUTH LOGIN %s\r\n", (char *)
  2706. BuffOut.QueryPtr());
  2707. BytesRet = lstrlen((char *) BuffOut.QueryPtr());
  2708. } else {
  2709. DisconnectClient();
  2710. m_Error = ERROR_LOGON_FAILURE;
  2711. SetDiagnosticInfo(AQUEUE_E_SASL_LOGON_FAILURE, "AUTH", NULL); //Should not pass param Inputline: uuencoded
  2712. fRet = FALSE;
  2713. }
  2714. }
  2715. if (fRet && BytesRet) {
  2716. SendSmtpResponse();
  2717. }
  2718. TraceFunctLeaveEx((LPARAM)this);
  2719. return fRet;
  2720. }
  2721. /*++
  2722. Name :
  2723. SMTP_CONNOUT::DoSTARTTLSCommand
  2724. Description:
  2725. Negotiates use of SSL via the STARTTLS command
  2726. Arguments:
  2727. InputLine -- Indicates response of remote server
  2728. ParameterSize -- Size of Inputline
  2729. UndecryptedTailSize -- The part of the received buffer that could not
  2730. be decrypted.
  2731. Returns:
  2732. TRUE if the connection should stay open.
  2733. FALSE if this object should be deleted.
  2734. --*/
  2735. BOOL SMTP_CONNOUT::DoSTARTTLSCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2736. {
  2737. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoSTARTTLSCommand");
  2738. if (m_TlsState == MUST_DO_TLS) {
  2739. if (IsOptionSet(TLS_OPTION) || IsOptionSet(STARTTLS_OPTION)) {
  2740. FormatSmtpMessage(FSM_LOG_ALL, "STARTTLS\r\n");
  2741. SendSmtpResponse();
  2742. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSTARTTLSCommand: STARTTLS command sent");
  2743. m_TlsState = STARTTLS_SENT;
  2744. TraceFunctLeaveEx((LPARAM)this);
  2745. return ( TRUE );
  2746. } else {
  2747. //
  2748. // We were told to do secure connection, but the remote server doesn't
  2749. // support TLS! We have to drop this connection now.
  2750. //
  2751. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSTARTTLSCommand: ERROR! Remote server does not support TLS!");
  2752. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2753. SetDiagnosticInfo(AQUEUE_E_TLS_NOT_SUPPORTED_ERROR, "STARTTLS", InputLine);
  2754. DisconnectClient();
  2755. SetLastError(m_Error = ERROR_NO_SECURITY_ON_OBJECT);
  2756. TraceFunctLeaveEx((LPARAM)this);
  2757. return ( FALSE );
  2758. }
  2759. } else {
  2760. //
  2761. // We sent the STARTTLS command, and InputLine has the server's response
  2762. // Handle the server's response
  2763. //
  2764. _ASSERT(m_TlsState == STARTTLS_SENT);
  2765. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSTARTTLSCommand: Server response to STARTTLS is \"%s\"", InputLine);
  2766. switch (InputLine[0]) {
  2767. case SMTP_COMPLETE_FAILURE:
  2768. {
  2769. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSTARTTLSCommand: Server returned error");
  2770. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  2771. SetDiagnosticInfo(AQUEUE_E_TLS_ERROR, "STARTTLS", InputLine);
  2772. DisconnectClient();
  2773. SetLastError(m_Error = ERROR_NO_SECURITY_ON_OBJECT);
  2774. TraceFunctLeaveEx((LPARAM)this);
  2775. return ( FALSE );
  2776. }
  2777. break;
  2778. case SMTP_COMPLETE_SUCCESS:
  2779. {
  2780. m_SecurePort = TRUE;
  2781. m_fNegotiatingSSL = TRUE;
  2782. m_TlsState = SSL_NEG;
  2783. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSTARTTLSCommand: Server accepted, beginning SSL handshake");
  2784. //
  2785. // Switch over to using a large receive buffer, because a SSL fragment
  2786. // may be up to 32K big.
  2787. if (!SwitchToBigSSLBuffers()) {
  2788. DebugTrace((LPARAM) this,
  2789. "SMTP_CONNOUT::DoSTARTTLSCommand: Failed to allocate Big SSL buffers %d\n",
  2790. GetLastError());
  2791. DisconnectClient();
  2792. TraceFunctLeaveEx((LPARAM)this);
  2793. return ( FALSE );
  2794. }
  2795. if (!DoSSLNegotiation(NULL, 0, 0)) {
  2796. DebugTrace(
  2797. (LPARAM) this,
  2798. "SMTP_CONNOUT::DoSTARTTLSCommand: SSL Client Hello failed %d!\n",
  2799. GetLastError());
  2800. DisconnectClient();
  2801. SetLastError(m_Error = ERROR_NO_SECURITY_ON_OBJECT);
  2802. TraceFunctLeaveEx((LPARAM)this);
  2803. return ( FALSE );
  2804. }
  2805. }
  2806. break;
  2807. default:
  2808. {
  2809. DebugTrace((LPARAM) this, "SMTP_CONNOUT::DoSTARTTLSCommand: Server sent malformed response to STARTTLS");
  2810. SetDiagnosticInfo(AQUEUE_E_TLS_ERROR, "STARTTLS", InputLine);
  2811. DisconnectClient();
  2812. SetLastError(m_Error = ERROR_NO_SECURITY_ON_OBJECT);
  2813. TraceFunctLeaveEx((LPARAM)this);
  2814. return ( FALSE );
  2815. }
  2816. break;
  2817. }
  2818. }
  2819. TraceFunctLeaveEx((LPARAM)this);
  2820. return TRUE;
  2821. }
  2822. /*++
  2823. Name :
  2824. SMTP_CONNOUT::DoRSETCommand
  2825. Description:
  2826. Sends the SMTP RSET command.
  2827. Arguments:
  2828. Are ignored
  2829. Returns:
  2830. TRUE if the connection should stay open.
  2831. FALSE if this object should be deleted.
  2832. --*/
  2833. BOOL SMTP_CONNOUT::DoRSETCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  2834. {
  2835. FormatSmtpMessage(FSM_LOG_ALL, "RSET\r\n");
  2836. m_cbReceived = 0;
  2837. m_cbParsable = 0;
  2838. m_fUseBDAT = FALSE;
  2839. //If we are issuing rset during the Per rcpt event due to some problem
  2840. //we need to preserve the state till we handle the
  2841. //this message and ack it
  2842. if (m_RsetReasonCode != ALL_RCPTS_FAILED) {
  2843. m_fNeedRelayedDSN = FALSE;
  2844. m_fHadHardError = FALSE;
  2845. m_fHadTempError = FALSE;
  2846. }
  2847. return SendSmtpResponse();
  2848. }
  2849. /*++
  2850. Name :
  2851. SMTP_CONNOUT::SendRemainingRecipients
  2852. Description:
  2853. SendRemainingRecipients
  2854. gets called when we get a 552 error during the
  2855. RCPT to command. This means that the server we connected
  2856. to has a fixed max amount of rcpts that it accepts, and we
  2857. went over that limit. Therefore, we send the mail file to
  2858. the recipients that it accepted and then start over sending
  2859. the mail to the remaining recipients. However, before doing
  2860. this, check the response to see if the server took the previous
  2861. mail O.K.
  2862. Arguments:
  2863. none
  2864. Returns:
  2865. nothing
  2866. --*/
  2867. void SMTP_CONNOUT::SendRemainingRecipients (void)
  2868. {
  2869. #if 0
  2870. CAddr * pwalk = NULL;
  2871. PLIST_ENTRY pListTemp = NULL;
  2872. //increment our counter
  2873. BUMP_COUNTER(QuerySmtpInstance(), NumMsgsSent);
  2874. //yea, baby. Save the start address
  2875. //so we can walk the list deleting
  2876. //any addresses that were successfully
  2877. //sent.
  2878. pwalk = m_StartAddress;
  2879. pListTemp = m_StartAddressLink;
  2880. _ASSERT (pwalk != NULL);
  2881. //set the start address to NULL, so
  2882. //that we can set it to a known good
  2883. //address below. See the other comment
  2884. //below.
  2885. m_StartAddress = NULL;
  2886. m_StartAddressLink = NULL;
  2887. //step through all the previously sent addresses.
  2888. //If they have an error code of 250, then they
  2889. //were received correctly. An error code of
  2890. //552 means that we sent too many, so we are
  2891. //going to attempt to send the remaining recipients.
  2892. //We will not attempt to send a rcpt that has any
  2893. //other error code.
  2894. while (pwalk && (pwalk->GetErrorCode() != SMTP_DUMMY_FAILURE)) {
  2895. if ((pwalk->GetErrorCode() == SMTP_OK_CODE) ||
  2896. (pwalk->GetErrorCode() == SMTP_USER_NOT_LOCAL_CODE)) {
  2897. MailInfo->RemoveAddress(pwalk);
  2898. delete pwalk;
  2899. }
  2900. //get the next address
  2901. pwalk = MailInfo->GetNextAddress(&pListTemp);
  2902. }
  2903. _ASSERT (pwalk != NULL);
  2904. _ASSERT (pwalk->GetErrorCode() == SMTP_DUMMY_FAILURE);
  2905. m_StartAddress = pwalk;
  2906. m_StartAddressLink = pListTemp;
  2907. _ASSERT (m_StartAddress != NULL);
  2908. _ASSERT (m_StartAddressLink != NULL);
  2909. //update the queue file to reflect the remaining
  2910. //recipients, if any
  2911. MailInfo->UpdateQueueFile(REMOTE_NAME);
  2912. //save the current RCPT address
  2913. m_NextAddress = pwalk;
  2914. m_NextAddressLink = pListTemp;
  2915. #endif
  2916. }
  2917. BOOL SMTP_CONNOUT::DoETRNCommand(void)
  2918. {
  2919. DWORD WaitRes = WAIT_TIMEOUT;
  2920. DWORD TimeToWait = 0;
  2921. HRESULT hr = S_OK;
  2922. DomainInfo DomainParams;
  2923. char szETRNDomainBuffer[MAX_PATH+1];
  2924. char *szNextETRNDomain = NULL;
  2925. const char *szETRNDomain = szETRNDomainBuffer;
  2926. DWORD cbETRNDomain = 0;
  2927. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoETRNCommand");
  2928. ZeroMemory(&DomainParams, sizeof(DomainParams));
  2929. ZeroMemory (&DomainParams, sizeof(DomainParams));
  2930. DomainParams.cbVersion = sizeof(DomainParams);
  2931. hr = m_pISMTPConnection->GetDomainInfo(&DomainParams);
  2932. if (FAILED(hr) || (DomainParams.szETRNDomainName == NULL)) {
  2933. TraceFunctLeaveEx((LPARAM)this);
  2934. return FALSE;
  2935. }
  2936. if (!m_szCurrentETRNDomain) {
  2937. m_szCurrentETRNDomain = DomainParams.szETRNDomainName;
  2938. }
  2939. //Find next in comma-delimited list of domains
  2940. szNextETRNDomain = strchr(m_szCurrentETRNDomain, ',');
  2941. if (!szNextETRNDomain) {
  2942. //We are done... this is the last domain
  2943. szETRNDomain = m_szCurrentETRNDomain;
  2944. m_Flags |= ETRN_SENT;
  2945. } else {
  2946. //There are more domains left... we need to copy the domain
  2947. //to our buffer where we can NULL terminate it.
  2948. cbETRNDomain = (DWORD) (sizeof(char)*(szNextETRNDomain-m_szCurrentETRNDomain));
  2949. if ((cbETRNDomain >= sizeof(szETRNDomainBuffer)) ||
  2950. (cbETRNDomain > DomainParams.cbETRNDomainNameLength)) {
  2951. //There is not enough room for this domain
  2952. ErrorTrace((LPARAM) this, "Domain configured for ETRN is greater than MAX_PATH");
  2953. TraceFunctLeaveEx((LPARAM) this);
  2954. return FALSE;
  2955. }
  2956. memcpy(szETRNDomainBuffer, m_szCurrentETRNDomain, cbETRNDomain);
  2957. szETRNDomainBuffer[cbETRNDomain/sizeof(char)] = '\0';
  2958. //Skip to beginning of next domain
  2959. m_szCurrentETRNDomain = szNextETRNDomain;
  2960. while (isspace((UCHAR)*m_szCurrentETRNDomain) || *m_szCurrentETRNDomain == ',') {
  2961. if (!(*m_szCurrentETRNDomain)) {
  2962. //End of string... we're done
  2963. m_Flags |= ETRN_SENT;
  2964. break;
  2965. }
  2966. m_szCurrentETRNDomain++;
  2967. }
  2968. }
  2969. #if 0
  2970. TimeToWait = m_pHashEntry->GetEtrnWaitTime() * 60 * 1000; //time in milliseconds
  2971. if (TimeToWait) {
  2972. WaitRes = WaitForSingleObject(QuerySmtpInstance()->GetQStopEvent(), m_pHashEntry->GetEtrnWaitTime());
  2973. }
  2974. if (WaitRes == WAIT_OBJECT_0) {
  2975. DisconnectClient();
  2976. TraceFunctLeaveEx((LPARAM)this);
  2977. return FALSE;
  2978. }
  2979. #endif
  2980. FormatSmtpMessage(FSM_LOG_ALL, "ETRN %s\r\n", szETRNDomain);
  2981. SendSmtpResponse();
  2982. //Keep on sending ETRNs until we are out of domains.
  2983. if (!IsOptionSet(ETRN_ONLY_OPTION) || !(m_Flags & ETRN_SENT)) {
  2984. SetNextState (&SMTP_CONNOUT::DoMessageStartEvent);
  2985. } else {
  2986. SetNextState (&SMTP_CONNOUT::DoCompletedMessage);
  2987. }
  2988. TraceFunctLeaveEx((LPARAM)this);
  2989. return TRUE;
  2990. }
  2991. /*++
  2992. Name :
  2993. SMTP_CONNOUT::DoMAILCommand
  2994. Description:
  2995. Responds to the SMTP MAIL command.
  2996. Arguments:
  2997. Are ignored
  2998. Returns:
  2999. TRUE if the connection should stay open.
  3000. FALSE if this object should be deleted.
  3001. --*/
  3002. BOOL SMTP_CONNOUT::DoMAILCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3003. {
  3004. char Options[1024] = "";
  3005. char BodySize[32];
  3006. char Address[MAX_INTERNET_NAME];
  3007. BOOL fRet = TRUE;
  3008. DWORD MsgOption = 0;
  3009. HRESULT hr = S_OK;
  3010. BOOL fFailedDueToBMIME = FALSE;
  3011. _ASSERT(m_pIMsg);
  3012. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoMAILCommand");
  3013. m_OutboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  3014. //clear the options
  3015. Options[0] = '\0';
  3016. //If the message has 8bitmime, make sure the
  3017. //server also supports it.
  3018. hr = m_pIMsg->GetDWORD(IMMPID_MP_EIGHTBIT_MIME_OPTION, &MsgOption);
  3019. if (MsgOption) {
  3020. if (IsOptionSet(EBITMIME_OPTION)) {
  3021. lstrcat(Options, " BODY=8bitmime");
  3022. } else {
  3023. // SetLastError(SMTP_OPTION_NOT_SUPPORTED_8BIT);
  3024. DebugTrace((LPARAM) this, "Message has 8bitmime but server does not support it");
  3025. m_OutboundContext.m_dwCommandStatus = EXPE_COMPLETE_FAILURE;
  3026. //Fill in the response context buffer so as to generate the right response
  3027. // Get the error code
  3028. m_ResponseContext.m_dwSmtpStatus = SMTP_RESP_BAD_CMD;
  3029. hr = m_ResponseContext.m_cabResponse.Append(
  3030. (char *)SMTP_REMOTE_HOST_REJECTED_FOR_TYPE,
  3031. strlen(SMTP_REMOTE_HOST_REJECTED_FOR_TYPE),
  3032. NULL);
  3033. TraceFunctLeaveEx((LPARAM)this);
  3034. return (TRUE);
  3035. }
  3036. } else {
  3037. MsgOption = 0;
  3038. hr = m_pIMsg->GetDWORD(IMMPID_MP_BINARYMIME_OPTION, &MsgOption);
  3039. if (MsgOption) {
  3040. //Check if we allow BINARYMIME outbound at domain level
  3041. if (m_MsgOptions & DOMAIN_INFO_DISABLE_BMIME) {
  3042. fFailedDueToBMIME = TRUE;
  3043. } else {
  3044. //Do we disallow it globally
  3045. if (QuerySmtpInstance()->AllowOutboundBMIME()) {
  3046. if (IsOptionSet(BINMIME_OPTION) && IsOptionSet(CHUNKING_OPTION)) {
  3047. lstrcat(Options, " BODY=BINARYMIME");
  3048. m_fUseBDAT = TRUE;
  3049. } else {
  3050. fFailedDueToBMIME = TRUE;
  3051. }
  3052. } else {
  3053. fFailedDueToBMIME = TRUE;
  3054. }
  3055. }
  3056. }
  3057. }
  3058. if (fFailedDueToBMIME) {
  3059. // SetLastError(SMTP_OPTION_NOT_SUPPORTED_BMIME);
  3060. DebugTrace((LPARAM) this, "Message has BINARYMIME but server does not support it");
  3061. m_OutboundContext.m_dwCommandStatus = EXPE_COMPLETE_FAILURE;
  3062. //Fill in the response context buffer so as to generate the right response
  3063. // Get the error code
  3064. m_ResponseContext.m_dwSmtpStatus = SMTP_RESP_BAD_CMD;
  3065. hr = m_ResponseContext.m_cabResponse.Append(
  3066. (char *)SMTP_REMOTE_HOST_REJECTED_FOR_TYPE,
  3067. strlen(SMTP_REMOTE_HOST_REJECTED_FOR_TYPE),
  3068. NULL);
  3069. TraceFunctLeaveEx((LPARAM)this);
  3070. return (TRUE);
  3071. }
  3072. //See if CHUNKING is preferred on this connection
  3073. //If it is and the remote site advertised chunking then we set the UseBDAT flag
  3074. else if (!m_fUseBDAT) {
  3075. //Does the remote server advertises chunking
  3076. if (IsOptionSet(CHUNKING_OPTION)) {
  3077. //Do we disallow chunking at domain level
  3078. if (m_MsgOptions & DOMAIN_INFO_DISABLE_CHUNKING) {
  3079. DebugTrace((LPARAM) this, "We disable chunking for this domain");
  3080. } else if ((m_MsgOptions & DOMAIN_INFO_USE_CHUNKING) || QuerySmtpInstance()->ShouldChunkOut()) {
  3081. m_fUseBDAT = TRUE;
  3082. }
  3083. } else if (m_MsgOptions & DOMAIN_INFO_USE_CHUNKING)
  3084. DebugTrace((LPARAM) this, "Remote server does not advertise chunking");
  3085. }
  3086. // produce a dot-stuffed handle if we aren't using bdat
  3087. if (!m_fUseBDAT) {
  3088. DWORD fFoundEmbeddedCrlfDot = FALSE;
  3089. DWORD fScanned = FALSE;
  3090. //
  3091. // get the properties to see if we have scanned for crlf.crlf, and
  3092. // to see if the message contained crlf.crlf when it was scanned.
  3093. // if either of these lookups fail then we will set fScanned to
  3094. // FALSE
  3095. //
  3096. if (FAILED(m_pIMsg->GetDWORD(IMMPID_MP_SCANNED_FOR_CRLF_DOT_CRLF,
  3097. &fScanned)) ||
  3098. FAILED(m_pIMsg->GetDWORD(IMMPID_MP_FOUND_EMBEDDED_CRLF_DOT_CRLF,
  3099. &fFoundEmbeddedCrlfDot)))
  3100. {
  3101. fScanned = FALSE;
  3102. }
  3103. //
  3104. // if we didn't scan, or if we found an embedded crlf.crlf then
  3105. // produce a dot stuff context
  3106. //
  3107. if (!fScanned || fFoundEmbeddedCrlfDot) {
  3108. if (!m_IMsgDotStuffedFileHandle) {
  3109. m_IMsgDotStuffedFileHandle = ProduceDotStuffedContext(
  3110. m_IMsgFileHandle,
  3111. NULL,
  3112. TRUE );
  3113. if (NULL == m_IMsgDotStuffedFileHandle)
  3114. {
  3115. SetDiagnosticInfo(AQUEUE_E_BIND_ERROR , NULL, NULL);
  3116. ErrorTrace((LPARAM) this, "Failed to get dot stuffed context");
  3117. TraceFunctLeaveEx((LPARAM)this);
  3118. return FALSE;
  3119. }
  3120. }
  3121. }
  3122. }
  3123. //get the size of the file
  3124. DWORD dwSizeHigh;
  3125. m_FileSize = GetFileSizeFromContext( (m_IMsgDotStuffedFileHandle && !m_fUseBDAT) ? m_IMsgDotStuffedFileHandle : m_IMsgFileHandle, &dwSizeHigh);
  3126. _ASSERT(dwSizeHigh == 0);
  3127. //if the server supports the size command
  3128. // give him the size of the file
  3129. if (IsOptionSet(SIZE_OPTION) && (m_SizeOptionSize > 0)) {
  3130. if ((m_FileSize != 0XFFFFFFFF) && (m_FileSize <= m_SizeOptionSize))
  3131. {
  3132. wsprintf(BodySize, " SIZE=%d", m_FileSize);
  3133. lstrcat(Options, BodySize);
  3134. }
  3135. else {
  3136. // SetLastError(SMTP_MSG_LARGER_THAN_SIZE);
  3137. DebugTrace((LPARAM) this, "(m_FileSize != 0XFFFFFFFF) && (m_FileSize <= m_SizeOptionSize) failed");
  3138. DebugTrace((LPARAM) this, "m_FileSize = %d, m_SizeOptionSize = %d - quiting", m_FileSize, m_SizeOptionSize );
  3139. m_OutboundContext.m_dwCommandStatus = EXPE_COMPLETE_FAILURE;
  3140. //Fill in the response context buffer so as to generate the right response
  3141. // Get the error code
  3142. m_ResponseContext.m_dwSmtpStatus = SMTP_RESP_BAD_CMD;
  3143. hr = m_ResponseContext.m_cabResponse.Append(
  3144. (char *)SMTP_REMOTE_HOST_REJECTED_FOR_SIZE,
  3145. strlen(SMTP_REMOTE_HOST_REJECTED_FOR_SIZE),
  3146. NULL);
  3147. TraceFunctLeaveEx((LPARAM)this);
  3148. return (TRUE);
  3149. }
  3150. }
  3151. if (IsOptionSet(DSN_OPTION)) {
  3152. char RetDsnValue[200];
  3153. BOOL fDSNDisallowed = TRUE;
  3154. //Do we disallow DSN at domain level
  3155. if (m_MsgOptions & DOMAIN_INFO_DISABLE_DSN) {
  3156. DebugTrace((LPARAM) this, "We disable DSN for this domain");
  3157. } else if (QuerySmtpInstance()->AllowOutboundDSN()) {
  3158. fDSNDisallowed = FALSE;
  3159. }
  3160. lstrcpy(RetDsnValue, " RET=");
  3161. hr = m_pIMsg->GetStringA(IMMPID_MP_DSN_RET_VALUE, sizeof(RetDsnValue) - lstrlen(RetDsnValue), RetDsnValue + lstrlen(RetDsnValue));
  3162. if (!FAILED(hr) && !fDSNDisallowed) {
  3163. lstrcat(Options, RetDsnValue);
  3164. }
  3165. lstrcpy(RetDsnValue, " ENVID=");
  3166. hr = m_pIMsg->GetStringA(IMMPID_MP_DSN_ENVID_VALUE, sizeof(RetDsnValue) - lstrlen(RetDsnValue), RetDsnValue + lstrlen(RetDsnValue));
  3167. if (!FAILED(hr) && !fDSNDisallowed) {
  3168. lstrcat(Options, RetDsnValue);
  3169. }
  3170. }
  3171. hr = m_pIMsg->GetStringA(IMMPID_MP_SENDER_ADDRESS_SMTP, sizeof(Address), Address);
  3172. if (!FAILED(hr)) {
  3173. //format the MAIL FROM command, with SIZE extension if necessary.
  3174. m_OutboundContext.m_cabNativeCommand.Reset();
  3175. if ( (Address[0] == '<') && (Address[1] == '>')) {
  3176. fRet = PE_AppendSmtpMessage("MAIL FROM:<>");
  3177. } else {
  3178. fRet = PE_AppendSmtpMessage("MAIL FROM:<") &&
  3179. PE_AppendSmtpMessage(Address) &&
  3180. PE_AppendSmtpMessage(">");
  3181. }
  3182. if (fRet) {
  3183. fRet = PE_AppendSmtpMessage(Options) &&
  3184. PE_AppendSmtpMessage("\r\n");
  3185. }
  3186. } else {
  3187. DebugTrace((LPARAM) this, "Could not get Sender Address %x", hr);
  3188. m_OutboundContext.m_dwCommandStatus = EXPE_COMPLETE_FAILURE;
  3189. TraceFunctLeaveEx((LPARAM)this);
  3190. return (TRUE);
  3191. }
  3192. TraceFunctLeaveEx((LPARAM)this);
  3193. return fRet;
  3194. }
  3195. /*++
  3196. Name :
  3197. SMTP_CONNOUT::DoMAILResponse
  3198. Description:
  3199. Responds to the SMTP MAIL command
  3200. Arguments:
  3201. Are ignored
  3202. Returns:
  3203. TRUE if the connection should stay open.
  3204. FALSE if this object should be deleted.
  3205. --*/
  3206. BOOL SMTP_CONNOUT::DoMAILResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3207. {
  3208. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoMAILResponse");
  3209. m_ResponseContext.m_dwResponseStatus = EXPE_SUCCESS;
  3210. char chInputGood = 0;
  3211. if ( isdigit( (UCHAR)InputLine[1] ) && isdigit( (UCHAR)InputLine[2] ) && ( ( InputLine[3] == ' ' ) || ( InputLine[3] == '-' ) ) ) {
  3212. chInputGood = InputLine[0];
  3213. } else {
  3214. chInputGood = SMTP_COMPLETE_FAILURE;
  3215. }
  3216. //the MAIL FROM: was rejected
  3217. if ( chInputGood != SMTP_COMPLETE_SUCCESS) {
  3218. // We expect either a 4xx or 5xx response. If it's 4xx we return
  3219. // a transient, all others will become a complete failure.
  3220. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, "MAIL", InputLine);
  3221. if ( chInputGood == SMTP_TRANSIENT_FAILURE) {
  3222. m_ResponseContext.m_dwResponseStatus = EXPE_TRANSIENT_FAILURE;
  3223. m_OutboundContext.m_pCurrentCommandContext = NULL;
  3224. } else {
  3225. m_ResponseContext.m_dwResponseStatus = EXPE_COMPLETE_FAILURE;
  3226. m_OutboundContext.m_pCurrentCommandContext = NULL;
  3227. DebugTrace((LPARAM) this,
  3228. "SMTP_CONNOUT::DoMAILResponse executing quit command err = %c%c%c",
  3229. InputLine [0],InputLine [1],InputLine [2]);
  3230. }
  3231. }
  3232. TraceFunctLeaveEx((LPARAM)this);
  3233. return (TRUE);
  3234. }
  3235. BOOL SMTP_CONNOUT::AddRcptsDsns(DWORD NotifyOptions, char * OrcptVal, char * AddrBuf, int& AddrSize)
  3236. {
  3237. BOOL FirstOption = TRUE;
  3238. if (NotifyOptions & ~(RP_DSN_NOTIFY_NEVER | RP_DSN_NOTIFY_SUCCESS | RP_DSN_NOTIFY_FAILURE | RP_DSN_NOTIFY_DELAY)) {
  3239. NotifyOptions = 0;
  3240. }
  3241. if (NotifyOptions) {
  3242. lstrcat(AddrBuf, " NOTIFY=");
  3243. AddrSize += 8;
  3244. if (NotifyOptions & RP_DSN_NOTIFY_SUCCESS) {
  3245. lstrcat(AddrBuf, "SUCCESS");
  3246. AddrSize += 7;
  3247. FirstOption = FALSE;
  3248. }
  3249. if (NotifyOptions & RP_DSN_NOTIFY_FAILURE) {
  3250. if (!FirstOption) {
  3251. lstrcat(AddrBuf, ",");
  3252. AddrSize += 1;
  3253. }
  3254. lstrcat(AddrBuf, "FAILURE");
  3255. AddrSize += 7;
  3256. FirstOption = FALSE;
  3257. }
  3258. if (NotifyOptions & RP_DSN_NOTIFY_DELAY) {
  3259. if (!FirstOption) {
  3260. lstrcat(AddrBuf, ",");
  3261. AddrSize += 1;
  3262. }
  3263. lstrcat(AddrBuf, "DELAY");
  3264. AddrSize += 5;
  3265. FirstOption = FALSE;
  3266. }
  3267. if (FirstOption) {
  3268. lstrcat(AddrBuf, "NEVER");
  3269. AddrSize += 5;
  3270. }
  3271. }
  3272. if (*OrcptVal != '\0') {
  3273. lstrcat(AddrBuf, " ORCPT=");
  3274. AddrSize += 7;
  3275. lstrcat(AddrBuf, OrcptVal);
  3276. AddrSize += lstrlen(OrcptVal);
  3277. }
  3278. return TRUE;
  3279. }
  3280. /*++
  3281. Name :
  3282. SMTP_CONNOUT::SaveToErrorFile
  3283. Description:
  3284. This function saves all errors to
  3285. a file so the NDR thread can send
  3286. a transcript of what tranpired back
  3287. to the original user
  3288. Arguments:
  3289. Buffer with recipient data, size of buffer,
  3290. Returns:
  3291. TRUE if we were able to open and write to the file
  3292. FALSE otherwise
  3293. --*/
  3294. BOOL SMTP_CONNOUT::SaveToErrorFile(char * Buffer, DWORD BufSize)
  3295. {
  3296. return TRUE;
  3297. }
  3298. /*++
  3299. Name :
  3300. SMTP_CONNOUT::DoRCPTCommand
  3301. Description:
  3302. This function sends the SMTP RCPT command.
  3303. Arguments:
  3304. Returns:
  3305. TRUE if the connection should stay open.
  3306. FALSE if this object should be deleted.
  3307. --*/
  3308. BOOL SMTP_CONNOUT::DoRCPTCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3309. {
  3310. char AddrBuf [MAX_INTERNET_NAME + 2024];
  3311. char szRecipName[MAX_INTERNET_NAME];
  3312. char szOrcptVal[MAX_RCPT_TO_ORCPT_LEN+1];
  3313. BOOL fOrcptSpecified;
  3314. BOOL fFoundUnhandledRcpt = FALSE;
  3315. int AddrSize = 0;
  3316. HRESULT hr = S_OK;
  3317. DWORD NextAddress = 0;
  3318. _ASSERT(QuerySmtpInstance() != NULL);
  3319. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoRCPTCommand");
  3320. //MessageTrace((LPARAM) this, (LPBYTE) InputLine, ParameterSize);
  3321. // We report success
  3322. m_OutboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  3323. //format as much of the recipients that could fit into
  3324. //the output buffer
  3325. NextAddress = m_NextAddress;
  3326. while (NextAddress < m_NumRcpts) {
  3327. DWORD dwRecipientFlags = 0;
  3328. hr = m_pIMsgRecips->GetDWORD(m_RcptIndexList[NextAddress], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  3329. if (FAILED(hr) && hr != MAILMSG_E_PROPNOTFOUND) {
  3330. //Problemmo
  3331. // Get property shouldn't fail since we've come this far
  3332. //_ASSERT(FALSE);
  3333. goto RcptError;
  3334. }
  3335. //NK** : I am moving this out as it breaks us when the first recipient is a handled recipient
  3336. // Default is pipelined
  3337. m_OutboundContext.m_dwCommandStatus = EXPE_PIPELINED | EXPE_REPEAT_COMMAND;
  3338. //check to see if this recipient needs to be looked at
  3339. if ((dwRecipientFlags & RP_HANDLED) ) {
  3340. //This recipient can be skipped over
  3341. //Mark it inaccesible so that when we sweep the tried recipient
  3342. //in case of connection drop we do not touch the guys we did not send
  3343. //We need to get atleast one guy each time we come in.
  3344. m_RcptIndexList[NextAddress] = INVALID_RCPT_IDX_VALUE;
  3345. m_NextAddress = NextAddress + 1;
  3346. NextAddress ++;
  3347. continue;
  3348. } else {
  3349. hr = m_pIMsgRecips->GetStringA(m_RcptIndexList[NextAddress], IMMPID_RP_ADDRESS_SMTP, sizeof(szRecipName), szRecipName);
  3350. if (!FAILED(hr)) {
  3351. //Format the first recipient
  3352. AddrSize = wsprintf (AddrBuf, "RCPT TO:<%s>", szRecipName);
  3353. fOrcptSpecified = FALSE;
  3354. hr = m_pIMsgRecips->GetStringA(m_RcptIndexList[NextAddress], IMMPID_RP_DSN_ORCPT_VALUE, sizeof(szOrcptVal), szOrcptVal);
  3355. if (!FAILED(hr) && (szOrcptVal[0] != '\0')) {
  3356. fOrcptSpecified = TRUE;
  3357. } else if (FAILED(hr) && hr != MAILMSG_E_PROPNOTFOUND) {
  3358. //Problemmo
  3359. // Get property shouldn't fail since we've come this far
  3360. //_ASSERT(FALSE);
  3361. goto RcptError;
  3362. } else {
  3363. szOrcptVal[0] = '\0';
  3364. }
  3365. //If some DSN property is set then
  3366. if ((dwRecipientFlags & RP_DSN_NOTIFY_MASK) || fOrcptSpecified) {
  3367. BOOL fAllowDSN = FALSE;
  3368. //Do we disallow DSN at domain level
  3369. if (m_MsgOptions & DOMAIN_INFO_DISABLE_DSN) {
  3370. DebugTrace((LPARAM) this, "We disable DSN for this domain");
  3371. } else if (QuerySmtpInstance()->AllowOutboundDSN()) {
  3372. fAllowDSN = TRUE;
  3373. }
  3374. if (IsOptionSet(DSN_OPTION) && fAllowDSN) {
  3375. AddRcptsDsns(dwRecipientFlags, szOrcptVal, AddrBuf, AddrSize);
  3376. m_fNeedRelayedDSN = FALSE;
  3377. } else {
  3378. //let AQ know that the remote server did not advertise DSN
  3379. m_fNeedRelayedDSN = TRUE;
  3380. dwRecipientFlags |= RP_REMOTE_MTA_NO_DSN;
  3381. hr = m_pIMsgRecips->PutDWORD(m_RcptIndexList[NextAddress],
  3382. IMMPID_RP_RECIPIENT_FLAGS,dwRecipientFlags);
  3383. if (FAILED(hr)) {
  3384. //Problemmo
  3385. goto RcptError;
  3386. }
  3387. }
  3388. }
  3389. lstrcat(AddrBuf, "\r\n");
  3390. AddrSize += 2;
  3391. m_OutboundContext.m_cabNativeCommand.Reset();
  3392. if (PE_AppendSmtpMessage(AddrBuf)) {
  3393. DebugTrace((LPARAM) this,"Sending %s", szRecipName);
  3394. m_NumRcptSent++;
  3395. m_NextAddress = NextAddress + 1;
  3396. if (!IsOptionSet(PIPELINE_OPTION))
  3397. m_OutboundContext.m_dwCommandStatus = EXPE_NOT_PIPELINED;
  3398. } else {
  3399. //no more space in the buffer, send what we have;
  3400. //_ASSERT(FALSE);
  3401. m_OutboundContext.m_dwCommandStatus = EXPE_NOT_PIPELINED;
  3402. return (TRUE);
  3403. }
  3404. } else {
  3405. //Problemmo
  3406. // Get property shouldn't fail since we've come this far
  3407. //_ASSERT(FALSE);
  3408. goto RcptError;
  3409. }
  3410. }
  3411. NextAddress ++;
  3412. break;
  3413. }
  3414. // If we are done, we will not pipeline further, this will save a loop
  3415. if (m_NumRcpts == NextAddress)
  3416. m_OutboundContext.m_dwCommandStatus = EXPE_NOT_PIPELINED;
  3417. TraceFunctLeaveEx((LPARAM)this);
  3418. return (TRUE);
  3419. RcptError:
  3420. m_OutboundContext.m_dwCommandStatus = EXPE_TRANSIENT_FAILURE;
  3421. TraceFunctLeaveEx((LPARAM)this);
  3422. return (FALSE);
  3423. }
  3424. /*++
  3425. Name :
  3426. SMTP_CONNOUT::DoRCPTResponse
  3427. Description:
  3428. This function handles the SMTP RCPT response.
  3429. Arguments:
  3430. Returns:
  3431. TRUE if the connection should stay open.
  3432. FALSE if this object should be deleted.
  3433. --*/
  3434. BOOL SMTP_CONNOUT::DoRCPTResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3435. {
  3436. HRESULT hr = S_OK;
  3437. DWORD NextAddress = 0;
  3438. DWORD dwRecipientFlags;
  3439. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoRCPTResponse");
  3440. _ASSERT(QuerySmtpInstance() != NULL);
  3441. //MessageTrace((LPARAM) this, (LPBYTE) InputLine, ParameterSize);
  3442. m_ResponseContext.m_dwResponseStatus = EXPE_SUCCESS;
  3443. //start at the beginning of the last
  3444. //pipelined address
  3445. NextAddress = m_FirstPipelinedAddress;
  3446. DebugTrace((LPARAM)this, "Response: %*s", ParameterSize, InputLine);
  3447. //step through the returned recipients and check
  3448. //their error code.
  3449. if (m_NumRcptSent) {
  3450. //Get to the next rcpt that we send out this time
  3451. while ((NextAddress < m_NumRcpts) && (m_RcptIndexList[NextAddress] == INVALID_RCPT_IDX_VALUE))
  3452. NextAddress++;
  3453. _ASSERT(NextAddress < m_NumRcpts);
  3454. if (NextAddress >= m_NumRcpts) {
  3455. //Problemo
  3456. SetLastError(ERROR_INVALID_DATA);
  3457. TraceFunctLeaveEx((LPARAM)this);
  3458. return (FALSE);
  3459. }
  3460. dwRecipientFlags = 0;
  3461. hr = m_pIMsgRecips->GetDWORD(m_RcptIndexList[NextAddress], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  3462. if (FAILED(hr) && hr != MAILMSG_E_PROPNOTFOUND) {
  3463. //Problemmo
  3464. SetLastError(ERROR_OUTOFMEMORY);
  3465. TraceFunctLeaveEx((LPARAM)this);
  3466. return (FALSE);
  3467. }
  3468. m_NumRcptSent--;
  3469. //Once we get 552 Too many recipients error .. we do not bother to look at the real responses
  3470. //Logically they will all be 552 as well
  3471. if (m_SendAgain)
  3472. m_ResponseContext.m_dwSmtpStatus = SMTP_ACTION_ABORTED_CODE;
  3473. // If we have no response status code set, we will abort action
  3474. if (m_ResponseContext.m_dwSmtpStatus == 0) {
  3475. m_NumFailedAddrs++;
  3476. m_NumRcptSentSaved ++;
  3477. hr = m_pIMsgRecips->PutDWORD(m_RcptIndexList[NextAddress], IMMPID_RP_ERROR_CODE, SMTP_ACTION_ABORTED_CODE);
  3478. if (FAILED(hr)) {
  3479. //Problemmo
  3480. SetLastError(ERROR_OUTOFMEMORY);
  3481. TraceFunctLeaveEx((LPARAM)this);
  3482. return (FALSE);
  3483. }
  3484. //Create the temp error string
  3485. char sztemp[100];
  3486. sprintf(sztemp,"%d %s",SMTP_ACTION_ABORTED_CODE, "Invalid recipient response");
  3487. hr = m_pIMsgRecips->PutStringA(m_RcptIndexList[NextAddress], IMMPID_RP_SMTP_STATUS_STRING,sztemp);
  3488. if (FAILED(hr)) {
  3489. //Problemmo
  3490. SetLastError(ERROR_OUTOFMEMORY);
  3491. TraceFunctLeaveEx((LPARAM)this);
  3492. return (FALSE);
  3493. }
  3494. //real bad error. SCuttle this message
  3495. MessageTrace((LPARAM) this, (LPBYTE) InputLine, ParameterSize);
  3496. SetLastError(ERROR_CAN_NOT_COMPLETE);
  3497. m_ResponseContext.m_dwResponseStatus = EXPE_TRANSIENT_FAILURE;
  3498. m_OutboundContext.m_pCurrentCommandContext = NULL;
  3499. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, "RCPT", InputLine);
  3500. return (TRUE);
  3501. } else {
  3502. //save the number of recipients sent so that
  3503. //we can compare this number to the number of
  3504. //failed recipients.
  3505. m_NumRcptSentSaved ++;
  3506. BUMP_COUNTER(QuerySmtpInstance(), NumRcptsSent);
  3507. // Save the error code (if there was an error)
  3508. // We do not want to save "250 OK" or "251 OK" responses because
  3509. // it wastes increases the memory footprint of messages in
  3510. // the queue
  3511. if ((SMTP_OK_CODE != m_ResponseContext.m_dwSmtpStatus) &&
  3512. (SMTP_USER_NOT_LOCAL_CODE != m_ResponseContext.m_dwSmtpStatus)) {
  3513. ErrorTrace((LPARAM)this,
  3514. "Saving rcpt error code %u - %s",
  3515. m_ResponseContext.m_dwSmtpStatus, InputLine);
  3516. hr = m_pIMsgRecips->PutDWORD(
  3517. m_RcptIndexList[NextAddress],
  3518. IMMPID_RP_ERROR_CODE,
  3519. m_ResponseContext.m_dwSmtpStatus);
  3520. if (FAILED(hr)) {
  3521. //Problemmo
  3522. SetLastError(ERROR_OUTOFMEMORY);
  3523. TraceFunctLeaveEx((LPARAM)this);
  3524. return (FALSE);
  3525. }
  3526. //Set the ful response as error string
  3527. hr = m_pIMsgRecips->PutStringA(m_RcptIndexList[NextAddress], IMMPID_RP_SMTP_STATUS_STRING, InputLine);
  3528. if (FAILED(hr)) {
  3529. //Problemmo
  3530. SetLastError(ERROR_OUTOFMEMORY);
  3531. TraceFunctLeaveEx((LPARAM)this);
  3532. return (FALSE);
  3533. }
  3534. } else {
  3535. //Recipient successful... trace it
  3536. DebugTrace((LPARAM) this,
  3537. "Recipient %d OK with response %s",
  3538. NextAddress, InputLine);
  3539. }
  3540. switch (m_ResponseContext.m_dwSmtpStatus) {
  3541. //4XX level code will lead to retry
  3542. case SMTP_ERROR_PROCESSING_CODE:
  3543. case SMTP_MBOX_BUSY_CODE:
  3544. case SMTP_SERVICE_UNAVAILABLE_CODE:
  3545. //Buffer[3] = ' '; put back the space character
  3546. m_fHadTempError = TRUE;
  3547. dwRecipientFlags |= (RP_ERROR_CONTEXT_MTA);
  3548. m_NumFailedAddrs++;
  3549. break;
  3550. case SMTP_ACTION_ABORTED_CODE:
  3551. case SMTP_INSUFF_STORAGE_CODE: // 452 per rfc2821
  3552. //this means we sent too many recipients.
  3553. //set the m_SendAgain flag which tells us
  3554. //to send whatever we have now, then start
  3555. //sending to the other recipients afterwards.
  3556. //We have to switch the error code because 552
  3557. //means different things depending on what operation
  3558. //we are doing.
  3559. if (!m_SendAgain) {
  3560. if (m_fHadSuccessfulDelivery) {
  3561. m_NumFailedAddrs++;
  3562. m_SendAgain = TRUE;
  3563. if( m_NumFailedAddrs >= m_NumRcptSentSaved )
  3564. {
  3565. m_fHadHardError = TRUE;
  3566. }
  3567. if (m_First552Address == -1)
  3568. m_First552Address = NextAddress;
  3569. break;
  3570. }
  3571. }
  3572. // If no rcpts have been accepted so far and this
  3573. // is a 452, then treat it as a temp failure like the
  3574. // other 4xx codes. If it's 552, we fall through
  3575. if (m_ResponseContext.m_dwSmtpStatus == SMTP_INSUFF_STORAGE_CODE) {
  3576. m_fHadTempError = TRUE;
  3577. dwRecipientFlags |= (RP_ERROR_CONTEXT_MTA);
  3578. m_NumFailedAddrs++;
  3579. break;
  3580. }
  3581. // Falls through to 5xx case
  3582. //5XX level codes will lead to NDR for the rcpt
  3583. case SMTP_UNRECOG_COMMAND_CODE:
  3584. case SMTP_SYNTAX_ERROR_CODE:
  3585. case SMTP_NOT_IMPLEMENTED_CODE:
  3586. case SMTP_BAD_SEQUENCE_CODE :
  3587. case SMTP_PARAM_NOT_IMPLEMENTED_CODE:
  3588. case SMTP_MBOX_NOTFOUND_CODE:
  3589. case SMTP_USERNOTLOCAL_CODE:
  3590. case SMTP_ACTION_NOT_TAKEN_CODE:
  3591. case SMTP_TRANSACT_FAILED_CODE:
  3592. // Buffer[3] = ' '; //put back the space character
  3593. //SaveToErrorFile(Buffer, IntermediateSize);
  3594. // DebugTrace((LPARAM) this, "User %s failed because of %d", pwalk->GetAddress(), Error);
  3595. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, "RCPT", InputLine);
  3596. m_fHadHardError = TRUE;
  3597. dwRecipientFlags |= (RP_FAILED | RP_ERROR_CONTEXT_MTA);
  3598. m_NumFailedAddrs++;
  3599. break;
  3600. case SMTP_OK_CODE:
  3601. case SMTP_USER_NOT_LOCAL_CODE:
  3602. m_fHadSuccessfulDelivery = TRUE;
  3603. BUMP_COUNTER(QuerySmtpInstance(), NumRcptsSent);
  3604. dwRecipientFlags |= (RP_DELIVERED | RP_ERROR_CONTEXT_MTA);
  3605. break;
  3606. case SMTP_SERVICE_CLOSE_CODE:
  3607. //fall through. This is deliberate.
  3608. //we don't want to continue if we get
  3609. //these errors
  3610. default:
  3611. //real bad error. Copy this error to the
  3612. //front of the input buffer, because this
  3613. //buffer will be passed to DoCompletedMessage
  3614. //to do the right thing.
  3615. {
  3616. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, "RCPT", InputLine);
  3617. char chInputGood = 0;
  3618. if ( isdigit( (UCHAR)InputLine[1] ) &&
  3619. isdigit( (UCHAR)InputLine[2] ) &&
  3620. ( ( InputLine[3] == ' ' ) || ( InputLine[3] == '-' ) ) ) {
  3621. chInputGood = InputLine[0];
  3622. } else {
  3623. chInputGood = SMTP_COMPLETE_FAILURE;
  3624. }
  3625. MessageTrace((LPARAM) this, (LPBYTE) InputLine, ParameterSize);
  3626. SetLastError(ERROR_CAN_NOT_COMPLETE);
  3627. if ( chInputGood == SMTP_TRANSIENT_FAILURE) {
  3628. m_ResponseContext.m_dwResponseStatus = EXPE_TRANSIENT_FAILURE;
  3629. m_OutboundContext.m_pCurrentCommandContext = NULL;
  3630. } else {
  3631. m_ResponseContext.m_dwResponseStatus = EXPE_COMPLETE_FAILURE;
  3632. m_OutboundContext.m_pCurrentCommandContext = NULL;
  3633. }
  3634. return (TRUE);
  3635. }
  3636. }
  3637. }
  3638. //Set the flags back
  3639. hr = m_pIMsgRecips->PutDWORD(m_RcptIndexList[NextAddress], IMMPID_RP_RECIPIENT_FLAGS,dwRecipientFlags);
  3640. if (FAILED(hr)) {
  3641. //Problemmo
  3642. SetLastError(ERROR_OUTOFMEMORY);
  3643. TraceFunctLeaveEx((LPARAM)this);
  3644. return (FALSE);
  3645. }
  3646. NextAddress++;
  3647. }
  3648. // Mark where we should pick up next time ...
  3649. m_FirstPipelinedAddress = NextAddress;
  3650. // No more unprocessed recipients, we are either done or we have to
  3651. // issue more RCPT commands
  3652. if ((NextAddress < m_NumRcpts) && !m_SendAgain)
  3653. m_ResponseContext.m_dwResponseStatus = EXPE_REPEAT_COMMAND;
  3654. TraceFunctLeaveEx((LPARAM)this);
  3655. return (TRUE);
  3656. }
  3657. /*++
  3658. Name :
  3659. SMTP_CONNOUT::DoDATACommand
  3660. Description:
  3661. Responds to the SMTP DATA command.
  3662. Arguments:
  3663. InputLine - Buffer received from client
  3664. paramterSize - amount of data in buffer
  3665. both are ignored
  3666. Returns:
  3667. TRUE if the connection should stay open.
  3668. FALSE if this object should be deleted.
  3669. --*/
  3670. BOOL SMTP_CONNOUT::DoDATACommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3671. {
  3672. _ASSERT(QuerySmtpInstance() != NULL);
  3673. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoDATACommand");
  3674. SetNextState (&SMTP_CONNOUT::DoDATACommandEx);
  3675. FormatSmtpMessage(FSM_LOG_ALL, "DATA\r\n");
  3676. TraceFunctLeaveEx((LPARAM)this);
  3677. return SendSmtpResponse();
  3678. }
  3679. /*++
  3680. Name :
  3681. SMTP_CONNOUT::DoBDATCommand
  3682. Description:
  3683. Send out the SMTP BDAT command.
  3684. Arguments:
  3685. InputLine - Buffer received from client
  3686. paramterSize - amount of data in buffer
  3687. both are ignored
  3688. Returns:
  3689. TRUE if the connection should stay open.
  3690. FALSE if this object should be deleted.
  3691. --*/
  3692. BOOL SMTP_CONNOUT::DoBDATCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3693. {
  3694. _ASSERT(QuerySmtpInstance() != NULL);
  3695. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoBDATCommand");
  3696. SetNextState (&SMTP_CONNOUT::DoDATACommandEx);
  3697. //We send the whole mesage file as a single BDAT chunk
  3698. //Verify if the CHUNK size should be FileSize or +2 for trailing CRLF
  3699. //
  3700. FormatSmtpMessage(FSM_LOG_ALL, "BDAT %d LAST\r\n", m_FileSize);
  3701. if (!SendSmtpResponse()) {
  3702. //BDAT command failed for some reason
  3703. DebugTrace((LPARAM) this, "Failed to send BDAT command");
  3704. TraceFunctLeaveEx((LPARAM)this);
  3705. return FALSE;
  3706. }
  3707. TraceFunctLeaveEx((LPARAM)this);
  3708. return DoDATACommandEx(InputLine, ParameterSize, UndecryptedTailSize);
  3709. }
  3710. /*++
  3711. Name :
  3712. SMTP_CONNOUT::DoDATACommandEx
  3713. Description:
  3714. This funcion sends the message body to remote server
  3715. after the DATA or BDAT command
  3716. Arguments:
  3717. InputLine - Buffer received from remote Server
  3718. paramterSize - amount of data in buffer
  3719. Returns:
  3720. TRUE if the connection should stay open.
  3721. FALSE if this object should be deleted.
  3722. --*/
  3723. BOOL SMTP_CONNOUT::DoDATACommandEx(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3724. {
  3725. LARGE_INTEGER LSize = {0,0};
  3726. BOOL fRet = TRUE;
  3727. HRESULT hr = S_OK;
  3728. char szResponse[] = "123";
  3729. LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers;
  3730. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoDATACommandEx");
  3731. // we need to receive a 354 from the server to go on in case of DATA command
  3732. // we actually check that we receive atleast a 3xy response (x,y = digits)
  3733. // In case of BDAT, the remote server never responds and so ignore the input line
  3734. //
  3735. if (m_fUseBDAT ||
  3736. (ParameterSize >= 5 &&
  3737. InputLine[0] == SMTP_INTERMEDIATE_SUCCESS &&
  3738. isdigit((UCHAR)InputLine[1]) &&
  3739. isdigit((UCHAR)InputLine[2]))) {
  3740. SetNextState (&SMTP_CONNOUT::DoContentResponse);
  3741. LSize.HighPart = 0;
  3742. LSize.LowPart = m_FileSize;
  3743. m_LastClientIo = SMTP_CONNOUT::TRANSFILEIO;
  3744. //use the trailer buffers only for DATA command
  3745. lpTransmitBuffers = (m_fUseBDAT) ? NULL : &m_TransmitBuffers;
  3746. fRet = TransmitFileEx ((!m_fUseBDAT && m_IMsgDotStuffedFileHandle) ? m_IMsgDotStuffedFileHandle->m_hFile : m_IMsgFileHandle->m_hFile,
  3747. LSize,
  3748. 0,
  3749. lpTransmitBuffers);
  3750. if (!fRet) {
  3751. m_Error = GetLastError();
  3752. DebugTrace((LPARAM) this, "TranmitFile in DoDATACommandEx failed with error %d !!!!", m_Error);
  3753. DisconnectClient();
  3754. return FALSE;
  3755. }
  3756. //need to reset the ATQ timeout thread since we pended two
  3757. //I/Os back to back.
  3758. AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_RESUME_IO, 0);
  3759. } else { //quit if we don't get the 354 response
  3760. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, "DATA", InputLine);
  3761. ErrorTrace((LPARAM) this, "DoDATACommandEx executing quit command err = %c%c%c", InputLine [0],InputLine [1],InputLine [2]);
  3762. if (InputLine[0] == SMTP_COMPLETE_SUCCESS) {
  3763. CopyMemory(InputLine, "599", 3);
  3764. DebugTrace((LPARAM) this, "DoDATACommandEx executing quit command err = %c%c%c", InputLine [0],InputLine [1],InputLine [2]);
  3765. }
  3766. //DoCompletedMessage uses the m_ResponseContext to get the error
  3767. if ((ParameterSize > 3) &&
  3768. ((InputLine[3] == ' ') || (InputLine[3] == CR))) {
  3769. //Try to get error from 3 digit code on input line
  3770. //In some cases (DoDataCommandEx), the m_ResponseContext is not
  3771. //used
  3772. memcpy(szResponse, InputLine, sizeof(szResponse)-sizeof(char));
  3773. m_ResponseContext.m_dwSmtpStatus = atoi(szResponse);
  3774. hr = m_ResponseContext.m_cabResponse.Append(
  3775. InputLine + sizeof(szResponse)/sizeof(char),
  3776. ParameterSize-sizeof(szResponse),
  3777. NULL);
  3778. if (FAILED(hr))
  3779. {
  3780. ErrorTrace((LPARAM) this,
  3781. "Unable to append Input line to Response Context", hr);
  3782. //Really nothing we can do about this... DoCompletedMailObj
  3783. //will send RSET and try again
  3784. }
  3785. }
  3786. fRet = DoCompletedMessage(InputLine, ParameterSize, UndecryptedTailSize);
  3787. //If DoCompletedMessage returns TRUE... then we must post a read for the
  3788. //response. Functions that call into this function expect it to handle
  3789. //the IO state if it returns TRUE.
  3790. if (fRet)
  3791. {
  3792. if (m_cbReceived >= m_cbMaxRecvBuffer)
  3793. m_cbReceived = 0;
  3794. IncPendingIoCount();
  3795. m_LastClientIo = SMTP_CONNOUT::READIO;
  3796. fRet = ReadFile(QueryMRcvBuffer() + m_cbReceived,
  3797. m_cbMaxRecvBuffer - m_cbReceived);
  3798. if (!fRet) {
  3799. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  3800. m_Error = GetLastError();
  3801. SetDiagnosticInfo(HRESULT_FROM_WIN32(m_Error), NULL, NULL);
  3802. DisconnectClient();
  3803. DecPendingIoCount();
  3804. DebugTrace((LPARAM) this, "ReadFile in DoDataCommandEx failed with error %d", m_Error);
  3805. }
  3806. }
  3807. }
  3808. TraceFunctLeaveEx((LPARAM)this);
  3809. return fRet;
  3810. }
  3811. /*++
  3812. Name :
  3813. SMTP_CONNOUT::DoContentResponse
  3814. Description:
  3815. This function catches the final response after transmitting
  3816. the message content
  3817. Arguments:
  3818. InputLine - Buffer received from remote Server
  3819. paramterSize - amount of data in buffer
  3820. Returns:
  3821. TRUE if the connection should stay open.
  3822. FALSE if this object should be deleted.
  3823. --*/
  3824. BOOL SMTP_CONNOUT::DoContentResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  3825. {
  3826. BOOL fRet = TRUE;
  3827. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoContentResponse");
  3828. //send again gets sent when we get a 552 error during the
  3829. //RCPT to command. This means that the server we connected
  3830. //to has a fixed max amount of rcpts that it accepts, and we
  3831. //went over that limit. Therefore, we send the mail file to
  3832. //the recipients that it accepted and then start over sending
  3833. //the mail to the remaining recipients. However, before doing
  3834. //this, check the response to see if the server took the previous
  3835. //mail O.K.
  3836. if (m_SendAgain) {
  3837. m_cbReceived = 0;
  3838. m_OutputBufferSize = 0;
  3839. m_Error = NO_ERROR;
  3840. m_NumRcptSent = 0;
  3841. m_NumRcptSentSaved = 0;
  3842. m_NumFailedAddrs = 0;
  3843. //NK** : I am moving this down as the check below seems
  3844. //meaningless with this in here
  3845. //m_SendAgain = FALSE;
  3846. m_FirstRcpt = FALSE;
  3847. _ASSERT(m_First552Address != -1);
  3848. //NK** : I am not sure what we are trying to do here by setting pipelined addr to 552addr.
  3849. //I am going to leave this in - but also assign this value to NextAddr which decides
  3850. //from where we should really start
  3851. m_FirstPipelinedAddress = (DWORD) m_First552Address;
  3852. m_NextAddress = (DWORD) m_First552Address;
  3853. m_FirstAddressinCurrentMail = (DWORD) m_First552Address;
  3854. m_First552Address = -1;
  3855. m_fHadSuccessfulDelivery = FALSE;
  3856. m_TransmitTailBuffer[0] = '.';
  3857. m_TransmitTailBuffer[1] = '\r';
  3858. m_TransmitTailBuffer[2] = '\n';
  3859. m_TransmitBuffers.Head = NULL;
  3860. m_TransmitBuffers.HeadLength = 0;
  3861. m_TransmitBuffers.Tail = m_TransmitTailBuffer;
  3862. m_TransmitBuffers.TailLength = 3;
  3863. BUMP_COUNTER(QuerySmtpInstance(), NumMsgsSent);
  3864. //reset the file pointer to the beginning
  3865. if (SetFilePointer(m_IMsgDotStuffedFileHandle ? m_IMsgDotStuffedFileHandle->m_hFile: m_IMsgFileHandle->m_hFile, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF)
  3866. {
  3867. m_Error = GetLastError();
  3868. DebugTrace((LPARAM) this, "SetFilePointer failed--err = %d", m_Error);
  3869. m_SendAgain = FALSE;
  3870. } else if (InputLine [0] != SMTP_COMPLETE_SUCCESS) {
  3871. //something went wrong before, so let's quit
  3872. DebugTrace((LPARAM) this,
  3873. "SMTP_CONNOUT::DoMAILCommand executing quit command err = %c%c%c",
  3874. InputLine [0],InputLine [1],InputLine [2]);
  3875. m_SendAgain = FALSE;
  3876. }
  3877. }
  3878. // Note the if clause above might change this flag so a simple
  3879. // else won't cut it
  3880. if (!m_SendAgain) {
  3881. fRet = DoCompletedMessage(InputLine, ParameterSize, UndecryptedTailSize);
  3882. } else {
  3883. m_SendAgain = FALSE;
  3884. fRet = DoMessageStartEvent(InputLine, ParameterSize, UndecryptedTailSize);
  3885. }
  3886. TraceFunctLeaveEx((LPARAM)this);
  3887. return (fRet);
  3888. }
  3889. BOOL SMTP_CONNOUT::TransmitFileEx (HANDLE hFile, LARGE_INTEGER &liSize,
  3890. DWORD Offset, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers)
  3891. {
  3892. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::TransmitFileEx");
  3893. BOOL fRet;
  3894. _ASSERT(hFile != INVALID_HANDLE_VALUE);
  3895. _ASSERT(liSize.QuadPart > 0);
  3896. QueryAtqContext()->Overlapped.Offset = Offset;
  3897. if (!m_SecurePort) {
  3898. DebugTrace((LPARAM) this, "Calling AtqTransmitFile");
  3899. TraceFunctLeaveEx((LPARAM)this);
  3900. IncPendingIoCount();
  3901. fRet = AtqTransmitFile(
  3902. QueryAtqContext(), // Atq context
  3903. hFile, // file data comes fro
  3904. liSize.LowPart, // Bytes To Send
  3905. lpTransmitBuffers, // header/tail buffers
  3906. 0
  3907. );
  3908. if (!fRet) {
  3909. DecPendingIoCount();
  3910. }
  3911. return ( fRet );
  3912. } else {
  3913. //
  3914. // If we are connected over an SSL port, we cannot use TransmitFile,
  3915. // since the data has to be encrypted first.
  3916. //
  3917. DebugTrace((LPARAM)this, "Doing async reads and writes on handle %x", hFile);
  3918. DebugTrace( (DWORD_PTR)this, "hfile %x", hFile );
  3919. //
  3920. // Send the transmit buffer header synchronously
  3921. //
  3922. fRet = TRUE;
  3923. //We have no headers or Trailers if we are doing BDAT processing
  3924. if (lpTransmitBuffers && m_TransmitBuffers.HeadLength > 0) {
  3925. m_OutputBufferSize = m_cbMaxOutputBuffer;
  3926. //NimishK : We look for Head length and then send Tail
  3927. //Dosn't seem right.
  3928. fRet = m_encryptCtx.SealMessage(
  3929. (LPBYTE) m_TransmitBuffers.Tail,
  3930. m_TransmitBuffers.TailLength,
  3931. (LPBYTE) m_pOutputBuffer,
  3932. &m_OutputBufferSize);
  3933. if (fRet)
  3934. fRet = WriteFile(m_pOutputBuffer, m_OutputBufferSize);
  3935. else
  3936. SetLastError(AQUEUE_E_SSL_ERROR);
  3937. }
  3938. if (fRet) {
  3939. //
  3940. // issue the first async read against the file
  3941. //
  3942. m_dwFileOffset = 0;
  3943. m_bComplete = FALSE;
  3944. if ( MessageReadFile() ) {
  3945. TraceFunctLeaveEx((LPARAM) this);
  3946. return TRUE;
  3947. }
  3948. }
  3949. //
  3950. // WriteFile\MessageReadFile will teardown the connection on errors
  3951. //
  3952. TraceFunctLeaveEx((LPARAM) this);
  3953. return FALSE;
  3954. }
  3955. }
  3956. /*++
  3957. Name:
  3958. SMTP_CONNOUT::MessageReadFile
  3959. Description:
  3960. When TransmitFile cannot be used to transfer a message (for example,
  3961. when using SSL), the message is transferred in chunks using
  3962. MessageReadFile to retrieve chunks of the file and issueing
  3963. corresponding asynchronous WriteFiles to the remote server.
  3964. Arguments:
  3965. None.
  3966. Returns:
  3967. TRUE if successfully read the next chunk of the file, FALSE otherwise
  3968. --*/
  3969. BOOL SMTP_CONNOUT::MessageReadFile( void )
  3970. {
  3971. TraceFunctEnterEx( (LPARAM)this, "SMTP_CONNOUT::MessageReadFile");
  3972. //
  3973. // Main line code path
  3974. //
  3975. CBuffer* pBuffer = new CBuffer();
  3976. if ( pBuffer != NULL ) {
  3977. LPBYTE lpData = pBuffer->GetData();
  3978. if ( lpData != NULL ) {
  3979. DWORD cbBufSize = CIoBuffer::Pool.GetInstanceSize();
  3980. // we never want to make SSL data > 16k
  3981. if (cbBufSize > 16*1024) cbBufSize = 16*1024;
  3982. DWORD cb = cbBufSize -
  3983. m_encryptCtx.GetSealHeaderSize() -
  3984. m_encryptCtx.GetSealTrailerSize();
  3985. lpData += m_encryptCtx.GetSealHeaderSize();
  3986. cb = min( cb, m_FileSize - m_dwFileOffset );
  3987. //
  3988. // set buffer specific IO state
  3989. //
  3990. pBuffer->SetIoState( MESSAGE_READ );
  3991. pBuffer->SetSize( cb );
  3992. DebugTrace( (LPARAM)this, "ReadFile 0x%08X, len: %d, LPO: 0x%08X",
  3993. lpData,
  3994. cb,
  3995. &pBuffer->m_Overlapped.SeoOverlapped.Overlapped );
  3996. ZeroMemory( (void*)&pBuffer->m_Overlapped.SeoOverlapped, sizeof(OVERLAPPED) );
  3997. pBuffer->m_Overlapped.SeoOverlapped.Overlapped.Offset = m_dwFileOffset;
  3998. pBuffer->m_Overlapped.SeoOverlapped.Overlapped.pfnCompletion = FIOInternetCompletion;
  3999. pBuffer->m_Overlapped.SeoOverlapped.ThisPtr = this;
  4000. // pBuffer->m_Overlapped.m_pIoBuffer = (LPBYTE)InputLine;
  4001. //
  4002. // increment the overall pending io count for this session
  4003. //
  4004. IncPendingIoCount();
  4005. if ( FIOReadFile(m_IMsgDotStuffedFileHandle ? m_IMsgDotStuffedFileHandle : m_IMsgFileHandle,
  4006. lpData,
  4007. cb,
  4008. &pBuffer->m_Overlapped.SeoOverlapped.Overlapped ) == FALSE ) {
  4009. DecPendingIoCount();
  4010. ErrorTrace( (LPARAM)this, "AtqReadFile failed.");
  4011. } else {
  4012. TraceFunctLeaveEx((LPARAM) this);
  4013. return TRUE;
  4014. }
  4015. }
  4016. delete pBuffer;
  4017. }
  4018. m_Error = GetLastError();
  4019. ErrorTrace( (LPARAM)this, "MessageReadFile failed. err: %d", m_Error );
  4020. DisconnectClient();
  4021. TraceFunctLeaveEx((LPARAM)this);
  4022. return FALSE;
  4023. }
  4024. /*++
  4025. Name :
  4026. SMTP_CONNOUT::FreeAtqFileContext
  4027. Description :
  4028. Frees AtqContext associated with message files transfered using
  4029. async reads and writes.
  4030. Arguments :
  4031. None. Operates on m_pAtqFileContext
  4032. Returns :
  4033. Nothing
  4034. --*/
  4035. void SMTP_CONNOUT::FreeAtqFileContext( void )
  4036. {
  4037. PATQ_CONTEXT pAtq;
  4038. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::FreeAtqFileContext");
  4039. #if 0
  4040. pAtq = (PATQ_CONTEXT)InterlockedExchangePointer( (PVOID *)&m_pAtqFileContext, (PVOID) NULL );
  4041. if ( pAtq != NULL ) {
  4042. DebugTrace((LPARAM) this, "Freeing AtqFileContext!");
  4043. pAtq->hAsyncIO = NULL;
  4044. AtqFreeContext( pAtq, FALSE );
  4045. }
  4046. #endif
  4047. TraceFunctLeaveEx((LPARAM) this);
  4048. }
  4049. /*++
  4050. Name :
  4051. SMTP_CONNOUT::DoCompletedMessage
  4052. Description:
  4053. Sends the SMTP QUIT command.
  4054. This function always returns false.
  4055. This will stop all processing and
  4056. delete this object.
  4057. Arguments:
  4058. Are ignored
  4059. Returns:
  4060. Always return FALSE
  4061. --*/
  4062. BOOL SMTP_CONNOUT::DoCompletedMessage(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  4063. {
  4064. BOOL fRet = TRUE;
  4065. DWORD MsgStatus = 0;
  4066. DWORD Error = 0;
  4067. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoCompletedMessage");
  4068. //see if the response received has some bad code
  4069. if (InputLine[0] != SMTP_COMPLETE_SUCCESS) {
  4070. // NimishK :
  4071. //Assumption : If I get 5XX response that leads to DOQUIT it means something real bad happened.
  4072. //We will consider this as a permanent error and NDR all recipients
  4073. //If the code is 4XX we will Retry all recipients
  4074. if (InputLine[0] == SMTP_COMPLETE_FAILURE) {
  4075. MsgStatus = MESSAGE_STATUS_NDR_ALL | MESSAGE_STATUS_EXTENDED_STATUS_CODES;
  4076. } else {
  4077. MsgStatus = MESSAGE_STATUS_RETRY_ALL | MESSAGE_STATUS_EXTENDED_STATUS_CODES;
  4078. if (m_fHadHardError) {
  4079. MsgStatus |= MESSAGE_STATUS_CHECK_RECIPS;
  4080. }
  4081. }
  4082. if (m_ResponseContext.m_cabResponse.Length() > 1) {
  4083. InputLine = m_ResponseContext.m_cabResponse.Buffer();
  4084. ParameterSize = m_ResponseContext.m_cabResponse.Length();
  4085. Error = m_ResponseContext.m_dwSmtpStatus;
  4086. }
  4087. } else if (m_Error == NO_ERROR) {
  4088. //if the message was delivered successfully
  4089. //then bump up our counter
  4090. BUMP_COUNTER(QuerySmtpInstance(), NumMsgsSent);
  4091. if (!IsOptionSet(DSN_OPTION)) {
  4092. MsgStatus |= MESSAGE_STATUS_DSN_NOT_SUPPORTED;
  4093. }
  4094. //If we have generate DELAY DSN or NDR DSN we need to tell
  4095. //AQ to look at the recipients
  4096. if (m_fNeedRelayedDSN || m_fHadHardError) {
  4097. MsgStatus |= MESSAGE_STATUS_CHECK_RECIPS;
  4098. }
  4099. //If we had no failures we can set this special status for optimization
  4100. //if only error we had was a hard error - there is no reason to report anything
  4101. if (!m_fHadTempError && !m_fHadHardError )
  4102. MsgStatus |= MESSAGE_STATUS_ALL_DELIVERED;
  4103. else if (m_fHadTempError)
  4104. MsgStatus |= MESSAGE_STATUS_RETRY_ALL;
  4105. } else {
  4106. //The remote server did not have a problem, but we had internal
  4107. //problem
  4108. //NimishK : Add an extended Status
  4109. MsgStatus = MESSAGE_STATUS_RETRY_ALL;
  4110. }
  4111. //figure out what to do with the completed
  4112. //message(e.g. send to retryq, remoteq, badmail,
  4113. //etc.)
  4114. //Includes per recipient 4xx level errors
  4115. if (InputLine[0] == SMTP_TRANSIENT_FAILURE) {
  4116. //If we cannot connect to the next IpAddress, then ack the message as is
  4117. if (ConnectToNextIpAddress()) {
  4118. //$$REVIEW - mikeswa 9/11/98
  4119. //In this case we will attempt to connect to another IP address.
  4120. //Most of the state is reset at this point (except for recipient
  4121. //failures and status strings). What happens if we fail to connect?
  4122. //Do we:
  4123. // - Ack the message as RETRY (current implementation)
  4124. // - Attempt to ack the message with CHECK_RECIPS as well and
  4125. // let the per-recip flags be enought detail for the DSN code?
  4126. TraceFunctLeaveEx((LPARAM)this);
  4127. return (FALSE);
  4128. }
  4129. }
  4130. HandleCompletedMailObj(MsgStatus, InputLine, ParameterSize);
  4131. if (InputLine[0] == SMTP_TRANSIENT_FAILURE
  4132. || (Error == SMTP_SERVICE_UNAVAILABLE_CODE)
  4133. || (Error == SMTP_INSUFF_STORAGE_CODE)|| (Error == SMTP_SERVICE_CLOSE_CODE)
  4134. || QuerySmtpInstance()->IsShuttingDown()) {
  4135. //No point in continuing with other messages in this connection
  4136. //Set the connection ack status to DROPPED - AQ will look at my last
  4137. //Msg Ack and determine what to do with remaining messages
  4138. m_dwConnectionStatus = CONNECTION_STATUS_DROPPED;
  4139. m_Active = FALSE;
  4140. DebugTrace( (LPARAM)this, "got this error %u on response, calling DoSessionEndEvent", Error );
  4141. fRet = DoSessionEndEvent(InputLine, ParameterSize, UndecryptedTailSize);
  4142. } else {
  4143. fRet = StartSession();
  4144. if (!fRet) {
  4145. //Set the connection Ack status
  4146. m_dwConnectionStatus = CONNECTION_STATUS_OK;
  4147. DebugTrace( (LPARAM)this, "StartSession failed, calling DoSessionEndEvent", Error );
  4148. fRet = DoSessionEndEvent(InputLine, ParameterSize, UndecryptedTailSize);
  4149. }
  4150. }
  4151. TraceFunctLeaveEx((LPARAM)this);
  4152. return (fRet);
  4153. }
  4154. /*++
  4155. Name :
  4156. SMTP_CONNOUT::DoQUITCommand
  4157. Description:
  4158. Just generates a QUIT command
  4159. Arguments:
  4160. Are ignored
  4161. Returns:
  4162. Always return FALSE
  4163. --*/
  4164. BOOL SMTP_CONNOUT::DoQUITCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  4165. {
  4166. BOOL fRet = TRUE;
  4167. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoQUITCommand");
  4168. m_OutboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  4169. m_OutboundContext.m_cabNativeCommand.Reset();
  4170. fRet = PE_AppendSmtpMessage("QUIT\r\n");
  4171. TraceFunctLeaveEx((LPARAM)this);
  4172. return (fRet);
  4173. }
  4174. /*++
  4175. Name :
  4176. SMTP_CONNOUT::WaitForRSETResponse
  4177. Description:
  4178. Waits for the response to the RSET command
  4179. Arguments:
  4180. The remote SMTP *MUST* send "250" as the status code in the RSET
  4181. response according to RFC 2821. This function ends the session if
  4182. the first digit of the RSET response is other than '2'.
  4183. Returns:
  4184. FALSE if object should be deleted, and session dropped
  4185. TRUE otherwise
  4186. --*/
  4187. BOOL SMTP_CONNOUT::WaitForRSETResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  4188. {
  4189. BOOL fRet = TRUE;
  4190. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::WaitForRSETResponse");
  4191. _ASSERT (QuerySmtpInstance() != NULL);
  4192. //
  4193. // The response is not RFC-compliant. It lacks the 3 digit status - drop
  4194. // connection. We need to do the minimum possible amount of processing of
  4195. // the input from this point on. Many SMTP_CONNOUT functions make assumptions
  4196. // about the well-formedness of the input, and will have problems if an
  4197. // RFC-violating InputLine is passed into them. For example DoCompletedMessage
  4198. // assumes that InputLine[0] is a digit at the very least. The safe thing to
  4199. // do is to disconnect and delete this object.
  4200. //
  4201. if(ParameterSize < 5 || !isdigit((UCHAR)InputLine[0]) ||
  4202. !isdigit((UCHAR)InputLine[1]) || !isdigit((UCHAR)InputLine[2]))
  4203. {
  4204. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, "RSET", NULL);
  4205. ErrorTrace((LPARAM)this, "Disconnecting: Bad remote SMTP response");
  4206. TraceFunctLeaveEx((LPARAM)this);
  4207. return FALSE;
  4208. }
  4209. if (m_RsetReasonCode == BETWEEN_MSG)
  4210. fRet = DoMessageStartEvent(InputLine, ParameterSize, UndecryptedTailSize);
  4211. else if (m_RsetReasonCode == ALL_RCPTS_FAILED) {
  4212. DebugTrace( (LPARAM)this, "m_RsetReasonCode = ALL_RCPTS_FAILED calling DoCompletedMessage" );
  4213. //Check to see if all of the failures were hard (and not transient)
  4214. //If so, set the error code to '5' instead of '4'
  4215. if (!m_fHadTempError) { //no temp errors
  4216. _ASSERT(m_fHadHardError); //therefore all errors must have been 5xx
  4217. //Must have had as many failures as processed RCPT TO's
  4218. _ASSERT(m_NumFailedAddrs >= m_NumRcptSentSaved);
  4219. InputLine[0] = SMTP_COMPLETE_FAILURE;
  4220. } else {
  4221. InputLine[0] = SMTP_TRANSIENT_FAILURE;
  4222. }
  4223. fRet = DoCompletedMessage(InputLine, ParameterSize, UndecryptedTailSize);
  4224. } else if (m_RsetReasonCode == NO_RCPTS_SENT) {
  4225. DebugTrace( (LPARAM)this, "m_RsetReasonCode = NO_RCPTS_SENT calling DoCompletedMessage" );
  4226. InputLine[0] = '2';
  4227. fRet = DoCompletedMessage(InputLine, ParameterSize, UndecryptedTailSize);
  4228. } else if (m_RsetReasonCode == FATAL_ERROR) {
  4229. DebugTrace( (LPARAM)this, "m_RsetReasonCode = FATAL_ERROR calling DoCompletedMessage" );
  4230. //make sure the quit code does not think everything is O.K.
  4231. InputLine[0] = '4';
  4232. fRet = DoCompletedMessage(InputLine, ParameterSize, UndecryptedTailSize);
  4233. }
  4234. return (fRet);
  4235. }
  4236. /*++
  4237. Name :
  4238. SMTP_CONNOUT::WaitForQuitResponse
  4239. Description:
  4240. Waits for the response to the quit command
  4241. Arguments:
  4242. Are ignored
  4243. Returns:
  4244. FALSE always
  4245. --*/
  4246. BOOL SMTP_CONNOUT::WaitForQuitResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  4247. {
  4248. _ASSERT (QuerySmtpInstance() != NULL);
  4249. DisconnectClient();
  4250. return (FALSE);
  4251. }
  4252. BOOL SMTP_CONNOUT::DoTURNCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize)
  4253. {
  4254. CLIENT_CONN_PARAMS clientParams;
  4255. SMTP_CONNECTION * SmtpConnIn = NULL;
  4256. DWORD Error = 0;
  4257. TraceFunctEnterEx((LPARAM) this,"SMTP_CONNOUT::DoTURNCommand");
  4258. if (InputLine[0] != SMTP_COMPLETE_SUCCESS) {
  4259. SetDiagnosticInfo(AQUEUE_E_SMTP_PROTOCOL_ERROR, "TURN", InputLine);
  4260. FormatSmtpMessage(FSM_LOG_ALL, "QUIT\r\n");
  4261. SendSmtpResponse();
  4262. DisconnectClient();
  4263. return FALSE;
  4264. }
  4265. SOCKADDR_IN sockAddr;
  4266. int cbAddr = sizeof( sockAddr);
  4267. if ( getsockname((SOCKET) m_pAtqContext->hAsyncIO,
  4268. (struct sockaddr *) &sockAddr,
  4269. &cbAddr )) {
  4270. }
  4271. clientParams.sClient = (SOCKET) m_pAtqContext->hAsyncIO;
  4272. clientParams.pAtqContext = m_pAtqContext;
  4273. clientParams.pAddrLocal = (PSOCKADDR) NULL;
  4274. clientParams.pAddrRemote = (PSOCKADDR)&sockAddr;
  4275. clientParams.pvInitialBuff = NULL;
  4276. clientParams.cbInitialBuff = 0 ;
  4277. clientParams.pEndpoint = (PIIS_ENDPOINT)NULL;
  4278. QuerySmtpInstance()->Reference();
  4279. QuerySmtpInstance()->IncrementCurrentConnections();
  4280. SmtpConnIn = (SMTP_CONNECTION *) QuerySmtpInstance()->CreateNewConnection( &clientParams);
  4281. if (SmtpConnIn == NULL) {
  4282. Error = GetLastError();
  4283. SendSmtpResponse();
  4284. DisconnectClient();
  4285. TraceFunctLeaveEx((LPARAM) this);
  4286. return FALSE;
  4287. }
  4288. //from here on, the smtpout class is responsible for
  4289. //cleaning up the AtqContext
  4290. m_DoCleanup = FALSE;
  4291. //copy the real domain we are connected to.
  4292. AtqContextSetInfo(m_pAtqContext, ATQ_INFO_COMPLETION, (DWORD_PTR) InternetCompletion);
  4293. AtqContextSetInfo(m_pAtqContext, ATQ_INFO_COMPLETION_CONTEXT, (DWORD_PTR) SmtpConnIn);
  4294. AtqContextSetInfo(m_pAtqContext, ATQ_INFO_TIMEOUT, QuerySmtpInstance()->GetRemoteTimeOut());
  4295. if (SmtpConnIn->StartSession()) {
  4296. }
  4297. return ( FALSE );
  4298. }
  4299. /*++
  4300. Name :
  4301. BOOL SMTP_CONNOUT::SwitchToBigReceiveBuffer
  4302. Description:
  4303. Helper routine to allocate a 32K buffer and use it for posting reads.
  4304. SSL fragments can be up to 32K large, and we need to accumulate an
  4305. entire fragment to be able to decrypt it.
  4306. Arguments:
  4307. none
  4308. Returns:
  4309. TRUE if the receive buffer was successfully allocated, FALSE otherwise
  4310. --*/
  4311. BOOL SMTP_CONNOUT::SwitchToBigSSLBuffers(void)
  4312. {
  4313. char *pTempBuffer;
  4314. pTempBuffer = new char [MAX_SSL_FRAGMENT_SIZE];
  4315. if (pTempBuffer != NULL) {
  4316. m_precvBuffer = pTempBuffer;
  4317. m_cbMaxRecvBuffer = MAX_SSL_FRAGMENT_SIZE;
  4318. pTempBuffer = new char [MAX_SSL_FRAGMENT_SIZE];
  4319. if (pTempBuffer != NULL) {
  4320. m_pOutputBuffer = pTempBuffer;
  4321. m_cbMaxOutputBuffer = MAX_SSL_FRAGMENT_SIZE;
  4322. return ( TRUE );
  4323. }
  4324. }
  4325. return ( FALSE );
  4326. }
  4327. /*++
  4328. Name :
  4329. char * IsLineCompleteBW
  4330. Description:
  4331. Looks for a CRLF starting at the back
  4332. of the buffer. This is from some Gibraltar
  4333. code.
  4334. Arguments:
  4335. pchRecv - The buffer to be scanned
  4336. cbRecvd - size of data in the buffer
  4337. Returns:
  4338. A pointer where the CR is if both CRLF is found,
  4339. or NULL
  4340. --*/
  4341. char * IsLineCompleteBW(IN OUT char * pchRecvd, IN DWORD cbRecvd, IN DWORD cbMaxRecvBuffer)
  4342. {
  4343. register int Loop;
  4344. _ASSERT( pchRecvd != NULL);
  4345. if (cbRecvd == 0)
  4346. return NULL;
  4347. if (cbRecvd > cbMaxRecvBuffer)
  4348. return NULL;
  4349. //
  4350. // Scan the entire buffer ( from back) looking for pattern <cr><lf>
  4351. //
  4352. for ( Loop = (int) (cbRecvd - 2); Loop >= 0; Loop-- ) {
  4353. //
  4354. // Check if consecutive characters are <cr> <lf>
  4355. //
  4356. if ( ( pchRecvd[Loop] == '\r') &&
  4357. ( pchRecvd[Loop + 1]== '\n')) {
  4358. //return where the CR is in the buffer
  4359. return &pchRecvd[Loop];
  4360. }
  4361. } // for
  4362. return (NULL);
  4363. }
  4364. /*++
  4365. Name :
  4366. SMTP_CONNOUT::PE_AppendSmtpMessage( IN const char * Text)
  4367. Description:
  4368. This function o places it's data in the native command output buffer.
  4369. Arguments:
  4370. Text - Data to place in the buffer
  4371. Returns:
  4372. --*/
  4373. BOOL SMTP_CONNOUT::PE_AppendSmtpMessage(IN char *Text)
  4374. {
  4375. return SUCCEEDED (m_OutboundContext.m_cabNativeCommand.Append(Text, strlen(Text), NULL));
  4376. }
  4377. //---[ SMTP_CONNOUT::SetDiagnosticInfo ]---------------------------------------
  4378. //
  4379. //
  4380. // Description:
  4381. // Sets member diagnostic information that is later Ack'd back to AQueue
  4382. // Parameters:
  4383. // IN hrDiagnosticError Error code... if SUCCESS we thow away
  4384. // the rest of the information
  4385. // IN szDiagnosticVerb Verb that caused the failure or NULL
  4386. // if it was not a protocol failure.
  4387. // IN szDiagnosticResponse String that contains the remote
  4388. // servers response or NULL if the
  4389. // failure was not a protocol failure.
  4390. // Returns:
  4391. // -
  4392. // History:
  4393. // 2/18/99 - MikeSwa Created
  4394. // 7/12/99 - GPulla Modified
  4395. //
  4396. //-----------------------------------------------------------------------------
  4397. void SMTP_CONNOUT::SetDiagnosticInfo(IN HRESULT hrDiagnosticError,
  4398. IN LPCSTR szDiagnosticVerb,
  4399. IN LPCSTR szDiagnosticResponse)
  4400. {
  4401. //
  4402. // Guard against SMTP bugs where we set the diagnostic multiple times.
  4403. // In 99.9% cases the first diagnostic is the one we want to keep (since
  4404. // it is the one that was set closest to the original error).
  4405. //
  4406. if(m_hrDiagnosticError != S_OK)
  4407. return;
  4408. if(m_szDiagnosticVerb)
  4409. free(m_szDiagnosticVerb);
  4410. m_hrDiagnosticError = hrDiagnosticError;
  4411. if(szDiagnosticVerb)
  4412. m_szDiagnosticVerb = _strdup(szDiagnosticVerb);
  4413. else
  4414. m_szDiagnosticVerb = NULL;
  4415. ZeroMemory(&m_szDiagnosticResponse, sizeof(m_szDiagnosticResponse));
  4416. //Force terminating NULL
  4417. m_szDiagnosticResponse[sizeof(m_szDiagnosticResponse)-1] = '\0';
  4418. //Not an SMTP protocol failure
  4419. if(!szDiagnosticResponse) return;
  4420. //copy buffers
  4421. strncpy(m_szDiagnosticResponse, szDiagnosticResponse,
  4422. sizeof(m_szDiagnosticResponse)-1);
  4423. }
  4424. //---[ SMTP_CONNOUT::GetSessionPropertiesFromAQ ]-----------------------------
  4425. //
  4426. //
  4427. // Description:
  4428. // Calls into AQ's Connection object to get properties for a session object
  4429. // This is primarily used to allow sinks to know who AQ thinks we are
  4430. // connecting to.
  4431. // Parameters:
  4432. // -
  4433. // Returns:
  4434. // -
  4435. // History:
  4436. // 11/27/2001 - MikeSwa Created
  4437. //
  4438. //-----------------------------------------------------------------------------
  4439. VOID SMTP_CONNOUT::GetSessionPropertiesFromAQ()
  4440. {
  4441. TraceFunctEnterEx((LPARAM)this,
  4442. "SMTP_CONNOUT::GetSessionPropertiesFromAQ");
  4443. IConnectionPropertyManagement *pIConnectionPropertyManagement = NULL;
  4444. HRESULT hr = S_OK;
  4445. if (!m_pISMTPConnection) {
  4446. DebugTrace((LPARAM) this, "No ISMTPConnection... bailing");
  4447. goto Exit;
  4448. }
  4449. hr = m_pISMTPConnection->QueryInterface(IID_IConnectionPropertyManagement,
  4450. (LPVOID *) &pIConnectionPropertyManagement);
  4451. if (FAILED(hr)) {
  4452. pIConnectionPropertyManagement = NULL;
  4453. // if talking to an old AQ... this is expected
  4454. DebugTrace((LPARAM) this,
  4455. "Cannot QI for IConnectionPropertyManagement 0x%08X", hr);
  4456. goto Exit;
  4457. }
  4458. hr = pIConnectionPropertyManagement->CopyQueuePropertiesToSession(
  4459. GetSessionPropertyBag());
  4460. if (FAILED(hr))
  4461. ErrorTrace((LPARAM) this,
  4462. "CopyQueuePropertiesToSession failed - 0x%08X", hr);
  4463. Exit:
  4464. if (pIConnectionPropertyManagement)
  4465. pIConnectionPropertyManagement->Release();
  4466. TraceFunctLeave();
  4467. }
  4468. //---[ SMTP_CONNOUT::PromoteSessionPropertiesToAQ ]-----------------------------
  4469. //
  4470. //
  4471. // Description:
  4472. // Calls into AQ's Connection object to get properties for a session object
  4473. // This is primarily used to allow sinks to know who AQ thinks we are
  4474. // connecting to.
  4475. //
  4476. // This is static to allow remoteq to call in as well (for connections
  4477. // that fail before a SMTP_CONNOUT object is created).
  4478. // Parameters:
  4479. // pISession Session properties for connection
  4480. // pISMTPConnection AQ connection object
  4481. // Returns:
  4482. // -
  4483. // History:
  4484. // 11/29/2001 - MikeSwa Created
  4485. //
  4486. //-----------------------------------------------------------------------------
  4487. VOID SMTP_CONNOUT::PromoteSessionPropertiesToAQ(IUnknown *pISession,
  4488. ISMTPConnection *pISMTPConnection)
  4489. {
  4490. TraceFunctEnterEx((LPARAM)pISession,
  4491. "SMTP_CONNOUT::PromoteSessionPropertiesToAQ");
  4492. IConnectionPropertyManagement *pIConnectionPropertyManagement = NULL;
  4493. HRESULT hr = S_OK;
  4494. if (!pISMTPConnection) {
  4495. DebugTrace((LPARAM) pISession, "No ISMTPConnection... bailing");
  4496. goto Exit;
  4497. }
  4498. hr = pISMTPConnection->QueryInterface(IID_IConnectionPropertyManagement,
  4499. (LPVOID *) &pIConnectionPropertyManagement);
  4500. if (FAILED(hr)) {
  4501. pIConnectionPropertyManagement = NULL;
  4502. // if talking to an old AQ... this is expected
  4503. DebugTrace((LPARAM) pISession,
  4504. "Cannot QI for IConnectionPropertyManagement 0x%08X", hr);
  4505. goto Exit;
  4506. }
  4507. hr = pIConnectionPropertyManagement->CopySessionPropertiesToQueue(
  4508. pISession);
  4509. if (FAILED(hr))
  4510. ErrorTrace((LPARAM) pISession,
  4511. "CopyQueuePropertiesToSession failed - 0x%08X", hr);
  4512. Exit:
  4513. if (pIConnectionPropertyManagement)
  4514. pIConnectionPropertyManagement->Release();
  4515. TraceFunctLeave();
  4516. }
  4517. //---[ SMTP_CONNOUT::CopyRemoteIPAddressToSession ]---------------------------
  4518. //
  4519. //
  4520. // Description:
  4521. // Copies the CLIENT_CONNECTION data into the session property bag used by
  4522. // inbound and outbound connections
  4523. // Parameters:
  4524. // -
  4525. // Returns:
  4526. // -
  4527. // History:
  4528. // 11/28/2001 - MikeSwa Created
  4529. //
  4530. //-----------------------------------------------------------------------------
  4531. VOID SMTP_CONNOUT::CopyRemoteIPAddressToSession()
  4532. {
  4533. TraceFunctEnterEx((LPARAM) this,
  4534. "SSMTP_CONNOUT::CopyRemoteIPAddressToSession");
  4535. HRESULT hr = S_OK;
  4536. IMailMsgPropertyBag *pISessionProperties =
  4537. (IMailMsgPropertyBag *)GetSessionPropertyBag();
  4538. if (!pISessionProperties) {
  4539. ErrorTrace((LPARAM) this, "NULL ISession - bailing");
  4540. goto Exit;
  4541. }
  4542. hr = pISessionProperties->PutStringA(ISESSION_PID_REMOTE_IP_ADDRESS,
  4543. QueryClientHostName());
  4544. if (FAILED(hr)) {
  4545. //Trace error... otherwise ignore.
  4546. ErrorTrace((LPARAM) this,
  4547. "Failed to write ISESSION_PID_REMOTE_IP_ADDRESS 0x%08X", hr);
  4548. }
  4549. Exit:
  4550. TraceFunctLeave();
  4551. }
  4552. /************************ End of File ***********************/