Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5392 lines
174 KiB

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