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.

9924 lines
310 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name :
  4. smtpcli.cxx
  5. Abstract:
  6. This module defines the functions for derived class of connections
  7. for Internet Services ( class SMTP_CONNECTION)
  8. Author:
  9. Rohan Phillips ( Rohanp ) 11-Dec-1995
  10. Project:
  11. SMTP Server DLL
  12. Functions Exported:
  13. SMTP_CONNECTION::~SMTP_CONNECTION()
  14. BOOL SMTP_CONNECTION::ProcessClient( IN DWORD cbWritten,
  15. IN DWORD dwCompletionStatus,
  16. IN BOOL fIOCompletion)
  17. BOOL SMTP_CONNECTION::StartupSession( VOID)
  18. Revision History:
  19. --*/
  20. /************************************************************
  21. * Include Headers
  22. ************************************************************/
  23. #define INCL_INETSRV_INCS
  24. #include "smtpinc.h"
  25. //
  26. // ATL includes
  27. //
  28. #define _ATL_NO_DEBUG_CRT
  29. #define _ASSERTE _ASSERT
  30. #define _WINDLL
  31. #include "atlbase.h"
  32. extern CComModule _Module;
  33. #include "atlcom.h"
  34. #undef _WINDLL
  35. //
  36. // SEO includes
  37. //
  38. #include "seo.h"
  39. #include "seolib.h"
  40. #include <memory.h>
  41. #include <issperr.h>
  42. #include "smtpcli.hxx"
  43. #include "headers.hxx"
  44. #include "timeconv.h"
  45. #include "base64.hxx"
  46. #include "smtpout.hxx"
  47. #include <smtpevents.h>
  48. #include <smtpevent.h>
  49. #include <smtpguid.h>
  50. //
  51. // Dispatcher implementation
  52. //
  53. #include "pe_dispi.hxx"
  54. int strcasecmp(char *s1, char *s2);
  55. int strncasecmp(char *s1, char *s2, int n);
  56. #define IMSG_PROGID L"Exchange.IMsg"
  57. #define MAILMSG_PROGID L"Exchange.MailMsg"
  58. extern CHAR g_VersionString[];
  59. // provide memory for static declared in SMTP_CONNECTION
  60. CPool SMTP_CONNECTION::Pool(CLIENT_CONNECTION_SIGNATURE_VALID);
  61. static char * HelpText= "214-This server supports the following commands:\r\n"
  62. "214 HELO EHLO STARTTLS RCPT DATA RSET MAIL QUIT HELP";
  63. static char * Daynames[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  64. static const char * SMTP_TOO_MANY_RCPTS = "Too many recipients";
  65. static const char * PasswordParam = "Password:";
  66. static const char * UserParam = "Username:";
  67. static LONG g_NumRoutingThreads = 0;
  68. LONG g_cProcessClientThreads = 0;
  69. // Format strings for "Received:" lines
  70. static const char szFormatReceivedFormatSuccess[] = "Received: from %s ([%s]) by %s%s with Microsoft SMTPSVC(%s);\r\n\t %s, %s\r\n";
  71. static const char szFormatReceivedFormatUnverified[] = "Received: from %s ([%s] unverified) by %s%s with Microsoft SMTPSVC(%s);\r\n\t %s, %s\r\n";
  72. static const char szFormatReceivedFormatFailed[] = "Received: from %s ([%s] RDNS failed) by %s%s with Microsoft SMTPSVC(%s);\r\n\t %s, %s\r\n";
  73. // Search strings to identify our own "Received:" lines (subsections of above lines)
  74. static const char szFormatReceivedServer[] = ") by %s";
  75. static const char szFormatReceivedService[] = " with Microsoft SMTPSVC(";
  76. // Amount of time (FILETIME / 100ns units) we wait if we detect a looping message
  77. #define SMTP_LOOP_DELAY 6000000000 // == 10 minutes
  78. #define SMTP_TIMEOUT 99
  79. #define SMTP_CONTENT_FILE_IO_TIMEOUT 5*60*1000
  80. #define ATQ_LOCATOR (DWORD)'Dd9D'
  81. //ATQ Write file modes
  82. #define BLOCKING 0
  83. #define NONBLOCKING 1
  84. //Trailer byte status
  85. #define CRLF_NEEDED 0
  86. #define CR_SEEN 1
  87. #define CRLF_SEEN 2
  88. #define CR_MISSING 3
  89. #define WHITESPACE " \t\r\n"
  90. /************************************************************
  91. * Functions
  92. ************************************************************/
  93. extern void GenerateMessageId (char * Buffer, DWORD BuffLen);
  94. extern DWORD GetIncreasingMsgId();
  95. extern VOID InternetCompletion(PVOID pvContext, DWORD cbWritten,
  96. DWORD dwCompletionStatus, OVERLAPPED * lpo);
  97. /*++
  98. Name:
  99. SMTP_CONNECTION::SMTP_CONNECTION
  100. Constructs a new SMTP connection object for the client
  101. connection given the client connection socket and socket
  102. address. This constructor is private. Only the Static
  103. member funtion, declared below, can call it.
  104. Arguments:
  105. sClient socket for communicating with client
  106. psockAddrRemote pointer to address of the remote client
  107. ( the value should be copied).
  108. psockAddrLocal pointer to address for the local card through
  109. which the client came in.
  110. pAtqContext pointer to ATQ Context used for AcceptEx'ed conn.
  111. pvInitialRequest pointer to void buffer containing the initial request
  112. cbInitialData count of bytes of data read initially.
  113. --*/
  114. SMTP_CONNECTION::SMTP_CONNECTION(
  115. IN PSMTP_SERVER_INSTANCE pInstance,
  116. IN SOCKET sClient,
  117. IN const SOCKADDR_IN * psockAddrRemote,
  118. IN const SOCKADDR_IN * psockAddrLocal /* = NULL */ ,
  119. IN PATQ_CONTEXT pAtqContext /* = NULL */ ,
  120. IN PVOID pvInitialRequest/* = NULL*/ ,
  121. IN DWORD cbInitialData /* = 0 */
  122. )
  123. : m_encryptCtx(FALSE),
  124. m_securityCtx(pInstance,
  125. TCPAUTH_SERVER| TCPAUTH_UUENCODE,
  126. ((PSMTP_SERVER_INSTANCE)pInstance)->QueryAuthentication()),
  127. CLIENT_CONNECTION ( sClient, psockAddrRemote,
  128. psockAddrLocal, pAtqContext,
  129. pvInitialRequest, cbInitialData )
  130. {
  131. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::SMTP_CONNECTION");
  132. DebugTrace( (LPARAM)this, "New connection created");
  133. _ASSERT(pInstance != NULL);
  134. //
  135. // By default, we use the smallish receive buffer inherited from
  136. // the case CLIENT_CONNECTION object.
  137. //
  138. m_precvBuffer = m_recvBuffer;
  139. m_cbMaxRecvBuffer = sizeof(m_recvBuffer);
  140. m_pOutputBuffer = m_OutputBuffer;
  141. m_cbMaxOutputBuffer = sizeof(m_OutputBuffer);
  142. m_pIMsg = NULL;
  143. m_pIMsgRecips = NULL;
  144. m_szHeloAddr[0] = '\0';
  145. // m_FromAddr = NULL;
  146. m_szFromAddress[0] = '\0';
  147. m_pszArgs = NULL;
  148. m_pInstance = pInstance;
  149. m_IMsgHandle = NULL;
  150. m_pBindInterface = NULL;
  151. m_cbRecvBufferOffset = 0;
  152. m_pFileWriteBuffer = NULL;
  153. m_pFileWriteBuffer = new CBuffer();
  154. m_HopCount = 0;
  155. m_LocalHopCount = 0;
  156. if(!m_pFileWriteBuffer)
  157. {
  158. ErrorTrace((LPARAM)this, "Failed to get the write buffer Err : %d",
  159. GetLastError());
  160. _ASSERT(0);
  161. }
  162. m_AtqLocator = ATQ_LOCATOR;
  163. // inbound protocol extension related initializations
  164. m_fAsyncEventCompletion = FALSE;
  165. m_fAsyncEOD = FALSE;
  166. m_fIsPeUnderway = FALSE;
  167. m_pIEventRouter = NULL;
  168. m_pCInboundDispatcher = NULL;
  169. // m_CInboundContext is a data member
  170. m_fPeBlobReady = FALSE;
  171. m_pPeBlobCallback = NULL;
  172. ZeroMemory(&m_FileOverLapped, sizeof(m_FileOverLapped));
  173. m_FileOverLapped.SeoOverlapped.Overlapped.pfnCompletion = SmtpCompletionFIO;
  174. m_FileOverLapped.SeoOverlapped.ThisPtr = this;
  175. m_LineCompletionState = SEEN_NOTHING;
  176. m_Truncate = FALSE;
  177. m_BufrWasFull = FALSE;
  178. m_szUsedAuthKeyword[0] = '\0';
  179. m_szAuthenticatedUserNameFromSink[0] = '\0';
  180. //keep track of per instance connection object
  181. pInstance->IncConnInObjs();
  182. TraceFunctLeaveEx((LPARAM) this);
  183. }
  184. SMTP_CONNECTION::~SMTP_CONNECTION (void)
  185. {
  186. PSMTP_SERVER_INSTANCE pInstance = NULL;
  187. CAddr * pAddress = NULL;
  188. char *pTempBuffer = NULL;
  189. // CBuffer * pBuff;
  190. TraceFunctEnterEx((LPARAM)this, "~SMTP_CONNECTION");
  191. ReleasImsg(TRUE);
  192. /*
  193. pAddress = (CAddr *) InterlockedExchangePointer((PVOID *) &m_FromAddr, (PVOID) NULL);
  194. if(pAddress != NULL)
  195. {
  196. delete pAddress;
  197. }*/
  198. pInstance = (PSMTP_SERVER_INSTANCE ) InterlockedExchangePointer((PVOID *) &m_pInstance, (PVOID) NULL);
  199. if(pInstance != NULL)
  200. {
  201. pInstance->DecrementCurrentConnections();
  202. pInstance->DecConnInObjs();
  203. pInstance->Dereference();
  204. }
  205. pTempBuffer = (char *) InterlockedExchangePointer((PVOID *) &m_precvBuffer, (PVOID) &m_recvBuffer[0]);
  206. if (pTempBuffer != m_recvBuffer) {
  207. delete [] pTempBuffer;
  208. }
  209. pTempBuffer = (char *) InterlockedExchangePointer((PVOID *) &m_pOutputBuffer, (PVOID) &m_OutputBuffer[0]);
  210. if (pTempBuffer != m_OutputBuffer) {
  211. delete [] pTempBuffer;
  212. }
  213. if(m_pFileWriteBuffer)
  214. {
  215. delete m_pFileWriteBuffer;
  216. }
  217. if ( NULL != m_pPeBlobCallback) {
  218. m_pPeBlobCallback->Release();
  219. }
  220. if(m_pCInboundDispatcher)
  221. m_pCInboundDispatcher->Release();
  222. DebugTrace( (LPARAM)this, "Connection deleted");
  223. TraceFunctLeaveEx((LPARAM)this);
  224. }
  225. /*++
  226. Name :
  227. SMTP_CONNECTION::InitializeObject
  228. Description:
  229. Initializes all member variables and pre-allocates
  230. a mail context class
  231. Arguments:
  232. Returns:
  233. TRUE if memory can be allocated.
  234. FALSE if no memory can be allocated
  235. --*/
  236. BOOL SMTP_CONNECTION::InitializeObject (BOOL IsSecureConnection)
  237. {
  238. BOOL fRet = TRUE;
  239. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::InitializeObject");
  240. _ASSERT(m_pInstance != NULL);
  241. StartProcessingTimer();
  242. m_cbReceived = 0;
  243. m_cbParsable = 0;
  244. m_cbTempBDATLen = 0;
  245. m_OutputBufferSize = 0;
  246. m_cbCurrentWriteBuffer = 0;
  247. m_TotalMsgSize = 0;
  248. m_SessionSize = 0;
  249. m_cbDotStrippedTotalMsgSize = 0;
  250. m_ProtocolErrors = 0;
  251. m_dwUnsuccessfulLogons = 0;
  252. m_HeaderSize = 0;
  253. m_cPendingIoCount = 0;
  254. m_cActiveThreads = 0;
  255. m_CurrentOffset = 0;
  256. m_HelloSent = FALSE;
  257. m_RecvdMailCmd = FALSE;
  258. m_RecvdAuthCmd = FALSE;
  259. m_Nooped = FALSE;
  260. m_TimeToRewriteHeader = TRUE;
  261. m_RecvdRcptCmd = FALSE;
  262. m_InHeader = TRUE;
  263. m_LongBodyLine = FALSE;
  264. m_fFoundEmbeddedCrlfDotCrlf = FALSE;
  265. m_fScannedForCrlfDotCrlf = FALSE;
  266. m_fSeenRFC822FromAddress = FALSE;
  267. m_fSeenRFC822ToAddress = FALSE;
  268. m_fSeenRFC822CcAddress = FALSE;
  269. m_fSeenRFC822BccAddress = FALSE;
  270. m_fSeenRFC822Subject = FALSE;
  271. m_fSeenRFC822MsgId = FALSE;
  272. m_fSeenRFC822SenderAddress = FALSE;
  273. m_fSeenXPriority = FALSE;
  274. m_fSeenXOriginalArrivalTime = FALSE;
  275. m_fSeenContentType = FALSE;
  276. m_fSetContentType = FALSE;
  277. m_HeaderFlags = 0;
  278. m_MailBodyError = NO_ERROR;
  279. m_State = HELO;
  280. m_NeedToQuit = FALSE;
  281. m_pSmtpStats = NULL;
  282. m_WritingData = FALSE;
  283. //m_fReverseDnsFailed = FALSE;
  284. m_DNSLookupRetCode = SUCCESS;
  285. m_fUseMbsCta = FALSE;
  286. m_fAuthenticated = FALSE;
  287. m_fClearText = FALSE;
  288. m_fDidUserCmd = FALSE;
  289. m_fAuthAnon = FALSE;
  290. m_SecurePort = IsSecureConnection;
  291. m_fNegotiatingSSL = m_SecurePort;
  292. m_szUserName[0] = '\0';
  293. m_szUsedAuthKeyword[0] = '\0';
  294. m_cbRecvBufferOffset = 0;
  295. m_fPeBlobReady = FALSE;
  296. if ( NULL != m_pPeBlobCallback) {
  297. m_pPeBlobCallback->Release();
  298. }
  299. m_pPeBlobCallback = NULL;
  300. m_fIsLastChunk = FALSE;
  301. m_fIsBinaryMime = FALSE;
  302. m_fIsChunkComplete = FALSE;
  303. m_dwTrailerStatus = CRLF_SEEN;
  304. m_nChunkSize = 0;
  305. m_nBytesRemainingInChunk = 0;
  306. m_MailBodyDiagnostic = ERR_NONE;
  307. m_LineCompletionState = SEEN_NOTHING;
  308. m_Truncate = FALSE;
  309. m_BufrWasFull = FALSE;
  310. m_fBufferFullInBDAT = FALSE;
  311. m_pInstance->LockGenCrit();
  312. if(m_pInstance->QueryAuthentication() & INET_INFO_AUTH_ANONYMOUS)
  313. {
  314. m_fAuthAnon = TRUE;
  315. }
  316. //
  317. // Initialize Security Context
  318. //
  319. if (!m_securityCtx.SetInstanceAuthPackageNames(
  320. m_pInstance->GetProviderPackagesCount(),
  321. m_pInstance->GetProviderNames(),
  322. m_pInstance->GetProviderPackages()))
  323. {
  324. ErrorTrace((LPARAM)this, "SetInstanceAuthPackageNames FAILED <Err=%u>",
  325. GetLastError());
  326. fRet = FALSE;
  327. }
  328. //
  329. // We want to set up the Cleartext authentication package
  330. // for this connection based on the instance configuration.
  331. // To enable MBS CTA,
  332. // MD_SMTP_CLEARTEXT_AUTH_PROVIDER must be set to the package name.
  333. // To disable it, the md value must be set to "".
  334. //
  335. if(fRet)
  336. {
  337. m_securityCtx.SetCleartextPackageName(
  338. m_pInstance->GetCleartextAuthPackage(),
  339. m_pInstance->GetMembershipBroker());
  340. if (*m_pInstance->GetCleartextAuthPackage() == '\0' ||
  341. *m_pInstance->GetMembershipBroker() == '\0')
  342. {
  343. m_fUseMbsCta = FALSE;
  344. }
  345. else
  346. {
  347. m_fUseMbsCta = TRUE;
  348. }
  349. }
  350. //m_EventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
  351. m_pInstance->UnLockGenCrit();
  352. TraceFunctLeaveEx((LPARAM) this);
  353. return fRet;
  354. }
  355. /*++
  356. Name :
  357. SMTP_CONNECTION::CreateSmtpConnection
  358. Description:
  359. This is the static member function than is the only
  360. entity that is allowed to create an SMTP_CONNECTION
  361. class. This class cannot be allocated on the stack.
  362. Arguments:
  363. sClient socket for communicating with client
  364. psockAddrRemote pointer to address of the remote client
  365. ( the value should be copied).
  366. psockAddrLocal pointer to address for the local card through
  367. which the client came in.
  368. pAtqContext pointer to ATQ Context used for AcceptEx'ed conn.
  369. pvInitialRequest pointer to void buffer containing the initial request
  370. cbInitialData count of bytes of data read initially.
  371. Returns:
  372. A pointer to an SMTP_CONNECTION class or NULL
  373. --*/
  374. SMTP_CONNECTION * SMTP_CONNECTION::CreateSmtpConnection (IN PCLIENT_CONN_PARAMS ClientParams,
  375. IN PSMTP_SERVER_INSTANCE pInst)
  376. {
  377. SMTP_CONNECTION * pSmtpObj = NULL;
  378. PIIS_ENDPOINT pTmpEndPoint = NULL;
  379. pSmtpObj = new SMTP_CONNECTION (pInst, ClientParams->sClient, (const SOCKADDR_IN *) ClientParams->pAddrRemote, (const SOCKADDR_IN *) ClientParams->pAddrLocal,
  380. ClientParams->pAtqContext, ClientParams->pvInitialBuff, ClientParams->cbInitialBuff);
  381. if(pSmtpObj == NULL)
  382. {
  383. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  384. return NULL;
  385. }
  386. pTmpEndPoint = (PIIS_ENDPOINT)ClientParams->pEndpoint;
  387. if(!pSmtpObj->InitializeObject(FALSE))
  388. {
  389. delete pSmtpObj;
  390. return NULL;
  391. }
  392. return pSmtpObj;
  393. }
  394. #define MAX_LOG_ERROR_LEN (500)
  395. void SMTP_CONNECTION::TransactionLog(
  396. const char *pszOperation,
  397. const char *pszParameters,
  398. const char *pszTarget,
  399. DWORD dwWin32Error,
  400. DWORD dwServiceSpecificStatus
  401. )
  402. {
  403. INETLOG_INFORMATION translog;
  404. DWORD dwLog;
  405. DWORD cchError = MAX_LOG_ERROR_LEN;
  406. char VersionString[] = "SMTP";
  407. char szParametersBuffer[1024] = ""; //Data portion of buffer information
  408. ZeroMemory(&translog, sizeof(translog));
  409. translog.pszVersion = VersionString;
  410. translog.pszClientHostName = (char *) QueryClientHostName();
  411. translog.cbClientHostName = lstrlen(translog.pszClientHostName);
  412. translog.pszClientUserName = (char *) QueryClientUserName();
  413. translog.pszServerAddress = (char *) QueryLocalHostName();
  414. translog.pszOperation = (char *)pszOperation;
  415. translog.cbOperation = lstrlen ((char *)pszOperation);
  416. translog.pszTarget = (char *)pszTarget;
  417. translog.cbTarget = lstrlen ((char *)pszTarget);
  418. translog.pszParameters = (char *)pszParameters;
  419. if (pszParameters) {
  420. lstrcpyn(szParametersBuffer, pszParameters, sizeof(szParametersBuffer)-sizeof(CHAR));
  421. translog.pszParameters = szParametersBuffer;
  422. } else {
  423. translog.pszParameters = "";
  424. }
  425. translog.dwBytesSent = m_dwCmdBytesSent;
  426. translog.dwBytesRecvd = m_dwCmdBytesRecv;
  427. translog.dwWin32Status = dwWin32Error;
  428. translog.dwProtocolStatus = dwServiceSpecificStatus;
  429. translog.msTimeForProcessing = QueryProcessingTime();
  430. if(QuerySmtpInstance() != NULL)
  431. dwLog = QuerySmtpInstance()->m_Logging.LogInformation( &translog);
  432. }
  433. /*++
  434. Name :
  435. SMTP_CONNECTION::SendSmtpResponse
  436. Description:
  437. This function sends data that was queued in the internal
  438. m_pOutputBuffer buffer
  439. Arguments:
  440. SyncSend - Flag that signifies sync or async send
  441. Returns:
  442. TRUE if the string was sent. False otherwise
  443. --*/
  444. BOOL SMTP_CONNECTION::SendSmtpResponse(BOOL SyncSend)
  445. {
  446. BOOL RetStatus = TRUE;
  447. DWORD cbMessage = m_OutputBufferSize;
  448. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::SendSmtpResponse");
  449. //IncPendingIoCount();
  450. //if m_OutputBufferSize > 0that means there is
  451. //something in the buffer, therefore, we will send it.
  452. if (m_OutputBufferSize)
  453. {
  454. //
  455. // If we are using SSL, encrypt the output buffer now. Note that
  456. // FormatSmtpMsg already left header space for the seal header.
  457. //
  458. if (m_SecurePort && !m_fNegotiatingSSL)
  459. {
  460. char *Buffer = &(m_pOutputBuffer[m_encryptCtx.GetSealHeaderSize()]);
  461. RetStatus = m_encryptCtx.SealMessage(
  462. (UCHAR *) Buffer,
  463. m_OutputBufferSize,
  464. (UCHAR *) m_pOutputBuffer,
  465. &cbMessage);
  466. }
  467. if (RetStatus) {
  468. RetStatus = CLIENT_CONNECTION::WriteFile(m_pOutputBuffer, cbMessage);
  469. }
  470. if(RetStatus)
  471. {
  472. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesSentTotal, cbMessage);
  473. AddCommandBytesSent(cbMessage);
  474. }
  475. else
  476. {
  477. DebugTrace((LPARAM) this, "WriteFile failed with error %d", GetLastError());
  478. }
  479. m_OutputBufferSize = 0;
  480. }
  481. //DecPendingIoCount();
  482. TraceFunctLeaveEx((LPARAM) this);
  483. return RetStatus;
  484. }
  485. BOOL SMTP_CONNECTION::WriteMailFile(char * Buffer, DWORD BuffSize, BOOL *lpfWritePended)
  486. {
  487. BOOL fResult = FALSE;
  488. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::WriteMailFileBuffer");
  489. if(!BuffSize && !Buffer)
  490. {
  491. //Simply flush the buffers if we have something to flush
  492. if(!m_cbCurrentWriteBuffer)
  493. {//Aynsc WRITE succeeded. Let this thread go thru - atq will call back
  494. *lpfWritePended = FALSE;
  495. return TRUE;
  496. }
  497. if(!WriteMailFileAtq((char*)m_pFileWriteBuffer->GetData(), m_cbCurrentWriteBuffer, NONBLOCKING))
  498. {
  499. m_MailBodyError = GetLastError();
  500. ErrorTrace((LPARAM) this, "Async Write of to mail file failed : %d",m_MailBodyError);
  501. fResult = FALSE;
  502. }
  503. else
  504. {
  505. //Aynsc WRITE succeeded. Let this thread go thru - atq will call back
  506. *lpfWritePended = TRUE;
  507. fResult = TRUE;
  508. }
  509. TraceFunctLeaveEx((LPARAM) this);
  510. return fResult;
  511. }
  512. //If the data to be written can fit in we take it in and come back
  513. //else we pend a write
  514. //Though the var name is a result of its original use, we now use this
  515. //buffer as a WRITEFILE buffer
  516. if(m_cbCurrentWriteBuffer + BuffSize > SMTP_WRITE_BUFFER_SIZE)
  517. {
  518. //Update the input buffers so that we can
  519. //go back and start processing the input buffers
  520. //after the async WRITE completes
  521. //MoveMemory ((void *)QueryMRcvBuffer(), Buffer, m_cbReceived);
  522. if(!WriteMailFileAtq((char*)m_pFileWriteBuffer->GetData(), m_cbCurrentWriteBuffer, NONBLOCKING))
  523. {
  524. *lpfWritePended = FALSE;
  525. m_MailBodyError = GetLastError();
  526. ErrorTrace((LPARAM) this, "Async Write of to mail file failed : %d",m_MailBodyError);
  527. fResult = FALSE;
  528. }
  529. else
  530. {
  531. //Aynsc WRITE succeeded. Let this thread go thru - atq will call back
  532. *lpfWritePended = TRUE;
  533. fResult = TRUE;
  534. }
  535. }
  536. //Copy the data into the buffer only if we have the space to do so
  537. else
  538. {
  539. CopyMemory((m_pFileWriteBuffer->GetData() + m_cbCurrentWriteBuffer),Buffer,BuffSize);
  540. m_cbCurrentWriteBuffer += BuffSize;
  541. fResult = TRUE;
  542. }
  543. TraceFunctLeaveEx((LPARAM) this);
  544. return fResult;
  545. }
  546. /*++
  547. Name:
  548. SMTP_CONNECTION::WriteMailFileAtq
  549. Description:
  550. While chunking, once header parsing is done the data is
  551. dumped to the disk asynchronously using WriteMailFileAtq( )
  552. Arguments:
  553. None.
  554. Returns:
  555. TRUE if successfully pended a write to the file, FALSE otherwise
  556. --*/
  557. BOOL SMTP_CONNECTION::WriteMailFileAtq(char * InputLine, DWORD dwBytesToWrite, DWORD dwIoMode)
  558. {
  559. TraceFunctEnterEx( (LPARAM)this, "SMTP_CONNECTION::WriteMailFileAtq");
  560. DebugTrace( (LPARAM)this, "WriteFile 0x%08X, len: %d, LPO: 0x%08X",
  561. InputLine,
  562. dwBytesToWrite,
  563. m_FileOverLapped.SeoOverlapped );
  564. //ZeroMemory( (void*)&m_FileOverLapped, sizeof(OVERLAPPED) );
  565. //if(dwIoMode == BLOCKING)
  566. // m_FileOverLapped.SeoOverlapped.Overlapped.hEvent = (HANDLE)1;
  567. m_FileOverLapped.SeoOverlapped.Overlapped.Offset = m_CurrentOffset;
  568. m_FileOverLapped.m_LastIoState = WRITEFILEIO;
  569. m_FileOverLapped.m_cbIoSize = dwBytesToWrite;
  570. m_FileOverLapped.m_pIoBuffer = (LPBYTE)InputLine;
  571. //
  572. // increment the overall pending io count for this session
  573. //
  574. IncPendingIoCount();
  575. _ASSERT(m_FileOverLapped.SeoOverlapped.Overlapped.pfnCompletion != NULL);
  576. m_FileOverLapped.SeoOverlapped.ThisPtr = this;
  577. if (FIOWriteFile(m_IMsgHandle,
  578. InputLine,
  579. dwBytesToWrite,
  580. &(m_FileOverLapped.SeoOverlapped.Overlapped)) == FALSE)
  581. #if 0
  582. if ( AtqWriteFile(m_pAtqFileContext,
  583. InputLine,
  584. dwBytesToWrite,
  585. (OVERLAPPED *) &(m_FileOverLapped.SeoOverlapped.Overlapped) ) == FALSE)
  586. #endif
  587. {
  588. DecPendingIoCount();
  589. ErrorTrace( (LPARAM)this, "AtqWriteFile failed.");
  590. }
  591. else
  592. {
  593. TraceFunctLeaveEx((LPARAM) this);
  594. return TRUE;
  595. }
  596. ErrorTrace( (LPARAM)this, "WriteMailFileAtq failed. err: %d", GetLastError() );
  597. if(!HandleInternalError(GetLastError()))
  598. DisconnectClient();
  599. TraceFunctLeaveEx((LPARAM)this);
  600. return FALSE;
  601. }
  602. /*++
  603. Name:
  604. SMTP_CONNECTION::HandleInternalError
  605. Description:
  606. Contains code to handle common error conditions during inbound message flow.
  607. Arguments:
  608. [IN] DWORD Error code
  609. Returns:
  610. TRUE if error was handled
  611. FALSE otherwise
  612. --*/
  613. BOOL SMTP_CONNECTION::HandleInternalError(DWORD dwErr)
  614. {
  615. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::HandleInternalError");
  616. if(dwErr == ERROR_DISK_FULL)
  617. {
  618. FormatSmtpMessage(SMTP_RESP_NORESOURCES, NULL, " Unable to accept message because the server is out of disk space.\r\n");
  619. ErrorTrace((LPARAM) this, "Rejecting message: Out of disk space");
  620. SendSmtpResponse();
  621. DisconnectClient();
  622. return TRUE;
  623. }
  624. return FALSE;
  625. }
  626. #if 0
  627. /*++
  628. Name :
  629. SMTP_CONNECTION::FreeAtqFileContext
  630. Description :
  631. Frees AtqContext associated with message file used for doing async writes
  632. in case of chunking.
  633. Arguments :
  634. None. Operates on m_pAtqFileContext
  635. Returns :
  636. Nothing
  637. --*/
  638. void SMTP_CONNECTION::FreeAtqFileContext( void )
  639. {
  640. PFIO_CONTEXT pFIO;
  641. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::FreeAtqFileContext");
  642. pFIO = (PATQ_CONTEXT)InterlockedExchangePointer( (PVOID *)&m_pIMsgHandle, NULL );
  643. if ( pFIO != NULL )
  644. {
  645. DebugTrace((LPARAM) this, "Freeing AtqFileContext!");
  646. ReleaseContext(pFIO);
  647. }
  648. TraceFunctLeaveEx((LPARAM) this);
  649. }
  650. #endif
  651. void SMTP_CONNECTION::ReInitClassVariables(void)
  652. {
  653. _ASSERT (m_pInstance != NULL);
  654. //reset all variables to their initial state
  655. m_HeaderFlags = 0;
  656. m_TotalMsgSize = 0;
  657. m_cbDotStrippedTotalMsgSize = 0;
  658. m_CurrentOffset = 0;
  659. m_State = HELO;
  660. m_RecvdMailCmd = FALSE;
  661. m_InHeader = TRUE;
  662. m_LongBodyLine = FALSE;
  663. m_fFoundEmbeddedCrlfDotCrlf = FALSE;
  664. m_fScannedForCrlfDotCrlf = FALSE;
  665. m_fSeenRFC822FromAddress = FALSE;
  666. m_fSeenRFC822ToAddress = FALSE;
  667. m_fSeenRFC822CcAddress = FALSE;
  668. m_fSeenRFC822BccAddress = FALSE;
  669. m_fSeenRFC822Subject = FALSE;
  670. m_fSeenRFC822MsgId = FALSE;
  671. m_fSeenXPriority = FALSE;
  672. m_fSeenXOriginalArrivalTime = FALSE;
  673. m_fSeenContentType = FALSE;
  674. m_fSetContentType = FALSE;
  675. m_fSeenRFC822SenderAddress = FALSE;
  676. m_TimeToRewriteHeader = TRUE;
  677. m_MailBodyError = NO_ERROR;
  678. m_RecvdRcptCmd = FALSE;
  679. m_WritingData = FALSE;
  680. m_fIsLastChunk = FALSE;
  681. m_fIsBinaryMime = FALSE;
  682. m_fIsChunkComplete = FALSE;
  683. m_dwTrailerStatus = CRLF_SEEN;
  684. m_nChunkSize = 0;
  685. m_nBytesRemainingInChunk = 0;
  686. m_MailBodyDiagnostic = ERR_NONE;
  687. m_cbCurrentWriteBuffer = 0;
  688. m_cbRecvBufferOffset = 0;
  689. m_fAsyncEOD = FALSE;
  690. m_LineCompletionState = SEEN_NOTHING;
  691. m_Truncate = FALSE;
  692. m_BufrWasFull = FALSE;
  693. m_fBufferFullInBDAT = FALSE;
  694. //Clear the senders address if it's
  695. //still there.
  696. m_szFromAddress[0] = '\0';
  697. ReleasImsg(TRUE);
  698. }
  699. /*++
  700. Name :
  701. SMTP_CONNECTION::SmtpGetCommand
  702. Description:
  703. This function determines which SMTP input command was sent
  704. by the client
  705. Arguments:
  706. Request - Buffer the client sent
  707. RequestLen - Length of the buffer
  708. Returns:
  709. Index into our array of function pointers
  710. --*/
  711. int SMTP_CONNECTION::SmtpGetCommand(const char * Request, DWORD RequestLen, LPDWORD CmdSize)
  712. {
  713. DWORD Loop = 0;
  714. char Cmd[SMTP_MAX_COMMANDLINE_LEN];
  715. char * ptr = NULL;
  716. char *psearch = NULL;
  717. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::SmtpGetCommand");
  718. //start out with no errors
  719. CommandErrorType = SMTP_COMMAND_NOERROR;
  720. //we will copy the command into Cmd starting from
  721. //the beginning
  722. ptr = Cmd;
  723. //start looking for the space from the beginning
  724. //of the buffer
  725. psearch = (char *) Request;
  726. //search for the command and copy it into Cmd. We stop
  727. //looking when we encounter the first space.
  728. while (*psearch != '\0' && !isspace (*psearch) && ptr < &Cmd[SMTP_MAX_COMMANDLINE_LEN - 2])
  729. *ptr++ = *psearch++;
  730. //null terminate the buffer
  731. *ptr = '\0';
  732. //now, look through the list of SMTP commands
  733. //and compare it to what the client gave us.
  734. while (SmtpCommands[Loop] != NULL)
  735. {
  736. if (!::strcasecmp((char *) Cmd, (char * )SmtpCommands[Loop]))
  737. {
  738. goto Exit;
  739. }
  740. Loop++;
  741. }
  742. //set error to COMMAND_NOT FOUND if we do not recognize the command
  743. CommandErrorType = SMTP_COMMAND_NOT_FOUND;
  744. Exit:
  745. //If no error, send back the index in the array
  746. //If there was an error, send back the index of
  747. //the last element in the array. This is our
  748. //catch all error function.
  749. if (CommandErrorType == SMTP_COMMAND_NOERROR)
  750. {
  751. *CmdSize = DWORD (ptr - Cmd);
  752. TraceFunctLeaveEx((LPARAM) this);
  753. return Loop;
  754. }
  755. else
  756. {
  757. ErrorTrace((LPARAM) this, "Command %s was not found in command table", Cmd);
  758. TraceFunctLeaveEx((LPARAM) this);
  759. return (DWORD) LAST_SMTP_STATE;
  760. }
  761. }
  762. /*++
  763. Name :
  764. SMTP_CONNECTION::FormatSmtpMessage( IN const char * Format, ...)
  765. Description:
  766. This function will build a sting in the output buffer with
  767. the given input parameters. If the new data cannot fit in the
  768. buffer, this function will send whatever data is in the buffer,
  769. then build the string.
  770. Arguments:
  771. String to send
  772. Returns:
  773. TRUE if response was queued
  774. --*/
  775. BOOL SMTP_CONNECTION::FormatSmtpMessage(DWORD dwCode, const char * szEnhancedCodes, IN const char * Format, ...)
  776. {
  777. int BytesWritten;
  778. va_list arglist;
  779. char *Buffer;
  780. DWORD AvailableBytes;
  781. DWORD HeaderOffset = (m_SecurePort ? m_encryptCtx.GetSealHeaderSize() : 0);
  782. DWORD SealOverhead = (m_SecurePort ?
  783. (m_encryptCtx.GetSealHeaderSize() +
  784. m_encryptCtx.GetSealTrailerSize()) : 0);
  785. char RealFormat[MAX_PATH];
  786. RealFormat[0] = '\0';
  787. //If we get passed dwCode we use it
  788. //If we get passed enhance status code only if we advertise them
  789. //
  790. if(dwCode)
  791. {
  792. if(m_pInstance->AllowEnhancedCodes() && szEnhancedCodes)
  793. {
  794. sprintf(RealFormat,"%d %s",dwCode,szEnhancedCodes);
  795. }
  796. else
  797. sprintf(RealFormat,"%d",dwCode);
  798. }
  799. strcat(RealFormat,Format);
  800. Buffer = &(m_pOutputBuffer[m_OutputBufferSize + HeaderOffset]);
  801. AvailableBytes = m_cbMaxOutputBuffer - m_OutputBufferSize - SealOverhead;
  802. //if BytesWritten is < 0, that means there is no space
  803. //left in the buffer. Therefore, we flush any pending
  804. //responses to make space. Then we try to place the
  805. //information in the buffer again. It should never
  806. //fail this time.
  807. va_start (arglist, Format);
  808. BytesWritten = _vsnprintf (Buffer, AvailableBytes, (const char *)RealFormat, arglist);
  809. if(BytesWritten < 0)
  810. {
  811. //flush any pending response
  812. SendSmtpResponse();
  813. _ASSERT (m_OutputBufferSize == 0);
  814. Buffer = &m_pOutputBuffer[HeaderOffset];
  815. AvailableBytes = m_cbMaxOutputBuffer - SealOverhead;
  816. BytesWritten = _vsnprintf (Buffer, AvailableBytes, Format, arglist);
  817. _ASSERT (BytesWritten > 0);
  818. }
  819. va_end(arglist);
  820. m_OutputBufferSize += (DWORD) BytesWritten;
  821. //m_OutputBufferSize += vsprintf (&m_pOutputBuffer[m_OutputBufferSize], Format, arglist);
  822. return TRUE;
  823. }
  824. BOOL SMTP_CONNECTION::FormatSmtpMessage(unsigned char *DataBuffer, DWORD dwBytes)
  825. {
  826. int BytesWritten;
  827. char *Buffer;
  828. DWORD AvailableBytes = 0;
  829. DWORD HeaderOffset = (m_SecurePort ? m_encryptCtx.GetSealHeaderSize() : 0);
  830. DWORD SealOverhead = (m_SecurePort ?
  831. (m_encryptCtx.GetSealHeaderSize() +
  832. m_encryptCtx.GetSealTrailerSize()) : 0);
  833. Buffer = &(m_pOutputBuffer[m_OutputBufferSize + HeaderOffset]);
  834. AvailableBytes = m_cbMaxOutputBuffer - m_OutputBufferSize - SealOverhead;
  835. //if BytesWritten is < 0, that means there is no space
  836. //left in the buffer. Therefore, we flush any pending
  837. //responses to make space. Then we try to place the
  838. //information in the buffer again. It should never
  839. //fail this time.
  840. if( dwBytes + 2 < AvailableBytes)//+2 for CRLF
  841. {
  842. CopyMemory(Buffer, DataBuffer, dwBytes);
  843. Buffer[dwBytes] = '\r';
  844. Buffer[dwBytes + 1] = '\n';
  845. BytesWritten = dwBytes + 2;
  846. }
  847. else
  848. {
  849. //flush any pending response
  850. SendSmtpResponse();
  851. _ASSERT (m_OutputBufferSize == 0);
  852. Buffer = &m_pOutputBuffer[HeaderOffset];
  853. AvailableBytes = m_cbMaxOutputBuffer - SealOverhead;
  854. CopyMemory(Buffer, DataBuffer, dwBytes);
  855. Buffer[dwBytes] = '\r';
  856. Buffer[dwBytes + 1] = '\n';
  857. BytesWritten = dwBytes + 2;
  858. _ASSERT (BytesWritten > 0);
  859. }
  860. m_OutputBufferSize += (DWORD) BytesWritten;
  861. return TRUE;
  862. }
  863. BOOL SMTP_CONNECTION::PE_FastFormatSmtpMessage(LPSTR pszBuffer, DWORD dwBytes)
  864. {
  865. LPSTR Buffer;
  866. DWORD BytesWritten = 0;
  867. DWORD AvailableBytes = 0;
  868. DWORD HeaderOffset = (m_SecurePort ? m_encryptCtx.GetSealHeaderSize() : 0);
  869. DWORD SealOverhead = (m_SecurePort ?
  870. (m_encryptCtx.GetSealHeaderSize() +
  871. m_encryptCtx.GetSealTrailerSize()) : 0);
  872. Buffer = &(m_pOutputBuffer[m_OutputBufferSize + HeaderOffset]);
  873. AvailableBytes = m_cbMaxOutputBuffer - m_OutputBufferSize - SealOverhead;
  874. // Write as many buffers as we need to
  875. while (BytesWritten < dwBytes)
  876. {
  877. if ((dwBytes - BytesWritten) <= AvailableBytes)
  878. {
  879. CopyMemory(Buffer, pszBuffer, dwBytes - BytesWritten);
  880. m_OutputBufferSize += (dwBytes - BytesWritten);
  881. BytesWritten = dwBytes; // BytesWritten += (dwBytes - BytesWritten)
  882. }
  883. else
  884. {
  885. // We don't have enough buffer space, so write whatever we have left
  886. CopyMemory(Buffer, pszBuffer, AvailableBytes);
  887. BytesWritten += AvailableBytes;
  888. m_OutputBufferSize += AvailableBytes;
  889. pszBuffer += AvailableBytes;
  890. //flush any pending response
  891. SendSmtpResponse();
  892. _ASSERT (m_OutputBufferSize == 0);
  893. Buffer = &m_pOutputBuffer[HeaderOffset];
  894. AvailableBytes = m_cbMaxOutputBuffer - SealOverhead;
  895. }
  896. }
  897. return(TRUE);
  898. }
  899. BOOL SMTP_CONNECTION::DoesClientHaveIpAccess()
  900. {
  901. AC_RESULT acIpAccess;
  902. ADDRESS_CHECK acAccessCheck;
  903. METADATA_REF_HANDLER rfAccessCheck;
  904. BOOL fNeedDnsCheck = FALSE;
  905. BOOL fRet = TRUE;
  906. struct hostent* pH = NULL;
  907. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::DoesClientHaveIpAccess");
  908. m_pInstance->LockGenCrit();
  909. acAccessCheck.BindAddr( (PSOCKADDR)&m_saClient );
  910. if ( !rfAccessCheck.CopyFrom( m_pInstance->QueryRelayMetaDataRefHandler() ) )
  911. {
  912. m_pInstance->UnLockGenCrit();
  913. TraceFunctLeaveEx((LPARAM)this);
  914. return FALSE;
  915. }
  916. m_pInstance->UnLockGenCrit();
  917. acAccessCheck.BindCheckList( (LPBYTE)rfAccessCheck.GetPtr(), rfAccessCheck.GetSize() );
  918. acIpAccess = acAccessCheck.CheckIpAccess( &fNeedDnsCheck);
  919. if ( (acIpAccess == AC_IN_DENY_LIST) ||
  920. ((acIpAccess == AC_NOT_IN_GRANT_LIST) && !fNeedDnsCheck) )
  921. {
  922. fRet = FALSE;
  923. }
  924. else if (fNeedDnsCheck)
  925. {
  926. pH = gethostbyaddr( (char*)(&((PSOCKADDR_IN)(&m_saClient))->sin_addr),
  927. 4, PF_INET );
  928. if(pH != NULL)
  929. {
  930. acIpAccess = acAccessCheck.CheckName(pH->h_name);
  931. }
  932. else
  933. {
  934. acIpAccess = AC_IN_DENY_LIST;
  935. }
  936. }
  937. if ( (acIpAccess == AC_IN_DENY_LIST) ||
  938. (acIpAccess == AC_NOT_IN_GRANT_LIST))
  939. {
  940. fRet = FALSE;
  941. }
  942. acAccessCheck.UnbindCheckList();
  943. rfAccessCheck.Reset( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  944. if(!fRet)
  945. {
  946. SetLastError(ERROR_ACCESS_DENIED);
  947. }
  948. TraceFunctLeaveEx((LPARAM)this);
  949. return fRet;
  950. }
  951. /*++
  952. Name :
  953. SMTP_CONNECTION::ProcessReadIO
  954. Description:
  955. This function gets a buffer from ATQ, parses the buffer to
  956. find out what command the client sent, then executes that
  957. cammnd.
  958. Arguments:
  959. InputBufferLen - Number of bytes that was written
  960. dwCompletionStatus -Holds error code from ATQ, if any
  961. lpo - Pointer to overlapped structure
  962. Returns:
  963. TRUE if the connection should stay open.
  964. FALSE if this object should be deleted.
  965. --*/
  966. BOOL SMTP_CONNECTION::ProcessReadIO(IN DWORD InputBufferLen,
  967. IN DWORD dwCompletionStatus,
  968. IN OUT OVERLAPPED * lpo)
  969. {
  970. BOOL fReturn = TRUE;
  971. const char * InputLine;
  972. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::ProcessReadIO");
  973. _ASSERT (m_pInstance != NULL);
  974. m_cbReceived += InputBufferLen;
  975. // Firewall against bugs where we overflow the read buffer
  976. if(m_cbReceived > QueryMaxReadSize())
  977. {
  978. DisconnectClient();
  979. _ASSERT(0 && "Buffer overflow");
  980. return FALSE;
  981. }
  982. InputLine = QueryMRcvBuffer();
  983. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesRcvdTotal, InputBufferLen);
  984. if (!m_fNegotiatingSSL)
  985. {
  986. if (m_SecurePort)
  987. {
  988. fReturn = DecryptInputBuffer();
  989. }
  990. else
  991. {
  992. m_cbParsable = m_cbReceived;
  993. }
  994. if (fReturn)
  995. {
  996. fReturn = ProcessInputBuffer();
  997. }
  998. }
  999. else // negotiating SSL
  1000. {
  1001. //
  1002. // If we are still doing the handshake let CEncryptCtx::Converse handle it.
  1003. // Converse returns in the following situations:
  1004. //
  1005. // o The negotiation succeeded. cbExtra returns the number of bytes that
  1006. // were not consumed by the SSL handshake. These bytes are "application
  1007. // data" which should be decrypted and processed by the SMTP protocol.
  1008. //
  1009. // o The negotiation failed. The connection will be dropped.
  1010. //
  1011. // o More data is needed from the client to complete the negotiation. A
  1012. // read should be posted.
  1013. //
  1014. DWORD dw;
  1015. BOOL fMore;
  1016. DWORD dwBytes = MAX_SSL_FRAGMENT_SIZE;
  1017. BOOL fApplicationDataAvailable = FALSE;
  1018. DWORD cbExtra = 0;
  1019. dw = m_encryptCtx.Converse( (LPVOID) InputLine,
  1020. m_cbReceived,
  1021. (PUCHAR) m_pOutputBuffer,
  1022. &dwBytes,
  1023. &fMore,
  1024. (LPSTR) QueryLocalHostName(),
  1025. (LPSTR) QueryLocalPortName(),
  1026. (LPVOID) QuerySmtpInstance(),
  1027. QuerySmtpInstance()->QueryInstanceId(),
  1028. &cbExtra
  1029. );
  1030. if ( dw == NO_ERROR )
  1031. {
  1032. //
  1033. // reset the read buffer
  1034. //
  1035. if ( cbExtra )
  1036. {
  1037. fApplicationDataAvailable = TRUE;
  1038. MoveMemory( (PVOID)InputLine, InputLine + (m_cbReceived - cbExtra), cbExtra );
  1039. }
  1040. m_cbReceived = cbExtra;
  1041. //
  1042. // send any bytes required for the client
  1043. //
  1044. if ( dwBytes != 0 )
  1045. {
  1046. WriteFile( m_pOutputBuffer, dwBytes );
  1047. }
  1048. if ( fMore )
  1049. {
  1050. //
  1051. // more handshaking required - repost the read
  1052. //
  1053. _ASSERT( dwBytes != 0 );
  1054. }
  1055. else
  1056. {
  1057. //
  1058. // completed negotiation. Turn off the flag indicating thats
  1059. // what we are doing
  1060. //
  1061. m_fNegotiatingSSL = FALSE;
  1062. }
  1063. }
  1064. else if ( dw == SEC_E_INCOMPLETE_MESSAGE )
  1065. {
  1066. //
  1067. // haven't received the full packet from the client
  1068. //
  1069. _ASSERT( dwBytes == 0 );
  1070. }
  1071. else
  1072. {
  1073. ErrorTrace((LPARAM)this, "SSL handshake failed, Error = %d", dw);
  1074. DisconnectClient( dw );
  1075. fReturn = FALSE;
  1076. }
  1077. if ( fApplicationDataAvailable )
  1078. {
  1079. //
  1080. // Application data is already available, no need to post a read
  1081. //
  1082. fReturn = DecryptInputBuffer();
  1083. if ( fReturn )
  1084. fReturn = ProcessInputBuffer();
  1085. }
  1086. else if (fReturn)
  1087. {
  1088. //
  1089. // If we are continuing, we need to post a read
  1090. //
  1091. _ASSERT (m_cbReceived < QueryMaxReadSize() );
  1092. IncPendingIoCount();
  1093. m_LastClientIo = READIO;
  1094. fReturn = ReadFile( QueryMRcvBuffer() + m_cbReceived,
  1095. QueryMaxReadSize() - m_cbReceived );
  1096. if (!fReturn)
  1097. {
  1098. DisconnectClient();
  1099. DecPendingIoCount();
  1100. }
  1101. }
  1102. else
  1103. {
  1104. m_CInboundContext.SetWin32Status(dw);
  1105. ProtocolLog(STARTTLS, (char *) QueryClientUserName(), dw, SMTP_RESP_BAD_SEQ, 0, 0);
  1106. }
  1107. }
  1108. return( fReturn );
  1109. }
  1110. /*++
  1111. Name :
  1112. SMTP_CONNECTION::ProcessFileWrite
  1113. Description:
  1114. Handles completion of an async Write issued against a message file by
  1115. WriteMailFileAtq
  1116. Arguments:
  1117. cbRead count of bytes read
  1118. dwCompletionStatus Error code for IO operation
  1119. lpo Overlapped structure
  1120. Returns:
  1121. TRUE if connection should stay open
  1122. FALSE if this object should be deleted
  1123. --*/
  1124. BOOL SMTP_CONNECTION::ProcessFileWrite(
  1125. IN DWORD BytesWritten,
  1126. IN DWORD dwCompletionStatus,
  1127. IN OUT OVERLAPPED *lpo
  1128. )
  1129. {
  1130. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::ProcessFileWrite");
  1131. SMTPCLI_FILE_OVERLAPPED * lpFileOverlapped;
  1132. _ASSERT(lpo);
  1133. lpFileOverlapped = (SMTPCLI_FILE_OVERLAPPED*)lpo;
  1134. // In case of async Write to disk file, a successsful completion
  1135. // will always mean all the data has been written out
  1136. // Check for partial completions or errors
  1137. // and Disconnect if something failed
  1138. if( BytesWritten != lpFileOverlapped->m_cbIoSize || dwCompletionStatus != NO_ERROR )
  1139. {
  1140. ErrorTrace( (LPARAM)this,
  1141. "Message WriteFile error: %d, bytes %d, expected %d",
  1142. dwCompletionStatus,
  1143. BytesWritten,
  1144. lpFileOverlapped->m_cbIoSize );
  1145. //Close the file Handle and disconnect the client
  1146. DisconnectClient();
  1147. return( FALSE );
  1148. }
  1149. else
  1150. {
  1151. DebugTrace( (LPARAM)this,
  1152. "WriteFile complete. bytes %d, lpo: 0x%08X",
  1153. BytesWritten, lpo );
  1154. }
  1155. m_CurrentOffset += BytesWritten;
  1156. //We write out of the Write buffer
  1157. //We need throw away the data that was written out and
  1158. //move the remaining data to the start of the buffer
  1159. // if(m_cbReceived)
  1160. // MoveMemory ((void *)QueryMRcvBuffer(), (void *)(QueryMRcvBuffer() + BytesWritten), m_cbReceived);
  1161. m_cbCurrentWriteBuffer = 0;
  1162. //Time to go back and process the remaining data in the buffer
  1163. //Adjust the buffer for the next read only in case of Sync write
  1164. return(ProcessInputBuffer());
  1165. }
  1166. BOOL SMTP_CONNECTION::PendReadIO(DWORD UndecryptedTailSize)
  1167. {
  1168. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::PendReadIO");
  1169. BOOL fReturn = TRUE;
  1170. m_LastClientIo = READIO;
  1171. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  1172. _ASSERT (m_cbReceived < QueryMaxReadSize());
  1173. //increment this IO
  1174. IncPendingIoCount();
  1175. fReturn = ReadFile(QueryMRcvBuffer() + m_cbReceived, QueryMaxReadSize() - m_cbReceived);
  1176. if(!fReturn)
  1177. {
  1178. DecPendingIoCount();
  1179. DisconnectClient();
  1180. }
  1181. TraceFunctLeaveEx((LPARAM) this);
  1182. return fReturn;
  1183. }
  1184. /*++
  1185. Name :
  1186. SMTP_CONNECTION::ProcessInputBuffer
  1187. Description:
  1188. This function takes the receive buffer, parses the buffer to
  1189. find out what command the client sent, then executes that
  1190. command.
  1191. Arguments:
  1192. lpfMailQueued -- On return, TRUE if processing the client command
  1193. caused a mail message to be queued.
  1194. Returns:
  1195. TRUE if the connection should stay open.
  1196. FALSE if this object should be deleted.
  1197. --*/
  1198. BOOL SMTP_CONNECTION::ProcessInputBuffer(void)
  1199. {
  1200. BOOL fReturn = TRUE;
  1201. const char * InputLine;
  1202. DWORD UndecryptedTailSize = m_cbReceived - m_cbParsable;
  1203. BOOL fWritePended = FALSE;
  1204. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::ProcessInputBuffer");
  1205. //Start processing at the point after the temporary BDAT chunk
  1206. InputLine = QueryMRcvBuffer() + m_cbTempBDATLen + m_cbRecvBufferOffset;
  1207. if ( TRUE == m_fPeBlobReady) {
  1208. fReturn = ProcessPeBlob( InputLine, m_cbParsable);
  1209. if ( FALSE == fReturn) {
  1210. TraceFunctLeaveEx( ( LPARAM) this);
  1211. return FALSE;
  1212. }
  1213. MoveMemory ( ( void *) QueryMRcvBuffer(), InputLine + m_cbParsable, UndecryptedTailSize);
  1214. m_cbParsable = 0;
  1215. m_cbReceived = UndecryptedTailSize;
  1216. // always pend a read in this state regardless of previous fReturn
  1217. fReturn = PendReadIO( UndecryptedTailSize);
  1218. TraceFunctLeaveEx( ( LPARAM) this);
  1219. return fReturn;
  1220. }
  1221. if (m_State == DATA)
  1222. {
  1223. BOOL fAsyncOp = FALSE;
  1224. fReturn = ProcessDATAMailBody(InputLine, UndecryptedTailSize, &fAsyncOp);
  1225. if(!fReturn || fAsyncOp)
  1226. {
  1227. TraceFunctLeaveEx((LPARAM) this);
  1228. return fReturn;
  1229. }
  1230. //
  1231. // There is additional data pipelined behind the mailbody. DoDATACommandEx
  1232. // moves this to the beginning of the input buffer after processing the
  1233. // mail body, so the place to begin parsing is m_pRecvBuffer.
  1234. //
  1235. InputLine = QueryMRcvBuffer();
  1236. _ASSERT(m_State == HELO);
  1237. }
  1238. PROCESS_BDAT:
  1239. if(m_State == BDAT && !m_fIsChunkComplete)
  1240. {
  1241. BOOL fAsyncOp = FALSE;
  1242. fReturn = ProcessBDATMailBody(InputLine, UndecryptedTailSize, &fAsyncOp);
  1243. if(!fReturn || fAsyncOp)
  1244. {
  1245. TraceFunctLeaveEx((LPARAM) this);
  1246. return fReturn;
  1247. }
  1248. //
  1249. // All the mailbody was processed and the input buffer points to the
  1250. // next SMTP command after readjusting for any *saved* BDAT data (If a
  1251. // header spans 2 chunks, ProcessBDAT will save the partial header from
  1252. // the previous chunk till it can complete it using the next chunk.
  1253. // m_cbTempBdatLen represents the number of saved bytes)
  1254. //
  1255. InputLine = QueryMRcvBuffer() + m_cbTempBDATLen;
  1256. }
  1257. else if(m_State == AUTH)
  1258. {
  1259. fReturn = DoAuthNegotiation(InputLine, m_cbParsable);
  1260. if(!fReturn)
  1261. {
  1262. ++m_ProtocolErrors;
  1263. }
  1264. if(m_State == AUTH)
  1265. {
  1266. fReturn = PendReadIO(UndecryptedTailSize);
  1267. TraceFunctLeaveEx((LPARAM) this);
  1268. return fReturn;
  1269. }
  1270. }
  1271. _ASSERT(m_State != DATA);
  1272. PCHAR pszSearch;
  1273. DWORD IntermediateSize;
  1274. DWORD CmdSize = 0;
  1275. //if we got here, then a read completed. Process the
  1276. //entire buffer before returning(Pipelining).
  1277. //use to be while ((pszSearch = strstr(QueryRcvBuffer(), CRLF)) != NULL)
  1278. //Reset the the offset ptr into the recv buffer
  1279. //we use this to keeo track of where to continue processing from
  1280. m_cbRecvBufferOffset = 0;
  1281. BOOL fAsyncOp = FALSE;
  1282. while ((pszSearch = IsLineComplete(InputLine,m_cbParsable - m_cbTempBDATLen)) != NULL)
  1283. {
  1284. //Null terminate the end of the command
  1285. *pszSearch = '\0';
  1286. IntermediateSize = (DWORD)(pszSearch - InputLine);
  1287. StartProcessingTimer();
  1288. ResetCommandCounters();
  1289. SetCommandBytesRecv( IntermediateSize );
  1290. // ignore blank lines
  1291. if (*InputLine == 0) {
  1292. // update the counters into InputLine
  1293. m_cbParsable -= (IntermediateSize + 2);
  1294. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  1295. MoveMemory((PVOID)InputLine, pszSearch + 2, m_cbReceived - m_cbTempBDATLen);
  1296. continue;
  1297. }
  1298. //
  1299. // Error case when an overly long SMTP command has to be discarded because there
  1300. // isn't enough space to hold it all in the buffer. Can happen while chunking (see
  1301. // where this member variable is set). In this situation, all bytes received are
  1302. // discarded without parsing, till a CRLF is encountered. Then IsLineComplete
  1303. // succeeds and we execute this if-statement.
  1304. //
  1305. if(m_fBufferFullInBDAT) {
  1306. m_fBufferFullInBDAT = FALSE;
  1307. m_cbParsable -= (IntermediateSize + 2);
  1308. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  1309. MoveMemory((PVOID)InputLine, pszSearch + 2, m_cbReceived - m_cbTempBDATLen);
  1310. m_ProtocolErrors++;
  1311. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  1312. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","BDAT Expected" );
  1313. PE_SendSmtpResponse();
  1314. continue;
  1315. }
  1316. //
  1317. // SMTP events are fired upon the _EOD event and "_EOD" appears in the SmtpDispatchTable
  1318. // and SmtpCommands[] array. However it isn't an SMTP command. Ideally "_EOD" should never
  1319. // be returned from SmtpGetCommand, but the event firing mechanism is tied to "_EOD" as a
  1320. // string. So we firewall this case in the parser.
  1321. //
  1322. // Other strings unimplemented by SMTP should be handled by GlueDispatch which checks if
  1323. // SMTP sinks are registered to handle that string and fires events for that string. "_EOD"
  1324. // is the only string for which events must not be fired from the SMTP command parser.
  1325. //
  1326. if(!strncasecmp((char *)InputLine, "_EOD", sizeof("_EOD") - 1))
  1327. {
  1328. m_cbParsable -= (IntermediateSize + 2);
  1329. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  1330. MoveMemory((PVOID)InputLine, pszSearch + 2, m_cbReceived - m_cbTempBDATLen);
  1331. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  1332. m_ProtocolErrors++;
  1333. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_CMD, ENOT_IMPLEMENTED," %s\r\n", SMTP_BAD_CMD_STR);
  1334. PE_SendSmtpResponse();
  1335. continue;
  1336. }
  1337. m_dwCurrentCommand = SmtpGetCommand(InputLine, IntermediateSize, &CmdSize);
  1338. if(m_State == BDAT &&
  1339. m_dwCurrentCommand != BDAT &&
  1340. m_dwCurrentCommand != RSET &&
  1341. m_dwCurrentCommand != QUIT &&
  1342. m_dwCurrentCommand != NOOP)
  1343. {
  1344. ErrorTrace((LPARAM)this, "Illegal command during BDAT: %s", InputLine);
  1345. m_cbParsable -= (IntermediateSize + 2);
  1346. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  1347. MoveMemory((PVOID)InputLine, pszSearch + 2, m_cbReceived - m_cbTempBDATLen);
  1348. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","BDAT Expected" );
  1349. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  1350. ++m_ProtocolErrors;
  1351. continue;
  1352. }
  1353. // we might run into a sink that does async operations
  1354. // So update state data that the async thread could see
  1355. m_cbRecvBufferOffset += (DWORD)(pszSearch - (QueryMRcvBuffer() + m_cbTempBDATLen) + 2);
  1356. m_cbParsable -= (IntermediateSize + 2);
  1357. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  1358. // before firing each command load any session properties from ISession
  1359. HrGetISessionProperties();
  1360. //
  1361. // Increment the pending IO count in case this returns async
  1362. //
  1363. IncPendingIoCount();
  1364. fReturn = GlueDispatch((char *)InputLine, IntermediateSize, CmdSize, &fAsyncOp);
  1365. //
  1366. // Assert check that fAsyncOp isn't true when GlueDispatch fails
  1367. //
  1368. _ASSERT( (!fAsyncOp) || fReturn );
  1369. if(!fAsyncOp)
  1370. {
  1371. //
  1372. // Decrement the pending IO count since GlueDispatch
  1373. // returned sync (and we incremented the count above).
  1374. // ProcessClient is above us in the callstack and always has a
  1375. // pending IO reference, so this should never return zero
  1376. //
  1377. _VERIFY(DecPendingIoCount() > 0);
  1378. }
  1379. if (fReturn == TRUE)
  1380. {
  1381. //Check if some sink performed an async operation
  1382. if(fAsyncOp)
  1383. {
  1384. //We returned from Dispatcher because some sink did an async operation
  1385. //We need to simply leave. The thread on which the async operation completes
  1386. //will continue with the sink firing. The dispacther will keep the context
  1387. //which will allow it to do so..
  1388. //So as to keep the connection alive - bump up the ref count
  1389. TraceFunctLeaveEx((LPARAM) this);
  1390. return TRUE;
  1391. }//end if(AsyncSink)
  1392. // log this command and the response
  1393. if (m_State != DATA &&
  1394. m_State != BDAT &&
  1395. m_State != AUTH &&
  1396. m_dwCurrentCommand != DATA &&
  1397. m_dwCurrentCommand != BDAT &&
  1398. m_dwCurrentCommand != AUTH)
  1399. {
  1400. const char *pszCommand = SmtpCommands[m_dwCurrentCommand];
  1401. DWORD cCmd = (pszCommand) ? strlen(pszCommand) : 0;
  1402. ProtocolLog(m_dwCurrentCommand,
  1403. InputLine + cCmd,
  1404. m_CInboundContext.m_dwWin32Status,
  1405. m_CInboundContext.m_dwSmtpStatus,
  1406. 0,
  1407. 0);
  1408. }
  1409. //All Sinks completed synchronously
  1410. m_cbRecvBufferOffset = 0;
  1411. InputLine = pszSearch + 2; //skip CRLF
  1412. if(m_State != DATA && m_State != BDAT)
  1413. {
  1414. continue;
  1415. }
  1416. else if (m_State == BDAT && m_cbParsable)
  1417. {
  1418. //We are in BDAT mode and there is some data that can be parsed
  1419. MoveMemory ((void *)(QueryMRcvBuffer() + m_cbTempBDATLen), InputLine, m_cbReceived - m_cbTempBDATLen );
  1420. InputLine = QueryMRcvBuffer();
  1421. goto PROCESS_BDAT;
  1422. }
  1423. else
  1424. {
  1425. break; // no more data
  1426. }
  1427. }
  1428. else
  1429. {
  1430. TraceFunctLeaveEx((LPARAM) this);
  1431. return FALSE;
  1432. }//end if(fReturn == TRUE)
  1433. }//end while
  1434. // if IsLineComplete failed because we passed in a negative length
  1435. // than drop the session. something is corrupt
  1436. if ((int) m_cbParsable - (int) m_cbTempBDATLen < 0) {
  1437. DisconnectClient();
  1438. TraceFunctLeaveEx((LPARAM) this);
  1439. return FALSE;
  1440. }
  1441. _ASSERT(m_cbReceived <= QueryMaxReadSize());
  1442. if(m_cbParsable != 0)
  1443. {
  1444. MoveMemory ((void *)(QueryMRcvBuffer() + m_cbTempBDATLen), InputLine, m_cbParsable+UndecryptedTailSize-m_cbTempBDATLen);
  1445. }
  1446. SendSmtpResponse();
  1447. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  1448. //
  1449. // The receive buffer can fill up if we have more than QueryMaxReadSize()
  1450. // bytes in it that cannot be processed till more data is received from
  1451. // the client. This does not happen in the normal processing of SMTP
  1452. // commands, since each SMTP command is limited to 512 bytes, which can
  1453. // comfortable fit in the recv buffer.
  1454. //
  1455. // However, while processing BDAT, CreateMailBodyFromChunk may save the
  1456. // bytes of a partially received header line in the recv buffer by setting
  1457. // m_cbTempBDATLen > 0. For example, suppose the client is sending BDAT
  1458. // chunks, and the first chunk comprises of 500 bytes of data without a
  1459. // CRLF. In this case, SMTP cannot determine whether or not these 500
  1460. // bytes are part of a header. This is because unless the complete line
  1461. // has been received we cannot decide whether or not the line follows
  1462. // the header syntax. Therefore SMTP will save the 500 bytes by setting
  1463. // m_cbTempBDATLen == 500, and any bytes sent by the client will be
  1464. // appended at an offset of 500 in the recv buffer. When the next chunk
  1465. // arrives, SMTP will again try to process the 500 saved bytes + the
  1466. // bytes from the new chunk, as a complete header line.
  1467. //
  1468. // A client cannot fill up the buffer by sending thousands of bytes like
  1469. // this, without CRLFs, forcing SMTP to save the partial header. SMTP has
  1470. // a maximum limit on how long a header line can be (this is 1000 bytes).
  1471. // If this limit is exceeded, SMTP can determine that the line is not a
  1472. // syntactically correct header, and it does not have to wait for the
  1473. // terminating CRLF for the line so that it can parse the complete line
  1474. // according to header syntax.
  1475. //
  1476. // Thus it is impossible to occupy > 999 bytes of space as a saved BDAT
  1477. // partial header. However, even if we occupy the maximum possible 999
  1478. // bytes, the recv buffer (being sized 1024) will have only 26 bytes
  1479. // available for processing SMTP commands. This is clearly inadequate
  1480. // (in theory anyway) because SMTP commands have a max size of 512. In
  1481. // fact, we do not even have enough space to receive and reject illegal
  1482. // commands which can exceed 26 bytes (since a command-line must be
  1483. // completely received, till the terminating CRLF, before we can parse
  1484. // and reject it, in the ordinary course of things).
  1485. //
  1486. // Fortunately, during BDAT, the only commands allowed to be issued are
  1487. // all shorter than 26 bytes: BDAT, RSET and QUIT. We take advantage of
  1488. // this fact if we are caught in the "out of space" scenario, and the
  1489. // follwing if-statement sets m_fBufrFullInBDAT flag. When this flag is
  1490. // set, SMTP will discard every byte it receives from the client
  1491. // (following the saved m_cbTempBDATLen bytes) till a CRLF is received
  1492. // (note that the CRLF marks the end of the illegal SMTP command). Then
  1493. // SMTP responds with "BDAT expected" (see the command loop above) and
  1494. // resets the m_fBufrFullInBDAT flag to the normal state.
  1495. //
  1496. // Note that if the buffer is full, then m_cbTempBDATLen *must* be > 0.
  1497. // We only run out of recv buffer space during chunking. Thus the
  1498. // additional check for m_cbTempBDATLen > 0 in the following if-statement.
  1499. //
  1500. if(QueryMaxReadSize() == m_cbReceived && m_cbTempBDATLen > 0)
  1501. {
  1502. // Flag error so we know what response to generate when we do get CRLF
  1503. m_fBufferFullInBDAT = TRUE;
  1504. // Discard everything after the saved BDAT chunk except Undecrypted tail
  1505. PBYTE pbDiscardStart = (PBYTE) (QueryMRcvBuffer() + m_cbTempBDATLen);
  1506. PBYTE pbDiscardEnd = (PBYTE) (QueryMRcvBuffer() + m_cbParsable);
  1507. DWORD cbBytesToDiscard = m_cbParsable - m_cbTempBDATLen;
  1508. DWORD cbBytesToMove = UndecryptedTailSize;
  1509. // Keep CR if it's at the end. If the next packet has LF as the
  1510. // first byte we want to process the CRLF with IsLineComplete.
  1511. if(*(pbDiscardEnd - 1) == CR)
  1512. {
  1513. pbDiscardEnd--;
  1514. cbBytesToDiscard--;
  1515. cbBytesToMove++;
  1516. }
  1517. m_cbParsable -= cbBytesToDiscard;
  1518. m_cbReceived -= cbBytesToDiscard;
  1519. _ASSERT(pbDiscardEnd > pbDiscardStart);
  1520. MoveMemory((PVOID)pbDiscardStart, pbDiscardEnd, cbBytesToMove);
  1521. }
  1522. _ASSERT (m_cbReceived < QueryMaxReadSize());
  1523. //pend an I/O
  1524. IncPendingIoCount();
  1525. m_LastClientIo = READIO;
  1526. fReturn = ReadFile(QueryMRcvBuffer() + m_cbReceived, QueryMaxReadSize() - m_cbReceived);
  1527. if(!fReturn)
  1528. {
  1529. DisconnectClient();
  1530. DecPendingIoCount();
  1531. }
  1532. TraceFunctLeaveEx((LPARAM) this);
  1533. return fReturn;
  1534. }
  1535. /*++
  1536. Name:
  1537. SMTP_CONNECTION::HrGetISessionProperties
  1538. Description:
  1539. A sink may set certain properties on the session
  1540. which should control SMTP behaviour, this function
  1541. pulls those properties from ISession and writes
  1542. them as session parameters (members of SMTP_CONNECTION)
  1543. Arguments:
  1544. None.
  1545. Returns:
  1546. HRESULT - Success or Error
  1547. If there isn't a sink or if a property was not
  1548. set by sink, an error may be returned.
  1549. --*/
  1550. HRESULT SMTP_CONNECTION::HrGetISessionProperties()
  1551. {
  1552. IMailMsgPropertyBag *pISessionProperties = NULL;
  1553. HRESULT hr = S_OK;
  1554. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::HrGetISessionProperties");
  1555. //
  1556. // This is for the EXPS (or any other) AUTH sink, which replaces SMTP
  1557. // authentication --- if the session is authenticated using the sink
  1558. // we should mark it as such. We pull the "AUTH" properties when we are
  1559. // about to process a command that is affected by the AUTH settings. We
  1560. // exclude commands like DATA and BDAT because they MUST have been
  1561. // preceded by MAIL, for which the AUTH setting would have already been
  1562. // checked.
  1563. //
  1564. if(!m_fAuthenticated &&
  1565. (m_dwCurrentCommand == MAIL ||
  1566. m_dwCurrentCommand == TURN ||
  1567. m_dwCurrentCommand == ETRN ||
  1568. m_dwCurrentCommand == VRFY ||
  1569. m_dwCurrentCommand == HELP))
  1570. {
  1571. pISessionProperties = (IMailMsgPropertyBag *)GetSessionPropertyBag();
  1572. //
  1573. // Usage of ISESSION_PID_IS_SESSION_AUTHENTICATED property: The AUTH
  1574. // sink sets this to TRUE when a successful authentication occurs.
  1575. //
  1576. hr = pISessionProperties->GetBool(
  1577. ISESSION_PID_IS_SESSION_AUTHENTICATED,
  1578. (DWORD *) &m_fAuthenticated);
  1579. if(FAILED(hr))
  1580. {
  1581. DebugTrace((LPARAM)this, "Can't get authenticated property for Session. hr - %08x", hr);
  1582. hr = S_OK;
  1583. goto Exit;
  1584. }
  1585. hr = pISessionProperties->GetStringA(
  1586. ISESSION_PID_AUTHENTICATED_USERNAME,
  1587. sizeof(m_szAuthenticatedUserNameFromSink),
  1588. m_szAuthenticatedUserNameFromSink);
  1589. if(FAILED(hr))
  1590. {
  1591. m_szAuthenticatedUserNameFromSink[0] = '\0';
  1592. DebugTrace((LPARAM)this, "Can't get username for Session. hr - %08x", hr);
  1593. hr = S_OK;
  1594. }
  1595. }
  1596. Exit:
  1597. TraceFunctLeaveEx((LPARAM)this);
  1598. return hr;
  1599. }
  1600. /*++
  1601. Name :
  1602. SMTP_CONNECTION::ProcessClient
  1603. Description:
  1604. Main function for this class. Processes the connection based
  1605. on current state of the connection.
  1606. It may invoke or be invoked by ATQ functions.
  1607. Arguments:
  1608. cbWritten count of bytes written
  1609. dwCompletionStatus Error Code for last IO operation
  1610. lpo Overlapped stucture
  1611. Returns:
  1612. TRUE when processing is incomplete.
  1613. FALSE when the connection is completely processed and this
  1614. object may be deleted.
  1615. Note :
  1616. --*/
  1617. BOOL SMTP_CONNECTION::ProcessClient( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  1618. {
  1619. BOOL fIsAtqThread = TRUE;
  1620. BOOL RetStatus;
  1621. BOOL fMailQueued = FALSE;
  1622. PSMTP_SERVER_INSTANCE pInstance = m_pInstance; //save the instance pointer
  1623. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::ProcessClient()");
  1624. _ASSERT (m_pInstance != NULL);
  1625. pInstance->IncProcessClientThreads();
  1626. InterlockedIncrement(&g_cProcessClientThreads);
  1627. //
  1628. // increment the number of threads processing this client
  1629. //
  1630. IncThreadCount();
  1631. //if lpo == NULL, then we timed out. Send an appropriate message
  1632. //then close the connection
  1633. if( (lpo == NULL) && (dwCompletionStatus == ERROR_SEM_TIMEOUT))
  1634. {
  1635. //
  1636. // fake a pending IO as we'll dec the overall count in the
  1637. // exit processing of this routine needs to happen before
  1638. // DisconnectClient else completing threads could tear us down
  1639. //
  1640. IncPendingIoCount();
  1641. FormatSmtpMessage (SMTP_RESP_ERROR, NULL," %s\r\n",SMTP_TIMEOUT_MSG );
  1642. SendSmtpResponse(); //flush the response
  1643. ProtocolLog(SMTP_TIMEOUT, (char *) QueryClientUserName(), inet_addr((char *)QueryClientHostName()), ERROR_SEM_TIMEOUT);
  1644. DebugTrace( (LPARAM)this, "client timed out");
  1645. DisconnectClient();
  1646. }
  1647. else if((InputBufferLen == 0) || (dwCompletionStatus != NO_ERROR))
  1648. {
  1649. //
  1650. DebugTrace((LPARAM) this, "SMTP_CONNECTION::ProcessClient: InputBufferLen = %d dwCompletionStatus = %d - Closing connection", InputBufferLen, dwCompletionStatus);
  1651. // If this is a mail file write then we handle error: if not a mailfile write or we are unable to
  1652. // handle the error, then we simply disconnect.
  1653. if( lpo != &m_Overlapped && lpo != QueryAtqOverlapped() && !HandleInternalError(dwCompletionStatus))
  1654. DisconnectClient();
  1655. }
  1656. else if (lpo == &m_Overlapped || lpo == QueryAtqOverlapped())
  1657. {
  1658. //
  1659. // Is this a real IO completion or a completion status
  1660. // posted to notify us an async sink completed?
  1661. //
  1662. if( m_fAsyncEventCompletion )
  1663. {
  1664. //
  1665. // A portocol event sink completed async, called our
  1666. // completion routine, posted a completion status which is
  1667. // being dequeued here. Our job is now to pend the next read
  1668. // (via ProcessInputBuffer)
  1669. //
  1670. m_fAsyncEventCompletion = FALSE;
  1671. RetStatus = ProcessInputBuffer();
  1672. }
  1673. else
  1674. {
  1675. //A client based async IO completed
  1676. RetStatus = ProcessReadIO(InputBufferLen, dwCompletionStatus, lpo);
  1677. if((m_pInstance->GetMaxErrors() > 0) && (m_ProtocolErrors >= m_pInstance->GetMaxErrors()))
  1678. {
  1679. //If there are too many error, break the connection
  1680. FormatSmtpMessage (SMTP_RESP_SRV_UNAVAIL, NULL," %s\r\n",SMTP_TOO_MANY_ERR_MSG);
  1681. FatalTrace((LPARAM) this, "Too many errors. Error count = %d", m_ProtocolErrors);
  1682. SendSmtpResponse();
  1683. DisconnectClient();
  1684. }
  1685. }
  1686. }
  1687. else
  1688. {
  1689. //A Mail File Write completed
  1690. SMTPCLI_FILE_OVERLAPPED* lpFileOverlapped = (SMTPCLI_FILE_OVERLAPPED*)lpo;
  1691. _ASSERT( lpFileOverlapped->m_LastIoState == WRITEFILEIO );
  1692. if (lpFileOverlapped->m_LastIoState == WRITEFILEIO)
  1693. {
  1694. RetStatus = ProcessFileWrite(InputBufferLen, dwCompletionStatus, lpo);
  1695. }
  1696. }
  1697. //
  1698. // decrement the number of threads processing this client if the thread exiting
  1699. // is an Atq pool thread
  1700. //
  1701. //if(fIsAtqThread)
  1702. DecThreadCount();
  1703. DebugTrace((LPARAM)this,"SMTPLCI - Pending IOs: %d", m_cPendingIoCount);
  1704. DebugTrace((LPARAM)this,"SMTPCLI - Num Threads: %d", m_cActiveThreads);
  1705. // Do NOT Touch the member variables past this POINT!
  1706. // This object may be deleted!
  1707. //
  1708. // decrement the overall pending IO count for this session
  1709. // tracing and ASSERTs if we're going down.
  1710. //
  1711. if (DecPendingIoCount() == 0)
  1712. {
  1713. ProtocolLog(QUIT, (char *) QueryClientUserName() , QuerySessionTime(), NO_ERROR);
  1714. DebugTrace((LPARAM)this, "Pending IO count == 0, disconnecting.");
  1715. if(m_DoCleanup)
  1716. DisconnectClient();
  1717. m_pInstance->RemoveConnection(this);
  1718. delete this;
  1719. }
  1720. //if(fIsAtqThread)
  1721. //{
  1722. pInstance->DecProcessClientThreads();
  1723. InterlockedDecrement(&g_cProcessClientThreads);
  1724. //}
  1725. // We are not the last thread, so we will return TRUE
  1726. // to keep the object around
  1727. //TraceFunctLeaveEx((LPARAM)this);
  1728. return TRUE;
  1729. }
  1730. /*++
  1731. Name :
  1732. SMTP_CONNECTION::StartSession
  1733. Description:
  1734. Starts up a session for new client.
  1735. starts off a receive request from client.
  1736. Arguments:
  1737. Returns:
  1738. TRUE if everything is O.K
  1739. FALSE if a write or a pended read failed
  1740. --*/
  1741. BOOL SMTP_CONNECTION::StartSession( void)
  1742. {
  1743. SYSTEMTIME st;
  1744. BOOL fRet;
  1745. char szDateBuf [cMaxArpaDate];
  1746. char FullName[MAX_PATH + 1];
  1747. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::StartSession");
  1748. StartSessionTimer();
  1749. if(!m_pFileWriteBuffer || !m_pFileWriteBuffer->GetData())
  1750. {
  1751. ErrorTrace((LPARAM)this, "Failed to get the write buffer Err : %d",
  1752. GetLastError());
  1753. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1754. TraceFunctLeaveEx((LPARAM) this);
  1755. return FALSE;
  1756. }
  1757. //
  1758. // If we are negotiating SSL, we need to let the SSL handshake happen
  1759. // first. Else, we are either done with the SSL handshake or we are
  1760. // talking over the unsecured port, so just go ahead and send the server
  1761. // greeting.
  1762. //
  1763. if (!m_fNegotiatingSSL)
  1764. {
  1765. _ASSERT (m_pInstance != NULL);
  1766. //Dump the header line into the buffer to format the greeting
  1767. GetLocalTime(&st);
  1768. GetArpaDate(szDateBuf);
  1769. m_pInstance->LockGenCrit();
  1770. lstrcpy(FullName, m_pInstance->GetFQDomainName());
  1771. m_pInstance->UnLockGenCrit();
  1772. PE_CdFormatSmtpMessage(SMTP_RESP_READY,NULL," %s %s %s, %s \r\n",
  1773. FullName,
  1774. m_pInstance->GetConnectResponse(),
  1775. Daynames[st.wDayOfWeek],
  1776. szDateBuf);
  1777. //send the greeting
  1778. if(!PE_SendSmtpResponse())
  1779. {
  1780. DebugTrace( (LPARAM) this, "SendSmtpResponse() returned FALSE!");
  1781. TraceFunctLeaveEx((LPARAM) this);
  1782. return FALSE;
  1783. }
  1784. }
  1785. //kick off our first read
  1786. m_LastClientIo = READIO;
  1787. //
  1788. // increment the overall pending io count for this session
  1789. //
  1790. _ASSERT(m_cPendingIoCount == 0);
  1791. IncPendingIoCount();
  1792. fRet = ReadFile(QueryMRcvBuffer(), QueryMaxReadSize());
  1793. if(!fRet)
  1794. {
  1795. int err = GetLastError();
  1796. ErrorTrace((LPARAM)this, "Readfile failed, err = %d", err);
  1797. DecPendingIoCount(); //if one of these operations fail,
  1798. PE_DisconnectClient(); //AND the ReadFile failed the "last error" is lost.
  1799. if(err != ERROR_SUCCESS)
  1800. SetLastError(err); //restore last error that occurred so higher level routine knows what happened
  1801. }
  1802. DebugTrace((LPARAM)this, "SendSmtpResponse() returned %d", fRet);
  1803. TraceFunctLeaveEx((LPARAM) this);
  1804. return fRet;
  1805. }
  1806. /*++
  1807. Name :
  1808. SMTP_CONNECTION::CheckArguments
  1809. Description:
  1810. checks the arguments of what the client sends
  1811. for the required space command et al.
  1812. Arguments:
  1813. Arguments from client
  1814. Returns:
  1815. NULL if arguments are not correct
  1816. A pointer into the input buffer where
  1817. the rest of the data is.
  1818. --*/
  1819. char * SMTP_CONNECTION::CheckArgument(char * Argument, BOOL WriteError)
  1820. {
  1821. if(*Argument =='\0')
  1822. {
  1823. if(WriteError)
  1824. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n","Argument missing" );
  1825. return NULL;
  1826. }
  1827. if(!isspace(*Argument))
  1828. {
  1829. if(WriteError)
  1830. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n",SMTP_BAD_CMD_STR);
  1831. return NULL;
  1832. }
  1833. //get rid of white space
  1834. while(isspace(*Argument))
  1835. Argument++;
  1836. //is there anything after here ?
  1837. if(*Argument =='\0')
  1838. {
  1839. if(WriteError)
  1840. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS," %s\r\n", "Argument missing" );
  1841. return NULL;
  1842. }
  1843. return Argument;
  1844. }
  1845. void SMTP_CONNECTION::FormatEhloResponse(void)
  1846. {
  1847. char FullName [MAX_PATH + 1];
  1848. unsigned char AuthPackages [500];
  1849. DWORD BytesRet = 0;
  1850. DWORD ConnectionStatus = 0;
  1851. if(m_SecurePort)
  1852. ConnectionStatus |= SMTP_IS_SSL_CONNECTION;
  1853. if(m_fAuthenticated)
  1854. ConnectionStatus |= SMTP_IS_AUTH_CONNECTION;
  1855. m_pInstance->LockGenCrit();
  1856. lstrcpy(FullName, m_pInstance->GetFQDomainName());
  1857. m_pInstance->UnLockGenCrit();
  1858. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL, "-%s %s [%s]\r\n", FullName,"Hello", QueryClientHostName());
  1859. if(m_pInstance->AllowAuth())
  1860. {
  1861. if(m_pInstance->QueryAuthentication() != 0)
  1862. {
  1863. if(m_pInstance->QueryAuthentication() & INET_INFO_AUTH_NT_AUTH)
  1864. {
  1865. BytesRet = sizeof(AuthPackages);
  1866. m_securityCtx.GetInstanceAuthPackageNames(AuthPackages, &BytesRet, PkgFmtSpace);
  1867. }
  1868. if(BytesRet > 0)
  1869. {
  1870. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-AUTH %s",AuthPackages);
  1871. if(m_pInstance->AllowLogin(ConnectionStatus))
  1872. {
  1873. PE_FormatSmtpMessage(" LOGIN\r\n");
  1874. }
  1875. else
  1876. {
  1877. PE_FormatSmtpMessage("\r\n");
  1878. }
  1879. if(m_pInstance->AllowLogin(ConnectionStatus))
  1880. {
  1881. //For backward compatibility
  1882. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-AUTH=%s\r\n","LOGIN");
  1883. }
  1884. }
  1885. else if(m_pInstance->AllowLogin(ConnectionStatus))
  1886. {
  1887. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-AUTH=LOGIN\r\n");
  1888. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-AUTH LOGIN\r\n");
  1889. }
  1890. if(g_SmtpPlatformType == PtNtServer && m_pInstance->AllowTURN())
  1891. {
  1892. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n","TURN");
  1893. }
  1894. }
  1895. }
  1896. if (m_pInstance->GetMaxMsgSize() > 0)
  1897. {
  1898. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-%s %u\r\n","SIZE", m_pInstance->GetMaxMsgSize());
  1899. }
  1900. else
  1901. {
  1902. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-%s\r\n","SIZE");
  1903. }
  1904. if(g_SmtpPlatformType == PtNtServer && m_pInstance->AllowETRN())
  1905. {
  1906. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-%s\r\n", "ETRN");
  1907. }
  1908. if(m_pInstance->ShouldPipeLineIn())
  1909. {
  1910. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-%s\r\n","PIPELINING");
  1911. }
  1912. if(m_pInstance->AllowDSN())
  1913. {
  1914. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n", "DSN");
  1915. }
  1916. if(m_pInstance->AllowEnhancedCodes())
  1917. {
  1918. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-%s\r\n", "ENHANCEDSTATUSCODES");
  1919. }
  1920. if(m_pInstance->AllowEightBitMime())
  1921. {
  1922. PE_CdFormatSmtpMessage(SMTP_RESP_OK, NULL,"-%s\r\n", "8bitmime");
  1923. }
  1924. // Chunking related advertisements
  1925. if(m_pInstance->AllowBinaryMime())
  1926. {
  1927. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n", "BINARYMIME");
  1928. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n", "CHUNKING");
  1929. }
  1930. else if(m_pInstance->AllowChunking())
  1931. {
  1932. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n", "CHUNKING");
  1933. }
  1934. // verify - we need to advertise it whether we support it or not.
  1935. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n","VRFY");
  1936. // Expand
  1937. if(m_pInstance->AllowExpand(ConnectionStatus))
  1938. {
  1939. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n", "EXPN");
  1940. }
  1941. //
  1942. // Check that we have a certificate installed with which we can negotiate
  1943. // a SSL session
  1944. //
  1945. if (!m_SecurePort && // TLS is advertized only if we haven't already negotiated it
  1946. m_encryptCtx.CheckServerCert(
  1947. (LPSTR) QueryLocalHostName(),
  1948. (LPSTR) QueryLocalPortName(),
  1949. (LPVOID) QuerySmtpInstance(),
  1950. QuerySmtpInstance()->QueryInstanceId()))
  1951. {
  1952. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n","TLS");
  1953. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL,"-%s\r\n", "STARTTLS");
  1954. }
  1955. PE_CdFormatSmtpMessage (SMTP_RESP_OK, NULL," %s\r\n","OK");
  1956. }
  1957. /*++
  1958. Name :
  1959. SMTP_CONNECTION::DoEHLOCommand
  1960. Description:
  1961. Responds to the SMTP EHLO command
  1962. Arguments:
  1963. Are ignored
  1964. Returns:
  1965. TRUE if the connection should stay open.
  1966. FALSE if this object should be deleted.
  1967. --*/
  1968. BOOL SMTP_CONNECTION::DoEHLOCommand(const char * InputLine, DWORD ParametSize)
  1969. {
  1970. BOOL RetStatus = TRUE;
  1971. char * Args = (char *) InputLine;
  1972. CAddr * NewAddress = NULL;
  1973. char ProtBuff[400];
  1974. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoEHLOCommand");
  1975. _ASSERT (m_pInstance != NULL);
  1976. //If the current state is BDAT the only command that can be received is BDAT
  1977. if(m_State == BDAT)
  1978. {
  1979. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","BDAT Expected" );
  1980. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  1981. ++m_ProtocolErrors;
  1982. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - BDAT Expected");
  1983. TraceFunctLeaveEx((LPARAM) this);
  1984. return TRUE;
  1985. }
  1986. // pgopi -- don't cound multiple ehlo's as error. This is per bug 82160
  1987. //if (m_HelloSent)
  1988. // ++m_ProtocolErrors;
  1989. Args = CheckArgument(Args, !m_pInstance->ShouldAcceptNoDomain());
  1990. if(Args != NULL)
  1991. {
  1992. if(m_pInstance->ShouldValidateHeloDomain())
  1993. {
  1994. if(!ValidateDRUMSDomain(Args, lstrlen(Args)))
  1995. {
  1996. //Not a valid domain -
  1997. SetLastError(ERROR_INVALID_DATA);
  1998. HandleAddressError((char *)InputLine);
  1999. }
  2000. else
  2001. {
  2002. //Is reverse DNS lookup enabled
  2003. if(m_pInstance->IsReverseLookupEnabled())
  2004. {
  2005. m_DNSLookupRetCode = VerifiyClient (Args, QueryClientHostName());
  2006. if(m_DNSLookupRetCode == NO_MATCH)
  2007. {
  2008. //We failed in DNS lookup
  2009. if(m_pInstance->fDisconnectOnRDNSFail())
  2010. {
  2011. PE_CdFormatSmtpMessage (SMTP_RESP_TRANS_FAILED, ENO_SECURITY," %s\r\n",SMTP_RDNS_REJECTION);
  2012. wsprintf(ProtBuff, "RDNS failed - %s", (char *)QueryClientHostName());
  2013. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2014. ++m_ProtocolErrors;
  2015. ErrorTrace((LPARAM) this, "Rejected Connection RDNS failed for %s", Args);
  2016. TraceFunctLeaveEx((LPARAM) this);
  2017. return FALSE;
  2018. }
  2019. }
  2020. else if(m_DNSLookupRetCode == LOOKUP_FAILED)
  2021. {
  2022. //We had an internal DNS failure
  2023. if(m_pInstance->fDisconnectOnRDNSFail())
  2024. {
  2025. PE_CdFormatSmtpMessage (SMTP_RESP_SRV_UNAVAIL, EINTERNAL_ERROR," %s\r\n",SMTP_RDNS_FAILURE);
  2026. wsprintf(ProtBuff, "RDNS DNS failure - Ip Address is %s", (char *)QueryClientHostName());
  2027. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2028. ++m_ProtocolErrors;
  2029. ErrorTrace((LPARAM) this, "Rejected Connection RDNS DNS failure for %s", Args);
  2030. TraceFunctLeaveEx((LPARAM) this);
  2031. return FALSE;
  2032. }
  2033. else
  2034. {
  2035. //Network/Internal problem prevented a lookup
  2036. wsprintf(ProtBuff, "Reverse DNS lookup failed due to internal error- Ip Address is %s", (char *)QueryClientHostName());
  2037. }
  2038. }
  2039. else
  2040. {
  2041. wsprintf(ProtBuff, "Reverse DNS lookup succeeded - Ip Address is %s", (char *)QueryClientHostName());
  2042. }
  2043. } else {
  2044. m_DNSLookupRetCode = SUCCESS;
  2045. }
  2046. FormatEhloResponse();
  2047. strncpy(m_szHeloAddr, Args, MAX_INTERNET_NAME);
  2048. m_szHeloAddr[MAX_INTERNET_NAME] = '\0';
  2049. m_HelloSent = TRUE;
  2050. m_State = HELO;
  2051. }
  2052. }
  2053. else
  2054. {
  2055. FormatEhloResponse();
  2056. strncpy(m_szHeloAddr, Args, MAX_INTERNET_NAME);
  2057. m_szHeloAddr[MAX_INTERNET_NAME] = '\0';
  2058. m_HelloSent = TRUE;
  2059. m_State = HELO;
  2060. }
  2061. }
  2062. else if(m_pInstance->ShouldAcceptNoDomain())
  2063. {
  2064. FormatEhloResponse();
  2065. if(m_szHeloAddr[0] != '\0')
  2066. {
  2067. m_szHeloAddr[0] = '\0';
  2068. }
  2069. m_HelloSent = TRUE;
  2070. m_State = HELO;
  2071. }
  2072. //we can either accept the HELO or EHLO as the 1st command
  2073. RetStatus = PE_SendSmtpResponse();
  2074. TraceFunctLeaveEx((LPARAM) this);
  2075. return RetStatus;
  2076. }
  2077. /*++
  2078. Name :
  2079. SMTP_CONNECTION::DoHELOCommand
  2080. Description:
  2081. Responds to the SMTP HELO command
  2082. Arguments:
  2083. Are ignored
  2084. Returns:
  2085. TRUE if the connection should stay open.
  2086. FALSE if this object should be deleted.
  2087. --*/
  2088. BOOL SMTP_CONNECTION::DoHELOCommand(const char * InputLine, DWORD ParameterSize)
  2089. {
  2090. char * Args = (char *) InputLine;
  2091. CAddr * NewAddress = NULL;
  2092. char FullName[MAX_PATH + 1];
  2093. char ProtBuff[400];
  2094. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoHELOCommand");
  2095. _ASSERT (m_pInstance != NULL);
  2096. //If the current state is BDAT the only command that can be received is BDAT
  2097. if(m_State == BDAT)
  2098. {
  2099. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","BDAT Expected" );
  2100. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2101. ++m_ProtocolErrors;
  2102. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - BDAT Expected");
  2103. TraceFunctLeaveEx((LPARAM) this);
  2104. return TRUE;
  2105. }
  2106. // pgopi--don't cound multiple ehlo's as error. This is per bug 82160
  2107. //if (m_HelloSent)
  2108. // ++m_ProtocolErrors;
  2109. Args = CheckArgument(Args, !m_pInstance->ShouldAcceptNoDomain());
  2110. if(Args != NULL)
  2111. {
  2112. if(m_pInstance->ShouldValidateHeloDomain())
  2113. {
  2114. if(!ValidateDRUMSDomain(Args, lstrlen(Args)))
  2115. {
  2116. //Not a valid domain -
  2117. SetLastError(ERROR_INVALID_DATA);
  2118. HandleAddressError((char *)InputLine);
  2119. }
  2120. else
  2121. {
  2122. if(m_pInstance->IsReverseLookupEnabled())
  2123. {
  2124. m_DNSLookupRetCode = VerifiyClient (Args, QueryClientHostName());
  2125. if(m_DNSLookupRetCode == NO_MATCH)
  2126. {
  2127. //We failed in DNS lookup
  2128. if(m_pInstance->fDisconnectOnRDNSFail())
  2129. {
  2130. PE_CdFormatSmtpMessage (SMTP_RESP_TRANS_FAILED, ENO_SECURITY," %s\r\n",SMTP_RDNS_REJECTION);
  2131. wsprintf(ProtBuff, "RDNS failed - %s", (char *)QueryClientHostName());
  2132. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2133. ++m_ProtocolErrors;
  2134. ErrorTrace((LPARAM) this, "Rejected Connection RDNS failed for %s", Args);
  2135. TraceFunctLeaveEx((LPARAM) this);
  2136. return FALSE;
  2137. }
  2138. }
  2139. else if(m_DNSLookupRetCode == LOOKUP_FAILED)
  2140. {
  2141. //We had an internal DNS failure
  2142. if(m_pInstance->fDisconnectOnRDNSFail())
  2143. {
  2144. PE_CdFormatSmtpMessage (SMTP_RESP_SRV_UNAVAIL, EINTERNAL_ERROR," %s\r\n",SMTP_RDNS_FAILURE);
  2145. wsprintf(ProtBuff, "RDNS DNS failure - Ip Address is %s", (char *)QueryClientHostName());
  2146. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2147. ++m_ProtocolErrors;
  2148. ErrorTrace((LPARAM) this, "Rejected Connection RDNS DNS failure for %s", Args);
  2149. TraceFunctLeaveEx((LPARAM) this);
  2150. return FALSE;
  2151. }
  2152. else
  2153. {
  2154. //Network/Internal problem prevented a lookup
  2155. wsprintf(ProtBuff, "Reverse DNS lookup failed due to internal error- Ip Address is %s", (char *)QueryClientHostName());
  2156. }
  2157. }
  2158. else
  2159. {
  2160. wsprintf(ProtBuff, "Reverse DNS lookup succeeded - Ip Address is %s", (char *)QueryClientHostName());
  2161. }
  2162. } else {
  2163. m_DNSLookupRetCode = SUCCESS;
  2164. }
  2165. m_pInstance->LockGenCrit();
  2166. strcpy(FullName, m_pInstance->GetFQDomainName());
  2167. m_pInstance->UnLockGenCrit();
  2168. PE_CdFormatSmtpMessage (SMTP_RESP_OK,NULL," %s %s [%s]\r\n", FullName,
  2169. "Hello", QueryClientHostName());
  2170. strncpy(m_szHeloAddr, Args, MAX_INTERNET_NAME);
  2171. m_szHeloAddr[MAX_INTERNET_NAME] = '\0';
  2172. m_HelloSent = TRUE;
  2173. m_State = HELO;
  2174. }
  2175. }
  2176. else
  2177. {
  2178. m_pInstance->LockGenCrit();
  2179. strcpy(FullName, m_pInstance->GetFQDomainName());
  2180. m_pInstance->UnLockGenCrit();
  2181. PE_CdFormatSmtpMessage (SMTP_RESP_OK,NULL," %s %s [%s]\r\n", FullName,
  2182. "Hello", QueryClientHostName());
  2183. strncpy(m_szHeloAddr, Args, MAX_INTERNET_NAME);
  2184. m_szHeloAddr[MAX_INTERNET_NAME] = '\0';
  2185. m_HelloSent = TRUE;
  2186. m_State = HELO;
  2187. }
  2188. }
  2189. else if(m_pInstance->ShouldAcceptNoDomain())
  2190. {
  2191. m_pInstance->LockGenCrit();
  2192. lstrcpy(FullName, m_pInstance->GetFQDomainName());
  2193. m_pInstance->UnLockGenCrit();
  2194. PE_CdFormatSmtpMessage (SMTP_RESP_OK,NULL," %s %s [%s]\r\n", FullName,
  2195. "Hello", QueryClientHostName());
  2196. if(m_szHeloAddr[0] != '\0')
  2197. {
  2198. m_szHeloAddr[0] = '\0';
  2199. }
  2200. m_HelloSent = TRUE;
  2201. m_State = HELO;
  2202. }
  2203. TraceFunctLeaveEx((LPARAM) this);
  2204. return TRUE;
  2205. }
  2206. /*++
  2207. Name :
  2208. SMTP_CONNECTION::DoRSETCommand
  2209. Description:
  2210. Responds to the SMTP RSET command.
  2211. Deletes all stored info, and resets
  2212. all flags.
  2213. Arguments:
  2214. Are ignored
  2215. Returns:
  2216. TRUE if the connection should stay open.
  2217. FALSE if this object should be deleted.
  2218. --*/
  2219. BOOL SMTP_CONNECTION::DoRSETCommand(const char * InputLine, DWORD parameterSize)
  2220. {
  2221. BOOL RetStatus;
  2222. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoRSETCommand");
  2223. _ASSERT (m_pInstance != NULL);
  2224. m_State = HELO;
  2225. m_TotalMsgSize = 0;
  2226. m_cbDotStrippedTotalMsgSize = 0;
  2227. m_RecvdMailCmd = FALSE;
  2228. m_HeaderSize = 0;
  2229. m_InHeader = TRUE;
  2230. m_LongBodyLine = FALSE;
  2231. m_fFoundEmbeddedCrlfDotCrlf = FALSE;
  2232. m_fScannedForCrlfDotCrlf = FALSE;
  2233. m_fSeenRFC822FromAddress = FALSE;
  2234. m_fSeenRFC822ToAddress = FALSE;
  2235. m_fSeenRFC822CcAddress = FALSE;
  2236. m_fSeenRFC822BccAddress = FALSE;
  2237. m_fSeenRFC822Subject = FALSE;
  2238. m_fSeenRFC822MsgId = FALSE;
  2239. m_fSeenXPriority = FALSE;
  2240. m_fSeenXOriginalArrivalTime = FALSE;
  2241. m_fSeenContentType = FALSE;
  2242. m_fSetContentType = FALSE;
  2243. m_TimeToRewriteHeader = TRUE;
  2244. m_MailBodyError = NO_ERROR;
  2245. m_RecvdRcptCmd = FALSE;
  2246. m_CurrentOffset = 0;
  2247. m_HopCount = 0;
  2248. m_LocalHopCount = 0;
  2249. m_fIsLastChunk = FALSE;
  2250. m_fIsBinaryMime = FALSE;
  2251. m_fIsChunkComplete = FALSE;
  2252. m_dwTrailerStatus = CRLF_SEEN;
  2253. m_nChunkSize = 0;
  2254. m_nBytesRemainingInChunk = 0;
  2255. m_MailBodyDiagnostic = ERR_NONE;
  2256. m_cbRecvBufferOffset = 0;
  2257. m_ProtocolErrors = 0;
  2258. m_fBufferFullInBDAT = FALSE;
  2259. if(m_cbTempBDATLen)
  2260. {
  2261. m_cbParsable -= m_cbTempBDATLen;
  2262. m_cbTempBDATLen = 0;
  2263. }
  2264. m_szFromAddress[0] = '\0';
  2265. //Free the possible ATQ context associated with this File handle
  2266. //This will be if we were processing BDAT before RSET
  2267. //FreeAtqFileContext();
  2268. ReleasImsg(TRUE);
  2269. PE_CdFormatSmtpMessage (SMTP_RESP_OK, EPROT_SUCCESS," %s\r\n",SMTP_RSET_OK_STR);
  2270. RetStatus= PE_SendSmtpResponse();
  2271. TraceFunctLeaveEx((LPARAM) this);
  2272. return RetStatus;
  2273. }
  2274. /*++
  2275. Name :
  2276. SMTP_CONNECTION::DoNOOPCommand
  2277. Description:
  2278. Responds to the SMTP NOOP command.
  2279. Arguments:
  2280. Are ignored
  2281. Returns:
  2282. TRUE if the connection should stay open.
  2283. FALSE if this object should be deleted.
  2284. --*/
  2285. BOOL SMTP_CONNECTION::DoNOOPCommand(const char * InputLine, DWORD parameterSize)
  2286. {
  2287. BOOL RetStatus;
  2288. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoNOOPCommand");
  2289. _ASSERT (m_pInstance != NULL);
  2290. //send an OK
  2291. PE_CdFormatSmtpMessage (SMTP_RESP_OK, EPROT_SUCCESS," OK\r\n");
  2292. RetStatus = PE_SendSmtpResponse();
  2293. if(!m_Nooped)
  2294. {
  2295. m_Nooped = TRUE;
  2296. }
  2297. else
  2298. {
  2299. m_ProtocolErrors++;
  2300. }
  2301. TraceFunctLeaveEx((LPARAM) this);
  2302. return RetStatus;
  2303. }
  2304. /*++
  2305. Name :
  2306. SMTP_CONNECTION::DoETRNCommand
  2307. Description:
  2308. Responds to the SMTP ETRN command.
  2309. Arguments:
  2310. Parsed to determine the ETRN domain(s) to send mail to.
  2311. Returns:
  2312. TRUE in all cases. We are agreeing to give our best effort to the ETRN command
  2313. re the protocol. Mail is not necessarily in the retry queue so it may not be delivered.
  2314. --*/
  2315. BOOL SMTP_CONNECTION::DoETRNCommand(const char * InputLine, DWORD parameterSize)
  2316. {
  2317. BOOL RetStatus;
  2318. char * Ptr = NULL;
  2319. CHAR szNode[SMTP_MAX_DOMAIN_NAME_LEN];
  2320. DWORD Ret = 0;
  2321. // DWORD strLen;
  2322. BOOL bSubDomain;
  2323. DWORD dwMessagesQueued;
  2324. HRESULT hr;
  2325. // BOOL bWildCard;
  2326. // DOMAIN_ROUTE_ACTION_TYPE action;
  2327. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoETRNCommand");
  2328. _ASSERT (m_pInstance != NULL);
  2329. bSubDomain = FALSE;
  2330. dwMessagesQueued = 0;
  2331. // bWildCard = FALSE;
  2332. //If the current state is BDAT the only command that can be received is BDAT
  2333. if(m_State == BDAT)
  2334. {
  2335. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","BDAT Expected" );
  2336. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2337. ++m_ProtocolErrors;
  2338. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - BDAT Expected");
  2339. TraceFunctLeaveEx((LPARAM) this);
  2340. return TRUE;
  2341. }
  2342. //
  2343. // check that the hello has been sent, and that we are not in the middle of sending
  2344. // a message
  2345. //NimishK : removed the check for HELO EHLO
  2346. //Check if the client is secure enough
  2347. if (!IsClientSecureEnough()) {
  2348. ErrorTrace((LPARAM) this, "DoETRNCommand - SMTP_RESP_MUST_SECURE, Must do STARTTLS first");
  2349. TraceFunctLeaveEx((LPARAM) this);
  2350. return TRUE;
  2351. }
  2352. if(!m_fAuthAnon && !m_fAuthenticated)
  2353. {
  2354. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY, " %s\r\n", "Client was not authenticated");
  2355. ErrorTrace((LPARAM) this, "DoDataCommand - SMTP_RESP_MUST_SECURE, user not authenticated");
  2356. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2357. ++m_ProtocolErrors;
  2358. TraceFunctLeaveEx((LPARAM) this);
  2359. return TRUE;
  2360. }
  2361. if(m_RecvdMailCmd)
  2362. {
  2363. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", SMTP_NO_ETRN_IN_MSG);
  2364. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2365. ++m_ProtocolErrors;
  2366. ErrorTrace((LPARAM) this, "In DoETRNCommand():SMTP_RESP_BAD_SEQ, SMTP_NO_ETRN_IN_MSG");
  2367. TraceFunctLeaveEx((LPARAM) this);
  2368. return TRUE;
  2369. }
  2370. //start parsing from the beginning of the line
  2371. Ptr = (char *) InputLine;
  2372. //check if argument have the right format (at least one parameter)
  2373. Ptr = CheckArgument(Ptr);
  2374. if (Ptr == NULL)
  2375. {
  2376. TraceFunctLeaveEx((LPARAM) this);
  2377. return TRUE;
  2378. }
  2379. // remove any whitespace
  2380. while((isspace (*Ptr)))
  2381. {
  2382. Ptr++;
  2383. }
  2384. // check for ETRN subdomain character
  2385. if (*Ptr == '@')
  2386. {
  2387. //We could probably move this check to aqueue or keep it here
  2388. if (!(QuerySmtpInstance()->AllowEtrnSubDomains()))
  2389. {
  2390. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS," %s\r\n", "Invalid domain name");
  2391. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2392. ++m_ProtocolErrors;
  2393. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_ARGS - Invalid domain name");
  2394. TraceFunctLeaveEx((LPARAM) this);
  2395. return TRUE;
  2396. }
  2397. bSubDomain = TRUE;
  2398. }
  2399. // for @ command, check that this is at least a second tier domain (to avoid @com attack)
  2400. if (bSubDomain)
  2401. {
  2402. if (!strchr(Ptr,'.'))
  2403. {
  2404. PE_CdFormatSmtpMessage (SMTP_RESP_NODE_INVALID, EINVALID_ARGS," Node %s not allowed: First tier domain\r\n", Ptr);
  2405. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2406. ++m_ProtocolErrors;
  2407. ErrorTrace((LPARAM) this, "SMTP_RESP_NODE_INVALID - First tier domains are not allowed");
  2408. TraceFunctLeaveEx((LPARAM) this);
  2409. return TRUE;
  2410. }
  2411. }
  2412. lstrcpyn(szNode, Ptr, AB_MAX_DOMAIN);
  2413. char *szTmp;
  2414. if (bSubDomain)
  2415. szTmp = szNode + 1;
  2416. else
  2417. szTmp = szNode;
  2418. if(!ValidateDRUMSDomain(szTmp, lstrlen(szTmp)))
  2419. {
  2420. //Not a valid domain -
  2421. SetLastError(ERROR_INVALID_DATA);
  2422. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS," %s\r\n", "Invalid domain name");
  2423. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2424. ++m_ProtocolErrors;
  2425. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_ARGS - Invalid domain name");
  2426. TraceFunctLeaveEx((LPARAM) this);
  2427. return TRUE;
  2428. }
  2429. //So we have a valid domain
  2430. //Call into connection manager to start the queue
  2431. //If multiple domains were dequed because the domain name was either wild card
  2432. //or specifed with the @ sign
  2433. hr = m_pInstance->GetConnManPtr()->ETRNDomain(lstrlen(szNode),szNode, &dwMessagesQueued);
  2434. if(!FAILED(hr))
  2435. {
  2436. //We know about this ETRN domain
  2437. if(!dwMessagesQueued)
  2438. {
  2439. if (bSubDomain || hr == AQ_S_SMTP_WILD_CARD_NODE)
  2440. {
  2441. PE_CdFormatSmtpMessage (SMTP_RESP_ETRN_ZERO_MSGS, EPROT_SUCCESS," Ok, no messages waiting for node %s and sub nodes\r\n",
  2442. szNode);
  2443. }
  2444. else
  2445. {
  2446. PE_CdFormatSmtpMessage (SMTP_RESP_ETRN_ZERO_MSGS, EPROT_SUCCESS," Ok, no messages waiting for node %s\r\n", szNode);
  2447. }
  2448. }
  2449. else
  2450. {
  2451. if (bSubDomain || hr == AQ_S_SMTP_WILD_CARD_NODE)
  2452. {
  2453. PE_CdFormatSmtpMessage (SMTP_RESP_ETRN_N_MSGS, EPROT_SUCCESS,
  2454. " OK, %d pending messages for wildcard node %s started\r\n", dwMessagesQueued, szNode);
  2455. }
  2456. else
  2457. {
  2458. PE_CdFormatSmtpMessage (SMTP_RESP_ETRN_N_MSGS, EPROT_SUCCESS,
  2459. " OK, %d pending messages for node %s started\r\n",dwMessagesQueued, szNode);
  2460. }
  2461. }
  2462. }
  2463. else
  2464. {
  2465. if (hr == AQ_E_SMTP_ETRN_NODE_INVALID)
  2466. {
  2467. PE_CdFormatSmtpMessage (SMTP_RESP_NODE_INVALID, EINVALID_ARGS," Node %s not allowed: not configured as ETRN domain\r\n", szNode);
  2468. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2469. ++m_ProtocolErrors;
  2470. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_ARGS - Domain not allowed");
  2471. TraceFunctLeaveEx((LPARAM) this);
  2472. return TRUE;
  2473. }
  2474. else
  2475. {
  2476. PE_CdFormatSmtpMessage (SMTP_RESP_ERROR, EINTERNAL_ERROR," Action aborted - internal error\r\n");
  2477. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2478. ErrorTrace((LPARAM) this, "Internal error ETRN processing");
  2479. TraceFunctLeaveEx((LPARAM) this);
  2480. return TRUE;
  2481. }
  2482. }
  2483. RetStatus = PE_SendSmtpResponse();
  2484. TraceFunctLeaveEx((LPARAM) this);
  2485. return RetStatus;
  2486. }
  2487. /*++
  2488. Name :
  2489. SMTP_CONNECTION::DoSTARTTLSCommand
  2490. Description:
  2491. Responds to the SMTP STARTTLS command.
  2492. Arguments:
  2493. Parsed to determine the TLS protocol to use.
  2494. Returns:
  2495. TRUE normally. A FALSE return indicates to the caller that the
  2496. client connection should be dropped.
  2497. --*/
  2498. BOOL SMTP_CONNECTION::DoSTARTTLSCommand(const char * InputLine, DWORD parameterSize)
  2499. {
  2500. BOOL RetStatus, fStartSSL;
  2501. char * Ptr = NULL;
  2502. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoSTARTTLSCommand");
  2503. _ASSERT (m_pInstance != NULL);
  2504. fStartSSL = FALSE;
  2505. //
  2506. // check that the hello has been sent, and that we are not in the middle of sending
  2507. // a message
  2508. //
  2509. if(!m_pInstance->AllowMailFromNoHello() && !m_HelloSent)
  2510. {
  2511. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","Send hello first" );
  2512. ProtocolLog(STARTTLS, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  2513. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2514. ++m_ProtocolErrors;
  2515. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Send hello first");
  2516. TraceFunctLeaveEx((LPARAM) this);
  2517. return TRUE;
  2518. }
  2519. //
  2520. // Check that we have a certificate installed with which we can negotiate
  2521. // a SSL session
  2522. //
  2523. if (!m_encryptCtx.CheckServerCert(
  2524. (LPSTR) QueryLocalHostName(),
  2525. (LPSTR) QueryLocalPortName(),
  2526. (LPVOID) QuerySmtpInstance(),
  2527. QuerySmtpInstance()->QueryInstanceId())) {
  2528. PE_CdFormatSmtpMessage (SMTP_RESP_TRANS_FAILED, ENO_SECURITY," %s\r\n", SMTP_NO_CERT_MSG);
  2529. ProtocolLog(STARTTLS, (char *)InputLine, NO_ERROR, SMTP_RESP_TRANS_FAILED, 0, 0);
  2530. ErrorTrace((LPARAM) this, "In DoSTARTTLSCommand():SMTP_RESP_TRANS_FAILED, SMTP_NO_CERT_MSG");
  2531. TraceFunctLeaveEx((LPARAM) this);
  2532. return TRUE;
  2533. }
  2534. //
  2535. // if we are already secure, reject the request
  2536. //
  2537. if(m_SecurePort)
  2538. {
  2539. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", SMTP_ONLY_ONE_TLS_MSG);
  2540. ProtocolLog(STARTTLS, (char *)InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  2541. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  2542. ++m_ProtocolErrors;
  2543. ErrorTrace((LPARAM) this, "In DoSTARTTLSCommand():SMTP_RESP_BAD_SEQ, SMTP_ONLY_ONE_TLS_MSG");
  2544. TraceFunctLeaveEx((LPARAM) this);
  2545. return TRUE;
  2546. }
  2547. //
  2548. // Switch over to using a large receive buffer, because a SSL fragment
  2549. // may be up to 32K big.
  2550. fStartSSL = SwitchToBigSSLBuffers();
  2551. if (fStartSSL) {
  2552. PE_CdFormatSmtpMessage(SMTP_RESP_READY, EPROT_SUCCESS," %s\r\n", SMTP_READY_STR);
  2553. ProtocolLog(STARTTLS, (char *) InputLine, NO_ERROR, SMTP_RESP_READY, 0, 0);
  2554. } else {
  2555. PE_CdFormatSmtpMessage(SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n", SMTP_NO_MEMORY);
  2556. ProtocolLog(STARTTLS, (char *) InputLine, NO_ERROR, SMTP_RESP_NORESOURCES, 0, 0);
  2557. }
  2558. fStartSSL = TRUE;
  2559. RetStatus = PE_SendSmtpResponse();
  2560. if (fStartSSL)
  2561. m_SecurePort = m_fNegotiatingSSL = TRUE;
  2562. TraceFunctLeaveEx((LPARAM) this);
  2563. return RetStatus;
  2564. }
  2565. BOOL SMTP_CONNECTION::DoTLSCommand(const char * InputLine, DWORD parameterSize)
  2566. {
  2567. return DoSTARTTLSCommand(InputLine, parameterSize);
  2568. }
  2569. //
  2570. // the default handler for end of data. commit the recipients list and
  2571. // insert the message into the queue
  2572. //
  2573. BOOL SMTP_CONNECTION::Do_EODCommand(const char * InputLine, DWORD parameterSize)
  2574. {
  2575. TraceFunctEnter("SMTP_CONNECTION::Do_EODCommand");
  2576. HRESULT hr;
  2577. char MessageId[1024];
  2578. BOOL fQRet;
  2579. MessageId[0] = 0;
  2580. if( m_pIMsg )
  2581. {
  2582. m_pIMsg->GetStringA( IMMPID_MP_RFC822_MSG_ID, sizeof( MessageId ), MessageId );
  2583. }
  2584. MessageId[sizeof(MessageId)-1] = 0; // NULL terminate it.
  2585. if(m_HopCount >= m_pInstance->GetMaxHopCount())
  2586. {
  2587. fQRet = m_pInstance->SubmitFailedMessage(m_pIMsg, MESSAGE_FAILURE_HOP_COUNT_EXCEEDED, 0);
  2588. FatalTrace((LPARAM) this, "Hop count exceeded");
  2589. }
  2590. else
  2591. {
  2592. // check to see if we need to hold this message
  2593. if (m_LocalHopCount >= 2) // if we're hitting this server for the 3rd time (or more)
  2594. {
  2595. if( m_pIMsg )
  2596. {
  2597. // Update the deferred delivery time property for this message
  2598. SYSTEMTIME SystemTime;
  2599. ULARGE_INTEGER ftDeferred; // == FILETIME
  2600. DWORD cbProp = 0;
  2601. BOOL fSuccess;
  2602. // Get the current system time
  2603. GetSystemTime (&SystemTime);
  2604. // Convert it to a file time
  2605. fSuccess = SystemTimeToFileTime(&SystemTime, (FILETIME*)&ftDeferred);
  2606. _ASSERT(fSuccess);
  2607. // Add 10 minutes
  2608. ftDeferred.QuadPart += SMTP_LOOP_DELAY;
  2609. hr = m_pIMsg->PutProperty(IMMPID_MP_DEFERRED_DELIVERY_FILETIME,
  2610. sizeof(FILETIME), (BYTE *) &ftDeferred);
  2611. DebugTrace( (LPARAM)this, "Possible Loop : Delaying message 10 minutes");
  2612. }
  2613. }
  2614. fQRet = m_pInstance->InsertIntoQueue(m_pIMsg);
  2615. }
  2616. if(fQRet)
  2617. {
  2618. ReleasImsg(FALSE);
  2619. }
  2620. else
  2621. {
  2622. m_MailBodyError = GetLastError();
  2623. //FatalTrace((LPARAM) this, "SetEndOfFile failed on file %s !!! (err=%d)", MailInfo->GetMailFileName(), m_MailBodyError);
  2624. m_CInboundContext.SetWin32Status(m_MailBodyError);
  2625. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE );
  2626. SendSmtpResponse();
  2627. TraceFunctLeaveEx((LPARAM) this);
  2628. return FALSE;
  2629. }
  2630. //Get the offset of the filename and send it back to the client
  2631. //we send the status back to the client immediately, just incase
  2632. //the session breaks abnormally, or because the client times out.
  2633. //If the client times out, it would send another message and we
  2634. //would get duplicate mail messages.
  2635. //Note that the response string is limited in length, so we'll truncate
  2636. //the MessageId if it is too long. Otherwise the reponse will be truncated
  2637. //elsewhere, like before the SMTP_QUEUE_MAIL string which will be confusing.
  2638. //MAX_REWRITTEN_MSGID is actually the maximum possible length of the entire
  2639. //"Message-ID: xxx" header in a message.
  2640. _ASSERT(sizeof(MessageId)/sizeof(char) > MAX_REWRITTEN_MSGID);
  2641. MessageId[MAX_REWRITTEN_MSGID] = '\0';
  2642. PE_CdFormatSmtpMessage (SMTP_RESP_OK, EMESSAGE_GOOD, " %s %s\r\n",MessageId, SMTP_QUEUE_MAIL);
  2643. return TRUE;
  2644. }
  2645. /*++
  2646. Name :
  2647. SMTP_CONNECTION::DoQUITCommand
  2648. Description:
  2649. Responds to the SMTP QUIT command.
  2650. This function always returns false.
  2651. This will stop all processing and
  2652. delete this object.
  2653. Arguments:
  2654. Are ignored
  2655. Returns:
  2656. Always return FALSE
  2657. --*/
  2658. BOOL SMTP_CONNECTION::DoQUITCommand(const char * InputLine, DWORD parameterSize)
  2659. {
  2660. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoQUITCommand");
  2661. _ASSERT (m_pInstance != NULL);
  2662. //set our state
  2663. m_State = QUIT;
  2664. //send the quit response
  2665. m_pInstance->LockGenCrit();
  2666. PE_CdFormatSmtpMessage (SMTP_RESP_CLOSE, E_GOODBYE, " %s %s\r\n",m_pInstance->GetFQDomainName(), SMTP_QUIT_OK_STR);
  2667. m_pInstance->UnLockGenCrit();
  2668. PE_SendSmtpResponse();
  2669. //disconnect the client
  2670. PE_DisconnectClient();
  2671. TraceFunctLeaveEx((LPARAM) this);
  2672. return FALSE;
  2673. }
  2674. BOOL SMTP_CONNECTION::DoSizeCommand(char * Value, char * InputLine)
  2675. {
  2676. DWORD EstMailSize = 0;
  2677. DWORD EstSessionSize = 0;
  2678. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoSizeCommand");
  2679. _ASSERT (m_pInstance != NULL);
  2680. EstMailSize = atoi (Value);
  2681. EstSessionSize = m_SessionSize + EstMailSize;
  2682. if (EstMailSize == 0)
  2683. {
  2684. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n","Invalid arguments" );
  2685. ErrorTrace((LPARAM) this, "SMTP_RESP_MBX_SYNTAX, SMTP_BAD_CMD_STR. Client sent 0 in SIZE command");
  2686. ++m_ProtocolErrors;
  2687. TraceFunctLeaveEx((LPARAM) this);
  2688. return FALSE;
  2689. }
  2690. //check to make sure we are going over the servers max file size
  2691. if((m_pInstance->GetMaxMsgSize() > 0) && (EstMailSize > m_pInstance->GetMaxMsgSize()))
  2692. {
  2693. BOOL fShouldImposeLimit = TRUE;
  2694. if( FAILED( m_pInstance->TriggerMaxMsgSizeEvent( GetSessionPropertyBag(), m_pIMsg, &fShouldImposeLimit ) ) || fShouldImposeLimit )
  2695. {
  2696. PE_CdFormatSmtpMessage (SMTP_RESP_NOSTORAGE, EMESSAGE_TOO_BIG," %s\r\n", SMTP_MAX_MSG_SIZE_EXCEEDED_MSG );
  2697. ErrorTrace((LPARAM) this, "SMTP_RESP_NOSTORAGE, SMTP_MAX_MSG_SIZE_EXCEEDED_MSG - %d", EstMailSize);
  2698. ++m_ProtocolErrors;
  2699. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToSize);
  2700. TraceFunctLeaveEx((LPARAM) this);
  2701. return FALSE;
  2702. }
  2703. }
  2704. // Check that the maximum session size has not been exceeded
  2705. if((m_pInstance->GetMaxMsgSizeBeforeClose() > 0) && (EstSessionSize > m_pInstance->GetMaxMsgSizeBeforeClose()))
  2706. {
  2707. BOOL fShouldImposeLimit = TRUE;
  2708. if( FAILED( m_pInstance->TriggerMaxMsgSizeEvent( GetSessionPropertyBag(), m_pIMsg, &fShouldImposeLimit ) ) || fShouldImposeLimit )
  2709. {
  2710. ErrorTrace((LPARAM) this, "SMTP_RESP_NOSTORAGE, SMTP_MAX_SESSION_SIZE_EXCEEDED_MSG - %d", EstMailSize);
  2711. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToSize);
  2712. DebugTrace((LPARAM) this, "GetMaxMsgSizeBeforeClose() exceeded - closing connection");
  2713. DisconnectClient();
  2714. TraceFunctLeaveEx((LPARAM) this);
  2715. return FALSE;
  2716. }
  2717. }
  2718. return TRUE;
  2719. }
  2720. BOOL SMTP_CONNECTION::DoBodyCommand (char * Value, char * InputLine)
  2721. {
  2722. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoBodyCommand");
  2723. _ASSERT (m_pInstance != NULL);
  2724. if(!_strnicmp(Value, (char * )"8bitmime", 8))
  2725. {
  2726. m_pIMsg->PutDWORD(IMMPID_MP_EIGHTBIT_MIME_OPTION, 1);
  2727. TraceFunctLeaveEx((LPARAM) this);
  2728. return TRUE;
  2729. }
  2730. else if((!_strnicmp(Value, (char * )"BINARYMIME", 10)) && m_pInstance->AllowBinaryMime())
  2731. {
  2732. m_fIsBinaryMime = TRUE; //Need to get rid of this **
  2733. m_pIMsg->PutDWORD(IMMPID_MP_BINARYMIME_OPTION, 1);
  2734. TraceFunctLeaveEx((LPARAM) this);
  2735. return TRUE;
  2736. }
  2737. else if(!_strnicmp(Value, (char * )"7bit", 4))
  2738. {
  2739. TraceFunctLeaveEx((LPARAM) this);
  2740. return TRUE;
  2741. }
  2742. else
  2743. {
  2744. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS," %s\r\n", SMTP_BAD_BODY_TYPE_STR );
  2745. ErrorTrace((LPARAM) this, "SMTP_RESP_MBX_SYNTAX, SMTP_BAD_BODY_TYPE_STR.");
  2746. ++m_ProtocolErrors;
  2747. TraceFunctLeaveEx((LPARAM) this);
  2748. return FALSE;
  2749. }
  2750. }
  2751. BOOL SMTP_CONNECTION::DoRetCommand (char * Value, char * InputLine)
  2752. {
  2753. char RetDsnValue[10];
  2754. HRESULT hr = S_OK;
  2755. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoRetCommand");
  2756. _ASSERT (m_pInstance != NULL);
  2757. //strict size checking
  2758. if(strlen(Value) < 5)
  2759. {
  2760. hr = m_pIMsg->GetStringA(IMMPID_MP_DSN_RET_VALUE, sizeof(RetDsnValue), RetDsnValue);
  2761. if(FAILED(hr))
  2762. {
  2763. if(!_strnicmp(Value, (char * )"FULL", 4) ||
  2764. !_strnicmp(Value, (char * )"HDRS", 4))
  2765. {
  2766. hr = m_pIMsg->PutStringA(IMMPID_MP_DSN_RET_VALUE, Value);
  2767. if(!FAILED(hr))
  2768. {
  2769. TraceFunctLeaveEx((LPARAM) this);
  2770. return TRUE;
  2771. }
  2772. else
  2773. {
  2774. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_STORAGE );
  2775. ErrorTrace((LPARAM) this, "Failed to set RET value to IMSG");
  2776. TraceFunctLeaveEx((LPARAM) this);
  2777. return FALSE;
  2778. }
  2779. }
  2780. }
  2781. }
  2782. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS," %s\r\n","Invalid arguments" );
  2783. ErrorTrace((LPARAM) this, "SMTP_RESP_MBX_SYNTAX, SMTP_BAD_CMD_STR. Client sent bad RET value");
  2784. ++m_ProtocolErrors;
  2785. TraceFunctLeaveEx((LPARAM) this);
  2786. return FALSE;
  2787. }
  2788. BOOL SMTP_CONNECTION::IsXtext(char * Xtext)
  2789. {
  2790. char * StartPtr = Xtext;
  2791. char NextChar = '\0';
  2792. while((NextChar = *StartPtr++) != '\0')
  2793. {
  2794. if(NextChar == '+')
  2795. {
  2796. NextChar = *StartPtr++;
  2797. if(!isascii(NextChar) || !isxdigit(NextChar))
  2798. {
  2799. return FALSE;
  2800. }
  2801. }
  2802. else if(NextChar < '!' || NextChar > '~' || NextChar == '=')
  2803. {
  2804. return FALSE;
  2805. }
  2806. }
  2807. return TRUE;
  2808. }
  2809. BOOL SMTP_CONNECTION::DoEnvidCommand (char * Value, char * InputLine)
  2810. {
  2811. HRESULT hr = S_OK;
  2812. char EnvidValue[105];
  2813. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoEnvidCommand");
  2814. _ASSERT (m_pInstance != NULL);
  2815. _ASSERT (Value != NULL);
  2816. _ASSERT (InputLine != NULL);
  2817. hr = m_pIMsg->GetStringA(IMMPID_MP_DSN_ENVID_VALUE, sizeof(EnvidValue), EnvidValue);
  2818. if(FAILED(hr))
  2819. {
  2820. DWORD ValueLength = lstrlen(Value);
  2821. if((ValueLength != 0) && (ValueLength <= 100))
  2822. {
  2823. if(IsXtext(Value))
  2824. {
  2825. hr = m_pIMsg->PutStringA(IMMPID_MP_DSN_ENVID_VALUE, Value);
  2826. if(!FAILED(hr))
  2827. {
  2828. TraceFunctLeaveEx((LPARAM) this);
  2829. return TRUE;
  2830. }
  2831. else
  2832. {
  2833. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_STORAGE );
  2834. ErrorTrace((LPARAM) this, "Failed to set ENVID value to IMSG");
  2835. TraceFunctLeaveEx((LPARAM) this);
  2836. return FALSE;
  2837. }
  2838. }
  2839. }
  2840. }
  2841. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS," %s\r\n","Invalid arguments" );
  2842. ErrorTrace((LPARAM) this, "SMTP_RESP_MBX_SYNTAX, SMTP_BAD_CMD_STR. Client sent bad envid value");
  2843. ++m_ProtocolErrors;
  2844. TraceFunctLeaveEx((LPARAM) this);
  2845. return FALSE;
  2846. }
  2847. BOOL SMTP_CONNECTION::DoNotifyCommand (char * Value, DWORD * pdwNotifyValue)
  2848. {
  2849. *pdwNotifyValue = 0;
  2850. char * SearchPtr = NULL;
  2851. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoNotifyCommand");
  2852. _ASSERT (Value != NULL);
  2853. if(!strcasecmp(Value, "NEVER"))
  2854. {
  2855. *pdwNotifyValue = RP_DSN_NOTIFY_NEVER;
  2856. }
  2857. else
  2858. {
  2859. for(SearchPtr = Value; SearchPtr != NULL; Value = SearchPtr)
  2860. {
  2861. SearchPtr = strchr(SearchPtr, ',');
  2862. if(SearchPtr != NULL)
  2863. {
  2864. *SearchPtr++ = '\0';
  2865. }
  2866. if(!strcasecmp(Value, "SUCCESS"))
  2867. {
  2868. *pdwNotifyValue |= RP_DSN_NOTIFY_SUCCESS;
  2869. }
  2870. else if(!strcasecmp(Value, "FAILURE"))
  2871. {
  2872. *pdwNotifyValue |= RP_DSN_NOTIFY_FAILURE;
  2873. }
  2874. else if(!strcasecmp(Value, "DELAY"))
  2875. {
  2876. *pdwNotifyValue |= RP_DSN_NOTIFY_DELAY;
  2877. }
  2878. else
  2879. {
  2880. *pdwNotifyValue = RP_DSN_NOTIFY_INVALID;
  2881. break;
  2882. }
  2883. }
  2884. if(!*pdwNotifyValue)
  2885. {
  2886. *pdwNotifyValue = RP_DSN_NOTIFY_INVALID;
  2887. }
  2888. }
  2889. if(*pdwNotifyValue == RP_DSN_NOTIFY_INVALID)
  2890. {
  2891. return FALSE;
  2892. }
  2893. TraceFunctLeaveEx((LPARAM) this);
  2894. return TRUE;
  2895. }
  2896. BOOL SMTP_CONNECTION::DoOrcptCommand (char * Value)
  2897. {
  2898. char * XtextPtr = NULL;
  2899. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoOrcptCommand");
  2900. _ASSERT (m_pInstance != NULL);
  2901. DWORD ValueLength = lstrlen(Value);
  2902. if((ValueLength != 0) && (ValueLength <= 500))
  2903. {
  2904. //NK** : Parse out the address type and validate it against IANA registered
  2905. // mail address type
  2906. if(XtextPtr = strchr(Value,';'))
  2907. {
  2908. if(IsXtext(XtextPtr+1))
  2909. {
  2910. TraceFunctLeaveEx((LPARAM) this);
  2911. return TRUE;
  2912. }
  2913. }
  2914. }
  2915. TraceFunctLeaveEx((LPARAM) this);
  2916. return FALSE;
  2917. }
  2918. BOOL SMTP_CONNECTION::DoSmtpArgs (char *InputLine)
  2919. {
  2920. char * Value = NULL;
  2921. char * EndOfLine = NULL;
  2922. char * Cmd = NULL;
  2923. char * LinePtr = InputLine;
  2924. char * SearchPtr = NULL;
  2925. while(LinePtr && *LinePtr != '\0')
  2926. {
  2927. //find the beginning of the keyword
  2928. LinePtr = (char *) strchr (LinePtr, '=');
  2929. if(LinePtr != NULL)
  2930. {
  2931. SearchPtr = LinePtr;
  2932. //Cmd = LinePtr - 4;
  2933. while( (*SearchPtr != ' ') && (*SearchPtr != '\0'))
  2934. {
  2935. SearchPtr--;
  2936. }
  2937. Cmd = SearchPtr + 1;
  2938. *LinePtr++ = '\0';
  2939. Value = LinePtr;
  2940. //Back track to the beginning of the byte
  2941. //before the size command. If that character
  2942. //is a space, then we need to Null terminate
  2943. //the string there. If it's not a space, then
  2944. //this is an error.
  2945. EndOfLine = Cmd - 1;
  2946. if(*EndOfLine == ' ')
  2947. *EndOfLine = '\0';
  2948. else if(*EndOfLine != '\0')
  2949. {
  2950. continue;
  2951. }
  2952. LinePtr = (char *) strchr(LinePtr, ' ');
  2953. if(LinePtr != NULL)
  2954. {
  2955. *LinePtr++ = '\0';
  2956. }
  2957. if(!strcasecmp(Cmd, "SIZE"))
  2958. {
  2959. if(!DoSizeCommand(Value, InputLine))
  2960. return FALSE;
  2961. }
  2962. else if(!strcasecmp(Cmd, "BODY"))
  2963. {
  2964. if(!DoBodyCommand (Value, InputLine))
  2965. return FALSE;
  2966. }
  2967. else if(!strcasecmp(Cmd, "RET") && m_pInstance->AllowDSN())
  2968. {
  2969. if(!DoRetCommand (Value, InputLine))
  2970. return FALSE;
  2971. }
  2972. else if(!strcasecmp(Cmd, "ENVID") && m_pInstance->AllowDSN())
  2973. {
  2974. if(!DoEnvidCommand (Value, InputLine))
  2975. return FALSE;
  2976. }
  2977. else if(!strcasecmp(Cmd, "AUTH"))
  2978. {
  2979. //
  2980. // We do not handle the AUTH= parameter on the MAIL command
  2981. // If the client sends this, just ignore it and keep processing
  2982. // In the future consider setting Value as a MailMsg prop so
  2983. // we can use it outbound and thus support RFC 2554 completely.
  2984. //
  2985. }
  2986. else
  2987. {
  2988. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n","Invalid arguments" );
  2989. ++m_ProtocolErrors;
  2990. return FALSE;
  2991. }
  2992. }
  2993. else
  2994. {
  2995. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n","Invalid arguments" );
  2996. ++m_ProtocolErrors;
  2997. return FALSE;
  2998. }
  2999. }
  3000. return TRUE;
  3001. }
  3002. BOOL SMTP_CONNECTION::DoRcptArgs (char *InputLine, char * szORCPTval, DWORD * pdwNotifyVal)
  3003. {
  3004. char * Value = NULL;
  3005. char * EndOfLine = NULL;
  3006. char * Cmd = NULL;
  3007. char * LinePtr = InputLine;
  3008. char * SearchPtr = NULL;
  3009. *pdwNotifyVal = 0;
  3010. BOOL fDoneOrcptCmd = FALSE;
  3011. BOOL fDoneNotifyCmd = FALSE;
  3012. while(LinePtr && *LinePtr != '\0')
  3013. {
  3014. if(!m_pInstance->AllowDSN())
  3015. return FALSE;
  3016. //find the beginning of the keyword
  3017. LinePtr = (char *) strchr (LinePtr, '=');
  3018. if(LinePtr != NULL)
  3019. {
  3020. SearchPtr = LinePtr;
  3021. //Cmd = LinePtr - 4;
  3022. while( (*SearchPtr != ' ') && (*SearchPtr != '\0'))
  3023. {
  3024. SearchPtr--;
  3025. }
  3026. Cmd = SearchPtr + 1;
  3027. *LinePtr++ = '\0';
  3028. Value = LinePtr;
  3029. //Back track to the beginning of the byte
  3030. //before the size command. If that character
  3031. //is a space, then we need to Null terminate
  3032. //the string there. If it's not a space, then
  3033. //this is an error.
  3034. EndOfLine = Cmd - 1;
  3035. if(*EndOfLine == ' ')
  3036. *EndOfLine = '\0';
  3037. else if(*EndOfLine != '\0')
  3038. {
  3039. continue;
  3040. }
  3041. LinePtr = (char *) strchr(LinePtr, ' ');
  3042. if(LinePtr != NULL)
  3043. {
  3044. *LinePtr++ = '\0';
  3045. }
  3046. if(!strcasecmp(Cmd, "NOTIFY") )
  3047. {
  3048. if( fDoneNotifyCmd || ( !DoNotifyCommand( Value, pdwNotifyVal ) ) )
  3049. {
  3050. return FALSE;
  3051. }
  3052. fDoneNotifyCmd = TRUE;
  3053. }
  3054. else if(!strcasecmp(Cmd, "ORCPT"))
  3055. {
  3056. if( ( !fDoneOrcptCmd ) && DoOrcptCommand(Value))
  3057. {
  3058. strcpy(szORCPTval,Value);
  3059. fDoneOrcptCmd = TRUE;
  3060. }
  3061. else
  3062. {
  3063. *szORCPTval = '\0';
  3064. return FALSE;
  3065. }
  3066. }
  3067. else
  3068. {
  3069. return FALSE;
  3070. }
  3071. }
  3072. else
  3073. {
  3074. return FALSE;
  3075. }
  3076. }
  3077. return TRUE;
  3078. }
  3079. //NK** : Receive header always completes sync
  3080. BOOL SMTP_CONNECTION::WriteRcvHeader(void)
  3081. {
  3082. char * Address = NULL;
  3083. char * VersionNum = NULL;
  3084. CHAR szText[2024];
  3085. char szDateBuf [cMaxArpaDate];
  3086. DWORD cbText = 0;
  3087. DWORD Error = NO_ERROR;
  3088. DWORD EstMailSize = 0;
  3089. SYSTEMTIME st;
  3090. HRESULT hr;
  3091. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::WriteRcvHeader");
  3092. if(m_IMsgHandle == NULL)
  3093. {
  3094. TraceFunctLeaveEx((LPARAM) this);
  3095. return FALSE;
  3096. }
  3097. //get the name of the sender to place into the mail header
  3098. Address = (char *) QueryClientUserName();
  3099. _ASSERT (Address != NULL);
  3100. //Dump the header line into the buffer
  3101. GetLocalTime(&st);
  3102. GetArpaDate(szDateBuf);
  3103. VersionNum = strchr(g_VersionString, ':');
  3104. if(VersionNum)
  3105. {
  3106. VersionNum += 2;
  3107. }
  3108. else
  3109. {
  3110. VersionNum = "";
  3111. }
  3112. m_pInstance->LockGenCrit();
  3113. if(m_DNSLookupRetCode == SUCCESS)
  3114. {
  3115. m_HeaderSize = wsprintf(szText,szFormatReceivedFormatSuccess,
  3116. Address, QueryClientHostName(), m_pInstance->GetFQDomainName(),
  3117. (m_SecurePort ? " over TLS secured channel" : ""),VersionNum,
  3118. Daynames[st.wDayOfWeek], szDateBuf);
  3119. }
  3120. else if ( m_DNSLookupRetCode == LOOKUP_FAILED)
  3121. {
  3122. m_HeaderSize = wsprintf(szText,szFormatReceivedFormatUnverified,
  3123. Address, QueryClientHostName(), m_pInstance->GetFQDomainName(),
  3124. (m_SecurePort ? " over TLS secured channel" : ""),VersionNum,
  3125. Daynames[st.wDayOfWeek], szDateBuf);
  3126. }
  3127. else
  3128. {
  3129. m_HeaderSize = wsprintf(szText,szFormatReceivedFormatFailed,
  3130. Address, QueryClientHostName(), m_pInstance->GetFQDomainName(),
  3131. (m_SecurePort ? " over TLS secured channel" : ""),VersionNum,
  3132. Daynames[st.wDayOfWeek], szDateBuf);
  3133. }
  3134. m_pInstance->UnLockGenCrit();
  3135. //if EstMailSize > 0, then the size command was given. If the size command was
  3136. //given and the value was 0, we would have caught it above and would skip over
  3137. //this peice of code.
  3138. if (EstMailSize > 0)
  3139. {
  3140. if(SetFilePointer(m_IMsgHandle->m_hFile, EstMailSize + cbText,
  3141. NULL, FILE_BEGIN) == 0xFFFFFFFF)
  3142. {
  3143. Error = GetLastError();
  3144. DWORD Offset = EstMailSize + cbText;
  3145. //FormatSmtpMessage ("%d %s %s\r\n",SMTP_RESP_NORESOURCES, ENO_RESOURCES, SMTP_NO_STORAGE );
  3146. //ProtocolLog(MAIL, (char *)InputLine, Error, SMTP_RESP_NORESOURCES, 0, 0);
  3147. FatalTrace((LPARAM) this, " SetFilePointer failed - %d", GetLastError());
  3148. TraceFunctLeaveEx((LPARAM) this);
  3149. return FALSE;
  3150. }
  3151. if(!SetEndOfFile(m_IMsgHandle->m_hFile))
  3152. {
  3153. Error = GetLastError();
  3154. //ProtocolLog(MAIL, (char *)InputLine, Error, SMTP_RESP_NORESOURCES, 0, 0);
  3155. //PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_STORAGE );
  3156. FatalTrace((LPARAM) this, " SetEndOfFile failed - %d", GetLastError());
  3157. TraceFunctLeaveEx((LPARAM) this);
  3158. return FALSE;
  3159. }
  3160. DWORD dwPointer;
  3161. dwPointer = SetFilePointer(m_IMsgHandle->m_hFile, 0, NULL, FILE_BEGIN);
  3162. //if we get here, this should always pass
  3163. _ASSERT (dwPointer != 0xFFFFFFFF);
  3164. }
  3165. _ASSERT(m_pFileWriteBuffer);
  3166. _ASSERT(m_pFileWriteBuffer->GetData());
  3167. CopyMemory((m_pFileWriteBuffer->GetData() + m_cbCurrentWriteBuffer),szText,m_HeaderSize);
  3168. m_cbCurrentWriteBuffer += m_HeaderSize;
  3169. wsprintf(szText,"%s, %s",Daynames[st.wDayOfWeek], szDateBuf);
  3170. hr = m_pIMsg->PutStringA(IMMPID_MP_ARRIVAL_TIME, szText);
  3171. if(FAILED(hr))
  3172. {
  3173. Error = GetLastError();
  3174. //FormatSmtpMessage ("%d %s %s\r\n",SMTP_RESP_NORESOURCES, ENO_RESOURCES, SMTP_NO_STORAGE );
  3175. FatalTrace((LPARAM) this, " MailInfo->SetSenderToStream");
  3176. TraceFunctLeaveEx((LPARAM) this);
  3177. return FALSE;
  3178. }
  3179. return TRUE;
  3180. }
  3181. /*++
  3182. Name :
  3183. SMTP_CONNECTION::DoMAILCommand
  3184. Description:
  3185. Responds to the SMTP MAIL command.
  3186. Arguments:
  3187. Are ignored
  3188. Returns:
  3189. TRUE if the connection should stay open.
  3190. FALSE if this object should be deleted.
  3191. --*/
  3192. BOOL SMTP_CONNECTION::DoMAILCommand(const char * InputLine, DWORD ParameterSize)
  3193. {
  3194. char * Ptr = NULL;
  3195. char * SizeCmd = NULL;
  3196. //char * Address = NULL;
  3197. char * Value = NULL;
  3198. CAddr * NewAddress = NULL;
  3199. DWORD EstMailSize = 0;
  3200. DWORD cbText = 0;
  3201. DWORD Error = NO_ERROR;
  3202. BOOL FoundSizeCmd = FALSE;
  3203. HRESULT hr = S_OK;
  3204. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoMAILCommand");
  3205. _ASSERT (m_pInstance != NULL);
  3206. //If the current state is BDAT the only command that can be received is BDAT
  3207. if(m_State == BDAT)
  3208. {
  3209. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","BDAT Expected" );
  3210. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3211. ++m_ProtocolErrors;
  3212. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - BDAT Expected");
  3213. TraceFunctLeaveEx((LPARAM) this);
  3214. return TRUE;
  3215. }
  3216. //Check if HELO or EHELO
  3217. if(!m_pInstance->AllowMailFromNoHello() && !m_HelloSent)
  3218. {
  3219. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n", "Send hello first" );
  3220. ErrorTrace((LPARAM) this, "DoMAILCommand - SMTP_RESP_BAD_SEQ, Send hello first");
  3221. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3222. ++m_ProtocolErrors;
  3223. TraceFunctLeaveEx((LPARAM) this);
  3224. return TRUE;
  3225. }
  3226. if(!m_HelloSent)
  3227. {
  3228. m_HelloSent = TRUE;
  3229. }
  3230. //Check if the client is secure enough
  3231. if (!IsClientSecureEnough())
  3232. {
  3233. ErrorTrace((LPARAM) this, "DoMAILCommand - SMTP_RESP_MUST_SECURE, Must do STARTTLS first");
  3234. TraceFunctLeaveEx((LPARAM) this);
  3235. return TRUE;
  3236. }
  3237. if(!m_fAuthAnon && !m_fAuthenticated)
  3238. {
  3239. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY, " %s\r\n", "Client was not authenticated");
  3240. ErrorTrace((LPARAM) this, "DoMAILCommand - SMTP_RESP_MUST_SECURE, user not authenticated");
  3241. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3242. ++m_ProtocolErrors;
  3243. TraceFunctLeaveEx((LPARAM) this);
  3244. return TRUE;
  3245. }
  3246. //disallow mail from more than once
  3247. if(m_RecvdMailCmd)
  3248. {
  3249. ++m_ProtocolErrors;
  3250. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n", "Sender already specified" );
  3251. ErrorTrace((LPARAM) this, "DoMAILCommand - SMTP_RESP_BAD_SEQ, Sender already specified");
  3252. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3253. TraceFunctLeaveEx((LPARAM) this);
  3254. return TRUE;
  3255. }
  3256. //We should reset the current hop count when we get a new mail. If we don't
  3257. //do this, we might NDR the mail that comes later in the session.
  3258. m_HopCount = 0;
  3259. m_LocalHopCount = 0;
  3260. //start parsing from the beginning of the line
  3261. Ptr = (char *) InputLine;
  3262. //check if argument have the right format
  3263. Ptr = CheckArgument(Ptr);
  3264. if (Ptr == NULL)
  3265. {
  3266. TraceFunctLeaveEx((LPARAM) this);
  3267. return TRUE;
  3268. }
  3269. //skip over from:
  3270. Ptr = SkipWord (Ptr, "From", 4);
  3271. if (Ptr == NULL)
  3272. {
  3273. TraceFunctLeaveEx((LPARAM) this);
  3274. return TRUE;
  3275. }
  3276. DWORD dwAddressLen = 0;
  3277. DWORD dwCanonAddrLen = 0;
  3278. char * ArgPtr = NULL;
  3279. char * AddrPtr = NULL;
  3280. char * CanonAddrPtr = NULL;
  3281. char * DomainPtr = NULL;
  3282. m_szFromAddress[0] = '\0';
  3283. //Parse out the address and possible argumets from the input line
  3284. if(!Extract821AddressFromLine(Ptr,&AddrPtr,&dwAddressLen,&ArgPtr))
  3285. {
  3286. SetLastError(ERROR_INVALID_DATA);
  3287. HandleAddressError((char *)InputLine);
  3288. TraceFunctLeaveEx((LPARAM) this);
  3289. return TRUE;
  3290. }
  3291. //Check if we have valid tail and address
  3292. if(!AddrPtr)
  3293. {
  3294. SetLastError(ERROR_INVALID_DATA);
  3295. HandleAddressError((char *)InputLine);
  3296. TraceFunctLeaveEx((LPARAM) this);
  3297. return TRUE;
  3298. }
  3299. //Check for the special case <> from address
  3300. if(dwAddressLen == 2 && *AddrPtr == '<' && *(AddrPtr+1) == '>')
  3301. {
  3302. //simply copy this as the validated address
  3303. lstrcpy(m_szFromAddress,"<>");
  3304. }
  3305. else
  3306. {
  3307. //Extract the canonical address in the <local-part> "@" <domain> form
  3308. if(!ExtractCanonical821Address(AddrPtr,dwAddressLen,&CanonAddrPtr,&dwCanonAddrLen))
  3309. {
  3310. SetLastError(ERROR_INVALID_DATA);
  3311. HandleAddressError((char *)InputLine);
  3312. TraceFunctLeaveEx((LPARAM) this);
  3313. return TRUE;
  3314. }
  3315. //If we have a Canonical addr - validate it
  3316. if(CanonAddrPtr)
  3317. {
  3318. strncpy(m_szFromAddress,CanonAddrPtr,dwCanonAddrLen);
  3319. *(m_szFromAddress + dwCanonAddrLen) = '\0';
  3320. if(!Validate821Address(m_szFromAddress, dwCanonAddrLen))
  3321. {
  3322. HandleAddressError((char *)InputLine);
  3323. TraceFunctLeaveEx((LPARAM) this);
  3324. return TRUE;
  3325. }
  3326. //Extract
  3327. if(!Get821AddressDomain(m_szFromAddress,dwCanonAddrLen,&DomainPtr))
  3328. {
  3329. SetLastError(ERROR_INVALID_DATA);
  3330. HandleAddressError((char *)InputLine);
  3331. TraceFunctLeaveEx((LPARAM) this);
  3332. return TRUE;
  3333. }
  3334. else
  3335. {
  3336. //Is the RDNS enabled for MAILFROM command
  3337. if(m_pInstance->IsRDNSEnabledForMAIL())
  3338. {
  3339. DWORD dwDNSLookupRetCode = SUCCESS;
  3340. char ProtBuff[255];
  3341. //If we have the RDNS option on MAIL from domain
  3342. //we cannot get a NULL domain on MAIL from
  3343. if(DomainPtr)
  3344. {
  3345. dwDNSLookupRetCode = VerifiyClient (DomainPtr,DomainPtr);
  3346. if(dwDNSLookupRetCode == NO_MATCH)
  3347. {
  3348. PE_CdFormatSmtpMessage (SMTP_RESP_REJECT_MAILFROM, ENO_SECURITY_TMP," %s\r\n",SMTP_RDNS_REJECTION);
  3349. wsprintf(ProtBuff, "RDNS failed - %s", DomainPtr);
  3350. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3351. ++m_ProtocolErrors;
  3352. ErrorTrace((LPARAM) this, "Rejected Mail From : RDNS failed for %s", DomainPtr);
  3353. TraceFunctLeaveEx((LPARAM) this);
  3354. return TRUE;
  3355. }
  3356. else if(m_DNSLookupRetCode == LOOKUP_FAILED)
  3357. {
  3358. //We had an internal DNS failure
  3359. PE_CdFormatSmtpMessage (SMTP_RESP_REJECT_MAILFROM, EINTERNAL_ERROR," %s\r\n",SMTP_RDNS_FAILURE);
  3360. wsprintf(ProtBuff, "RDNS DNS failure - %s", DomainPtr);
  3361. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3362. ++m_ProtocolErrors;
  3363. ErrorTrace((LPARAM) this, "Rejected MAILFROM : RDNS DNS failure for %s", DomainPtr);
  3364. TraceFunctLeaveEx((LPARAM) this);
  3365. return TRUE;
  3366. }
  3367. }
  3368. else
  3369. {
  3370. PE_CdFormatSmtpMessage (SMTP_RESP_REJECT_MAILFROM, ENO_SECURITY_TMP," %s\r\n",SMTP_RDNS_REJECTION);
  3371. wsprintf(ProtBuff, "RDNS failed - no domain specified");
  3372. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3373. ++m_ProtocolErrors;
  3374. ErrorTrace((LPARAM) this, "Rejected Mail From : no domain specified");
  3375. TraceFunctLeaveEx((LPARAM) this);
  3376. return TRUE;
  3377. }
  3378. }
  3379. }
  3380. }
  3381. else
  3382. {
  3383. SetLastError(ERROR_INVALID_DATA);
  3384. HandleAddressError((char *)InputLine);
  3385. TraceFunctLeaveEx((LPARAM) this);
  3386. return TRUE;
  3387. }
  3388. //rewrite the address if it's not the NULL address,
  3389. //and the masquerading option is on
  3390. if(m_pInstance->ShouldMasquerade() && !ISNULLADDRESS(m_szFromAddress)
  3391. && (!DomainPtr || m_pInstance->IsALocalDomain(DomainPtr)))
  3392. {
  3393. if(!m_pInstance->MasqueradeDomain(m_szFromAddress, DomainPtr))
  3394. {
  3395. //it failed. Inform the user
  3396. SetLastError(ERROR_INVALID_DATA);
  3397. HandleAddressError((char *)InputLine);
  3398. TraceFunctLeaveEx((LPARAM) this);
  3399. return TRUE;
  3400. }
  3401. }
  3402. else if(!DomainPtr && !ISNULLADDRESS(m_szFromAddress))
  3403. {
  3404. //If there is no domain on this address,
  3405. //then append the current domain to this
  3406. //address. However, if we get the <>
  3407. //address, don't append a domain
  3408. if(!m_pInstance->AppendLocalDomain (m_szFromAddress))
  3409. {
  3410. //it failed. Inform the user
  3411. SetLastError(ERROR_INVALID_DATA);
  3412. HandleAddressError((char *)InputLine);
  3413. TraceFunctLeaveEx((LPARAM) this);
  3414. return TRUE;
  3415. }
  3416. }
  3417. }
  3418. //parse the arguments, if any
  3419. if(!DoSmtpArgs (ArgPtr))
  3420. {
  3421. TraceFunctLeaveEx((LPARAM) this);
  3422. return TRUE;
  3423. }
  3424. _ASSERT (m_szFromAddress != NULL);
  3425. _ASSERT (m_szFromAddress[0] != '\0');
  3426. hr = m_pIMsg->PutStringA(IMMPID_MP_SENDER_ADDRESS_SMTP, m_szFromAddress);
  3427. if(FAILED(hr))
  3428. {
  3429. Error = GetLastError();
  3430. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_STORAGE );
  3431. m_CInboundContext.SetWin32Status(Error);
  3432. FatalTrace((LPARAM) this, " MailInfo->SetSenderToStream");
  3433. TraceFunctLeaveEx((LPARAM) this);
  3434. return TRUE;
  3435. }
  3436. hr = m_pIMsg->PutStringA(IMMPID_MP_HELO_DOMAIN, QueryClientUserName());
  3437. if(FAILED(hr))
  3438. {
  3439. Error = GetLastError();
  3440. //FatalTrace((LPARAM) this, "SetEndOfFile failed on file %s !!! (err=%d)", MailInfo->GetMailFileName(), m_MailBodyError);
  3441. m_CInboundContext.SetWin32Status(Error);
  3442. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE);
  3443. TraceFunctLeaveEx((LPARAM) this);
  3444. return TRUE;
  3445. }
  3446. m_RecvdMailCmd = TRUE;
  3447. m_State = MAIL;
  3448. PE_CdFormatSmtpMessage (SMTP_RESP_OK, ESENDER_ADDRESS_VALID," %s....Sender OK\r\n", m_szFromAddress);
  3449. TraceFunctLeaveEx((LPARAM) this);
  3450. return TRUE;
  3451. }
  3452. /*++
  3453. Name :
  3454. SMTP_CONNECTION::DoRCPTCommand
  3455. Description:
  3456. Responds to the SMTP RCPT command.
  3457. This funcion gets the addresses and
  3458. places then into a linked list.
  3459. Arguments:
  3460. InputLine - Buffer received from client
  3461. paramterSize - amount of data in buffer
  3462. Returns:
  3463. TRUE if the connection should stay open.
  3464. FALSE if this object should be deleted.
  3465. --*/
  3466. BOOL SMTP_CONNECTION::DoRCPTCommand(const char * InputLine, DWORD ParameterSize)
  3467. {
  3468. char * ToName = NULL;
  3469. char * ThisAddress = NULL;
  3470. CAddr * NewAddress = NULL;
  3471. CAddr * TempAddress = NULL;
  3472. BOOL IsDomainValid = TRUE; //assume everyone is O.K
  3473. BOOL RelayThisMail = FALSE;
  3474. BOOL DropQuotaExceeded = FALSE;
  3475. DWORD dwPropId = IMMPID_RP_ADDRESS_SMTP;
  3476. DWORD dwNewRecipIndex = 0;
  3477. HRESULT hr = S_OK;
  3478. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoRCPTCommand");
  3479. _ASSERT(m_pInstance != NULL);
  3480. //If the current state is BDAT the only command that can be received is BDAT
  3481. if(m_State == BDAT)
  3482. {
  3483. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", "BDAT Expected" );
  3484. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3485. ++m_ProtocolErrors;
  3486. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - BDAT Expected");
  3487. TraceFunctLeaveEx((LPARAM) this);
  3488. return TRUE;
  3489. }
  3490. if(!m_HelloSent)
  3491. {
  3492. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", "Send hello first" );
  3493. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3494. ++m_ProtocolErrors;
  3495. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Send hello first");
  3496. TraceFunctLeaveEx((LPARAM) this);
  3497. return TRUE;
  3498. }
  3499. //Check if the client is secure enough
  3500. if (!IsClientSecureEnough()) {
  3501. ErrorTrace((LPARAM) this, "DoRcptCommand - SMTP_RESP_MUST_SECURE, Must do STARTTLS first");
  3502. TraceFunctLeaveEx((LPARAM) this);
  3503. return TRUE;
  3504. }
  3505. if(!m_fAuthAnon && !m_fAuthenticated)
  3506. {
  3507. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY, " %s\r\n","Client was not authenticated");
  3508. ErrorTrace((LPARAM) this, "DoRCPTCommand - SMTP_RESP_MUST_SECURE, user not authenticated");
  3509. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3510. ++m_ProtocolErrors;
  3511. TraceFunctLeaveEx((LPARAM) this);
  3512. return TRUE;
  3513. }
  3514. //see if we got a valid return path before going on
  3515. if(!m_RecvdMailCmd || !m_pIMsg || !m_pIMsgRecips || (m_szFromAddress[0] == '\0') )
  3516. {
  3517. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", SMTP_NEED_MAIL_FROM_MSG);
  3518. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3519. ++m_ProtocolErrors;
  3520. ErrorTrace((LPARAM) this, "In DoRCPTCommand():SMTP_RESP_BAD_SEQ, SMTP_NEED_MAIL_FROM_MSG");
  3521. TraceFunctLeaveEx((LPARAM) this);
  3522. return TRUE;
  3523. }
  3524. //start at inputline
  3525. ToName = (char *) InputLine;
  3526. //set the state
  3527. m_State = RCPT;
  3528. m_RecvdRcptCmd = TRUE;
  3529. //check if argument have the right format
  3530. ToName = CheckArgument(ToName);
  3531. if (ToName == NULL)
  3532. {
  3533. TraceFunctLeaveEx((LPARAM) this);
  3534. return TRUE;
  3535. }
  3536. ToName = SkipWord (ToName, "To", 2);
  3537. if (ToName == NULL)
  3538. {
  3539. TraceFunctLeaveEx((LPARAM) this);
  3540. return TRUE;
  3541. }
  3542. char * DomainPtr = NULL;
  3543. char * ArgPtr = NULL;
  3544. char szRcptAddress[MAX_INTERNET_NAME + 1];
  3545. szRcptAddress[0] = '\0';
  3546. DWORD dwNotifyVal;
  3547. char szOrcptVal[MAX_INTERNET_NAME];
  3548. szOrcptVal[0] = '\0';
  3549. if(!ExtractAndValidateRcpt(ToName, &ArgPtr, szRcptAddress, &DomainPtr ))
  3550. {
  3551. // If failed. Inform the user
  3552. SetLastError(ERROR_INVALID_DATA);
  3553. HandleAddressError((char *)InputLine);
  3554. TraceFunctLeaveEx((LPARAM) this);
  3555. return TRUE;
  3556. }
  3557. //Parse out the DSN values if any
  3558. //parse the arguments, if any
  3559. if(!DoRcptArgs (ArgPtr, szOrcptVal, &dwNotifyVal))
  3560. {
  3561. //it failed. Inform the user
  3562. DWORD dwError = GetLastError();
  3563. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n","Invalid arguments" );
  3564. m_CInboundContext.SetWin32Status(dwError);
  3565. TraceFunctLeaveEx((LPARAM) this);
  3566. return TRUE;
  3567. }
  3568. //check to see if there is any room in the inn.
  3569. //if the remote queue entry is allocated, check
  3570. //that also.
  3571. DWORD TotalRcpts = 0;
  3572. hr = m_pIMsgRecips->Count(&TotalRcpts);
  3573. if( FAILED(hr) || ((m_pInstance->GetMaxRcpts() > 0) && ((TotalRcpts + 1) > m_pInstance->GetMaxRcpts())))
  3574. {
  3575. PE_CdFormatSmtpMessage (SMTP_RESP_NOSTORAGE, ETOO_MANY_RCPTS, " %s\r\n",SMTP_TOO_MANY_RCPTS);
  3576. ErrorTrace((LPARAM) this, "Exceeded MaxRcpts %d", TotalRcpts + 1);
  3577. // don't count too many rcpts as protocol errors as this will
  3578. // close the connection.
  3579. //++m_ProtocolErrors;
  3580. delete NewAddress;
  3581. NewAddress = NULL;
  3582. TraceFunctLeaveEx((LPARAM) this);
  3583. return TRUE;
  3584. }
  3585. //If the relay check feature is enabled make sure that this
  3586. //recipient is to be accepted for relay
  3587. if(m_pInstance->IsRelayEnabled())
  3588. {
  3589. RelayThisMail = ShouldAcceptRcpt(DomainPtr);
  3590. }
  3591. if (m_pInstance->IsDropDirQuotaCheckingEnabled())
  3592. {
  3593. //We only enforce drop dir quota checking on local and alias domains. Since
  3594. //this is turned on by default, we assume that anyone who has setup there own
  3595. //non-default drop domain is has some agent the will process mail put in it.
  3596. if (m_pInstance->IsADefaultOrAliasDropDomain(DomainPtr))
  3597. DropQuotaExceeded = m_pInstance->IsDropDirQuotaExceeded();
  3598. }
  3599. ThisAddress = szRcptAddress;
  3600. if(RelayThisMail && !DropQuotaExceeded)
  3601. {
  3602. hr = m_pIMsgRecips->AddPrimary(1, (LPCTSTR *) &ThisAddress, &dwPropId,
  3603. &dwNewRecipIndex, NULL, 0);
  3604. if(!FAILED(hr))
  3605. {
  3606. //If we have any associated DSn values set them
  3607. if(szOrcptVal && szOrcptVal[0] != '\0')
  3608. {
  3609. hr = m_pIMsgRecips->PutStringA(dwNewRecipIndex, IMMPID_RP_DSN_ORCPT_VALUE,(LPCSTR)szOrcptVal);
  3610. if(FAILED(hr))
  3611. {
  3612. PE_CdFormatSmtpMessage (SMTP_RESP_NOSTORAGE, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE);
  3613. ErrorTrace((LPARAM) this, "Exceeded MaxRcpts %d", TotalRcpts + 1);
  3614. }
  3615. }
  3616. //If we have any associated DSn values set them
  3617. //If we have any associated DSn values set them
  3618. if(!FAILED(hr) && dwNotifyVal & RP_DSN_NOTIFY_MASK)
  3619. {
  3620. //hr = m_pIMsgRecips->PutDWORD(dwNewRecipIndex, IMMPID_RP_DSN_NOTIFY_VALUE, dwNotifyVal);
  3621. hr = m_pIMsgRecips->PutDWORD(dwNewRecipIndex,IMMPID_RP_RECIPIENT_FLAGS, dwNotifyVal);
  3622. if(FAILED(hr))
  3623. {
  3624. PE_CdFormatSmtpMessage (SMTP_RESP_NOSTORAGE, ENO_RESOURCES, " %s\r\n", SMTP_NO_STORAGE);
  3625. ErrorTrace((LPARAM) this, "Exceeded MaxRcpts %d", TotalRcpts + 1);
  3626. }
  3627. }
  3628. if(!FAILED(hr))
  3629. {
  3630. PE_CdFormatSmtpMessage (SMTP_RESP_OK, EVALID_DEST_ADDRESS, " %s \r\n",szRcptAddress);
  3631. }
  3632. }
  3633. else
  3634. {
  3635. PE_CdFormatSmtpMessage (SMTP_RESP_NOSTORAGE, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE);
  3636. ErrorTrace((LPARAM) this, "Exceeded MaxRcpts %d", TotalRcpts + 1);
  3637. }
  3638. }
  3639. else if (DropQuotaExceeded)
  3640. {
  3641. PE_CdFormatSmtpMessage (SMTP_INSUFF_STORAGE_CODE, EMAILBOX_FULL, " Mailbox full\r\n");
  3642. }
  3643. else
  3644. {
  3645. PE_CdFormatSmtpMessage (SMTP_RESP_NOT_FOUND, ENO_FORWARDING, " Unable to relay for %s\r\n", szRcptAddress);
  3646. }
  3647. TraceFunctLeaveEx((LPARAM) this);
  3648. return TRUE;
  3649. }
  3650. BOOL SMTP_CONNECTION::ShouldAcceptRcpt(char * szDomainName )
  3651. {
  3652. HRESULT hr;
  3653. DWORD dwDomainInfoFlags;
  3654. hr = m_pInstance->HrGetDomainInfoFlags(szDomainName, &dwDomainInfoFlags);
  3655. if (SUCCEEDED(hr)) {
  3656. if ( (dwDomainInfoFlags & DOMAIN_INFO_LOCAL_MAILBOX) ||
  3657. (dwDomainInfoFlags & DOMAIN_INFO_ALIAS) ||
  3658. (dwDomainInfoFlags & DOMAIN_INFO_LOCAL_DROP)) {
  3659. return TRUE;
  3660. }
  3661. else if(dwDomainInfoFlags & DOMAIN_INFO_DOMAIN_RELAY) {
  3662. return TRUE;
  3663. }
  3664. else if( (m_fAuthenticated && (dwDomainInfoFlags & DOMAIN_INFO_AUTH_RELAY)) ||
  3665. DoesClientHaveIpAccess() ) {
  3666. return TRUE;
  3667. }
  3668. }
  3669. else
  3670. {
  3671. if(DoesClientHaveIpAccess() ||
  3672. (m_fAuthenticated && m_pInstance->RelayForAuthUsers())) {
  3673. return TRUE;
  3674. }
  3675. }
  3676. return FALSE;
  3677. }
  3678. BOOL SMTP_CONNECTION::ExtractAndValidateRcpt(char * ToName, char ** ppArgument, char * szRcptAddress, char ** ppDomain )
  3679. {
  3680. DWORD dwAddressLen = 0;
  3681. DWORD dwCanonAddrLen = 0;
  3682. char * AddrPtr = NULL;
  3683. char * CanonAddrPtr = NULL;
  3684. //Parse out the address and possible argumets from the input line
  3685. if(!Extract821AddressFromLine(ToName,&AddrPtr,&dwAddressLen,ppArgument))
  3686. {
  3687. SetLastError(ERROR_INVALID_DATA);
  3688. return FALSE;
  3689. }
  3690. //Check if we have valid tail and address
  3691. if(!AddrPtr)
  3692. {
  3693. SetLastError(ERROR_INVALID_DATA);
  3694. return FALSE;
  3695. }
  3696. //Extract the canonical address in the <local-part> "@" <domain> form
  3697. if(!ExtractCanonical821Address(AddrPtr,dwAddressLen,&CanonAddrPtr,&dwCanonAddrLen))
  3698. {
  3699. SetLastError(ERROR_INVALID_DATA);
  3700. return FALSE;
  3701. }
  3702. //If we have a Canonical addr - validate it
  3703. if(CanonAddrPtr)
  3704. {
  3705. strncpy(szRcptAddress,CanonAddrPtr,dwCanonAddrLen);
  3706. *(szRcptAddress + dwCanonAddrLen) = '\0';
  3707. if(!Validate821Address(szRcptAddress, dwCanonAddrLen))
  3708. {
  3709. SetLastError(ERROR_INVALID_DATA);
  3710. return FALSE;
  3711. }
  3712. //Extract
  3713. if(!Get821AddressDomain(szRcptAddress,dwCanonAddrLen,ppDomain))
  3714. {
  3715. SetLastError(ERROR_INVALID_DATA);
  3716. return FALSE;
  3717. }
  3718. }
  3719. else
  3720. {
  3721. SetLastError(ERROR_INVALID_DATA);
  3722. return FALSE;
  3723. }
  3724. if(!(*ppDomain) && !ISNULLADDRESS(szRcptAddress))
  3725. {
  3726. //If there is no domain on this address,
  3727. //then append the current domain to this
  3728. //address.
  3729. if(!m_pInstance->AppendLocalDomain (szRcptAddress))
  3730. {
  3731. return FALSE;
  3732. }
  3733. //Update the DomainPtr to be after the '@' sign
  3734. //we could be safer and do a strchr
  3735. (*ppDomain) = szRcptAddress + dwCanonAddrLen + 1;
  3736. }
  3737. return TRUE;
  3738. }
  3739. BOOL SMTP_CONNECTION::BindToStoreDrive(void)
  3740. {
  3741. SMTP_ALLOC_PARAMS AllocParams;
  3742. DWORD Error = 0;
  3743. BOOL fRet = FALSE;
  3744. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::BindToStore");
  3745. AllocParams.BindInterfacePtr = (PVOID) m_pBindInterface;
  3746. AllocParams.IMsgPtr = (PVOID) m_pIMsg;
  3747. AllocParams.hContent = NULL;
  3748. AllocParams.hr = S_OK;
  3749. //For client context pass in something that will stay around the lifetime of the
  3750. //atqcontext -
  3751. AllocParams.pAtqClientContext = m_pInstance;
  3752. if(m_pInstance->AllocNewMessage (&AllocParams) && !FAILED(AllocParams.hr))
  3753. {
  3754. m_IMsgHandle = AllocParams.hContent;
  3755. if(WriteRcvHeader())
  3756. {
  3757. fRet = TRUE;
  3758. }
  3759. else
  3760. {
  3761. Error = GetLastError();
  3762. //ProtocolLog(, (char *)InputLine, Error, SMTP_RESP_NORESOURCES, 0, 0);
  3763. FatalTrace((LPARAM) this, "WriteRcvHeader failed %d", Error);
  3764. SetLastError(Error);
  3765. }
  3766. }
  3767. TraceFunctLeaveEx((LPARAM) this);
  3768. return fRet;
  3769. }
  3770. /*++
  3771. Name :
  3772. SMTP_CONNECTION::DoDATACommand
  3773. Description:
  3774. Responds to the SMTP DATA command.
  3775. Arguments:
  3776. InputLine - Buffer received from client
  3777. paramterSize - amount of data in buffer
  3778. both are ignored
  3779. Returns:
  3780. TRUE if the connection should stay open.
  3781. FALSE if this object should be deleted.
  3782. --*/
  3783. BOOL SMTP_CONNECTION::DoDATACommand(const char * InputLine, DWORD parameterSize)
  3784. {
  3785. HRESULT hr = S_OK;
  3786. DWORD TotalRcpts = 0;
  3787. DWORD fIsBinaryMime = 0;
  3788. DWORD Error = 0;
  3789. _ASSERT(m_pInstance != NULL);
  3790. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoDATACommand");
  3791. //If the current state is BDAT the only command that can be received is BDAT
  3792. if(m_State == BDAT)
  3793. {
  3794. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","BDAT Expected" );
  3795. ProtocolLog(DATA, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3796. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3797. ++m_ProtocolErrors;
  3798. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - BDAT Expected");
  3799. TraceFunctLeaveEx((LPARAM) this);
  3800. return TRUE;
  3801. }
  3802. if(m_pIMsgRecips && m_pIMsg)
  3803. {
  3804. hr = m_pIMsgRecips->Count(&TotalRcpts);
  3805. if(!FAILED(hr))
  3806. {
  3807. m_pIMsg->PutDWORD( IMMPID_MP_NUM_RECIPIENTS, TotalRcpts );
  3808. hr = m_pIMsg->GetDWORD(IMMPID_MP_BINARYMIME_OPTION, &fIsBinaryMime);
  3809. }
  3810. }
  3811. // Cannot use the DATA command if the body type was BINARYMIME
  3812. if(!FAILED(hr) && fIsBinaryMime)
  3813. {
  3814. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n", "Body type BINARYMIME requires BDAT" );
  3815. ProtocolLog(DATA, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3816. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_ARGS - Body type BINARYMIME requires BDAT");
  3817. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3818. ++m_ProtocolErrors;
  3819. }
  3820. //Only save data if HELO was sent and there is at least one recipient.
  3821. //send a message stating "no good recepients",
  3822. //or something like that if both lists are empty.
  3823. else if(!m_HelloSent)
  3824. {
  3825. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n", "Send hello first" );
  3826. ProtocolLog(DATA, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3827. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Send hello first");
  3828. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3829. ++m_ProtocolErrors;
  3830. }
  3831. else if (!IsClientSecureEnough())
  3832. {
  3833. ErrorTrace((LPARAM) this, "DoDataCommand - SMTP_RESP_MUST_SECURE, Must do STARTTLS first");
  3834. TraceFunctLeaveEx((LPARAM) this);
  3835. return TRUE;
  3836. }
  3837. else if(!m_fAuthAnon && !m_fAuthenticated)
  3838. {
  3839. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY, " %s\r\n", "Client was not authenticated");
  3840. ProtocolLog(DATA, (char *) InputLine, NO_ERROR, SMTP_RESP_MUST_SECURE, 0, 0);
  3841. ErrorTrace((LPARAM) this, "DoDataCommand - SMTP_RESP_MUST_SECURE, user not authenticated");
  3842. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3843. ++m_ProtocolErrors;
  3844. TraceFunctLeaveEx((LPARAM) this);
  3845. return TRUE;
  3846. }
  3847. else if (!m_RecvdMailCmd || !m_pIMsg)
  3848. {
  3849. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","Need mail command." );
  3850. ProtocolLog(DATA, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3851. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Need mail command.");
  3852. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3853. ++m_ProtocolErrors;
  3854. }
  3855. else if(TotalRcpts)
  3856. {
  3857. m_cbCurrentWriteBuffer = 0;
  3858. if(BindToStoreDrive())
  3859. {
  3860. m_State = DATA;
  3861. if(!m_pInstance->ShouldParseHdrs())
  3862. {
  3863. m_TimeToRewriteHeader = FALSE;
  3864. m_InHeader = FALSE;
  3865. }
  3866. PE_CdFormatSmtpMessage (SMTP_RESP_START, NULL," %s\r\n",SMTP_START_MAIL_STR);
  3867. }
  3868. else
  3869. {
  3870. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_MEMORY);
  3871. m_CInboundContext.SetWin32Status(ERROR_NOT_ENOUGH_MEMORY);
  3872. ProtocolLog(DATA, (char *)InputLine, ERROR_NOT_ENOUGH_MEMORY, SMTP_RESP_NORESOURCES, 0, 0);
  3873. SmtpLogEventEx(SMTP_EVENT_INVALID_MAIL_QUEUE_DIR, "Invalid MailQueue Directory", GetLastError());
  3874. ErrorTrace((LPARAM) this, "DoDataCommand - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY - MailInfo = new MAILQ_ENTRY () failed");
  3875. }
  3876. }
  3877. else if (!m_RecvdRcptCmd)
  3878. {
  3879. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", "Need Rcpt command." );
  3880. ProtocolLog(DATA, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3881. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Need Rcpt command.");
  3882. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3883. ++m_ProtocolErrors;
  3884. }
  3885. else
  3886. {
  3887. PE_CdFormatSmtpMessage (SMTP_RESP_TRANS_FAILED, ESYNTAX_ERROR," %s\r\n", SMTP_NO_VALID_RECIPS );
  3888. ProtocolLog(DATA, (char *) InputLine, NO_ERROR, SMTP_RESP_TRANS_FAILED, 0, 0);
  3889. ErrorTrace((LPARAM) this, "SMTP_RESP_TRANS_FAILED, SMTP_NO_VALID_RECIPS");
  3890. ++m_ProtocolErrors;
  3891. }
  3892. TraceFunctLeaveEx((LPARAM) this);
  3893. return TRUE;
  3894. }
  3895. /*++
  3896. Name :
  3897. SMTP_CONNECTION::DoBDATCommand
  3898. Description:
  3899. Responds to the SMTP BDAT command. Parses to check if this
  3900. is the last BDAT.
  3901. Error processing for BDAT is more complex than other SMTP commands
  3902. because the RFC forbids a response to BDAT (even error responses that
  3903. the server cannot process BDAT). If we try to send an error response
  3904. immediately after a BDAT command, we will go out of sync with the
  3905. client because the client is not expecting a response... it is trying
  3906. to send us chunk data, and the client expects us to have posted a TCP
  3907. read to receive the chunk data.
  3908. Errors handled by this function fall into 2 categories:
  3909. (1) Errors by the client, such as sending improperly formatted
  3910. chunksizes, bad syntax for the BDAT command.
  3911. (2) Errors such as failure to allocate memory, restrictions on session
  3912. size and message size, client must authenticate first, client must
  3913. negotiate TLS first, etc.
  3914. In the first case, we can reject the command with an error response,
  3915. if the BDAT command is garbled, there's not much we can do.
  3916. In the second case, we can generate a reasonable error response to be
  3917. sent to the client. We should wait for our turn before doing so, i.e.
  3918. we should accept and discard the BDAT chunks sent by the client, and
  3919. when it it time to ack the chunk, then we send the error response. This
  3920. is implemented by setting the m_MailBodyDiagnostic to the appropriate error, and
  3921. calling AcceptAndDiscardBDAT which handles receiving and discarding
  3922. chunk data and generating error responses when appropriate.
  3923. Arguments:
  3924. InputLine - Buffer received from client
  3925. paramterSize - amount of data in buffer
  3926. Returns:
  3927. TRUE if the connection should stay open.
  3928. FALSE if this object should be deleted.
  3929. --*/
  3930. BOOL SMTP_CONNECTION::DoBDATCommand(const char * InputLine, DWORD parameterSize)
  3931. {
  3932. HRESULT hr = S_OK;
  3933. DWORD TotalRcpts = 0;
  3934. MailBodyDiagnosticCode mailBodyDiagnostic = ERR_NONE;
  3935. _ASSERT(m_pInstance != NULL);
  3936. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoBDATCommand");
  3937. if(m_pIMsgRecips && m_pIMsg)
  3938. {
  3939. hr = m_pIMsgRecips->Count(&TotalRcpts);
  3940. m_pIMsg->PutDWORD( IMMPID_MP_NUM_RECIPIENTS, TotalRcpts );
  3941. }
  3942. //We parse this command ONLY if we advertise either chunking or binarymime
  3943. if(!m_pInstance->AllowChunking() && !m_pInstance->AllowBinaryMime())
  3944. {
  3945. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_CMD, EINVALID_COMMAND," %s\r\n", SMTP_NOT_IMPL_STR);
  3946. ErrorTrace((LPARAM) this, "DoBDATCommand - SMTP_RESP_BAD_CMD, SMTP_NOT_IMPL_STR");
  3947. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3948. ++m_ProtocolErrors;
  3949. TraceFunctLeaveEx((LPARAM) this);
  3950. return TRUE;
  3951. }
  3952. if(!m_HelloSent)
  3953. {
  3954. ProtocolLog(BDAT, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3955. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Send hello first");
  3956. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3957. ++m_ProtocolErrors;
  3958. mailBodyDiagnostic = ERR_HELO_NEEDED;
  3959. }
  3960. else if(!m_fAuthAnon && !m_fAuthenticated)
  3961. {
  3962. ProtocolLog(BDAT, (char *) InputLine, NO_ERROR, SMTP_RESP_MUST_SECURE, 0, 0);
  3963. ErrorTrace((LPARAM) this, "DoBDATCommand - SMTP_RESP_MUST_SECURE, user not authenticated");
  3964. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3965. ++m_ProtocolErrors;
  3966. mailBodyDiagnostic = ERR_AUTH_NEEDED;
  3967. }
  3968. else if (!m_RecvdMailCmd || !m_pIMsg)
  3969. {
  3970. ProtocolLog(BDAT, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3971. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Need mail command.");
  3972. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3973. ++m_ProtocolErrors;
  3974. mailBodyDiagnostic = ERR_MAIL_NEEDED;
  3975. }
  3976. else if (!m_RecvdRcptCmd)
  3977. {
  3978. ProtocolLog(BDAT, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  3979. ErrorTrace((LPARAM) this, "SMTP_RESP_BAD_SEQ - Need Rcpt command.");
  3980. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  3981. ++m_ProtocolErrors;
  3982. mailBodyDiagnostic = ERR_RCPT_NEEDED;
  3983. }
  3984. else if (FAILED(hr) || 0 == TotalRcpts)
  3985. {
  3986. ProtocolLog(BDAT, (char *) InputLine, NO_ERROR, SMTP_RESP_TRANS_FAILED, 0, 0);
  3987. ErrorTrace((LPARAM) this, "SMTP_RESP_TRANS_FAILED, SMTP_NO_VALID_RECIPS");
  3988. ++m_ProtocolErrors;
  3989. mailBodyDiagnostic = ERR_NO_VALID_RCPTS;
  3990. }
  3991. char * Argument = (char *) InputLine;
  3992. DWORD dwEstMsgSize = 0;
  3993. DWORD dwEstSessionSize = 0;
  3994. m_nChunkSize = 0;
  3995. m_nBytesRemainingInChunk = 0;
  3996. // Just parse for size and if this is last chunk
  3997. Argument = CheckArgument(Argument);
  3998. if (Argument == NULL)
  3999. {
  4000. ProtocolLog(BDAT, (char *)InputLine, NO_ERROR, SMTP_RESP_MBX_SYNTAX, 0,0);
  4001. ErrorTrace((LPARAM) this, "DoBDATCommand - SMTP_RESP_MBX_SYNTAX, Client sent no CHUNK SIZE");
  4002. ++m_ProtocolErrors;
  4003. TraceFunctLeaveEx((LPARAM) this);
  4004. return TRUE;
  4005. }
  4006. // make sure the argument is all digits
  4007. BOOL fBadChunkSize = FALSE;
  4008. for( int i=0; ( ( Argument[i] != 0 ) && ( Argument[i] != ' ' ) ) ; i++ )
  4009. {
  4010. if( !isdigit( Argument[i] ) )
  4011. {
  4012. fBadChunkSize = TRUE;
  4013. break;
  4014. }
  4015. }
  4016. // Get the chunk size
  4017. m_nChunkSize = atoi (Argument);
  4018. m_nBytesRemainingInChunk = m_nChunkSize + m_cbTempBDATLen;
  4019. if( ( m_nChunkSize <= 0 ) || fBadChunkSize )
  4020. {
  4021. PE_CdFormatSmtpMessage (SMTP_RESP_MBX_SYNTAX, ESYNTAX_ERROR," %s\r\n","Invalid CHUNK size value" );
  4022. ProtocolLog(BDAT, (char *)InputLine, NO_ERROR, SMTP_RESP_MBX_SYNTAX, 0,0);
  4023. ErrorTrace((LPARAM) this, "DoBDATCommand - SMTP_RESP_MBX_SYNTAX, Client sent 0 as CHUNK SIZE");
  4024. ++m_ProtocolErrors;
  4025. TraceFunctLeaveEx((LPARAM) this);
  4026. return TRUE;
  4027. }
  4028. // Add this to the mail already received
  4029. // check to make sure we are not going over the servers max message size
  4030. dwEstSessionSize = m_nChunkSize + m_SessionSize;
  4031. dwEstMsgSize = m_nChunkSize + m_TotalMsgSize;
  4032. if(ERR_NONE == mailBodyDiagnostic)
  4033. {
  4034. if((m_pInstance->GetMaxMsgSize() > 0) && (dwEstMsgSize > m_pInstance->GetMaxMsgSize()))
  4035. {
  4036. BOOL fShouldImposeLimit = TRUE;
  4037. if( FAILED( m_pInstance->TriggerMaxMsgSizeEvent( GetSessionPropertyBag(), m_pIMsg, &fShouldImposeLimit ) ) || fShouldImposeLimit )
  4038. {
  4039. ProtocolLog(BDAT, (char *)InputLine, NO_ERROR, SMTP_RESP_NOSTORAGE, 0, 0);
  4040. ErrorTrace((LPARAM) this, "SMTP_RESP_NOSTORAGE, SMTP_MAX_MSG_SIZE_EXCEEDED_MSG - %d", dwEstMsgSize);
  4041. ++m_ProtocolErrors;
  4042. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToSize);
  4043. mailBodyDiagnostic = ERR_MAX_MSG_SIZE;
  4044. }
  4045. }
  4046. }
  4047. // Check that we are below the max session size allowed on the Server.
  4048. if((m_pInstance->GetMaxMsgSizeBeforeClose() > 0) && (dwEstSessionSize > m_pInstance->GetMaxMsgSizeBeforeClose()))
  4049. {
  4050. BOOL fShouldImposeLimit = TRUE;
  4051. if( FAILED( m_pInstance->TriggerMaxMsgSizeEvent( GetSessionPropertyBag(), m_pIMsg, &fShouldImposeLimit ) ) || fShouldImposeLimit )
  4052. {
  4053. ProtocolLog(BDAT, (char *)InputLine, NO_ERROR, SMTP_RESP_NOSTORAGE, 0, 0);
  4054. ErrorTrace((LPARAM) this, "SMTP_RESP_NOSTORAGE, SMTP_MAX_SESSION_SIZE_EXCEEDED_MSG - %d", dwEstSessionSize);
  4055. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToSize);
  4056. DisconnectClient();
  4057. TraceFunctLeaveEx((LPARAM) this);
  4058. return TRUE;
  4059. }
  4060. }
  4061. //Check for the presence of the LAST keyword
  4062. Argument = strchr(Argument,' ');
  4063. if(Argument != NULL)
  4064. {
  4065. //get rid of white space
  4066. while(isspace(*Argument))
  4067. Argument++;
  4068. if(*Argument != '\0')
  4069. {
  4070. if(!_strnicmp(Argument,(char *)"LAST",4))
  4071. {
  4072. //This is the last BDAT chunk
  4073. m_fIsLastChunk = TRUE;
  4074. }
  4075. else
  4076. {
  4077. PE_CdFormatSmtpMessage (SMTP_RESP_MBX_SYNTAX, ESYNTAX_ERROR," %s\r\n", "Invalid CHUNK size value" );
  4078. ProtocolLog(BDAT, (char *)InputLine, NO_ERROR, SMTP_RESP_MBX_SYNTAX, 0,0);
  4079. ErrorTrace((LPARAM) this, "DoBDATCommand - SMTP_RESP_MBX_SYNTAX, Client sent 0 as CHUNK SIZE");
  4080. ++m_ProtocolErrors;
  4081. TraceFunctLeaveEx((LPARAM) this);
  4082. return TRUE;
  4083. }
  4084. }
  4085. }
  4086. m_fIsChunkComplete = FALSE;
  4087. if(m_State != BDAT)
  4088. {
  4089. //we are here for the first time
  4090. m_State = BDAT;
  4091. m_cbCurrentWriteBuffer = 0;
  4092. // If there was an error while processing this BDAT command, flag it (m_MailBodyDiagnostic) & exit.
  4093. m_MailBodyDiagnostic = mailBodyDiagnostic;
  4094. if(mailBodyDiagnostic != ERR_NONE)
  4095. goto Exit;
  4096. if(!BindToStoreDrive())
  4097. {
  4098. //
  4099. // Cannot process BDAT due to error accessing mail-store. Flag the error & exit.
  4100. //
  4101. m_MailBodyError = ERROR_NOT_ENOUGH_MEMORY;
  4102. m_MailBodyDiagnostic = ERR_OUT_OF_MEMORY;
  4103. m_CInboundContext.SetWin32Status(ERROR_NOT_ENOUGH_MEMORY);
  4104. ProtocolLog(BDAT, (char *)InputLine, ERROR_NOT_ENOUGH_MEMORY, SMTP_RESP_NORESOURCES, 0, 0);
  4105. ErrorTrace((LPARAM) this, "DoBDATCommand - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY - Allocating stream failed");
  4106. SmtpLogEventEx(SMTP_EVENT_INVALID_MAIL_QUEUE_DIR , "Invalid Mail Queue Directory", GetLastError());
  4107. TraceFunctLeaveEx((LPARAM) this);
  4108. return TRUE;
  4109. }
  4110. }
  4111. //
  4112. // If there was an error during this BDAT, flag it... but if there was an error in a
  4113. // previous BDAT command (within the same group of BDATs), preserve it. Once a BDAT fails,
  4114. // all subsequent BDATs will fail. m_MailBodyDiagnostic is reset only when a new BDAT group is
  4115. // started (the first BDAT in a group of BDATs or RSET).
  4116. //
  4117. if(m_MailBodyDiagnostic == ERR_NONE) // Don't overwrite
  4118. m_MailBodyDiagnostic = mailBodyDiagnostic; // Error during this BDAT
  4119. if(m_MailBodyDiagnostic != ERR_NONE) // Should this BDAT succeed?
  4120. goto Exit;
  4121. // There is no response for BDAT command
  4122. if(!m_pInstance->ShouldParseHdrs())
  4123. {
  4124. m_TimeToRewriteHeader = FALSE;
  4125. m_InHeader = FALSE;
  4126. }
  4127. Exit:
  4128. TraceFunctLeaveEx((LPARAM) this);
  4129. return TRUE;
  4130. }
  4131. void SMTP_CONNECTION::ReleasRcptList(void)
  4132. {
  4133. IMailMsgRecipients * pRcptList = NULL;
  4134. IMailMsgRecipientsAdd * pRcptListAdd = NULL;
  4135. pRcptListAdd = (IMailMsgRecipientsAdd *) InterlockedExchangePointer((PVOID *) &m_pIMsgRecips, (PVOID) NULL);
  4136. if (pRcptListAdd != NULL)
  4137. {
  4138. pRcptListAdd->Release();
  4139. }
  4140. pRcptList = (IMailMsgRecipients *) InterlockedExchangePointer((PVOID *) &m_pIMsgRecipsTemp, (PVOID) NULL);
  4141. if (pRcptList != NULL)
  4142. {
  4143. pRcptList->Release();
  4144. }
  4145. }
  4146. void SMTP_CONNECTION::ReleasImsg(BOOL DeleteIMsg)
  4147. {
  4148. IMailMsgProperties * pMsg = NULL;
  4149. IMailMsgQueueMgmt * pMgmt = NULL;
  4150. HRESULT hr = S_OK;
  4151. pMsg = (IMailMsgProperties *) InterlockedExchangePointer((PVOID *) &m_pIMsg, (PVOID) NULL);
  4152. if (pMsg != NULL)
  4153. {
  4154. ReleasRcptList();
  4155. if(m_pBindInterface)
  4156. {
  4157. //if(DeleteIMsg)
  4158. //{
  4159. // m_pBindAtqInterface->ReleaseATQHandle();
  4160. //}
  4161. m_pBindInterface->Release();
  4162. }
  4163. if(DeleteIMsg)
  4164. {
  4165. hr = pMsg->QueryInterface(IID_IMailMsgQueueMgmt, (void **)&pMgmt);
  4166. if(!FAILED(hr))
  4167. {
  4168. pMgmt->Delete(NULL);
  4169. pMgmt->Release();
  4170. }
  4171. }
  4172. pMsg->Release();
  4173. }
  4174. }
  4175. /*++
  4176. Name :
  4177. SMTP_CONNECTION::HandleCompletedMessage
  4178. Description:
  4179. This function gets called when the client
  4180. sends the CRLF.CRLF sequence saying the
  4181. message is complete. The function truncates
  4182. the message to the current size, if the client
  4183. lied and gave us a larger size in the FROM command,
  4184. and them queues the message to the Local/Remote
  4185. queues for processing.
  4186. Arguments:
  4187. None.
  4188. Returns:
  4189. TRUE if the message was written to disk and queued.
  4190. FALSE in all other cases.
  4191. --*/
  4192. BOOL SMTP_CONNECTION::HandleCompletedMessage(DWORD dwCommand, BOOL *pfAsyncOp)
  4193. {
  4194. DWORD AbOffset = 0;
  4195. char MessageId[1024];
  4196. HRESULT hr = S_OK;
  4197. DWORD TrailerSize = 0; //used to strip out the trailing ".CRLF"
  4198. BOOL fPended = FALSE;
  4199. BOOL fQRet = TRUE;
  4200. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::HandleCompletedMessage(void)");
  4201. _ASSERT(m_pInstance != NULL);
  4202. //hack to make transaction logging work
  4203. AddCommandBytesRecv(m_TotalMsgSize);
  4204. MessageId[0] = 0;
  4205. if( m_pIMsg )
  4206. {
  4207. m_pIMsg->GetStringA( IMMPID_MP_RFC822_MSG_ID, sizeof( MessageId ), MessageId );
  4208. m_pIMsg->PutDWORD( IMMPID_MP_MSG_SIZE_HINT, m_TotalMsgSize );
  4209. }
  4210. MessageId[sizeof(MessageId)-1] = 0; // NULL terminate it.
  4211. //Depending on dwCommand the TrailerSize will vary
  4212. //For BDAT = 0
  4213. //For DATA = 3
  4214. if(dwCommand == BDAT )
  4215. TrailerSize = 0;
  4216. DWORD cbTotalMsgSize = 0;
  4217. if( m_fFoundEmbeddedCrlfDotCrlf )
  4218. {
  4219. cbTotalMsgSize = m_cbDotStrippedTotalMsgSize;
  4220. }
  4221. else
  4222. {
  4223. cbTotalMsgSize = m_TotalMsgSize;
  4224. }
  4225. if(SetFilePointer(m_IMsgHandle->m_hFile, (cbTotalMsgSize + m_HeaderSize) - TrailerSize, NULL, FILE_BEGIN) == 0xFFFFFFFF)
  4226. {
  4227. m_MailBodyError = GetLastError();
  4228. //FatalTrace((LPARAM) this, "SetFilePointer failed on file %s !!! (err=%d)", MailInfo->GetMailFileName(), m_MailBodyError);
  4229. m_CInboundContext.SetWin32Status(m_MailBodyError);
  4230. ProtocolLog(DATA, MessageId, m_MailBodyError, SMTP_RESP_NORESOURCES, 0, 0);
  4231. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE );
  4232. SendSmtpResponse();
  4233. TraceFunctLeaveEx((LPARAM) this);
  4234. return FALSE;
  4235. }
  4236. if(!SetEndOfFile(m_IMsgHandle->m_hFile))
  4237. {
  4238. m_MailBodyError = GetLastError();
  4239. // FatalTrace((LPARAM) this, "SetEndOfFile failed on file %s !!! (err=%d)", MailInfo->GetMailFileName(), m_MailBodyError);
  4240. m_CInboundContext.SetWin32Status(m_MailBodyError);
  4241. ProtocolLog(DATA, MessageId, m_MailBodyError, SMTP_RESP_NORESOURCES, 0, 0);
  4242. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE );
  4243. SendSmtpResponse();
  4244. TraceFunctLeaveEx((LPARAM) this);
  4245. return FALSE;
  4246. }
  4247. hr = m_pIMsgRecipsTemp->WriteList(m_pIMsgRecips);
  4248. if(FAILED(hr))
  4249. {
  4250. m_MailBodyError = GetLastError();
  4251. //FatalTrace((LPARAM) this, "SetEndOfFile failed on file %s !!! (err=%d)", MailInfo->GetMailFileName(), m_MailBodyError);
  4252. m_CInboundContext.SetWin32Status(m_MailBodyError);
  4253. ProtocolLog(DATA, MessageId, m_MailBodyError, SMTP_RESP_NORESOURCES, 0, 0);
  4254. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE );
  4255. SendSmtpResponse();
  4256. TraceFunctLeaveEx((LPARAM) this);
  4257. return FALSE;
  4258. }
  4259. m_pIMsg->PutDWORD( IMMPID_MP_SCANNED_FOR_CRLF_DOT_CRLF, m_fScannedForCrlfDotCrlf );
  4260. m_pIMsg->PutDWORD( IMMPID_MP_FOUND_EMBEDDED_CRLF_DOT_CRLF, m_fFoundEmbeddedCrlfDotCrlf );
  4261. hr = m_pIMsg->Commit(NULL);
  4262. if(FAILED(hr))
  4263. {
  4264. m_MailBodyError = GetLastError();
  4265. //FatalTrace((LPARAM) this, "SetEndOfFile failed on file %s !!! (err=%d)", MailInfo->GetMailFileName(), m_MailBodyError);
  4266. m_CInboundContext.SetWin32Status(m_MailBodyError);
  4267. ProtocolLog(DATA, MessageId, m_MailBodyError, SMTP_RESP_NORESOURCES, 0, 0);
  4268. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE );
  4269. SendSmtpResponse();
  4270. TraceFunctLeaveEx((LPARAM) this);
  4271. return FALSE;
  4272. }
  4273. ReleasRcptList();
  4274. // Fire the end of data event. The default implementation of this
  4275. // commits the message
  4276. char szVerb[] = "_EOD";
  4277. DWORD CmdSize;
  4278. BOOL fAsyncOp;
  4279. m_dwCurrentCommand = SmtpGetCommand(szVerb, sizeof(szVerb), &CmdSize);
  4280. BOOL fReturn =
  4281. GlueDispatch((char *)szVerb, sizeof(szVerb), CmdSize, pfAsyncOp);
  4282. if (!fReturn) {
  4283. TraceFunctLeaveEx((LPARAM) this);
  4284. return FALSE;
  4285. }
  4286. if (*pfAsyncOp) {
  4287. TraceFunctLeaveEx((LPARAM) this);
  4288. return TRUE;
  4289. }
  4290. SendSmtpResponse();
  4291. ProtocolLog(DATA, MessageId, NO_ERROR, SMTP_RESP_OK, 0, 0);
  4292. TraceFunctLeaveEx((LPARAM) this);
  4293. return TRUE;
  4294. }
  4295. /*++
  4296. Name :
  4297. BOOL SMTP_CONNECTION::RewriteHeader(void)
  4298. Description:
  4299. This function adds a message ID, and other
  4300. headers if they are missing
  4301. Arguments:
  4302. none
  4303. Returns:
  4304. FALSE if WriteFile failed
  4305. TRUE otherwise
  4306. --*/
  4307. BOOL SMTP_CONNECTION::RewriteHeader(void)
  4308. {
  4309. char Buffer [MAX_REWRITTEN_HEADERS];
  4310. char szMsgId[MAX_REWRITTEN_MSGID];
  4311. char MessageId [REWRITTEN_GEN_MSGID_BUFFER];
  4312. int NumToWrite;
  4313. int MsgIdSize = 0;
  4314. int CurrentRewriteBufferSize = 0;
  4315. BOOL fPended = FALSE;
  4316. //***NOTE***
  4317. //If you add *anything* to this function, make sure you update MAX_REWRITTEN_HEADERS
  4318. //to support allow room for this as well.
  4319. //Rewrite header creates its own buffer and writes out
  4320. //add the mail from field if we did not see it.
  4321. if(!(m_HeaderFlags & H_FROM))
  4322. {
  4323. _ASSERT(m_szFromAddress[0] != '\0');
  4324. NumToWrite = sprintf(Buffer + CurrentRewriteBufferSize, "From: %s\r\n", m_szFromAddress);
  4325. CurrentRewriteBufferSize += NumToWrite;
  4326. m_pIMsg->PutStringA( IMMPID_MP_RFC822_FROM_ADDRESS, m_szFromAddress );
  4327. char szSmtpFromAddress[MAX_INTERNET_NAME + 6] = "smtp:";
  4328. strncat(szSmtpFromAddress, m_szFromAddress, MAX_INTERNET_NAME);
  4329. m_pIMsg->PutStringA( IMMPID_MP_FROM_ADDRESS, szSmtpFromAddress );
  4330. }
  4331. _ASSERT(MAX_REWRITTEN_HEADERS > CurrentRewriteBufferSize);
  4332. if(!(m_HeaderFlags & H_RCPT))
  4333. {
  4334. NumToWrite = sprintf(Buffer + CurrentRewriteBufferSize, "Bcc:\r\n");
  4335. CurrentRewriteBufferSize += NumToWrite;
  4336. }
  4337. _ASSERT(MAX_REWRITTEN_HEADERS > CurrentRewriteBufferSize);
  4338. //add the return path field if we did not see it.
  4339. if(!(m_HeaderFlags & H_RETURNPATH))
  4340. {
  4341. _ASSERT(m_szFromAddress[0] != '\0');
  4342. NumToWrite = sprintf(Buffer + CurrentRewriteBufferSize, "Return-Path: %s\r\n", m_szFromAddress);
  4343. CurrentRewriteBufferSize += NumToWrite;
  4344. }
  4345. //add the message ID field if we did not see it.
  4346. _ASSERT(MAX_REWRITTEN_HEADERS > CurrentRewriteBufferSize);
  4347. if(!(m_HeaderFlags & H_MID))
  4348. {
  4349. GenerateMessageId (MessageId, sizeof(MessageId));
  4350. m_pInstance->LockGenCrit();
  4351. MsgIdSize = sprintf( szMsgId, "<%s%8.8x@%s>", MessageId, GetIncreasingMsgId(), m_pInstance->GetFQDomainName());
  4352. m_pInstance->UnLockGenCrit();
  4353. _ASSERT(MsgIdSize < MAX_REWRITTEN_MSGID);
  4354. NumToWrite = sprintf(Buffer + CurrentRewriteBufferSize, "Message-ID: %s\r\n", szMsgId);
  4355. szMsgId[sizeof(szMsgId)-1]=0;
  4356. CurrentRewriteBufferSize += NumToWrite;
  4357. if( FAILED( m_pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_ID, szMsgId ) ) )
  4358. return FALSE;
  4359. }
  4360. if( !( m_HeaderFlags & H_X_ORIGINAL_ARRIVAL_TIME ) )
  4361. {
  4362. char szOriginalArrivalTime[64];
  4363. GetSysAndFileTimeAsString( szOriginalArrivalTime );
  4364. NumToWrite = sprintf(Buffer + CurrentRewriteBufferSize, "X-OriginalArrivalTime: %s\r\n", szOriginalArrivalTime);
  4365. szOriginalArrivalTime[sizeof(szOriginalArrivalTime)-1]=0;
  4366. CurrentRewriteBufferSize += NumToWrite;
  4367. if( FAILED( m_pIMsg->PutStringA( IMMPID_MP_ORIGINAL_ARRIVAL_TIME, szOriginalArrivalTime ) ) )
  4368. return FALSE;
  4369. }
  4370. //add the Date field if we did not see it.
  4371. _ASSERT(MAX_REWRITTEN_HEADERS > CurrentRewriteBufferSize);
  4372. if(!(m_HeaderFlags & H_DATE))
  4373. {
  4374. char szDateBuf [cMaxArpaDate];
  4375. GetArpaDate(szDateBuf);
  4376. NumToWrite = sprintf(Buffer + CurrentRewriteBufferSize, "Date: %s\r\n", szDateBuf);
  4377. CurrentRewriteBufferSize += NumToWrite;
  4378. }
  4379. //Did we see the seperator. If not add one
  4380. //blank line to the message
  4381. _ASSERT(MAX_REWRITTEN_HEADERS > CurrentRewriteBufferSize);
  4382. if(!(m_HeaderFlags & H_EOH))
  4383. {
  4384. sprintf(Buffer + CurrentRewriteBufferSize,"\r\n", 2);
  4385. CurrentRewriteBufferSize += 2;
  4386. }
  4387. _ASSERT(MAX_REWRITTEN_HEADERS > CurrentRewriteBufferSize);
  4388. m_HeaderSize += CurrentRewriteBufferSize;
  4389. if(CurrentRewriteBufferSize)
  4390. {
  4391. //Write out the data
  4392. //NK** : Assumption with 32K buffers we should never need to
  4393. //write file to disk so the last param is ignored
  4394. if(!WriteMailFile(Buffer, CurrentRewriteBufferSize, &fPended))
  4395. {
  4396. m_MailBodyError = GetLastError();
  4397. return FALSE;
  4398. }
  4399. _ASSERT(!fPended);
  4400. }
  4401. return TRUE;
  4402. }
  4403. /*++
  4404. Name :
  4405. SMTP_CONNECTION::CreateMailBody
  4406. Description:
  4407. Responds to the SMTP data command.
  4408. This funcion spools the mail to a
  4409. directory
  4410. Arguments:
  4411. InputLine - Buffer received from client
  4412. paramterSize - amount of data in buffer
  4413. UndecryptedTailSize -- amount of undecrypted data at end of buffer
  4414. Returns:
  4415. TRUE if all data(incliding terminating .)
  4416. has been received
  4417. FALSE on all errors (disk full, etc.)
  4418. --*/
  4419. BOOL SMTP_CONNECTION::CreateMailBody(char * InputLine, DWORD ParameterSize,
  4420. DWORD UndecryptedTailSize, BOOL *lfWritePended)
  4421. {
  4422. BOOL MailDone = FALSE;
  4423. DWORD IntermediateSize = 0;
  4424. PCHAR pszSearch = NULL;
  4425. DWORD TotalMsgSize = 0;
  4426. DWORD SessionSize = 0;
  4427. char c1, c2;
  4428. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::CreateMailBody");
  4429. _ASSERT (m_pInstance != NULL);
  4430. _ASSERT (m_cbParsable <= QueryMaxReadSize());
  4431. // 7/2/99 MILANS
  4432. // The following if statement is completely unnecessary. One should not adjust the line
  4433. // state at the beginning of this function - rather, they should do so as a line is scanned
  4434. // and, for the final (potentially partial) line at the end, when they move the (partial) line
  4435. // as the last step in this function.
  4436. // Nevertheless, in the light of the time, I am leaving the if statement in place.
  4437. //the state of the line completion parsing automaton is to be set to the initial state if
  4438. //for each line except the following case:
  4439. //if the buffer was full in the last iteration --- the data got shifted out and now we are
  4440. //parsing the newly read data. so the state of the line completion automaton must
  4441. if(!m_BufrWasFull)
  4442. m_LineCompletionState = SEEN_NOTHING; //automaton state for IsLineCompleteRFC822
  4443. m_BufrWasFull = FALSE;
  4444. while ((pszSearch = IsLineCompleteRFC822(InputLine, m_cbParsable, UndecryptedTailSize, &m_BufrWasFull)) != NULL)
  4445. {
  4446. //If the buffer is full and we are not using the entire buffer; then some of the data in the full
  4447. //buffer is data that we have already parsed and processed in preceding iterations. we can discard
  4448. //this data and read in fresh data which may have the CRLFx we are looking for. to do this, break
  4449. //out of this loop, move inputline to the beginning of buffer (done outside loop) and just return.
  4450. //a read is pended and this function is called with freshly read data appended to the "inadequate
  4451. //data". If even then the no CRLFx is found, and the buffer is filled up, we need to start truncation.
  4452. if(m_BufrWasFull && InputLine > QueryMRcvBuffer())
  4453. break;
  4454. _ASSERT (m_cbParsable <= QueryMaxReadSize());
  4455. //stuff in CRLF if it wasn't there
  4456. if(!m_Truncate && m_BufrWasFull)
  4457. {
  4458. c1 = *pszSearch;
  4459. c2 = *(pszSearch+1);
  4460. *pszSearch = CR;
  4461. *(pszSearch+1) = LF;
  4462. }
  4463. IntermediateSize = (DWORD)(pszSearch - InputLine);
  4464. TotalMsgSize = m_TotalMsgSize + (IntermediateSize + 2);
  4465. SessionSize = m_SessionSize + (IntermediateSize + 2);
  4466. //don't write the '.' to the file
  4467. if( (InputLine [0] == '.') && (IntermediateSize == 1) && !m_LongBodyLine )
  4468. {
  4469. //the minimum message size is 3 (.CRLF). If we are done, and the size
  4470. //of the message is 3 bytes, that means that the body of the message
  4471. //is missing. So, just write the headers and go.
  4472. if(TotalMsgSize == 3 && m_TimeToRewriteHeader)
  4473. {
  4474. m_TimeToRewriteHeader = FALSE;
  4475. if(!RewriteHeader())
  4476. {
  4477. m_MailBodyDiagnostic = ERR_RETRY;
  4478. m_MailBodyError = GetLastError();
  4479. return TRUE;
  4480. }
  4481. }
  4482. //We are at the end of the message flush our write buffer
  4483. //if there is something in it
  4484. if(!WriteMailFile(NULL, 0, lfWritePended))
  4485. {
  4486. m_MailBodyDiagnostic = ERR_RETRY;
  4487. m_MailBodyError = GetLastError();
  4488. return TRUE;
  4489. }
  4490. else if(*lfWritePended)
  4491. {
  4492. //Go away - Atq will call us back when the write file completes
  4493. TraceFunctLeaveEx((LPARAM) this);
  4494. return FALSE;
  4495. }
  4496. m_cbParsable -= 3; // 1for the . + 2 for CRLF
  4497. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  4498. if(m_cbReceived)
  4499. {
  4500. //we need to +2 to pszSearch because it points to the CR character
  4501. MoveMemory ((void *)QueryMRcvBuffer(), pszSearch + 2, m_cbReceived);
  4502. }
  4503. //DebugTrace((LPARAM) this, "got ending dot for file %s", MailInfo->GetMailFileName());
  4504. TraceFunctLeaveEx((LPARAM) this);
  4505. return TRUE;
  4506. }
  4507. if(!m_Truncate && m_InHeader && ( m_MailBodyError == NO_ERROR ))
  4508. {
  4509. //the code I stole from SendMail assumes the
  4510. //line has a terminating '\0', so we terminate
  4511. //the line. We will put back the carriage
  4512. //return later.
  4513. *pszSearch = '\0';
  4514. char *pszValueBuf = NULL;
  4515. //remove header. return false if the line is not a header or the line is NULL
  4516. //chompheader returns everything after ":" into pszValueBuf
  4517. if( m_InHeader = ChompHeader( InputLine, m_HeaderFlags, &pszValueBuf) )
  4518. {
  4519. GetAndPersistRFC822Headers( InputLine, pszValueBuf);
  4520. if(FAILED(m_MailBodyError))
  4521. {
  4522. ErrorTrace((LPARAM) this, "Error persisting 822 headers 0x%08x", m_MailBodyError);
  4523. m_MailBodyDiagnostic = ERR_RETRY;
  4524. *pszSearch = CR;
  4525. TraceFunctLeaveEx((LPARAM) this);
  4526. return TRUE;
  4527. }
  4528. }
  4529. else
  4530. {
  4531. //in case we have <header><CRLF><CRLF><sp><body><CRLF><x> we have seen
  4532. //an end of header after the 1st <CRLF> but because IsLineComplete822 halts at
  4533. //the third <CRLF> the inputstring to ChompHeader() is
  4534. //<CRLF><sp><body><NULL> rather than just <NULL> had there been no <sp> after
  4535. //the second <CRLF>. Thus ChompHeader will not set the EOH flag correctly.
  4536. //we do this here.
  4537. if( InputLine[0] == CR &&
  4538. InputLine[1] == LF &&
  4539. (InputLine[2] == ' ' || InputLine[2] == '\t') )
  4540. m_HeaderFlags |= H_EOH;
  4541. }
  4542. *pszSearch = CR;
  4543. }
  4544. //If we are not in the header and this is
  4545. //the first time m_TimeToRewriteHeader is
  4546. //TRUE, then add any missing headers we
  4547. //care about. Set m_TimeToRewriteHeader
  4548. //to false so that we don't enter this
  4549. //part of the code again. If ReWriteHeader
  4550. //fails, we are probably out of disk space,
  4551. //so set m_NoStorage to TRUE such that we
  4552. //don't waste our time writing to disk.
  4553. //We still have to accept the mail, but we
  4554. //throw it away. We will send a message
  4555. //back to our client when all is done.
  4556. if(!m_InHeader && m_TimeToRewriteHeader && (m_MailBodyError == NO_ERROR))
  4557. {
  4558. //So there was nothing to write so now write headers
  4559. m_TimeToRewriteHeader = FALSE;
  4560. if(!RewriteHeader())
  4561. {
  4562. m_MailBodyDiagnostic = ERR_RETRY;
  4563. m_MailBodyError = GetLastError();
  4564. return TRUE;
  4565. }
  4566. }
  4567. //restore InputLine for mail file write, if we put in a CRLF
  4568. if(!m_Truncate && m_BufrWasFull)
  4569. {
  4570. *pszSearch = c1;
  4571. *(pszSearch + 1) = c2;
  4572. }
  4573. char *pInputLine = InputLine;
  4574. DWORD cbIntermediateSize = IntermediateSize + 2;
  4575. if(m_MailBodyError == NO_ERROR)
  4576. {
  4577. //if we had a full buffer without CRLFx in the previous iteration, we did a
  4578. //MoveMemory() and the beginning of the buffer is not the beginning of the line.
  4579. //dot stripping is only done for the beginning of the line.
  4580. if( !m_LongBodyLine )
  4581. {
  4582. if( *pInputLine == '.' )
  4583. {
  4584. pInputLine++;
  4585. cbIntermediateSize --;
  4586. m_fFoundEmbeddedCrlfDotCrlf = TRUE;
  4587. }
  4588. }
  4589. //write to the file if we have not gone over our alloted limit
  4590. //and if WriteFile did not give us back any errors
  4591. if((m_pInstance->GetMaxMsgSize() > 0) ||
  4592. (m_pInstance->GetMaxMsgSizeBeforeClose() > 0))
  4593. {
  4594. BOOL fShouldImposeLimit = TRUE;
  4595. // if the total msg size is not exceeded continue writing to file.
  4596. // Else, trigger the MaxMsgSize event to see
  4597. // if we can continue to write to the file.
  4598. if ( m_pInstance->GetMaxMsgSize() > 0 && TotalMsgSize > m_pInstance->GetMaxMsgSize() )
  4599. {
  4600. if( FAILED( m_pInstance->TriggerMaxMsgSizeEvent( GetSessionPropertyBag(), m_pIMsg, &fShouldImposeLimit ) ) || fShouldImposeLimit )
  4601. {
  4602. m_MailBodyError = ERROR_ALLOTTED_SPACE_EXCEEDED;
  4603. m_MailBodyDiagnostic = ERR_MAX_MSG_SIZE;
  4604. ProtocolLog(BDAT, (char *)InputLine, NO_ERROR, SMTP_RESP_NOSTORAGE, 0, 0);
  4605. ErrorTrace((LPARAM) this, "SMTP_RESP_NOSTORAGE, SMTP_MAX_MSG_SIZE_EXCEEDED_MSG - %d", TotalMsgSize);
  4606. ++m_ProtocolErrors;
  4607. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToSize);
  4608. TraceFunctLeaveEx((LPARAM) this);
  4609. return TRUE;
  4610. }
  4611. }
  4612. if ( m_pInstance->GetMaxMsgSizeBeforeClose() >0 && SessionSize > m_pInstance->GetMaxMsgSizeBeforeClose() )
  4613. {
  4614. if( FAILED( m_pInstance->TriggerMaxMsgSizeEvent( GetSessionPropertyBag(), m_pIMsg, &fShouldImposeLimit ) ) || fShouldImposeLimit )
  4615. {
  4616. m_MailBodyError = ERROR_ALLOTTED_SPACE_EXCEEDED;
  4617. ProtocolLog(MAIL, (char *)InputLine, NO_ERROR, SMTP_RESP_NOSTORAGE, 0, 0);
  4618. ErrorTrace((LPARAM) this, "SMTP_RESP_NOSTORAGE, SMTP_MAX_SESSION_SIZE_EXCEEDED_MSG - %d", SessionSize);
  4619. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToSize);
  4620. m_cbParsable = 0;
  4621. m_cbReceived = 0;
  4622. DisconnectClient();
  4623. TraceFunctLeaveEx((LPARAM) this);
  4624. return FALSE;
  4625. }
  4626. }
  4627. if(!WriteMailFile(pInputLine, cbIntermediateSize , lfWritePended))
  4628. {
  4629. m_MailBodyError = GetLastError();
  4630. m_MailBodyDiagnostic = ERR_RETRY;
  4631. return TRUE;
  4632. }
  4633. else if(*lfWritePended)
  4634. {
  4635. //Go away - Atq will call us back when the write file completes
  4636. TraceFunctLeaveEx((LPARAM) this);
  4637. return FALSE;
  4638. }
  4639. }
  4640. else if (!WriteMailFile(pInputLine, cbIntermediateSize, lfWritePended))
  4641. {
  4642. m_MailBodyError = GetLastError();
  4643. m_MailBodyDiagnostic = ERR_RETRY;
  4644. return TRUE;
  4645. }
  4646. else if(*lfWritePended)
  4647. {
  4648. //Go away - Atq will call us back when the write file completes
  4649. TraceFunctLeaveEx((LPARAM) this);
  4650. return FALSE;
  4651. }
  4652. }
  4653. else if(m_pInstance->GetMaxMsgSizeBeforeClose() > 0 && SessionSize > m_pInstance->GetMaxMsgSizeBeforeClose())
  4654. {
  4655. BOOL fShouldImposeLimit = TRUE;
  4656. if( FAILED( m_pInstance->TriggerMaxMsgSizeEvent( GetSessionPropertyBag(),m_pIMsg, &fShouldImposeLimit ) ) || fShouldImposeLimit )
  4657. {
  4658. ErrorTrace((LPARAM) this, "GetMaxMsgSizeBeforeClose() exceeded - closing connection");
  4659. m_cbParsable = 0;
  4660. m_cbReceived = 0;
  4661. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToSize);
  4662. DisconnectClient();
  4663. TraceFunctLeaveEx((LPARAM) this);
  4664. return TRUE;
  4665. }
  4666. }
  4667. //We managed to copy data to write buffer without having to write
  4668. //out
  4669. m_cbParsable -= IntermediateSize + 2;
  4670. m_cbReceived -= IntermediateSize + 2;
  4671. m_TotalMsgSize += IntermediateSize + 2;
  4672. m_SessionSize += IntermediateSize + 2;
  4673. m_cbDotStrippedTotalMsgSize += cbIntermediateSize;
  4674. m_cbRecvBufferOffset += IntermediateSize + 2;
  4675. _ASSERT (m_cbParsable <= QueryMaxReadSize());
  4676. InputLine = pszSearch + 2; //if m_BufrWasFull && UndecryptedTailSize == 0 then InputLine is pointing
  4677. //1 byte beyond end of allocated storage; this is not an error.
  4678. //if the buffer is filled up:
  4679. //since we are done parsing it, we can discard it. this is done by setting m_cbParsable to 0
  4680. //then we quit the IsLineComplete loop so that we can exit this function and pend a read for
  4681. //fresh data. If the buffer was filled up, it also means that a CRLFx was not found, so we
  4682. //ought to enter truncation mode (unless we have already done so) because this line was too
  4683. //long.
  4684. if(m_BufrWasFull)
  4685. {
  4686. m_cbParsable = 0;
  4687. m_LongBodyLine = TRUE; //Next return of pszSearch in while() is not beginning of a new line.
  4688. if(!m_Truncate)
  4689. m_Truncate = TRUE; //this looks silly... but the point is simply that we are "switching"
  4690. break; //states, to truncation mode. The if() reminds you that it is possible
  4691. } //to be in truncation mode already.
  4692. //if the buffer still has room:
  4693. //IsLineComplete returned because it found a CRLFx. in normal (non truncation mode) processing
  4694. //this case means nothing, but if we were in truncation mode, then it means that we have hit the
  4695. //end of the header we were truncating, so we need to get back to normal mode.
  4696. else
  4697. {
  4698. m_LongBodyLine = FALSE;
  4699. if(m_Truncate)
  4700. m_Truncate = FALSE;
  4701. }
  4702. }
  4703. if(m_cbParsable != 0)
  4704. {
  4705. //if there is stuff left in the buffer, move it up
  4706. //to the top, then pend a read at the end of where
  4707. //the last data left off
  4708. _ASSERT (m_cbParsable <= QueryMaxReadSize());
  4709. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  4710. MoveMemory ((void *)QueryMRcvBuffer(), InputLine, m_cbReceived);
  4711. m_cbRecvBufferOffset = 0;
  4712. m_LineCompletionState = SEEN_NOTHING;
  4713. }
  4714. else
  4715. { // m_cbParsable == 0
  4716. m_cbReceived = UndecryptedTailSize;
  4717. if(m_cbReceived > 0) //anything to move?
  4718. MoveMemory ((void *)QueryMRcvBuffer(), InputLine, m_cbReceived);
  4719. m_cbRecvBufferOffset = 0;
  4720. m_LineCompletionState = SEEN_NOTHING;
  4721. }
  4722. TraceFunctLeaveEx((LPARAM) this);
  4723. return FALSE;
  4724. }
  4725. /*++
  4726. NAME: SMTP_CONNECTION::IsLineCompleteRFC822
  4727. DESCRIPTION:
  4728. Parses InputLine till it encounters a CRLFx where x is a nontab
  4729. and nonspace character. When CRLFx is found it means that the true
  4730. end of a line has been found, in other words ... there are no more
  4731. continuations of this line.
  4732. Implementation is as a finite state machine (FSM).
  4733. The SMTP_CONNECTION member m_LineCompletion keeps track of the
  4734. state of the FSM between calls so that if the FSM does not hit
  4735. its exit state (CRLFx found) by the time it has scanned the input,
  4736. the caller may obtain more input data and resume the parsing. Thus
  4737. this member variable is "implicitly" passed to the function.
  4738. If the message receive buffer (returned by QueryMRcvBuffer()) is
  4739. filled up, so that the caller has no chance of getting more data
  4740. (since there is no space), this function returns a pointer to the
  4741. last but one item in InputLine and sets the flag FullBuffer. The
  4742. purpose being that the caller can put a CRLF at the position
  4743. returned and proceed as though CRLFx had been found.
  4744. A special condition occurs if the InputLine starts off as .CRLF
  4745. and the initial state is 1. This means that either the message is
  4746. just .CRLF or a CRLF.CRLF has been found. Thus the end of the message
  4747. has been found and the function returns.
  4748. PARAMETERS:
  4749. InputLine Input data
  4750. nBytes Bytes to parse
  4751. UndecryptedTailSize Data that occupies buffer space but can't
  4752. be parsed
  4753. p_FullBuffer If the function fails to find CRLFx, is there
  4754. enough room left in the buffer so that we can
  4755. get more input from the user?
  4756. m_LineCompletionState Implicit parameter... you need to Initialize
  4757. this.
  4758. RETURNS:
  4759. Pointer to CR if CRLFx found
  4760. Pointer to second last byte of buffer if there is no CRLFx
  4761. NULL if there is no CRLFx and the buffer is not full.
  4762. --*/
  4763. char * SMTP_CONNECTION::IsLineCompleteRFC822(
  4764. IN char *InputLine,
  4765. IN DWORD nBytes,
  4766. IN DWORD UndecryptedTailSize,
  4767. OUT BOOL *p_FullBuffer
  4768. )
  4769. {
  4770. ASSERT(InputLine);
  4771. char *pCh;
  4772. char *pLastChar = InputLine + nBytes - 1; //end of input
  4773. //One exceptional case : the last line of input
  4774. if(nBytes >= 3)
  4775. {
  4776. if(InputLine[0] == '.' && InputLine[1] == CR && InputLine[2] == LF)
  4777. {
  4778. *p_FullBuffer = FALSE;
  4779. return &InputLine[1];
  4780. }
  4781. }
  4782. for(pCh = InputLine; pCh <= pLastChar; pCh++)
  4783. {
  4784. switch(m_LineCompletionState)
  4785. {
  4786. case SEEN_NOTHING: //need CR
  4787. if(*pCh == CR)
  4788. m_LineCompletionState = SEEN_CR; //seen CR go on to state_2
  4789. break;
  4790. case SEEN_CR: //seen CR need LF
  4791. if(*pCh == LF)
  4792. m_LineCompletionState = SEEN_CRLF; //seen LF goto state_3
  4793. else if(*pCh != CR)
  4794. m_LineCompletionState = SEEN_NOTHING;//if CR stay in this state, else go back to 1
  4795. break;
  4796. case SEEN_CRLF: //seen CRLF, need x
  4797. if(*pCh != ' ' && *pCh != '\t') //CRLFx found
  4798. {
  4799. *p_FullBuffer = FALSE;
  4800. m_LineCompletionState = SEEN_NOTHING; //seen CRLFx, go back to initial state
  4801. return pCh - 2; //returning pointer to the CR
  4802. }
  4803. else
  4804. m_LineCompletionState = SEEN_NOTHING; //didn't get either x or CR, nothing matched --- start over again.
  4805. break;
  4806. }
  4807. }
  4808. //buffer is full && CRLFx not found.
  4809. if(*p_FullBuffer = (pLastChar >= QueryMRcvBuffer() + QueryMaxReadSize() - UndecryptedTailSize - 1))
  4810. return pLastChar - 1; //assumption: nbytes is atleast 2
  4811. return NULL; //buffer has room left && CRLFx not found
  4812. }
  4813. /*++
  4814. Name :
  4815. SMTP_CONNECTION::CreateMailBodyFromChunk
  4816. Description:
  4817. Responds to the SMTP BDAT command.
  4818. This function spools the mail to a
  4819. directory keeping track of chunk size
  4820. Arguments:
  4821. InputLine - Buffer received from client
  4822. paramterSize - amount of data in buffer
  4823. UndecryptedTailSize -- amount of undecrypted data at end of buffer
  4824. Returns:
  4825. TRUE if all data(incliding terminating .)
  4826. has been received
  4827. FALSE on all errors (disk full, etc.)
  4828. --*/
  4829. BOOL SMTP_CONNECTION::CreateMailBodyFromChunk (char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize, BOOL *lpfWritePended)
  4830. {
  4831. BOOL MailDone = FALSE;
  4832. DWORD IntermediateSize = 0;
  4833. PCHAR pszSearch = NULL;
  4834. char MailFileName[MAX_PATH];
  4835. DWORD TotalMsgSize = 0;
  4836. DWORD SessionSize = 0;
  4837. // HRESULT hr;
  4838. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::CreateMailBodyFromChunk");
  4839. _ASSERT (m_pInstance != NULL);
  4840. _ASSERT (m_cbParsable <= QueryMaxReadSize());
  4841. MailFileName[0] = '\0';
  4842. //
  4843. // m_cbTempBDATLen is used to save BDAT chunk bytes for headers spanning multiple
  4844. // BDAT chunks. So we need to parse from the very beginning of the buffer if there
  4845. // are saved BDAT header bytes.
  4846. //
  4847. if(m_cbTempBDATLen)
  4848. {
  4849. InputLine = QueryMRcvBuffer();
  4850. }
  4851. //
  4852. // Process the chunk data line-by-line till a line is encountered which is not a
  4853. // header. At this point m_InHeader is set to FALSE and we never enter this loop
  4854. // again. Instead all chunk data will be directly dumped to disk as the mail body.
  4855. //
  4856. // It can be determined that a line is either header/non-header if:
  4857. // - It is CRLF terminated (it can be examined for the header syntax)
  4858. // - If we have 1000+ bytes without a CRLF it is not a header
  4859. //
  4860. // If it does not meet either condition, we must save the data (set m_cbTempBDATLen)
  4861. // and post a read for the next chunk hoping that with the fresh data, either of the
  4862. // two conditions will apply.
  4863. //
  4864. while (m_InHeader &&
  4865. ((pszSearch = IsLineComplete((const char *)InputLine, m_cbParsable)) != NULL ||
  4866. m_cbParsable > 1000 ||
  4867. m_cbParsable >= (DWORD)m_nBytesRemainingInChunk))
  4868. {
  4869. _ASSERT (m_cbParsable <= QueryMaxReadSize());
  4870. // Found a CRLF in the received data
  4871. if(pszSearch != NULL)
  4872. {
  4873. IntermediateSize = (DWORD)(pszSearch - InputLine);
  4874. TotalMsgSize = m_TotalMsgSize + (IntermediateSize + 2);
  4875. SessionSize = m_SessionSize + (IntermediateSize + 2);
  4876. //
  4877. // There are IntermediateSize+2 bytes in this CRLF terminated line.
  4878. // If that is > the number of bytes in the current BDAT chunk:
  4879. // - The chunk was fully received
  4880. // - Some extra bytes (pipelined SMTP command) were received after it.
  4881. // - The CRLF found by IslineComplete is outside this chunk.
  4882. //
  4883. if(( m_nBytesRemainingInChunk - ((int)IntermediateSize + 2) ) < 0)
  4884. {
  4885. if(m_fIsLastChunk || m_nBytesRemainingInChunk > 1000)
  4886. {
  4887. //
  4888. // We're done with header parsing because either:
  4889. // - This is the last BDAT chunk OR
  4890. // - We have 1000 bytes of mailbody data without a CRLF
  4891. //
  4892. m_InHeader = FALSE;
  4893. }
  4894. else
  4895. {
  4896. //
  4897. // In this else-block the following is true:
  4898. // - This is not the last BDAT chunk
  4899. // - We have < 1000 bytes in the current BDAT chunk
  4900. // - The CRLF (pszSearch) is outside the current BDAT chunk
  4901. // - We still haven't hit a non-header line
  4902. //
  4903. // We do not know if this is a header. This chunk is saved
  4904. // as the first m_cbTempBDATLen bytes in the recv buffer. The
  4905. // next chunk is appended to this and both will be processed
  4906. // together.
  4907. //
  4908. m_cbTempBDATLen = m_nBytesRemainingInChunk;
  4909. m_nBytesRemainingInChunk = 0;
  4910. break;
  4911. }
  4912. }
  4913. //look for all the headers we care about.
  4914. //If we are out of storage, forget it.
  4915. if(m_InHeader && (m_MailBodyError == NO_ERROR))
  4916. {
  4917. //For parsing we terminate the line.
  4918. //We will put back the carraige return later
  4919. *pszSearch = '\0';
  4920. //skip over continuation lines
  4921. if((InputLine[0] != ' ') && (InputLine[0] != '\t'))
  4922. {
  4923. char *pszValueBuf = NULL;
  4924. //see if we are still in the header portion
  4925. //of the body
  4926. if (m_InHeader = ChompHeader(InputLine, m_HeaderFlags, &pszValueBuf))
  4927. {
  4928. //Process and Promote RFC822 Headers
  4929. GetAndPersistRFC822Headers( InputLine, pszValueBuf );
  4930. }
  4931. }
  4932. //put back the carraige return
  4933. *pszSearch = CR;
  4934. }
  4935. }
  4936. else if((m_cbParsable > 1000) || m_fIsLastChunk)
  4937. {
  4938. //We could not parse the line inspite of having 1000 parsable bytes
  4939. //That tells me it is not a 822 header
  4940. m_InHeader = FALSE;
  4941. }
  4942. else
  4943. {
  4944. //We do not have a parsed line and we have not exceeded the 1000byte
  4945. //limit - looks like what we have is a partial header line
  4946. //Keep the data around
  4947. m_cbTempBDATLen = m_nBytesRemainingInChunk;
  4948. m_nBytesRemainingInChunk = 0;
  4949. break;
  4950. }
  4951. //if we are done with headers break
  4952. if(!m_InHeader)
  4953. break;
  4954. //We are still processing headers
  4955. if(m_MailBodyError == NO_ERROR)
  4956. {
  4957. if(!WriteMailFile(InputLine, IntermediateSize + 2, lpfWritePended))
  4958. {
  4959. m_MailBodyDiagnostic = ERR_RETRY;
  4960. m_MailBodyError = GetLastError();
  4961. m_InHeader = FALSE;
  4962. }
  4963. else if(*lpfWritePended)
  4964. {
  4965. //Go away - Atq will call us back when the write file completes
  4966. TraceFunctLeaveEx((LPARAM) this);
  4967. return FALSE;
  4968. }
  4969. //Update the remaining bytes expected in this chunk
  4970. else
  4971. {
  4972. //Once we write out chunk data - we should no longer have any saved of portion of chunk
  4973. //
  4974. m_cbTempBDATLen = 0;
  4975. m_nBytesRemainingInChunk -= IntermediateSize + 2;
  4976. }
  4977. }
  4978. else
  4979. m_InHeader = FALSE;
  4980. InputLine = pszSearch + 2;
  4981. m_cbParsable -= (IntermediateSize + 2);
  4982. m_cbReceived -= IntermediateSize + 2;
  4983. m_TotalMsgSize += IntermediateSize + 2;
  4984. m_SessionSize += IntermediateSize + 2;
  4985. m_cbRecvBufferOffset += (IntermediateSize + 2);
  4986. _ASSERT (m_cbParsable < QueryMaxReadSize());
  4987. }//end while
  4988. // If we are done with headers - it is time to write any missing headers
  4989. if(!m_InHeader && m_TimeToRewriteHeader && (m_MailBodyError == NO_ERROR))
  4990. {
  4991. //So there was nothing to write so now write headers
  4992. m_TimeToRewriteHeader = FALSE;
  4993. if(!RewriteHeader())
  4994. {
  4995. m_MailBodyDiagnostic = ERR_RETRY;
  4996. m_MailBodyError = GetLastError();
  4997. ErrorTrace( (LPARAM)this, "Rewrite headers failed. err: %d",m_MailBodyError);
  4998. }
  4999. }
  5000. //If we did write something during our header parsing we need to update m_cbReceived
  5001. m_cbReceived = m_cbParsable + UndecryptedTailSize;
  5002. // Now that we are thru with headers and there is more data in the buffer
  5003. // that is part of the chunk - simply dump it to disk asynchronously
  5004. if(!m_InHeader && m_nBytesRemainingInChunk && m_cbParsable)
  5005. {
  5006. DWORD dwBytesToWrite = m_nBytesRemainingInChunk;
  5007. // Simply write the parsable data or the remaining bytes in this chunk
  5008. // to the file - which ever is smaller
  5009. if(m_nBytesRemainingInChunk > (int) m_cbParsable)
  5010. dwBytesToWrite = m_cbParsable;
  5011. if(m_MailBodyError == NO_ERROR)
  5012. {
  5013. //write to the file if we ahve had no errors
  5014. if(!WriteMailFile(InputLine, dwBytesToWrite, lpfWritePended))
  5015. {
  5016. m_MailBodyDiagnostic = ERR_RETRY;
  5017. m_MailBodyError = GetLastError();
  5018. }
  5019. else if(*lpfWritePended)
  5020. {
  5021. //Go away - Atq will call us back when the write file completes
  5022. TraceFunctLeaveEx((LPARAM) this);
  5023. return FALSE;
  5024. }
  5025. //Now that we have copied stuff to out buffer
  5026. //update the state data
  5027. m_TotalMsgSize += dwBytesToWrite;
  5028. m_SessionSize += dwBytesToWrite;
  5029. m_nBytesRemainingInChunk -= dwBytesToWrite;
  5030. //We are down to last bytes - keep a track of trailing CRLF
  5031. if(m_fIsLastChunk && (m_nBytesRemainingInChunk < 2) )
  5032. {
  5033. //We have the second last byte - there is one more byte to be read.
  5034. if(m_nBytesRemainingInChunk)
  5035. {
  5036. if(*(InputLine + dwBytesToWrite -1) == '\r')
  5037. m_dwTrailerStatus = CR_SEEN;
  5038. else
  5039. m_dwTrailerStatus = CR_MISSING;
  5040. }
  5041. else if(dwBytesToWrite > 1) //We got the last byte
  5042. {
  5043. //We have last two bytes together
  5044. if(!strncmp((InputLine + dwBytesToWrite - 2),"\r\n", 2))
  5045. m_dwTrailerStatus = CRLF_SEEN;
  5046. else
  5047. m_dwTrailerStatus = CRLF_NEEDED;
  5048. }
  5049. else if(m_dwTrailerStatus == CR_SEEN && (*(InputLine + dwBytesToWrite -1) == '\n'))
  5050. m_dwTrailerStatus = CRLF_SEEN;
  5051. else
  5052. m_dwTrailerStatus = CRLF_NEEDED;
  5053. }
  5054. //Update IO buffer parameters to reflect the state after WRITE
  5055. m_cbReceived -= dwBytesToWrite;
  5056. m_cbParsable -= dwBytesToWrite;
  5057. //Once we write out chunk data - we should no longer have any saved of portion of chunk
  5058. //
  5059. m_cbTempBDATLen = 0;
  5060. m_cbRecvBufferOffset = 0;
  5061. }
  5062. InputLine += dwBytesToWrite;
  5063. _ASSERT (m_cbParsable < QueryMaxReadSize());
  5064. }
  5065. //Adjust the buffer for the next read only if did comsume any data
  5066. //and there is more data remaining in the input buffer
  5067. //
  5068. if(m_cbReceived && (QueryMRcvBuffer() != InputLine))
  5069. {
  5070. MoveMemory ((void *)QueryMRcvBuffer(), InputLine, m_cbReceived);
  5071. }
  5072. m_cbRecvBufferOffset = 0;
  5073. //If we are done with current chunk
  5074. if(!m_nBytesRemainingInChunk)
  5075. {
  5076. if(m_MailBodyError == NO_ERROR)
  5077. {
  5078. if(m_fIsLastChunk)
  5079. {
  5080. //we are done with all the chunks
  5081. //We have written the Last chunk and need to see if we need to put the trailing CRLF
  5082. if(m_dwTrailerStatus == CRLF_NEEDED)
  5083. {
  5084. if(!WriteMailFile("\r\n", 2, lpfWritePended))
  5085. {
  5086. m_MailBodyDiagnostic = ERR_RETRY;
  5087. m_MailBodyError = GetLastError();
  5088. }
  5089. else if(*lpfWritePended)
  5090. {
  5091. //Go away - Atq will call us back when the write file completes
  5092. TraceFunctLeaveEx((LPARAM) this);
  5093. return FALSE;
  5094. }
  5095. m_TotalMsgSize += 2;
  5096. m_SessionSize += 2;
  5097. m_dwTrailerStatus = CRLF_SEEN;
  5098. }
  5099. //Flush all the data to disk
  5100. if(!WriteMailFile(NULL, 0, lpfWritePended))
  5101. {
  5102. m_MailBodyDiagnostic = ERR_RETRY;
  5103. m_MailBodyError = GetLastError();
  5104. }
  5105. else if(*lpfWritePended)
  5106. {
  5107. //Go away - Atq will call us back when the write file completes
  5108. TraceFunctLeaveEx((LPARAM) this);
  5109. return FALSE;
  5110. }
  5111. //Go back to HELO state
  5112. m_State = HELO;
  5113. m_fIsChunkComplete = FALSE;
  5114. TraceFunctLeaveEx((LPARAM) this);
  5115. return TRUE;
  5116. }
  5117. else
  5118. {
  5119. //Chunk completion response
  5120. ProtocolLog(BDAT, (char *) MailFileName, NO_ERROR, SMTP_RESP_OK, 0, 0);
  5121. FormatSmtpMessage (SMTP_RESP_OK, EMESSAGE_GOOD," %s, %d Octets\r\n", SMTP_BDAT_CHUNK_OK_STR, m_nChunkSize );
  5122. }
  5123. m_fIsChunkComplete = TRUE;
  5124. }
  5125. else
  5126. {
  5127. // We had error during handling the Chunk, now that chunk has been consumed
  5128. //we can respond with an error
  5129. TraceFunctLeaveEx((LPARAM) this);
  5130. return TRUE;
  5131. }
  5132. }
  5133. TraceFunctLeaveEx((LPARAM) this);
  5134. return FALSE;
  5135. }
  5136. /*++
  5137. Name :
  5138. SMTP_CONNECTION::DoDATACommandEx
  5139. Description:
  5140. Responds to the SMTP data command.
  5141. This funcion spools the mail to a
  5142. directory
  5143. Arguments:
  5144. InputLine - Buffer received from client
  5145. paramterSize - amount of data in buffer
  5146. Returns:
  5147. Currently this function always returns TRUE.
  5148. --*/
  5149. BOOL SMTP_CONNECTION::DoDATACommandEx(const char * InputLine1, DWORD ParameterSize, DWORD UndecryptedTailSize,BOOL *lpfWritePended, BOOL *pfAsyncOp)
  5150. {
  5151. LPSTR InputLine = (LPSTR) InputLine1;
  5152. BOOL fRet = TRUE;
  5153. BOOL fProcess = TRUE;
  5154. PSMTP_IIS_SERVICE pService = (PSMTP_IIS_SERVICE) g_pInetSvc;
  5155. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoDATACommandEx");
  5156. _ASSERT (m_pInstance != NULL);
  5157. //
  5158. // we scan for <CRLF>.<CRLF> when processing the msg body of the DATA
  5159. // command.
  5160. //
  5161. m_fScannedForCrlfDotCrlf = TRUE;
  5162. if(CreateMailBody(InputLine, ParameterSize, UndecryptedTailSize, lpfWritePended))
  5163. {
  5164. if(m_MailBodyDiagnostic != NO_ERROR)
  5165. {
  5166. TraceFunctLeaveEx((LPARAM) this);
  5167. return TRUE;
  5168. }
  5169. //increment our counters
  5170. //make sure the size of the file is proper.
  5171. //subtract 3 bytes accounting for the .CRLF.
  5172. //We don't write that portion to the file.
  5173. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesRcvdMsg, (m_TotalMsgSize - 3));
  5174. BUMP_COUNTER(QuerySmtpInstance(), NumMsgRecvd);
  5175. //if there was no error while parsing the
  5176. //header, and writimg the file to disk,
  5177. //then save the rest of the file, send
  5178. //a message id back to the client, and
  5179. //queue the message
  5180. if(m_MailBodyError == NO_ERROR)
  5181. {
  5182. //call HandleCompletedMessage() to see
  5183. //if we should process this message
  5184. HandleCompletedMessage(DATA, pfAsyncOp);
  5185. if (*pfAsyncOp) {
  5186. m_fAsyncEOD = TRUE;
  5187. return TRUE;
  5188. }
  5189. //We got all the data and written it to disk.
  5190. //Now, we'll pend a read to pick up the quit
  5191. //response, to get any other mail the client
  5192. //sends. We need to re-initialize our class
  5193. //variables first.
  5194. ReInitClassVariables();
  5195. _ASSERT (m_cbReceived < QueryMaxReadSize());
  5196. }
  5197. else
  5198. {
  5199. //else, format the appropriate message,
  5200. //and delete all objects pertaining to
  5201. //this file.
  5202. //flush any pending responses
  5203. SendSmtpResponse();
  5204. //re-initialize out class variables
  5205. ReInitClassVariables();
  5206. _ASSERT (m_cbReceived < QueryMaxReadSize());
  5207. }
  5208. }
  5209. TraceFunctLeaveEx((LPARAM) this);
  5210. return fRet;
  5211. }
  5212. //-----------------------------------------------------------------------------
  5213. // Description:
  5214. // This function handles receiving the mailbody sent by a client using the
  5215. // DATA command. It wraps the functionality of DoDATACommandEx (the
  5216. // function to parse and process the mailbody) and AcceptAndDiscardDATA
  5217. // (the function that handles errors that occur during DoDATACommandEx).
  5218. //
  5219. // The reason for a separate function to do error processing for mailbody
  5220. // errors is that when a mailbody error occurs, SMTP cannot directly abort
  5221. // the transaction and return an error response. Instead it must continue
  5222. // to post reads for the mailbody, and after the mailbody has been completely
  5223. // received, respond with the appropriate error.
  5224. //
  5225. // So when an error occurs during DoDATACommandEx, it stops processing and
  5226. // sets m_MailBodyDiagnostic. AcceptAndDiscardData takes over and keeps
  5227. // posting reads till the body has been sent by the client. Then it uses
  5228. // m_MailBodyDiagnostic to generate the error response.
  5229. //
  5230. // Arguments:
  5231. // IN char *InputLine - Pointer to data being processed
  5232. // IN DWORD UndecryptedTailSize - If using SSL, this is the encrypted tail
  5233. // OUT BOOL *pfAsyncOp - Set to TRUE if a read of write was pended. In
  5234. // this case.
  5235. // Returns:
  5236. // TRUE - Success. All received data must be passed into this function
  5237. // till the state goes to HELO after which data should be parsed as
  5238. // SMTP commands. Failures while processing DATA are caught internally
  5239. // (flagged by m_MailBodyDiagnostic and m_MailBodyError) and we still
  5240. // return TRUE.
  5241. // FALSE - A failure occurred that cannot be handled (like a TCP/IP
  5242. // failure). In this case we should disconnect.
  5243. //
  5244. //-----------------------------------------------------------------------------
  5245. BOOL SMTP_CONNECTION::ProcessDATAMailBody(const char *InputLine, DWORD UndecryptedTailSize, BOOL *pfAsyncOp)
  5246. {
  5247. BOOL fReturn = TRUE;
  5248. BOOL fWritePended = FALSE;
  5249. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::ProcessDATA");
  5250. _ASSERT(m_State == DATA);
  5251. *pfAsyncOp = FALSE;
  5252. if(m_MailBodyDiagnostic != ERR_NONE)
  5253. goto MAILBODY_ERROR;
  5254. //
  5255. // DoDATACommandEx handles parsing the mailbody, processing the headers and
  5256. // persisting them to the IMailMsg, and writing the mailbody to a file. In
  5257. // addition, when the mailbody has been received, it triggers the _EOD event
  5258. // which may return async (due to installed event handlers). We pre-increment
  5259. // the pending IO count to cover this case.
  5260. //
  5261. IncPendingIoCount();
  5262. fReturn = DoDATACommandEx(InputLine, m_cbParsable, UndecryptedTailSize, &fWritePended, pfAsyncOp);
  5263. _ASSERT(fReturn && "All errors are flagged by m_MailBodyDiagnostic and m_MailBodyError");
  5264. //
  5265. // DoDATACommandEx/CreateMailBody always return immediately after they pend I/Os
  5266. // (expecting the I/O completion threads to resume processing). So if the I/O pend
  5267. // succeeds, DoDATACommandEx must have succeeded.
  5268. //
  5269. if(*pfAsyncOp || fWritePended)
  5270. _ASSERT(m_MailBodyDiagnostic == ERR_NONE);
  5271. if(*pfAsyncOp)
  5272. {
  5273. TraceFunctLeaveEx((LPARAM) this);
  5274. return TRUE;
  5275. }
  5276. //
  5277. // Decrement the pending IO count since DoDataCommandEx returned sync (and
  5278. // we incremented the count above). ProcessClient is above us in the
  5279. // callstack and always has a pending IO reference, so this should never
  5280. // return zero
  5281. //
  5282. _VERIFY(DecPendingIoCount() > 0);
  5283. //
  5284. // If a Write was pended we simply want to release this thread back to IIS
  5285. // The completion thread will continue with the processing
  5286. //
  5287. if(fWritePended)
  5288. {
  5289. *pfAsyncOp = TRUE;
  5290. TraceFunctLeaveEx((LPARAM) this);
  5291. return TRUE;
  5292. }
  5293. if(m_MailBodyDiagnostic != ERR_NONE)
  5294. {
  5295. _ASSERT(!*pfAsyncOp && !fWritePended);
  5296. //
  5297. // Shift out bytes that already got parsed during DoDATACommandEx.
  5298. //
  5299. MoveMemory(QueryMRcvBuffer(), QueryMRcvBuffer() + m_cbRecvBufferOffset, m_cbParsable);
  5300. m_cbRecvBufferOffset = 0;
  5301. goto MAILBODY_ERROR;
  5302. }
  5303. if(m_State == DATA)
  5304. {
  5305. //
  5306. // If the state is still equal to DATA, then we need to keep
  5307. // pending reads to pickup the rest of the message. When the
  5308. // state changes to HELO, then we can stop pending reads, and
  5309. // go back into parsing commands.
  5310. //
  5311. fReturn = PendReadIO(UndecryptedTailSize);
  5312. *pfAsyncOp = TRUE;
  5313. TraceFunctLeaveEx((LPARAM) this);
  5314. return fReturn;
  5315. }
  5316. //
  5317. // Done with mail body. Go back to parsing commands.
  5318. //
  5319. _ASSERT(m_State == HELO);
  5320. TraceFunctLeaveEx((LPARAM) this);
  5321. return fReturn;
  5322. MAILBODY_ERROR:
  5323. _ASSERT(m_MailBodyDiagnostic != ERR_NONE);
  5324. //
  5325. // DoBDATCommandEx (and other parts of smtpcli) use m_cbRecvBufferOffset to
  5326. // keep track of bytes in the beginning of the recv buffer that have been
  5327. // processed, but have not been shifted out (this is needed for header parsing).
  5328. // All reads/processing therefore start at (recv buffer + offset). During error
  5329. // processing however there's no parsing of headers, so this offset should be
  5330. // set to 0 before calling into discard, and the saved bytes shifted out. The
  5331. // ASSERT below verifies this.
  5332. //
  5333. _ASSERT(m_cbRecvBufferOffset == 0 && "All of recv buffer is not being used");
  5334. fReturn = AcceptAndDiscardDATA(QueryMRcvBuffer(), UndecryptedTailSize, pfAsyncOp);
  5335. //
  5336. // If something failed during error processing, disconnect with an error
  5337. // (this violates the RFC, but there's no option). If we succeeded and a
  5338. // read was pended, all data in the input buffer has been consumed. The
  5339. // read I/O completion thread will pick up processing.
  5340. //
  5341. if(!fReturn || *pfAsyncOp)
  5342. {
  5343. TraceFunctLeaveEx((LPARAM) this);
  5344. return fReturn;
  5345. }
  5346. //
  5347. // All the mailbody was processed and the input buffer contains only SMTP
  5348. // commands. Fall through the the command parsing code.
  5349. //
  5350. _ASSERT(m_State == HELO);
  5351. TraceFunctLeaveEx((LPARAM) this);
  5352. return fReturn;
  5353. }
  5354. //-----------------------------------------------------------------------------
  5355. // Description:
  5356. // Analogous to ProcessDATAMailBody
  5357. // Arguments:
  5358. // Same as ProcessDATAMailBody
  5359. // Returns:
  5360. // Same as ProcessDATAMailBody
  5361. //-----------------------------------------------------------------------------
  5362. BOOL SMTP_CONNECTION::ProcessBDATMailBody(const char *InputLine, DWORD UndecryptedTailSize, BOOL *pfAsyncOp)
  5363. {
  5364. BOOL fWritePended = FALSE;
  5365. BOOL fReturn = FALSE;
  5366. _ASSERT(!m_fIsChunkComplete && m_State == BDAT);
  5367. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::ProcessBDATMailBody");
  5368. *pfAsyncOp = FALSE;
  5369. //
  5370. // We are in the midst of error processing, skip right to it.
  5371. //
  5372. if(m_MailBodyDiagnostic != ERR_NONE)
  5373. goto MAILBODY_ERROR;
  5374. //
  5375. // Increment the pending IO count in case DoBDATCommandEx returns async.
  5376. // When the message is completely received, DoBDATCommandEx will trigger
  5377. // into the _EOD event, which may optionally return async. This increment
  5378. // serves to keep track of that.
  5379. //
  5380. IncPendingIoCount();
  5381. fReturn = DoBDATCommandEx(InputLine, m_cbParsable, UndecryptedTailSize, &fWritePended, pfAsyncOp);
  5382. if(m_MailBodyDiagnostic != ERR_NONE)
  5383. {
  5384. _ASSERT(!fWritePended && !*pfAsyncOp && "Shouldn't be pending read/write on failure");
  5385. _VERIFY(DecPendingIoCount() > 0);
  5386. goto MAILBODY_ERROR;
  5387. }
  5388. // The completion thread will call us when more data arrives
  5389. if (*pfAsyncOp)
  5390. {
  5391. TraceFunctLeaveEx((LPARAM) this);
  5392. return TRUE;
  5393. }
  5394. //
  5395. // Decrement the pending IO count since DoBDATCommandEx returned sync (and
  5396. // we incremented the count above). ProcessClient is above us in the
  5397. // callstack and always has a pending IO reference, so this should never
  5398. // return zero
  5399. //
  5400. _VERIFY(DecPendingIoCount() > 0);
  5401. //
  5402. // If a Write was pended we simply want to release this thread back to IIS
  5403. // The completion thread will continue with the processing
  5404. //
  5405. if(fWritePended)
  5406. {
  5407. *pfAsyncOp = TRUE;
  5408. TraceFunctLeaveEx((LPARAM) this);
  5409. return TRUE;
  5410. }
  5411. if(m_State == BDAT && !m_fIsChunkComplete)
  5412. {
  5413. //
  5414. // We still are not done with current chunk So Pend read to pickup the
  5415. // rest of the chunk. When the state changes to HELO or the current
  5416. // chunk completes, then we can stop pending reads, and go back into
  5417. // parsing commands.
  5418. //
  5419. fReturn = PendReadIO(UndecryptedTailSize);
  5420. *pfAsyncOp = TRUE;
  5421. TraceFunctLeaveEx((LPARAM) this);
  5422. return fReturn;
  5423. }
  5424. else if(m_cbTempBDATLen)
  5425. {
  5426. //
  5427. // Flush the response to the chunk just processed (why is this needed
  5428. // specifically for m_cbTempBDATLen > 0 only? Shouldn't all responses
  5429. // be treated equally -- gpulla).
  5430. //
  5431. SendSmtpResponse();
  5432. }
  5433. //
  5434. // Done with current chunk. Go back to parsing commands.
  5435. //
  5436. TraceFunctLeaveEx((LPARAM) this);
  5437. return fReturn;
  5438. MAILBODY_ERROR:
  5439. _ASSERT(m_MailBodyDiagnostic != ERR_NONE);
  5440. //
  5441. // m_cbTempBDATLen is an offset into the recv buffer. It is set to > 0 when
  5442. // DoBDATCommandEx needs to save the start of a header that spans multiple
  5443. // chunks. Since discard does no header parsing, this is unneccessary, and
  5444. // the "offset" number of bytes (if non-zero) should be reset and shifted
  5445. // out before calling into discard.
  5446. //
  5447. if(m_cbTempBDATLen)
  5448. {
  5449. MoveMemory(QueryMRcvBuffer(), QueryMRcvBuffer() + m_cbTempBDATLen, m_cbReceived);
  5450. m_cbTempBDATLen = 0;
  5451. }
  5452. fReturn = AcceptAndDiscardBDAT(QueryMRcvBuffer(), UndecryptedTailSize, pfAsyncOp);
  5453. //
  5454. // If something failed during error processing, disconnect with an error
  5455. // (this violates the RFC, but there's no option). If we succeeded and a
  5456. // read was pended, all data in the input buffer has been consumed. The
  5457. // read I/O completion thread will pick up processing.
  5458. //
  5459. if(!fReturn || *pfAsyncOp)
  5460. {
  5461. TraceFunctLeaveEx((LPARAM) this);
  5462. return fReturn;
  5463. }
  5464. _ASSERT(m_nBytesRemainingInChunk == 0 && "Expected chunk to be done");
  5465. TraceFunctLeaveEx((LPARAM) this);
  5466. return fReturn;
  5467. }
  5468. /*++
  5469. Name :
  5470. SMTP_CONNECTION::DoBDATCommandEx
  5471. Description:
  5472. Handles the data chunks received after a valid BDAT command.
  5473. This funcion spools the mail to a directory.
  5474. In the first chunk it parses for header.
  5475. Once the header is written in current chunk and subsequent chunks it simply
  5476. dumps the data to the disk
  5477. Arguments:
  5478. InputLine - Buffer received from client
  5479. paramterSize - amount of data in buffer
  5480. Returns:
  5481. TRUE if the connection should stay open.
  5482. FALSE if this object should be deleted.
  5483. --*/
  5484. BOOL SMTP_CONNECTION::DoBDATCommandEx(const char * InputLine1, DWORD ParameterSize, DWORD UndecryptedTailSize, BOOL *lpfWritePended, BOOL *pfAsyncOp)
  5485. {
  5486. LPSTR InputLine = (LPSTR) InputLine1;
  5487. BOOL fRet = TRUE;
  5488. BOOL fProcess = TRUE;
  5489. PSMTP_IIS_SERVICE pService = (PSMTP_IIS_SERVICE) g_pInetSvc;
  5490. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoBDATCommandEx");
  5491. _ASSERT (m_pInstance != NULL);
  5492. *lpfWritePended = FALSE;
  5493. // We make this call only while we are parsing the headers
  5494. // Once we are done parsing the headers
  5495. if(CreateMailBodyFromChunk (InputLine, ParameterSize, UndecryptedTailSize, lpfWritePended))
  5496. {
  5497. m_WritingData = FALSE;
  5498. //increment our counters
  5499. //make sure the size of the file is proper.
  5500. //subtract 3 bytes accounting for the .CRLF.
  5501. //We don't write that portion to the file.
  5502. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesRcvdMsg, (m_TotalMsgSize - 3));
  5503. BUMP_COUNTER(QuerySmtpInstance(), NumMsgRecvd);
  5504. //if there was no error while parsing the
  5505. //header, and writimg the file to disk,
  5506. //then save the rest of the file, send
  5507. //a message id back to the client, and
  5508. //queue the message
  5509. if(m_MailBodyError == NO_ERROR)
  5510. {
  5511. //call HandleCompletedMessage() to see
  5512. //if we should process this message
  5513. fProcess = HandleCompletedMessage(BDAT, pfAsyncOp);
  5514. if (*pfAsyncOp) {
  5515. m_fAsyncEOD = TRUE;
  5516. return TRUE;
  5517. }
  5518. //We got all the data and written it to disk.
  5519. //Now, we'll pend a read to pick up the quit
  5520. //response, to get any other mail the client
  5521. //sends. We need to re-initialize our class
  5522. //variables first.
  5523. ReInitClassVariables();
  5524. _ASSERT (m_cbReceived < QueryMaxReadSize());
  5525. }
  5526. else
  5527. {
  5528. //else, format the appropriate message,
  5529. //and delete all objects pertaining to
  5530. //this file.
  5531. if(m_MailBodyError == ERROR_BAD_LENGTH)
  5532. FormatSmtpMessage (SMTP_RESP_MBX_SYNTAX,ESYNTAX_ERROR," %s\r\n","Badly formed BDAT Chunk");
  5533. else
  5534. FormatSmtpMessage (SMTP_RESP_NORESOURCES,ENO_RESOURCES," %s\r\n", SMTP_NO_STORAGE);
  5535. //flush any pending responses
  5536. SendSmtpResponse();
  5537. //re-initialize out class variables
  5538. ReInitClassVariables();
  5539. _ASSERT (m_cbReceived < QueryMaxReadSize());
  5540. }
  5541. }
  5542. TraceFunctLeaveEx((LPARAM) this);
  5543. return fRet;
  5544. }
  5545. /*++
  5546. Name :
  5547. SMTP_CONNECTION::DoHELPCommand
  5548. Description:
  5549. Responds to the SMTP HELP command.
  5550. send a help text description
  5551. Arguments:
  5552. Are ignored
  5553. Returns:
  5554. TRUE if the connection should stay open.
  5555. FALSE if this object should be deleted.
  5556. --*/
  5557. BOOL SMTP_CONNECTION::DoHELPCommand(const char * InputLine, DWORD parameterSize)
  5558. {
  5559. BOOL RetStatus;
  5560. char szHelpCmds[MAX_PATH];
  5561. DWORD ConnectionStatus = 0;
  5562. if(m_SecurePort)
  5563. ConnectionStatus |= SMTP_IS_SSL_CONNECTION;
  5564. if(m_fAuthenticated)
  5565. ConnectionStatus |= SMTP_IS_AUTH_CONNECTION;
  5566. _ASSERT (m_pInstance != NULL);
  5567. //Check if the client is secure enough
  5568. if (!IsClientSecureEnough()) {
  5569. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY," %s\r\n",SMTP_MSG_MUST_SECURE);
  5570. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  5571. ++m_ProtocolErrors;
  5572. return TRUE;
  5573. }
  5574. if(!m_fAuthAnon && !m_fAuthenticated)
  5575. {
  5576. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY," %s\r\n", "Client was not authenticated");
  5577. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  5578. ++m_ProtocolErrors;
  5579. return TRUE;
  5580. }
  5581. strcpy(szHelpCmds,HelpText);
  5582. if(m_pInstance->AllowAuth())
  5583. {
  5584. if(m_pInstance->QueryAuthentication() != 0)
  5585. {
  5586. strcat(szHelpCmds, " AUTH");
  5587. if(g_SmtpPlatformType == PtNtServer && m_pInstance->AllowTURN())
  5588. {
  5589. strcat(szHelpCmds, " TURN");
  5590. }
  5591. }
  5592. }
  5593. if(g_SmtpPlatformType == PtNtServer && m_pInstance->AllowETRN())
  5594. {
  5595. strcat(szHelpCmds, " ETRN");
  5596. }
  5597. // Chunking related advertisements
  5598. if(m_pInstance->AllowBinaryMime() || m_pInstance->AllowChunking())
  5599. {
  5600. strcat(szHelpCmds, " BDAT");
  5601. }
  5602. // verify
  5603. if(m_pInstance->AllowVerify(ConnectionStatus))
  5604. {
  5605. strcat(szHelpCmds, " VRFY");
  5606. }
  5607. // Expand
  5608. if(m_pInstance->AllowExpand(ConnectionStatus))
  5609. {
  5610. strcat(szHelpCmds, " EXPN");
  5611. }
  5612. PE_FormatSmtpMessage ("%s\r\n", szHelpCmds);
  5613. RetStatus = PE_SendSmtpResponse();
  5614. return RetStatus;
  5615. }
  5616. /*++
  5617. Name :
  5618. SMTP_CONNECTION::DoVRFYCommand
  5619. Description:
  5620. Responds to the SMTP VRFY command.
  5621. send a help text description. We
  5622. do not really verify an address here.
  5623. This function is just a stub.
  5624. Arguments:
  5625. The name to verify from the client
  5626. Returns:
  5627. TRUE if the connection should stay open.
  5628. FALSE if this object should be deleted.
  5629. --*/
  5630. BOOL SMTP_CONNECTION::DoVRFYCommand(const char * InputLine, DWORD ParameterSize)
  5631. {
  5632. BOOL RetStatus = TRUE;
  5633. char * VrfyAddr = NULL;
  5634. char * DomainPtr = NULL;
  5635. char * ArgPtr = NULL;
  5636. char szAddr[MAX_INTERNET_NAME + 1];
  5637. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoVRFYCommand");
  5638. szAddr[0] = '\0';
  5639. DWORD ConnectionStatus = 0;
  5640. _ASSERT (m_pInstance != NULL);
  5641. //Check if the client is secure enough
  5642. if (!IsClientSecureEnough()) {
  5643. ErrorTrace((LPARAM) this, "DoVrfyCommand - SMTP_RESP_MUST_SECURE, Must do STARTTLS first");
  5644. TraceFunctLeaveEx((LPARAM) this);
  5645. return TRUE;
  5646. }
  5647. if(!m_fAuthAnon && !m_fAuthenticated)
  5648. {
  5649. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY," %s\r\n","Client was not authenticated");
  5650. ErrorTrace((LPARAM) this, "DoVrfyCommand - SMTP_RESP_MUST_SECURE, user not authenticated");
  5651. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  5652. ++m_ProtocolErrors;
  5653. TraceFunctLeaveEx((LPARAM) this);
  5654. return TRUE;
  5655. }
  5656. //start at the beginning;
  5657. VrfyAddr = (char *) InputLine;
  5658. //check the arguments for good form
  5659. VrfyAddr = CheckArgument(VrfyAddr);
  5660. if (VrfyAddr == NULL)
  5661. {
  5662. TraceFunctLeaveEx((LPARAM) this);
  5663. return TRUE;
  5664. }
  5665. if(ExtractAndValidateRcpt(VrfyAddr, &ArgPtr, szAddr, &DomainPtr))
  5666. {
  5667. //The address is valid
  5668. //Do we allow verify
  5669. if(m_SecurePort)
  5670. ConnectionStatus |= SMTP_IS_SSL_CONNECTION;
  5671. if(m_fAuthenticated)
  5672. ConnectionStatus |= SMTP_IS_AUTH_CONNECTION;
  5673. if(!m_pInstance->AllowVerify(ConnectionStatus))
  5674. {
  5675. PE_CdFormatSmtpMessage (SMTP_RESP_VRFY_CODE, EVALID_DEST_ADDRESS," %s <%s>\r\n", "Cannot VRFY user, but will accept message for", szAddr);
  5676. TraceFunctLeaveEx((LPARAM) this);
  5677. return TRUE;
  5678. }
  5679. //Check the domain to be either belonging to a ALIAS,DEFAULT,DROP,DELIVER
  5680. //We also check the relay restrictions
  5681. if(!ShouldAcceptRcpt(DomainPtr))
  5682. {
  5683. PE_CdFormatSmtpMessage (SMTP_RESP_NOT_FOUND, ENO_FORWARDING, " Cannot relay to <%s>\r\n",szAddr);
  5684. }
  5685. else
  5686. {
  5687. PE_CdFormatSmtpMessage (SMTP_RESP_VRFY_CODE, EVALID_DEST_ADDRESS," %s <%s>\r\n", "Cannot VRFY user, but will take message for", szAddr);
  5688. }
  5689. }
  5690. else
  5691. {
  5692. //it failed. Inform the user
  5693. HandleAddressError((char *)InputLine);
  5694. TraceFunctLeaveEx((LPARAM) this);
  5695. return TRUE;
  5696. }
  5697. TraceFunctLeaveEx((LPARAM) this);
  5698. return TRUE;
  5699. }
  5700. //
  5701. // Reply strings
  5702. //
  5703. typedef struct _AUTH_REPLY {
  5704. LPSTR Reply;
  5705. DWORD Len;
  5706. } AUTH_REPLY, *PAUTH_REPLY;
  5707. char *
  5708. SzNextSeparator(IN char * sz, IN CHAR ch1, IN CHAR ch2)
  5709. {
  5710. char * sz1 = NULL;
  5711. char * sz2 = NULL;
  5712. sz1 = strchr(sz, ch1);
  5713. sz2 = strchr(sz, ch2);
  5714. if (sz1 == sz2)
  5715. return sz1;
  5716. else if (sz1 > sz2)
  5717. return sz2 ? sz2 : sz1;
  5718. else
  5719. return sz1 ? sz1 : sz2;
  5720. }
  5721. BOOL SMTP_CONNECTION::DoUSERCommand(const CHAR *InputLine, DWORD dwLineSize, unsigned char * OutputBuffer, DWORD * dwBytes, DWORD * ResponseCode, char * szECode)
  5722. {
  5723. char szUserName[MAX_USER_NAME_LEN + MAX_SERVER_NAME_LEN +1];
  5724. char szLogonDomainAndUserName[MAX_USER_NAME_LEN + (2 * (MAX_SERVER_NAME_LEN + 1))];
  5725. char lpszDefaultLogonDomain [MAX_SERVER_NAME_LEN + 1];
  5726. char * pSeparator1 = NULL;
  5727. char * pSeparator2 = NULL;
  5728. BUFFER BuffData;
  5729. DWORD DecodedLen = 0;
  5730. DWORD BuffLen = 0;
  5731. BOOL fRet = TRUE;
  5732. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::DoUSERCommand");
  5733. *ResponseCode = SMTP_RESP_AUTH1_PASSED;;
  5734. m_pInstance->LockGenCrit();
  5735. lstrcpyn(lpszDefaultLogonDomain, m_pInstance->GetDefaultLogonDomain(), MAX_SERVER_NAME_LEN);
  5736. m_pInstance->UnLockGenCrit();
  5737. if(!uudecode ((char *) InputLine, &BuffData, &DecodedLen, FALSE) ||
  5738. (DecodedLen == 0))
  5739. {
  5740. *ResponseCode = SMTP_RESP_BAD_ARGS;
  5741. lstrcpy((char *)szECode, "5.7.3");
  5742. lstrcpy((char *) OutputBuffer, "Cannot decode arguments");
  5743. m_securityCtx.Reset();
  5744. TraceFunctLeaveEx((LPARAM)this);
  5745. return FALSE;
  5746. }
  5747. lstrcpyn(szUserName, (char *) BuffData.QueryPtr(), sizeof(szUserName) - 1);
  5748. lstrcpyn(m_szUserName, (char *) BuffData.QueryPtr(), sizeof(m_szUserName) - 1);
  5749. if (UseMbsCta())
  5750. {
  5751. // MBS clear text authentication, does not support domain
  5752. m_pInstance->LockGenCrit();
  5753. lstrcpyn(m_szUsedAuthMechanism, m_pInstance->GetCleartextAuthPackage(), MAX_PATH);
  5754. m_pInstance->UnLockGenCrit();
  5755. lstrcpy(szLogonDomainAndUserName, szUserName);
  5756. }
  5757. else
  5758. {
  5759. // if NT clear text authentication is used, we do some special operations:
  5760. lstrcpyn(m_szUsedAuthMechanism, "NT_BASIC", MAX_PATH);
  5761. // if no default logon domain is not present in user name, and default
  5762. // logon domain is set, then prepend default logon domain to username
  5763. pSeparator1 = SzNextSeparator(szUserName,'\\','/');
  5764. if(pSeparator1 == NULL && (lpszDefaultLogonDomain[0] != '\0'))
  5765. {
  5766. wsprintf(szLogonDomainAndUserName, "%s/%s", lpszDefaultLogonDomain, szUserName);
  5767. }
  5768. else
  5769. {
  5770. if( pSeparator1 != NULL )
  5771. {
  5772. //
  5773. // HACK-O-RAMA: bug 74776 hack, if a username comes in with "REDMOND\\user"
  5774. // allow it. This is to allow buggy Netscape 4.0 browser client to work.
  5775. //
  5776. if( ( (*pSeparator1) == '\\' ) && ( (*(pSeparator1+1)) == '\\' ) )
  5777. {
  5778. MoveMemory( pSeparator1, pSeparator1+1, strlen( pSeparator1 ) );
  5779. }
  5780. //check if we have the mailbox name at the end
  5781. pSeparator2 = SzNextSeparator(pSeparator1 + 1,'\\','/');
  5782. if(pSeparator2 != NULL)
  5783. {
  5784. //If we do simply ignore it by terminating the user string there
  5785. *pSeparator2 = '\0';
  5786. }
  5787. }
  5788. lstrcpy(szLogonDomainAndUserName, szUserName);
  5789. }
  5790. }
  5791. DebugTrace((LPARAM)this, "LogonDomainAndUser: %s", szLogonDomainAndUserName);
  5792. fRet = ProcessAuthInfo(AuthCommandUser, szLogonDomainAndUserName, OutputBuffer, &BuffLen);
  5793. if(!fRet)
  5794. {
  5795. *ResponseCode = SMTP_RESP_AUTH_REJECT;
  5796. lstrcpy((char *)szECode, "5.7.3");
  5797. lstrcpy((char *) OutputBuffer, "5.7.3 Invalid user name");
  5798. *dwBytes = lstrlen("Invalid user name");
  5799. }
  5800. else
  5801. {
  5802. *dwBytes = BuffLen;
  5803. }
  5804. return fRet;
  5805. }
  5806. //+---------------------------------------------------------------
  5807. //
  5808. // Function: SMTP_CONNECTION::DoPASSCommand
  5809. //
  5810. // Synopsis: Valid in the Authorization State.
  5811. // pswd for clear-text logon
  5812. //
  5813. // PASS password\r\n
  5814. //
  5815. // Arguments: const CHAR *: argument line
  5816. // DWORD: sizeof argument line
  5817. //
  5818. // Returns: BOOL: continue processing
  5819. //
  5820. //----------------------------------------------------------------
  5821. BOOL SMTP_CONNECTION::DoPASSCommand(const CHAR *InputLine, DWORD dwLineSize, unsigned char * OutputBuffer, DWORD * dwBytes, DWORD *ResponseCode, char * szECode)
  5822. {
  5823. BOOL fStatus = TRUE;
  5824. BUFFER BuffData;
  5825. DWORD DecodedLen = 0;
  5826. DWORD BuffLen = 0;
  5827. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::DoPASSCommand");
  5828. *ResponseCode = SMTP_RESP_AUTHENTICATED;
  5829. if(!uudecode ((char *)InputLine, &BuffData, &DecodedLen, FALSE))
  5830. {
  5831. m_RecvdAuthCmd = FALSE;
  5832. m_fClearText = FALSE;
  5833. m_State = HELO;
  5834. *ResponseCode = SMTP_RESP_BAD_ARGS;
  5835. if ( ++m_dwUnsuccessfulLogons >= m_pInstance->GetMaxLogonFailures() )
  5836. {
  5837. ErrorTrace((LPARAM)this, "Logon failed");
  5838. DisconnectClient( ERROR_LOGON_FAILURE );
  5839. }
  5840. lstrcpy((char *)szECode, "5.7.3");
  5841. lstrcpy((char *) OutputBuffer, "Cannot decode password");
  5842. m_securityCtx.Reset();
  5843. TraceFunctLeaveEx((LPARAM)this);
  5844. return FALSE;
  5845. }
  5846. OutputBuffer[0] = '\0';
  5847. fStatus = ProcessAuthInfo( AuthCommandPassword, (char *) BuffData.QueryPtr(), OutputBuffer, &BuffLen );
  5848. if (!fStatus)
  5849. {
  5850. ErrorTrace( (LPARAM)this, "Bad pswd %s", QueryClientUserName() );
  5851. *ResponseCode = SMTP_RESP_AUTH_REJECT;
  5852. lstrcpy((char *)szECode, "5.7.3");
  5853. lstrcpy((char *) OutputBuffer, "Authentication unsuccessful");
  5854. if ( ++m_dwUnsuccessfulLogons >= m_pInstance->GetMaxLogonFailures() )
  5855. {
  5856. ErrorTrace((LPARAM)this, "Logon failed");
  5857. DisconnectClient( ERROR_LOGON_FAILURE );
  5858. }
  5859. else
  5860. {
  5861. m_RecvdAuthCmd = FALSE;
  5862. m_fClearText = FALSE;
  5863. m_State = HELO;
  5864. }
  5865. }
  5866. *dwBytes = BuffLen;
  5867. TraceFunctLeaveEx((LPARAM)this);
  5868. return fStatus;
  5869. }
  5870. BOOL SMTP_CONNECTION::ProcessAuthInfo(AUTH_COMMAND Command, LPSTR Blob, unsigned char * OutBuff, DWORD * dwBytes)
  5871. {
  5872. DWORD nbytes = 0;
  5873. REPLY_LIST replyID;
  5874. BOOL f = TRUE;
  5875. m_pInstance->LockGenCrit();
  5876. f = m_securityCtx.ProcessAuthInfo(m_pInstance, Command, Blob,
  5877. OutBuff, &nbytes, &replyID);
  5878. m_pInstance->UnLockGenCrit();
  5879. *dwBytes = nbytes;
  5880. //
  5881. // if replyID == NULL we're conversing for challenge/response logon
  5882. //
  5883. if ( replyID == SecNull )
  5884. {
  5885. _ASSERT( nbytes != 0 );
  5886. }
  5887. if (m_securityCtx.IsAuthenticated())
  5888. {
  5889. f = TRUE;
  5890. }
  5891. if ( f == FALSE )
  5892. {
  5893. //
  5894. // if we fail for any reason reset the state to accept user/auth/apop
  5895. //
  5896. m_securityCtx.Reset();
  5897. }
  5898. return f;
  5899. }
  5900. //+---------------------------------------------------------------
  5901. //
  5902. // Function: SMTP_CONNECTION::DoAuthNegotiation
  5903. //
  5904. // Synopsis: process base64/uuendcoded blobs in AUTH_NEGOTIATE
  5905. //
  5906. // base64 text\r\n
  5907. //
  5908. // Arguments: const CHAR *: argument line
  5909. // DWORD: sizeof argument line
  5910. //
  5911. // Returns: BOOL: continue processing
  5912. //
  5913. //----------------------------------------------------------------
  5914. BOOL SMTP_CONNECTION::DoAuthNegotiation(const CHAR *InputLine, DWORD dwLineSize)
  5915. {
  5916. //The size of the output buffer should vary with the package being used.
  5917. //Currently we use a quickfix which should work for LOGIN and NTLM but will
  5918. //fail for other (new) packages if the response string > 25*255+1. Replace
  5919. //this by a CBuffer later. Size of the CBuffer should be the max token size
  5920. //for the package from CACHED_CREDENTIAL::GetCachedCredential.
  5921. //GPulla, 6/15/1999
  5922. unsigned char OutputBuff[MAX_REPLY_SIZE];
  5923. char szECode[25];
  5924. BOOL fAuthed = TRUE;
  5925. BUFFER BuffData;
  5926. DWORD ResponseCode;
  5927. DWORD dwBytes = 0;
  5928. BOOL fDoBase64 = FALSE;
  5929. char * pszSearch = NULL;
  5930. TraceFunctEnterEx((LPARAM)this, "DoAuthNegotiation");
  5931. if (m_cbParsable >= QueryMaxReadSize())
  5932. {
  5933. m_cbParsable = 0;
  5934. m_ProtocolErrors++;
  5935. }
  5936. pszSearch = IsLineComplete(InputLine,m_cbParsable);
  5937. if(pszSearch == NULL)
  5938. {
  5939. TraceFunctLeaveEx((LPARAM)this);
  5940. return TRUE;
  5941. }
  5942. *pszSearch = '\0';
  5943. m_cbParsable = 0;
  5944. if(!lstrcmp(InputLine, "*"))
  5945. {
  5946. FormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n", "Auth command cancelled");
  5947. m_fDidUserCmd = FALSE;
  5948. m_RecvdAuthCmd = FALSE;
  5949. m_fClearText = FALSE;
  5950. m_State = HELO;
  5951. m_securityCtx.Reset();
  5952. SendSmtpResponse();
  5953. ProtocolLog(AUTH, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_ARGS, 0, 0);
  5954. TraceFunctLeaveEx((LPARAM)this);
  5955. return TRUE;
  5956. }
  5957. if(m_fClearText)
  5958. {
  5959. if(m_fDidUserCmd)
  5960. {
  5961. if(DoPASSCommand(InputLine, dwLineSize, OutputBuff, &dwBytes, &ResponseCode, szECode))
  5962. {
  5963. m_State = HELO;
  5964. m_fAuthenticated = TRUE;
  5965. lstrcpy((char *)szECode, "2.7.0");
  5966. lstrcpy((char *)OutputBuff, "Authentication successful");
  5967. }
  5968. else
  5969. {
  5970. fAuthed = FALSE;
  5971. // lstrcpy((char *)OutputBuff, "5.7.3 Authentication unsuccessful");
  5972. }
  5973. }
  5974. else
  5975. {
  5976. if(DoUSERCommand(InputLine, dwLineSize, OutputBuff, &dwBytes, &ResponseCode, szECode))
  5977. {
  5978. lstrcpy((char *)OutputBuff, PasswordParam);
  5979. m_fDidUserCmd = TRUE;
  5980. fDoBase64 = TRUE;
  5981. }
  5982. else
  5983. {
  5984. fAuthed = FALSE;
  5985. }
  5986. }
  5987. if(fDoBase64)
  5988. {
  5989. if(!uuencode ((unsigned char *)OutputBuff, lstrlen((char *) OutputBuff), &BuffData, FALSE))
  5990. {
  5991. TraceFunctLeaveEx((LPARAM)this);
  5992. return FALSE;
  5993. }
  5994. else
  5995. {
  5996. FormatSmtpMessage (ResponseCode,NULL," %s\r\n", BuffData.QueryPtr());
  5997. }
  5998. }
  5999. else
  6000. {
  6001. FormatSmtpMessage (ResponseCode,szECode," %s\r\n", OutputBuff);
  6002. }
  6003. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, ResponseCode, 0, 0);
  6004. }
  6005. else
  6006. {
  6007. fAuthed = ProcessAuthInfo( AuthCommandTransact, (LPSTR)InputLine, OutputBuff, &dwBytes );
  6008. if(fAuthed)
  6009. {
  6010. if(m_securityCtx.IsAuthenticated())
  6011. {
  6012. m_State = HELO;
  6013. m_fAuthenticated = TRUE;
  6014. fAuthed = TRUE;
  6015. PE_CdFormatSmtpMessage (SMTP_RESP_AUTHENTICATED, ESEC_SUCCESS," %s\r\n", "Authentication successful");
  6016. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_AUTHENTICATED, 0, 0);
  6017. }
  6018. else
  6019. {
  6020. FormatSmtpMessage (SMTP_RESP_AUTH1_PASSED,NULL," ");
  6021. FormatSmtpMessage(OutputBuff, dwBytes);
  6022. }
  6023. }
  6024. else
  6025. {
  6026. FormatSmtpMessage (SMTP_RESP_AUTH_REJECT, ENO_SECURITY, " %s\r\n", "Authentication unsuccessful");
  6027. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_AUTH_REJECT, 0, 0);
  6028. }
  6029. }
  6030. if(!fAuthed)
  6031. {
  6032. m_State = HELO;
  6033. m_RecvdAuthCmd = FALSE;
  6034. m_fDidUserCmd = FALSE;
  6035. }
  6036. SendSmtpResponse();
  6037. TraceFunctLeaveEx((LPARAM)this);
  6038. return fAuthed;
  6039. }
  6040. /*++
  6041. Name :
  6042. SMTP_CONNECTION::DoAUTHCommand
  6043. Description:
  6044. This function performs authentication
  6045. for the SMTP service
  6046. Arguments:
  6047. Are ignored
  6048. Returns:
  6049. TRUE if the connection should stay open.
  6050. FALSE if this object should be deleted.
  6051. --*/
  6052. BOOL SMTP_CONNECTION::DoAUTHCommand(const char * InputLine, DWORD ParameterSize)
  6053. {
  6054. BOOL fAuthPassed = TRUE;
  6055. char * pMechanism = NULL;
  6056. char * pInitialResponse = NULL;
  6057. //The size of the output buffer should vary with the package being used.
  6058. //Currently we use a quickfix which should work for LOGIN and NTLM but will
  6059. //fail for other (new) packages if the response string > 25*255+1. Replace
  6060. //this by a CBuffer later. Size of the CBuffer should be the max token size
  6061. //for the package from CACHED_CREDENTIAL::GetCachedCredential.
  6062. //GPulla, 6/15/1999
  6063. unsigned char OutputBuffer[MAX_REPLY_SIZE];
  6064. char szECode[16];
  6065. BUFFER BuffData;
  6066. DWORD DecodedLen = 0;
  6067. DWORD dwBytes = 0;
  6068. DWORD ResponseCode;
  6069. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoAUTHCommand");
  6070. //Do we allow AUTH command
  6071. if(!m_pInstance->AllowAuth())
  6072. {
  6073. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_CMD, ENOT_IMPLEMENTED," %s\r\n", SMTP_BAD_CMD_STR);
  6074. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_BAD_CMD, 0, 0);
  6075. ++m_ProtocolErrors;
  6076. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6077. TraceFunctLeaveEx((LPARAM) this);
  6078. return TRUE;
  6079. }
  6080. //check if helo was sent
  6081. if(!m_pInstance->AllowMailFromNoHello() && !m_HelloSent)
  6082. {
  6083. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n", "Send hello first" );
  6084. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  6085. ++m_ProtocolErrors;
  6086. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6087. TraceFunctLeaveEx((LPARAM) this);
  6088. return TRUE;
  6089. }
  6090. OutputBuffer[0] = '\0';
  6091. //Check if the client is secure enough
  6092. if (!IsClientSecureEnough())
  6093. {
  6094. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_MUST_SECURE, 0, 0);
  6095. ErrorTrace((LPARAM) this, "DoAuthCommand - SMTP_RESP_MUST_SECURE, Must do STARTTLS first");
  6096. TraceFunctLeaveEx((LPARAM) this);
  6097. return TRUE;
  6098. }
  6099. //disallow mail from more than once
  6100. if(m_RecvdAuthCmd)
  6101. {
  6102. ++m_ProtocolErrors;
  6103. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", "Auth already specified" );
  6104. ProtocolLog(AUTH, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  6105. ErrorTrace((LPARAM) this, "DoAuthCommand - SMTP_RESP_BAD_SEQ, Sender already specified");
  6106. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6107. TraceFunctLeaveEx((LPARAM) this);
  6108. return TRUE;
  6109. }
  6110. //
  6111. // Check syntax: one required parameter, and one optional parameter
  6112. //
  6113. pMechanism = strtok((char *) InputLine, WHITESPACE);
  6114. pInitialResponse = strtok(NULL, WHITESPACE);
  6115. if(pMechanism == NULL)
  6116. {
  6117. ++m_ProtocolErrors;
  6118. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS, " %s\r\n","No mechanism" );
  6119. ProtocolLog(AUTH, (char *) InputLine, NO_ERROR, SMTP_RESP_BAD_SEQ, 0, 0);
  6120. ErrorTrace((LPARAM) this, "DoAuthCommand - SMTP_RESP_BAD_SEQ, Sender already specified");
  6121. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6122. TraceFunctLeaveEx((LPARAM) this);
  6123. return TRUE;
  6124. }
  6125. strncpy(m_szUsedAuthKeyword, pMechanism, sizeof(m_szUsedAuthKeyword)-1);
  6126. if(!lstrcmpi(pMechanism, "login"))
  6127. {
  6128. if(m_pInstance->QueryAuthentication() & INET_INFO_AUTH_CLEARTEXT)
  6129. {
  6130. m_fClearText = TRUE;
  6131. if(pInitialResponse)
  6132. {
  6133. if(DoUSERCommand((const char *) pInitialResponse, lstrlen(pInitialResponse), OutputBuffer, &dwBytes, &ResponseCode, szECode))
  6134. {
  6135. m_fDidUserCmd = TRUE;
  6136. }
  6137. else
  6138. {
  6139. PE_CdFormatSmtpMessage (ResponseCode, szECode," %s\r\n",OutputBuffer );
  6140. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_PARM_NOT_IMP, 0, 0);
  6141. fAuthPassed = FALSE;
  6142. }
  6143. }
  6144. }
  6145. else
  6146. {
  6147. PE_CdFormatSmtpMessage (SMTP_RESP_PARM_NOT_IMP, ENO_SEC_PACKAGE, " %s \r\n","Unrecognized authentication type" );
  6148. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_PARM_NOT_IMP, 0, 0);
  6149. fAuthPassed = FALSE;
  6150. }
  6151. }
  6152. else
  6153. {
  6154. /*
  6155. //
  6156. // Switch over to using a large receive buffer, because a AUTH blob
  6157. // may be up to 32K big.
  6158. BOOL fStartSASL = SwitchToBigSSLBuffers();
  6159. if (fStartSASL) {
  6160. //PE_CdFormatSmtpMessage(SMTP_RESP_READY, EPROT_SUCCESS," %s\r\n", SMTP_READY_STR);
  6161. ProtocolLog(AUTH, (char *) InputLine, NO_ERROR, SMTP_RESP_READY, 0, 0);
  6162. } else {
  6163. PE_CdFormatSmtpMessage(SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n", SMTP_NO_MEMORY);
  6164. ProtocolLog(AUTH, (char *) InputLine, NO_ERROR, SMTP_RESP_NORESOURCES, 0, 0);
  6165. }
  6166. */
  6167. //
  6168. // Register our principal names, if necessary
  6169. //
  6170. QuerySmtpInstance()->RegisterServicePrincipalNames(TRUE);
  6171. if(ProcessAuthInfo( AuthCommandTransact, pMechanism, OutputBuffer, &dwBytes ) == FALSE )
  6172. {
  6173. fAuthPassed = FALSE;
  6174. PE_CdFormatSmtpMessage (SMTP_RESP_PARM_NOT_IMP, ENO_SEC_PACKAGE, " %s \r\n", "Unrecognized authentication type" );
  6175. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_PARM_NOT_IMP, 0, 0);
  6176. }
  6177. else if(pInitialResponse)
  6178. {
  6179. if(ProcessAuthInfo( AuthCommandTransact, pInitialResponse, OutputBuffer, &dwBytes ) == FALSE )
  6180. {
  6181. PE_CdFormatSmtpMessage (SMTP_RESP_AUTH_REJECT, EINVALID_ARGS, " %s\r\n","Cannot authenticate parameter" );
  6182. ProtocolLog(AUTH, (char *) QueryClientUserName(), NO_ERROR, SMTP_RESP_AUTH_REJECT, 0, 0);
  6183. fAuthPassed = FALSE;
  6184. }
  6185. }
  6186. }
  6187. if(fAuthPassed)
  6188. {
  6189. m_RecvdAuthCmd = TRUE;
  6190. m_State = AUTH;
  6191. if(!pInitialResponse)
  6192. {
  6193. if(!m_fClearText)
  6194. {
  6195. PE_CdFormatSmtpMessage (SMTP_RESP_AUTH1_PASSED,NULL," %s supported\r\n", pMechanism);
  6196. }
  6197. else
  6198. {
  6199. if(!uuencode ((unsigned char *)UserParam, lstrlen(UserParam), &BuffData, FALSE))
  6200. {
  6201. TraceFunctLeaveEx((LPARAM)this);
  6202. return TRUE;
  6203. }
  6204. else
  6205. {
  6206. PE_CdFormatSmtpMessage (SMTP_RESP_AUTH1_PASSED,NULL," %s\r\n", BuffData.QueryPtr());
  6207. }
  6208. }
  6209. }
  6210. else
  6211. {
  6212. if(!m_fClearText)
  6213. {
  6214. PE_CdFormatSmtpMessage (SMTP_RESP_AUTH1_PASSED,NULL," ");
  6215. OutputBuffer[dwBytes] = '\0';
  6216. PE_FormatSmtpMessage("%s\r\n",OutputBuffer);
  6217. }
  6218. else
  6219. {
  6220. if(!uuencode ((unsigned char *)PasswordParam, lstrlen(PasswordParam), &BuffData, FALSE))
  6221. {
  6222. TraceFunctLeaveEx((LPARAM)this);
  6223. return TRUE;
  6224. }
  6225. PE_CdFormatSmtpMessage (SMTP_RESP_AUTH1_PASSED,NULL," %s\r\n", BuffData.QueryPtr());
  6226. }
  6227. }
  6228. }
  6229. else
  6230. m_ProtocolErrors++; //even though we kick out the connection
  6231. //after MaxLogonFailures, count this as a
  6232. //protocol error
  6233. TraceFunctLeaveEx((LPARAM) this);
  6234. return(TRUE);
  6235. }
  6236. BOOL SMTP_CONNECTION::DoTURNCommand(const char * InputLine, DWORD parameterSize)
  6237. {
  6238. sockaddr_in AddrRemote;
  6239. SMTP_CONNOUT * SmtpConn = NULL;
  6240. DWORD Error = 0;
  6241. DWORD Options = 0;
  6242. char * pszUserName = "";
  6243. MULTISZ* pmsz = NULL;
  6244. TURN_DOMAIN_LIST TurnDomainList;
  6245. LPSTR pszAuthenticatedUserName = NULL;
  6246. const char * StartPtr = NULL;
  6247. BOOL Found = FALSE;
  6248. ISMTPConnection *pISMTPConnection = NULL;
  6249. char Domain[MAX_INTERNET_NAME];
  6250. Domain[0] = '\0';
  6251. DWORD DomainOptions = 0;
  6252. HRESULT hr = S_OK;
  6253. DomainInfo DomainParams;
  6254. ZeroMemory (&DomainParams, sizeof(DomainParams));
  6255. DomainParams.cbVersion = sizeof(DomainParams);
  6256. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoTurnCommand");
  6257. //check if helo was sent
  6258. if(!m_pInstance->AllowMailFromNoHello() && !m_HelloSent)
  6259. {
  6260. ErrorTrace((LPARAM) this, "In TURN - Hello not sent");
  6261. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR, " %s\r\n","Send hello first" );
  6262. ++m_ProtocolErrors;
  6263. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6264. TraceFunctLeaveEx((LPARAM) this);
  6265. return TRUE;
  6266. }
  6267. //Check if the client is secure enough
  6268. if (!IsClientSecureEnough())
  6269. {
  6270. ErrorTrace((LPARAM) this, "DoTURNCommand - SMTP_RESP_MUST_SECURE, Must do STARTTLS first");
  6271. TraceFunctLeaveEx((LPARAM) this);
  6272. return TRUE;
  6273. }
  6274. if(!m_fAuthenticated)
  6275. {
  6276. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY, " %s\r\n", "Client was not authenticated");
  6277. ErrorTrace((LPARAM) this, "DoTURNCommand - SMTP_RESP_MUST_SECURE, user not authenticated");
  6278. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6279. ++m_ProtocolErrors;
  6280. TraceFunctLeaveEx((LPARAM) this);
  6281. return TRUE;
  6282. }
  6283. if(m_securityCtx.QueryUserName())
  6284. ErrorTrace((LPARAM) this, "Looking up user %s in TURN table", m_securityCtx.QueryUserName());
  6285. pmsz = new MULTISZ();
  6286. if(pmsz == NULL)
  6287. {
  6288. Error = GetLastError();
  6289. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_MEMORY );
  6290. FatalTrace((LPARAM) this, "SMTP_CONNOUT::CreateSmtpConnection failed for TURN");
  6291. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  6292. TraceFunctLeaveEx((LPARAM) this);
  6293. return TRUE;
  6294. }
  6295. pmsz->Reset();
  6296. // Do we have a username from an AUTH sink? If not use anything from SMTP auth
  6297. pszAuthenticatedUserName =
  6298. m_szAuthenticatedUserNameFromSink[0] ?
  6299. m_szAuthenticatedUserNameFromSink : m_securityCtx.QueryUserName();
  6300. QuerySmtpInstance()->IsUserInTurnTable(pszAuthenticatedUserName, pmsz);
  6301. //QuerySmtpInstance()->IsUserInTurnTable("joe", &msz);
  6302. if(pmsz->IsEmpty())
  6303. {
  6304. delete pmsz;
  6305. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY," %s\r\n","This authenticated user is not allowed to issue TURN");
  6306. ErrorTrace((LPARAM) this, "DoTURNCommand - SMTP_RESP_MUST_SECURE, user not in turn table");
  6307. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6308. ++m_ProtocolErrors;
  6309. TraceFunctLeaveEx((LPARAM) this);
  6310. return TRUE;
  6311. }
  6312. //Determine the primary domain in the list of domains that are to be turned
  6313. //The primary domain is one that matches the domain name specified in the EHLO command
  6314. //NimishK : What if EHLO is not provided
  6315. //
  6316. for (StartPtr = pmsz->First();
  6317. ( (StartPtr != NULL) && !QuerySmtpInstance()->IsShuttingDown());
  6318. StartPtr = pmsz->Next( StartPtr ))
  6319. {
  6320. ErrorTrace((LPARAM) this, "looking for %s in TURN domains", QueryClientUserName());
  6321. if(!lstrcmpi(QueryClientUserName(), StartPtr))
  6322. {
  6323. Found = TRUE;
  6324. break;
  6325. }
  6326. }
  6327. //If we can determine the primary domain - we get the ISMTPCONNECTION for that domain
  6328. //else we get the first domain in the list and get the ISMTPCONNECTION for it
  6329. BOOL fPrimaryDomain = FALSE;
  6330. if(!Found)
  6331. StartPtr = pmsz->First();
  6332. else
  6333. fPrimaryDomain = TRUE;
  6334. //We need to keep walking the list of domains that are to be turned
  6335. //till we get a valid ISMTPCONN or run out of domains
  6336. while(StartPtr && !QuerySmtpInstance()->IsShuttingDown())
  6337. {
  6338. hr = QuerySmtpInstance()->GetConnManPtr()->GetNamedConnection(lstrlen(StartPtr), (CHAR*)StartPtr, &pISMTPConnection);
  6339. if(FAILED(hr))
  6340. {
  6341. //Something bad happened on this call
  6342. //report to client
  6343. delete pmsz;
  6344. PE_CdFormatSmtpMessage (SMTP_ERROR_PROCESSING_CODE, ENO_RESOURCES," %s\r\n", "Error processing the command");
  6345. ErrorTrace((LPARAM) this, "DoTURNCommand - SMTP_ERROR_PROCESSING_CODE, GetNamedConnection failed %d",hr);
  6346. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6347. ++m_ProtocolErrors;
  6348. TraceFunctLeaveEx((LPARAM) this);
  6349. return TRUE;
  6350. }
  6351. //If the link corresponding to this domain does not exist in AQ, we get a NULL
  6352. //ISMTPConnection at this point
  6353. //If we do there is nothing to TURN
  6354. if(pISMTPConnection)
  6355. break;
  6356. else
  6357. {
  6358. if(fPrimaryDomain)
  6359. {
  6360. StartPtr = pmsz->First();
  6361. fPrimaryDomain = FALSE;
  6362. }
  6363. else
  6364. {
  6365. StartPtr = pmsz->Next( StartPtr );
  6366. }
  6367. continue;
  6368. }
  6369. }
  6370. if(pISMTPConnection == NULL && StartPtr == NULL)
  6371. {
  6372. //We ran out of the domains and there is not a single link
  6373. PE_CdFormatSmtpMessage (250, EPROT_SUCCESS, " %s\r\n", "Server was turned");
  6374. PE_SendSmtpResponse();
  6375. PE_FormatSmtpMessage ("%s\r\n","QUIT");
  6376. PE_SendSmtpResponse();
  6377. //we will disconnect indirectly by returning false
  6378. //DisconnectClient();
  6379. return FALSE;
  6380. }
  6381. else
  6382. {
  6383. //leave quickly if we are shutting down
  6384. if(QuerySmtpInstance()->IsShuttingDown()
  6385. || (QuerySmtpInstance()->QueryServerState( ) == MD_SERVER_STATE_STOPPED)
  6386. || (QuerySmtpInstance()->QueryServerState( ) == MD_SERVER_STATE_INVALID))
  6387. {
  6388. if(pISMTPConnection)
  6389. {
  6390. //Ack the last connection
  6391. pISMTPConnection->AckConnection((eConnectionStatus)CONNECTION_STATUS_OK);
  6392. pISMTPConnection->Release();
  6393. pISMTPConnection = NULL;
  6394. }
  6395. delete pmsz;
  6396. TraceFunctLeaveEx((LPARAM)this);
  6397. return FALSE;
  6398. }
  6399. DomainParams.szDomainName = Domain;
  6400. DomainParams.cbDomainNameLength = sizeof(Domain);
  6401. hr = pISMTPConnection->GetDomainInfo(&DomainParams);
  6402. TurnDomainList.pmsz = pmsz;
  6403. //If we just got the ISMTPCOnn for the primary domain then we probably
  6404. //jumped over a few domains in the domain list.
  6405. //Set the ptr to start of the domain list
  6406. //That way we will look at all the domains, once we are done with the
  6407. //current primary domain
  6408. if(fPrimaryDomain)
  6409. {
  6410. TurnDomainList.szCurrentDomain = pmsz->First();
  6411. fPrimaryDomain = FALSE;
  6412. }
  6413. else
  6414. {
  6415. //Looks like we failed to get primary domain
  6416. //so got to have started from the top - continue that
  6417. TurnDomainList.szCurrentDomain = StartPtr;
  6418. }
  6419. //If we could not determine a primary domain - use the base
  6420. //connection options
  6421. if(!Found)
  6422. DomainOptions = 0;
  6423. else
  6424. DomainOptions = DomainParams.dwDomainInfoFlags;
  6425. //set the remote IP address we connected to
  6426. AddrRemote.sin_addr.s_addr = 0;
  6427. //
  6428. // Create an outbound connection
  6429. // Note: The last parameter, m_pSSLVerificationName is NULL. This is the
  6430. // name used to match against the SSL certificate that the server gives
  6431. // us during an outbound session. Setting it to NULL skips certificate
  6432. // subject validation, which is fine in the case of TURN, since either
  6433. // the server is already authenticated through other means (if ATRN) or
  6434. // we don't care about authentication (if TURN is configured).
  6435. //
  6436. SmtpConn = SMTP_CONNOUT::CreateSmtpConnection(
  6437. QuerySmtpInstance(),
  6438. (SOCKET) m_pAtqContext->hAsyncIO,
  6439. (SOCKADDR_IN *)&AddrRemote,
  6440. (SOCKADDR_IN *)&AddrRemote,
  6441. NULL,
  6442. (PVOID)&TurnDomainList,
  6443. 0,
  6444. DomainOptions,
  6445. NULL);
  6446. if(SmtpConn == NULL)
  6447. {
  6448. delete pmsz;
  6449. Error = GetLastError();
  6450. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_MEMORY );
  6451. FatalTrace((LPARAM) this, "SMTP_CONNOUT::CreateSmtpConnection failed for TURN");
  6452. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  6453. TraceFunctLeaveEx((LPARAM) this);
  6454. return TRUE;
  6455. }
  6456. //from here on, the smtpout class is responsible for
  6457. //cleaning up the AtqContext
  6458. m_DoCleanup = FALSE;
  6459. SmtpConn->SetAtqContext(m_pAtqContext);
  6460. //copy the real domain we are connected to.
  6461. SmtpConn->SetConnectedDomain((char *) Domain);
  6462. AtqContextSetInfo(m_pAtqContext, ATQ_INFO_COMPLETION, (DWORD_PTR) InternetCompletion);
  6463. AtqContextSetInfo(m_pAtqContext, ATQ_INFO_COMPLETION_CONTEXT, (DWORD_PTR) SmtpConn);
  6464. AtqContextSetInfo(m_pAtqContext, ATQ_INFO_TIMEOUT, m_pInstance->GetRemoteTimeOut());
  6465. //insert the outbound connection object into
  6466. //our list of outbound conection objects
  6467. if(!QuerySmtpInstance()->InsertNewOutboundConnection(SmtpConn, TRUE))
  6468. {
  6469. Error = GetLastError();
  6470. FatalTrace((LPARAM) this, "m_pInstance->InsertNewOutboundConnection failed for TURN");
  6471. SmtpConn->DisconnectClient();
  6472. delete SmtpConn;
  6473. SmtpConn = NULL;
  6474. SetLastError(Error);
  6475. TraceFunctLeaveEx((LPARAM) this);
  6476. return FALSE;
  6477. }
  6478. SmtpConn->SetCurrentObject(pISMTPConnection);
  6479. PE_CdFormatSmtpMessage (250, EPROT_SUCCESS, " %s\r\n", "Server was turned");
  6480. PE_SendSmtpResponse();
  6481. }
  6482. //start session will pend a read to pick
  6483. //up the servers signon banner
  6484. if(!SmtpConn->StartSession())
  6485. {
  6486. //get the error
  6487. Error = GetLastError();
  6488. FatalTrace((LPARAM) this, "SmtpConn->StartSession failed for TURN");
  6489. SmtpConn->DisconnectClient();
  6490. QuerySmtpInstance()->RemoveOutboundConnection(SmtpConn);
  6491. delete SmtpConn;
  6492. SmtpConn = NULL;
  6493. SetLastError (Error);
  6494. TraceFunctLeaveEx((LPARAM) this);
  6495. return FALSE;
  6496. }
  6497. m_State = TURN;
  6498. TraceFunctLeaveEx((LPARAM) this);
  6499. return(FALSE);
  6500. }
  6501. /*++
  6502. Name :
  6503. SMTP_CONNECTION::DoLASTCommand
  6504. Description:
  6505. This is our catch all error function.
  6506. It will determin what kind of error
  6507. made it execute and send an appropriate
  6508. message back to the client.
  6509. Arguments:
  6510. Are ignored
  6511. Returns:
  6512. TRUE if the connection should stay open.
  6513. FALSE if this object should be deleted.
  6514. --*/
  6515. BOOL SMTP_CONNECTION::DoLASTCommand(const char * InputLine, DWORD parameterSize)
  6516. {
  6517. BOOL RetStatus = TRUE;
  6518. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoLASTCommand");
  6519. _ASSERT (m_pInstance != NULL);
  6520. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_CMD, ENOT_IMPLEMENTED," %s\r\n", SMTP_BAD_CMD_STR);
  6521. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6522. m_ProtocolErrors++;
  6523. PE_SendSmtpResponse();
  6524. TraceFunctLeaveEx((LPARAM) this);
  6525. return RetStatus;
  6526. }
  6527. /*++
  6528. Name :
  6529. SMTP_CONNECTION::VerifiyClient
  6530. Description:
  6531. This function compares the IP address the connection
  6532. was made on to the IP address of the name given with
  6533. the HELO or EHLO commands
  6534. Arguments:
  6535. ClientHostName - from HELO or EHLO command
  6536. KnownIpAddress - from accept
  6537. Returns:
  6538. TRUE if the connection should stay open.
  6539. FALSE if this object should be deleted.
  6540. --*/
  6541. DWORD SMTP_CONNECTION::VerifiyClient (const char * ClientHostName, const char * KnownIpAddress)
  6542. {
  6543. in_addr UNALIGNED * P_Addr = NULL;
  6544. PHOSTENT Hp = NULL;
  6545. DWORD KnownClientAddress = 0;
  6546. DWORD dwRet = SUCCESS;
  6547. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::VerifiyClient");
  6548. Hp = gethostbyname (ClientHostName);
  6549. if (Hp == NULL)
  6550. {
  6551. DWORD dwErr = WSAGetLastError();
  6552. if(dwErr == WSANO_DATA)
  6553. dwRet = NO_MATCH;
  6554. else
  6555. dwRet = LOOKUP_FAILED;
  6556. TraceFunctLeaveEx((LPARAM) this);
  6557. return dwRet;
  6558. }
  6559. KnownClientAddress = inet_addr (KnownIpAddress);
  6560. if (KnownClientAddress == INADDR_NONE)
  6561. {
  6562. dwRet = LOOKUP_FAILED;
  6563. TraceFunctLeaveEx((LPARAM) this);
  6564. return dwRet;
  6565. }
  6566. while( (P_Addr = (in_addr UNALIGNED *)*Hp->h_addr_list++) != NULL)
  6567. {
  6568. if (P_Addr->s_addr == KnownClientAddress)
  6569. {
  6570. TraceFunctLeaveEx((LPARAM) this);
  6571. return dwRet;
  6572. }
  6573. }
  6574. dwRet = NO_MATCH;
  6575. TraceFunctLeaveEx((LPARAM) this);
  6576. return dwRet;
  6577. }
  6578. /*++
  6579. Name :
  6580. void SMTP_CONNECTION::HandleAddressError(char * InputLine)
  6581. Description:
  6582. common code to determine what error occurred
  6583. as a result of address validation/allocation failure failure
  6584. Arguments:
  6585. none
  6586. Returns:
  6587. none
  6588. --*/
  6589. void SMTP_CONNECTION::HandleAddressError(char * InputLine)
  6590. {
  6591. DWORD Error = GetLastError();
  6592. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::HandleAddressError");
  6593. if(Error == ERROR_NOT_ENOUGH_MEMORY)
  6594. {
  6595. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToNoCAddrObjects);
  6596. PE_CdFormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_MEMORY );
  6597. m_CInboundContext.SetWin32Status(Error);
  6598. FatalTrace((LPARAM) this, "HandleAddressError - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY");
  6599. }
  6600. else if (Error == ERROR_INVALID_DATA)
  6601. {
  6602. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS, EINVALID_ARGS," %s\r\n",SMTP_INVALID_ADDR_MSG);
  6603. FatalTrace((LPARAM) this, "HandleAddressError - SMTP_RESP_BAD_ARGS, SMTP_INVALID_ADDR_MSG");
  6604. m_ProtocolErrors++;
  6605. }
  6606. else
  6607. {
  6608. PE_CdFormatSmtpMessage (SMTP_RESP_MBX_SYNTAX, ESYNTAX_ERROR," %s\r\n","Unknown Error");
  6609. m_CInboundContext.SetWin32Status(Error);
  6610. FatalTrace((LPARAM) this, "HandleAddressError - SMTP_RESP_MBX_SYNTAX, Unknown Error");
  6611. m_ProtocolErrors++;
  6612. }
  6613. TraceFunctLeaveEx((LPARAM) this);
  6614. }
  6615. /*++
  6616. Name :
  6617. void SMTP_CONNECTION::SkipWord
  6618. Description:
  6619. skips over words in a buffer and
  6620. returns pointer to next word
  6621. Arguments:
  6622. none
  6623. Returns:
  6624. none
  6625. --*/
  6626. char * SMTP_CONNECTION::SkipWord (char * Buffer, char * WordToSkip, DWORD WordLen)
  6627. {
  6628. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::SkipWord");
  6629. //find string
  6630. if (strncasecmp(Buffer, WordToSkip, WordLen))
  6631. {
  6632. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS , EINVALID_ARGS," %s %s\r\n", "Unrecognized parameter", Buffer);
  6633. ProtocolLog(USE_CURRENT, Buffer, NO_ERROR, SMTP_RESP_BAD_ARGS, 0, 0);
  6634. return NULL;
  6635. }
  6636. //skip past word
  6637. Buffer += WordLen;
  6638. //skip white spaces looking for the ":" character
  6639. while( (*Buffer != '\0') && (isspace (*Buffer) || (*Buffer == '\t')))
  6640. Buffer++;
  6641. if( (*Buffer == '\0') || (*Buffer != ':'))
  6642. {
  6643. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_ARGS , EINVALID_ARGS," %s %s\r\n", "Unrecognized parameter", Buffer);
  6644. ProtocolLog(USE_CURRENT, Buffer, NO_ERROR, SMTP_RESP_BAD_ARGS, 0, 0);
  6645. return NULL;
  6646. }
  6647. //add one to skip the ":"
  6648. Buffer++;
  6649. TraceFunctLeaveEx((LPARAM) this);
  6650. return Buffer;
  6651. }
  6652. VOID SMTP_CONNECTION::ProtocolLog(DWORD dwCommand, LPCSTR pszParameters, DWORD dwWin32Error,
  6653. DWORD dwSmtpError, DWORD BytesSent, DWORD BytesRecv, LPSTR pszTarget)
  6654. {
  6655. LPCSTR pszCmd = "";
  6656. char szKeyword[100];
  6657. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::ProtocolLog");
  6658. _ASSERT (m_pInstance != NULL);
  6659. //
  6660. // verify if we should use the current command
  6661. //
  6662. if ( dwCommand == USE_CURRENT )
  6663. {
  6664. dwCommand = m_dwCurrentCommand;
  6665. }
  6666. if(dwCommand == SMTP_TIMEOUT)
  6667. {
  6668. strcpy(szKeyword, "TIMEOUT");
  6669. pszCmd = szKeyword;
  6670. }
  6671. else
  6672. {
  6673. pszCmd = (LPSTR)SmtpCommands[ dwCommand ];
  6674. if ( pszCmd == NULL )
  6675. {
  6676. // if this is a PE word then its the first word in the
  6677. // parameters
  6678. char *pszSpace = strchr(pszParameters, ' ');
  6679. if (pszSpace) {
  6680. DWORD_PTR cToCopy = (DWORD_PTR) pszSpace - (DWORD_PTR) pszParameters;
  6681. if (cToCopy >= sizeof(szKeyword)) cToCopy = sizeof(szKeyword) - 1;
  6682. strncpy(szKeyword, pszParameters, cToCopy);
  6683. szKeyword[cToCopy] = 0;
  6684. pszParameters += cToCopy;
  6685. pszCmd = szKeyword;
  6686. } else {
  6687. pszCmd = pszParameters;
  6688. pszParameters = NULL;
  6689. }
  6690. }
  6691. }
  6692. //
  6693. // filter so we only get the desired commands
  6694. //
  6695. DebugTrace( (LPARAM)this,
  6696. "Allow cmds: 0x%08X, cmd: 0x%08X",
  6697. m_pInstance->GetCmdLogFlags(),
  6698. (1<<dwCommand) );
  6699. if ( m_pInstance->GetCmdLogFlags() & (1<<dwCommand) )
  6700. {
  6701. TransactionLog(
  6702. pszCmd,
  6703. pszParameters,
  6704. pszTarget,
  6705. dwWin32Error,
  6706. dwSmtpError);
  6707. }
  6708. TraceFunctLeaveEx((LPARAM) this);
  6709. }
  6710. BOOL
  6711. SMTP_CONNECTION::BindInstanceAccessCheck(
  6712. )
  6713. /*++
  6714. Routine Description:
  6715. Bind IP/DNS access check for this request to instance data
  6716. Arguments:
  6717. None
  6718. Returns:
  6719. BOOL - TRUE if success, otherwise FALSE.
  6720. --*/
  6721. {
  6722. m_pInstance->LockGenCrit();
  6723. if ( m_rfAccessCheck.CopyFrom( m_pInstance->QueryMetaDataRefHandler() ) )
  6724. {
  6725. m_acAccessCheck.BindCheckList( (LPBYTE)m_rfAccessCheck.GetPtr(), m_rfAccessCheck.GetSize() );
  6726. m_pInstance->UnLockGenCrit();
  6727. return TRUE;
  6728. }
  6729. m_pInstance->UnLockGenCrit();
  6730. return FALSE;
  6731. }
  6732. VOID
  6733. SMTP_CONNECTION::UnbindInstanceAccessCheck()
  6734. /*++
  6735. Routine Description:
  6736. Unbind IP/DNS access check for this request to instance data
  6737. Arguments:
  6738. None
  6739. Returns:
  6740. Nothing
  6741. --*/
  6742. {
  6743. m_acAccessCheck.UnbindCheckList();
  6744. m_rfAccessCheck.Reset( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  6745. }
  6746. /*++
  6747. Name :
  6748. void SMTP_CONNECTION::SwitchToBigReceiveBuffer
  6749. Description:
  6750. Helper routine to allocate a 32K buffer and use it for posting reads.
  6751. SSL fragments can be up to 32K large, and we need to accumulate an
  6752. entire fragment to be able to decrypt it.
  6753. Arguments:
  6754. none
  6755. Returns:
  6756. TRUE if the receive buffer was successfully allocated, FALSE otherwise
  6757. --*/
  6758. BOOL SMTP_CONNECTION::SwitchToBigSSLBuffers(void)
  6759. {
  6760. char *pTempBuffer;
  6761. pTempBuffer = new char [MAX_SSL_FRAGMENT_SIZE];
  6762. if (pTempBuffer != NULL) {
  6763. m_precvBuffer = pTempBuffer;
  6764. m_cbMaxRecvBuffer = MAX_SSL_FRAGMENT_SIZE;
  6765. pTempBuffer = new char [MAX_SSL_FRAGMENT_SIZE];
  6766. if (pTempBuffer != NULL) {
  6767. m_pOutputBuffer = pTempBuffer;
  6768. m_cbMaxOutputBuffer = MAX_SSL_FRAGMENT_SIZE;
  6769. return( TRUE );
  6770. }
  6771. }
  6772. return( FALSE );
  6773. }
  6774. /*++
  6775. Name :
  6776. void SMTP_CONNECTION::DecryptInputBuffer
  6777. Description:
  6778. Helper routine to decrypt the recieve buffer when session is SSL
  6779. encypted.
  6780. Arguments:
  6781. none
  6782. Returns:
  6783. TRUE if the receive buffer was successfully decrypted, FALSE otherwise
  6784. --*/
  6785. BOOL SMTP_CONNECTION::DecryptInputBuffer(void)
  6786. {
  6787. TraceFunctEnterEx( (LPARAM)this, "SMTP_CONNECTION::DecryptInputBuffer");
  6788. DWORD cbExpected;
  6789. DWORD cbReceived;
  6790. DWORD cbParsable;
  6791. DWORD dwError;
  6792. dwError = m_encryptCtx.DecryptInputBuffer(
  6793. (LPBYTE) QueryMRcvBuffer() + m_cbParsable,
  6794. m_cbReceived - m_cbParsable,
  6795. &cbReceived,
  6796. &cbParsable,
  6797. &cbExpected );
  6798. if ( dwError == NO_ERROR )
  6799. {
  6800. //
  6801. // new total received size is the residual from last processing
  6802. // and whatever is left in the current decrypted read buffer
  6803. //
  6804. m_cbReceived = m_cbParsable + cbReceived;
  6805. //
  6806. // new total parsable size is the residual from last processing
  6807. // and whatever was decrypted from this read io operation
  6808. //
  6809. m_cbParsable += cbParsable;
  6810. }
  6811. else
  6812. {
  6813. //
  6814. // errors from this routine indicate that the stream has been
  6815. // tampered with or we have an internal error
  6816. //
  6817. ErrorTrace( (LPARAM)this,
  6818. "DecryptInputBuffer failed: 0x%08X",
  6819. dwError );
  6820. DisconnectClient( dwError );
  6821. }
  6822. return( dwError == NO_ERROR );
  6823. }
  6824. /*++
  6825. Name :
  6826. void SMTP_CONNECTION::IsClientSecureEnough
  6827. Description:
  6828. Finds out whether the client has negotiated the appropriate security
  6829. level for the server instance for which this connection was
  6830. created.
  6831. Arguments:
  6832. none
  6833. Returns:
  6834. TRUE if the client has negotiated appropriate level of security
  6835. --*/
  6836. BOOL SMTP_CONNECTION::IsClientSecureEnough(void)
  6837. {
  6838. BOOL fRet = TRUE;
  6839. if (QuerySmtpInstance()->RequiresSSL() && !m_SecurePort) {
  6840. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, E_TLSNEEDED, " %s\r\n", SMTP_MSG_MUST_SECURE);
  6841. fRet = FALSE;
  6842. }
  6843. else if (m_SecurePort &&
  6844. QuerySmtpInstance()->Requires128Bits() &&
  6845. m_encryptCtx.QueryKeySize() < 128) {
  6846. PE_CdFormatSmtpMessage (SMTP_RESP_TRANS_FAILED, E_TLSNEEDED, " %s\r\n", SMTP_MSG_NOT_SECURE_ENOUGH);
  6847. fRet = FALSE;
  6848. }
  6849. if(!fRet)
  6850. {
  6851. BUMP_COUNTER(QuerySmtpInstance(), NumProtocolErrs);
  6852. ++m_ProtocolErrors;
  6853. }
  6854. return fRet;
  6855. }
  6856. /*++
  6857. Name :
  6858. BOOL SMTP_CONNECTION::OnEvent
  6859. Description:
  6860. Is invoked from GlueDispatch. Micro-manages sink firing scenarios.
  6861. Arguments:
  6862. char * InputLine null terminated chunk of inbound input buffer from beginning of cmd to CRLF
  6863. DWORD IntermediateSize length of the above
  6864. DWORD CmdSize length of command keyword
  6865. Returns:
  6866. TRUE if native handler did or by default
  6867. FALSE if native handler did
  6868. --*/
  6869. HRESULT SMTP_CONNECTION::OnEvent(
  6870. IUnknown * pIserver,
  6871. IUnknown * pISession,
  6872. IMailMsgProperties *pIMessage,
  6873. LPPE_COMMAND_NODE pCommandNode,
  6874. LPPE_BINDING_NODE pBindingNode,
  6875. char * szArgs
  6876. )
  6877. {
  6878. PMFI PointerToMemberFunction = SmtpDispatchTable[m_dwCurrentCommand];
  6879. HRESULT hr = S_OK;
  6880. BOOL fResult;
  6881. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::OnEvent");
  6882. _ASSERT(pCommandNode);
  6883. _ASSERT(pBindingNode);
  6884. m_CInboundContext.m_pCurrentBinding = pBindingNode;
  6885. // If this is not a native command
  6886. if (SmtpCommands[m_dwCurrentCommand] == NULL)
  6887. {
  6888. hr = m_pCInboundDispatcher->ChainSinks(
  6889. pIserver,
  6890. pISession,
  6891. pIMessage,
  6892. &m_CInboundContext,
  6893. PRIO_LOWEST,
  6894. pCommandNode,
  6895. &(m_CInboundContext.m_pCurrentBinding)
  6896. );
  6897. if (hr == MAILTRANSPORT_S_PENDING)
  6898. goto AsyncCompletion;
  6899. }
  6900. else
  6901. {
  6902. if (pBindingNode->dwPriority <= PRIO_DEFAULT)
  6903. {
  6904. if (pBindingNode->dwFlags & PEBN_DEFAULT)
  6905. {
  6906. // We are firing the default handler, skip the pre-loop
  6907. m_CInboundContext.m_pCurrentBinding = pBindingNode->pNext;
  6908. }
  6909. else
  6910. {
  6911. hr = m_pCInboundDispatcher->ChainSinks(
  6912. pIserver,
  6913. pISession,
  6914. pIMessage,
  6915. &m_CInboundContext,
  6916. PRIO_DEFAULT,
  6917. pCommandNode,
  6918. &(m_CInboundContext.m_pCurrentBinding)
  6919. );
  6920. if (hr == MAILTRANSPORT_S_PENDING)
  6921. goto AsyncCompletion;
  6922. }
  6923. if (FAILED(hr))
  6924. return hr;
  6925. if((hr != EXPE_S_CONSUMED) && (hr != S_FALSE)) {
  6926. fResult = (this->*PointerToMemberFunction)(szArgs,strlen(szArgs));
  6927. if (!fResult)
  6928. m_CInboundContext.SetCommandStatus(EXPE_DROP_SESSION);
  6929. else
  6930. m_CInboundContext.SetCommandStatus(EXPE_SUCCESS);
  6931. }
  6932. }
  6933. if ((m_CInboundContext.m_pCurrentBinding) &&
  6934. (hr != EXPE_S_CONSUMED) && (hr != S_FALSE))
  6935. {
  6936. hr = m_pCInboundDispatcher->ChainSinks(
  6937. pIserver,
  6938. pISession,
  6939. pIMessage,
  6940. &m_CInboundContext,
  6941. PRIO_LOWEST,
  6942. pCommandNode,
  6943. &(m_CInboundContext.m_pCurrentBinding)
  6944. );
  6945. if (hr == MAILTRANSPORT_S_PENDING)
  6946. goto AsyncCompletion;
  6947. }
  6948. }
  6949. TraceFunctLeaveEx((LPARAM)this);
  6950. return(hr);
  6951. AsyncCompletion:
  6952. DebugTrace((LPARAM)this, "Leaving because of S_PENDING");
  6953. TraceFunctLeaveEx((LPARAM)this);
  6954. return(hr);
  6955. }
  6956. HRESULT SMTP_CONNECTION::OnNotifyAsyncCompletion(
  6957. HRESULT hrResult
  6958. )
  6959. {
  6960. HRESULT hr = S_OK;
  6961. LPSTR pArgs = NULL;
  6962. CHAR chTerm = '\0';
  6963. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::OnNotifyAsyncCompletion");
  6964. // Make sure the sink did not call back with S_PENDING
  6965. if (hrResult == MAILTRANSPORT_S_PENDING)
  6966. hrResult = S_OK;
  6967. // See if we have to continue chaining
  6968. if (hrResult == S_OK)
  6969. {
  6970. LPPE_BINDING_NODE pNextBinding;
  6971. PE_BINDING_NODE bnDefault;
  6972. // If we have more bindings, or if we are before a native command, we have to
  6973. // go in again
  6974. _ASSERT(m_CInboundContext.m_pCurrentBinding);
  6975. pNextBinding = m_CInboundContext.m_pCurrentBinding->pNext;
  6976. // Now see if we have a native handler
  6977. if (SmtpDispatchTable[m_dwCurrentCommand] &&
  6978. (m_CInboundContext.m_pCurrentBinding->dwPriority <= PRIO_DEFAULT))
  6979. {
  6980. if (!pNextBinding || (pNextBinding->dwPriority > PRIO_DEFAULT))
  6981. {
  6982. bnDefault.dwPriority = PRIO_DEFAULT;
  6983. bnDefault.pNext = pNextBinding;
  6984. bnDefault.dwFlags = PEBN_DEFAULT;
  6985. pNextBinding = &bnDefault;
  6986. }
  6987. }
  6988. if (pNextBinding != NULL)
  6989. {
  6990. // Call the OnEvent to resume chaining ...
  6991. pArgs = strchr(m_CInboundContext.m_cabCommand.Buffer(), ' ');
  6992. if (!pArgs)
  6993. pArgs = &chTerm;
  6994. hrResult = OnEvent(
  6995. m_pInstance->GetInstancePropertyBag(),
  6996. GetSessionPropertyBag(),
  6997. m_pIMsg,
  6998. m_CInboundContext.m_pCurrentCommandContext,
  6999. pNextBinding,
  7000. pArgs
  7001. );
  7002. // We have scenarios:
  7003. // 1) S_OK, S_FALSE: We send the response and do the
  7004. // next command
  7005. // 2) ERROR: Drop the connection
  7006. // 3) S_PENDING: Just let the thread return
  7007. }
  7008. else
  7009. hrResult = S_OK;
  7010. }
  7011. if (hrResult != MAILTRANSPORT_S_PENDING)
  7012. {
  7013. // It's not another async operation
  7014. if (SUCCEEDED(hrResult))
  7015. {
  7016. // Send the response and process the next command
  7017. if (!ProcessAndSendResponse())
  7018. hrResult = E_FAIL;
  7019. // Disconnect client if needed
  7020. if (m_CInboundContext.m_dwCommandStatus & EXPE_DROP_SESSION)
  7021. {
  7022. DisconnectClient();
  7023. hrResult = E_FAIL;
  7024. }
  7025. // do things unique to handling the end of a message
  7026. if (m_fAsyncEOD) {
  7027. ReInitClassVariables();
  7028. }
  7029. }
  7030. if (SUCCEEDED(hrResult)) {
  7031. //
  7032. // ProcessClient usually posts an async ReadFile --
  7033. // This can be a problem since ntos actuall completes the
  7034. // read with the thread that issued it. If the sink
  7035. // writer has the thread exit before the read completes,
  7036. // we would get an error. To get around this, we call
  7037. // PostQueuedCompleteionStatus with the sink's thread and
  7038. // then post the next async read with an atq thread.
  7039. //
  7040. //ProcessClient(0, NO_ERROR,
  7041. //(LPOVERLAPPED)&m_CInboundContext);
  7042. m_fAsyncEventCompletion = TRUE;
  7043. if(
  7044. PostCompletionStatus(
  7045. 1 // Post 1 byte (a zero byte completion means the
  7046. // remote host disconnected
  7047. ) == FALSE) {
  7048. //
  7049. // We were unable to post completion status, fail
  7050. // below
  7051. //
  7052. hrResult = E_FAIL;
  7053. m_fAsyncEventCompletion = FALSE;
  7054. }
  7055. }
  7056. if(FAILED(hrResult))
  7057. ProcessClient(0, ERROR_OPERATION_ABORTED, NULL);
  7058. }
  7059. TraceFunctLeaveEx((LPARAM)this);
  7060. // We always keep the sink happy
  7061. return(S_OK);
  7062. }
  7063. BOOL SMTP_CONNECTION::ProcessAndSendResponse()
  7064. {
  7065. BOOL fResult = TRUE;
  7066. DWORD dwResponseSize = 0;
  7067. LPSTR pszResponse = NULL;
  7068. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::ProcessAndSendResponse");
  7069. // See if we have a custom response from sinks
  7070. dwResponseSize = m_CInboundContext.m_cabResponse.Length();
  7071. pszResponse = m_CInboundContext.m_cabResponse.Buffer();
  7072. if (!dwResponseSize || !*pszResponse)
  7073. {
  7074. // Try the native buffer
  7075. dwResponseSize = m_CInboundContext.m_cabNativeResponse.Length();
  7076. pszResponse = m_CInboundContext.m_cabNativeResponse.Buffer();
  7077. // If the response buffer is NULL, we won't send any response.
  7078. // For example BDAT exhibits this behavior
  7079. }
  7080. if (dwResponseSize && *pszResponse)
  7081. PE_FastFormatSmtpMessage(pszResponse, dwResponseSize - 1);
  7082. // SendResponse if needed
  7083. if (!(m_CInboundContext.m_dwCommandStatus & EXPE_PIPELINED))
  7084. fResult = SendSmtpResponse();
  7085. TraceFunctLeaveEx((LPARAM)this);
  7086. return(fResult);
  7087. }
  7088. /*++
  7089. Name :
  7090. BOOL SMTP_CONNECTION::GlueDispatch
  7091. Description:
  7092. Is invoked from ProcessInputBuffer as if it were native handler for SMTP command.
  7093. Finds out if the command is native/non-native, and,if native, extended/non-extended.
  7094. Then, fires the corresponding mix of native handler and inbound sinks.
  7095. Arguments:
  7096. char * InputLine null terminated chunk of inbound input buffer from beginning of cmd to CRLF
  7097. DWORD IntermediateSize length of the above
  7098. DWORD CmdSize length of command keyword
  7099. Returns:
  7100. TRUE if native handler did or by default
  7101. FALSE if native handler did
  7102. --*/
  7103. BOOL SMTP_CONNECTION::GlueDispatch(char * InputLine, DWORD IntermediateSize, DWORD CmdSize, BOOL * pfAsyncOp)
  7104. {
  7105. HRESULT hr;
  7106. PMFI PointerToMemberFunction = SmtpDispatchTable[m_dwCurrentCommand];
  7107. BOOL fSinksInstalled = FALSE;
  7108. BOOL fResult = TRUE;
  7109. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::GlueDispatch");
  7110. _ASSERT(InputLine);
  7111. _ASSERT(m_pInstance);
  7112. _ASSERT(pfAsyncOp);
  7113. if (!InputLine || !m_pInstance || !pfAsyncOp)
  7114. return(FALSE);
  7115. // Default is no async operation
  7116. *pfAsyncOp = FALSE;
  7117. if(m_pCInboundDispatcher == NULL) {
  7118. m_pIEventRouter = m_pInstance->GetRouter();
  7119. _ASSERT(m_pIEventRouter);
  7120. if (!m_pIEventRouter)
  7121. return(FALSE);
  7122. hr = m_pIEventRouter->GetDispatcherByClassFactory(
  7123. CLSID_CInboundDispatcher,
  7124. &g_cfInbound,
  7125. CATID_SMTP_ON_INBOUND_COMMAND,
  7126. IID_ISmtpInboundCommandDispatcher,
  7127. (IUnknown **)&m_pCInboundDispatcher);
  7128. if (!SUCCEEDED(hr)){
  7129. ErrorTrace((LPARAM) this, "Unable to get dispatcher by CLF on router");
  7130. TraceFunctLeaveEx((LPARAM) this);
  7131. return FALSE;
  7132. }
  7133. }
  7134. // pre-loading per command buffer into the context object...
  7135. m_CInboundContext.ResetInboundContext();
  7136. // null-terminating the command keyword...
  7137. char * szTemp;
  7138. char * szTemp2;
  7139. char * szArgs;
  7140. BOOL fSpaceReplaced = FALSE;
  7141. szTemp = strstr(InputLine, " ");
  7142. if (szTemp == NULL)
  7143. {
  7144. szTemp = InputLine + strlen(InputLine);
  7145. _ASSERT(szTemp);
  7146. szArgs = szTemp;
  7147. }
  7148. else
  7149. {
  7150. *szTemp='\0';
  7151. szArgs = szTemp;
  7152. fSpaceReplaced = TRUE;
  7153. }
  7154. szTemp2 = InputLine;
  7155. while (szTemp2 < szTemp)
  7156. {
  7157. *szTemp2 = (CHAR)tolower(*szTemp2);
  7158. szTemp2++;
  7159. }
  7160. // OK, we know we're dealing with native...
  7161. hr = m_pCInboundDispatcher->SinksInstalled(
  7162. InputLine,
  7163. &(m_CInboundContext.m_pCurrentCommandContext));
  7164. if (hr == S_OK)
  7165. fSinksInstalled = TRUE;
  7166. if (fSpaceReplaced)
  7167. *szTemp = ' ';
  7168. hr = m_CInboundContext.m_cabCommand.Append(
  7169. (char *)InputLine,
  7170. strlen(InputLine) + 1,
  7171. NULL);
  7172. if (FAILED(hr))
  7173. {
  7174. ErrorTrace((LPARAM) this, "Unable to set command buffer");
  7175. TraceFunctLeaveEx((LPARAM) this);
  7176. return FALSE;
  7177. }
  7178. // Make sure IMsg is instantiated...if it's not, instantiate it !!!
  7179. if (m_dwCurrentCommand == MAIL)
  7180. {
  7181. BOOL fCreateIMsg = TRUE;
  7182. if (m_pIMsg != NULL)
  7183. {
  7184. char b;
  7185. // If we have an allocated IMailMsg but we don't have a valid
  7186. // sender address yet, we will release the object and re-allocate it.
  7187. // Otherwise, we will skip the creation of another message object.
  7188. hr = m_pIMsg->GetStringA(IMMPID_MP_SENDER_ADDRESS_SMTP, 1, &b);
  7189. if(hr == MAILMSG_E_PROPNOTFOUND)
  7190. ReleasImsg(TRUE);
  7191. else
  7192. fCreateIMsg = FALSE;
  7193. }
  7194. if (fCreateIMsg)
  7195. {
  7196. // Create a new MailMsg
  7197. hr = CoCreateInstance(CLSID_MsgImp,
  7198. NULL,
  7199. CLSCTX_INPROC_SERVER,
  7200. IID_IMailMsgProperties,
  7201. (LPVOID *)&m_pIMsg);
  7202. // Next, check if we are over the inbound cutoff limit. If so, we will release the message
  7203. // and not proceed.
  7204. if (SUCCEEDED(hr))
  7205. {
  7206. DWORD dwCreationFlags;
  7207. hr = m_pIMsg->GetDWORD(
  7208. IMMPID_MPV_MESSAGE_CREATION_FLAGS,
  7209. &dwCreationFlags);
  7210. if (FAILED(hr) ||
  7211. (dwCreationFlags & MPV_INBOUND_CUTOFF_EXCEEDED))
  7212. {
  7213. // If we fail to get this property of if the inbound cutoff
  7214. // exceeded flag is set, discard the message and return failure
  7215. if (SUCCEEDED(hr))
  7216. {
  7217. DebugTrace((LPARAM)this, "Failing because inbound cutoff reached");
  7218. hr = E_OUTOFMEMORY;
  7219. }
  7220. m_pIMsg->Release();
  7221. m_pIMsg = NULL;
  7222. }
  7223. }
  7224. if (SUCCEEDED(hr))
  7225. {
  7226. hr = m_pIMsg->QueryInterface(IID_IMailMsgRecipients, (void **) &m_pIMsgRecipsTemp);
  7227. if (SUCCEEDED(hr))
  7228. {
  7229. hr = m_pIMsg->QueryInterface(IID_IMailMsgBind, (void **)&m_pBindInterface);
  7230. if (SUCCEEDED(hr))
  7231. {
  7232. hr = m_pIMsgRecipsTemp->AllocNewList(&m_pIMsgRecips);
  7233. if (FAILED(hr))
  7234. {
  7235. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_MEMORY);
  7236. m_CInboundContext.SetWin32Status(ERROR_NOT_ENOUGH_MEMORY);
  7237. ErrorTrace((LPARAM) this, "GlueDispatch - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY - MailInfo = new MAILQ_ENTRY () failed");
  7238. fResult = FALSE;
  7239. goto ErrorCleanup;
  7240. }
  7241. hr = SetAvailableMailMsgProperties();
  7242. if(FAILED(hr))
  7243. {
  7244. m_CInboundContext.SetWin32Status(ERROR_NOT_ENOUGH_MEMORY);
  7245. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_STORAGE);
  7246. ErrorTrace((LPARAM) this, "GlueDispatch - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY - MailInfo = new MAILQ_ENTRY () failed");
  7247. fResult = FALSE;
  7248. goto ErrorCleanup;
  7249. }
  7250. }
  7251. else
  7252. {
  7253. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_MEMORY);
  7254. m_CInboundContext.SetWin32Status(ERROR_NOT_ENOUGH_MEMORY);
  7255. ErrorTrace((LPARAM) this, "GlueDispatch - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY - MailInfo = new MAILQ_ENTRY () failed");
  7256. fResult = FALSE;
  7257. goto ErrorCleanup;
  7258. }
  7259. }
  7260. else
  7261. {
  7262. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_MEMORY);
  7263. m_CInboundContext.SetWin32Status(ERROR_NOT_ENOUGH_MEMORY);
  7264. ErrorTrace((LPARAM) this, "GlueDispatch - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY - MailInfo = new MAILQ_ENTRY () failed");
  7265. fResult = FALSE;
  7266. goto ErrorCleanup;
  7267. }
  7268. }
  7269. else
  7270. {
  7271. BUMP_COUNTER(QuerySmtpInstance(), MsgsRefusedDueToNoMailObjects);
  7272. FormatSmtpMessage (SMTP_RESP_NORESOURCES, ENO_RESOURCES," %s\r\n", SMTP_NO_MEMORY);
  7273. m_CInboundContext.SetWin32Status(ERROR_NOT_ENOUGH_MEMORY);
  7274. ErrorTrace((LPARAM) this, "GlueDispatch - SMTP_RESP_NORESOURCES, SMTP_NO_MEMORY - MailInfo = new MAILQ_ENTRY () failed");
  7275. fResult = FALSE;
  7276. goto ErrorCleanup;
  7277. }
  7278. }
  7279. }
  7280. // Mark the start fo protocol event processing
  7281. m_fIsPeUnderway = TRUE;
  7282. if (PointerToMemberFunction != NULL)
  7283. {
  7284. // This is native
  7285. if (fSinksInstalled)
  7286. {
  7287. LPPE_BINDING_NODE pBinding;
  7288. PE_BINDING_NODE bnDefault;
  7289. pBinding = m_CInboundContext.m_pCurrentCommandContext->pFirstBinding;
  7290. _ASSERT(pBinding);
  7291. // Now if the first sink is a post sink, we must create a sentinel
  7292. // so we know not to miss the native handler
  7293. if (pBinding->dwPriority > PRIO_DEFAULT)
  7294. {
  7295. bnDefault.dwPriority = PRIO_DEFAULT;
  7296. bnDefault.pNext = pBinding;
  7297. bnDefault.dwFlags = PEBN_DEFAULT;
  7298. pBinding = &bnDefault;
  7299. }
  7300. hr = OnEvent(
  7301. m_pInstance->GetInstancePropertyBag(),
  7302. GetSessionPropertyBag(),
  7303. m_pIMsg,
  7304. m_CInboundContext.m_pCurrentCommandContext,
  7305. pBinding,
  7306. szArgs
  7307. );
  7308. if (hr == MAILTRANSPORT_S_PENDING)
  7309. goto AsyncCompletion;
  7310. }
  7311. else
  7312. {
  7313. //OK, it's native, not extended command...
  7314. // So we just run the native handler on it
  7315. fResult = (this->*PointerToMemberFunction)(szArgs, strlen(szArgs));
  7316. // all changes got dumped into context automatically...
  7317. // the interpreter will see the default response nonempty
  7318. // and sink response empty, and it will flush just that...
  7319. // for conformity with onevent
  7320. hr = S_OK;
  7321. // however, we need to modify command status here to signify if
  7322. // we need to keep connection open
  7323. if (!fResult)
  7324. m_CInboundContext.SetCommandStatus(EXPE_DROP_SESSION);
  7325. else
  7326. m_CInboundContext.SetCommandStatus(EXPE_SUCCESS);
  7327. }
  7328. }
  7329. else
  7330. {
  7331. // This is not a native command
  7332. if (fSinksInstalled)
  7333. {
  7334. hr = OnEvent(
  7335. m_pInstance->GetInstancePropertyBag(),
  7336. GetSessionPropertyBag(),
  7337. m_pIMsg,
  7338. m_CInboundContext.m_pCurrentCommandContext,
  7339. m_CInboundContext.m_pCurrentCommandContext->pFirstBinding,
  7340. szArgs
  7341. );
  7342. if (hr == MAILTRANSPORT_S_PENDING)
  7343. goto AsyncCompletion;
  7344. }
  7345. // once again, all info now sits in the context
  7346. // the only thing left is just to check if any sinks were chained for this command,
  7347. // and, if not, run a chain of *-sinks on it. If they did not pick it up, either,
  7348. // we call on DoLASTCommand and that's all!
  7349. //
  7350. // jstamerj 1998/10/29 18:01:53: Check the command status code
  7351. // to see if we need to continue (in addition to smtp status)
  7352. //
  7353. if ((m_CInboundContext.m_dwSmtpStatus == 0) &&
  7354. (m_CInboundContext.m_dwCommandStatus == EXPE_UNHANDLED))
  7355. {
  7356. hr = m_pCInboundDispatcher->SinksInstalled(
  7357. "*",&(m_CInboundContext.m_pCurrentCommandContext));
  7358. if (hr == S_OK)
  7359. {
  7360. hr = OnEvent(
  7361. m_pInstance->GetInstancePropertyBag(),
  7362. GetSessionPropertyBag(),
  7363. m_pIMsg,
  7364. m_CInboundContext.m_pCurrentCommandContext,
  7365. m_CInboundContext.m_pCurrentCommandContext->pFirstBinding,
  7366. szArgs
  7367. );
  7368. if (hr == MAILTRANSPORT_S_PENDING)
  7369. goto AsyncCompletion;
  7370. }
  7371. if ((m_CInboundContext.m_dwSmtpStatus == 0) &&
  7372. (m_CInboundContext.m_dwCommandStatus == EXPE_UNHANDLED))
  7373. {
  7374. fResult=(this->DoLASTCommand)(szArgs, strlen(szArgs));
  7375. // the context at this point does not contain anything meaningful, so just return
  7376. if (!fResult)
  7377. m_CInboundContext.m_dwCommandStatus = EXPE_DROP_SESSION;
  7378. else
  7379. m_CInboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  7380. hr = S_OK;
  7381. }
  7382. }
  7383. }
  7384. // first, we interpret the hr-logic
  7385. // if chaining was normal, proceed, otherwise...
  7386. if ((hr != S_OK) &&
  7387. (hr != EXPE_S_CONSUMED))
  7388. {
  7389. ErrorTrace((LPARAM) this, "Error occured during chaining of sinks (%08x)", hr);
  7390. }
  7391. if (SUCCEEDED(hr))
  7392. {
  7393. // Format the message if needed
  7394. fResult = ProcessAndSendResponse();
  7395. // Disconnect client if needed
  7396. if (m_CInboundContext.m_dwCommandStatus & EXPE_DROP_SESSION)
  7397. {
  7398. //Special case for TURN
  7399. //where we do not diconnect the connection but simply destroy smtpcli
  7400. if(m_DoCleanup)
  7401. DisconnectClient();
  7402. fResult = FALSE;
  7403. }
  7404. if ( ( m_CInboundContext.m_dwCommandStatus & EXPE_BLOB_READY) &&
  7405. ( NULL != m_CInboundContext.m_pICallback))
  7406. {
  7407. m_fPeBlobReady = TRUE;
  7408. m_pPeBlobCallback = m_CInboundContext.m_pICallback;
  7409. }
  7410. // do things unique to handling the end of a message
  7411. if (m_fAsyncEOD) {
  7412. ReInitClassVariables();
  7413. }
  7414. }
  7415. else
  7416. fResult = FALSE;
  7417. // Final cleanup
  7418. m_fIsPeUnderway = FALSE;
  7419. TraceFunctLeaveEx((LPARAM)this);
  7420. return(fResult);
  7421. AsyncCompletion:
  7422. DebugTrace((LPARAM)this, "Leaving because of S_PENDING");
  7423. *pfAsyncOp = TRUE;
  7424. TraceFunctLeaveEx((LPARAM)this);
  7425. return(TRUE);
  7426. ErrorCleanup:
  7427. // Cleanup on error. This includes sending out all final
  7428. // responses
  7429. SendSmtpResponse();
  7430. m_fIsPeUnderway = FALSE;
  7431. TraceFunctLeaveEx((LPARAM)this);
  7432. return(fResult);
  7433. }
  7434. /*++
  7435. Name :
  7436. BOOL SMTP_CONNECTION::ProcessPeBlob
  7437. Description:
  7438. Calls the protocol extension sink callback with a blob buffer.
  7439. Arguments:
  7440. pbInputLine
  7441. cbSize
  7442. Returns:
  7443. BOOL : continue processing
  7444. --*/
  7445. BOOL SMTP_CONNECTION::ProcessPeBlob(const char * pbInputLine, DWORD cbSize)
  7446. {
  7447. BOOL fResult;
  7448. HRESULT hr;
  7449. TraceQuietEnter( "SMTP_CONNECTION::ProcessPeBlob");
  7450. _ASSERT( m_fPeBlobReady);
  7451. if ( NULL == m_pPeBlobCallback) {
  7452. ErrorTrace( ( LPARAM) this, "Sink provided no callback interface");
  7453. fResult = FALSE;
  7454. goto cleanup;
  7455. }
  7456. m_CInboundContext.ResetInboundContext();
  7457. m_CInboundContext.m_pbBlob = ( PBYTE) pbInputLine;
  7458. m_CInboundContext.m_cbBlob = cbSize;
  7459. hr = m_pPeBlobCallback->OnSmtpInCallback(
  7460. m_pInstance->GetInstancePropertyBag(),
  7461. GetSessionPropertyBag(),
  7462. m_pIMsg,
  7463. ( ISmtpInCallbackContext *) &m_CInboundContext);
  7464. if ( FAILED( hr)) {
  7465. ErrorTrace( ( LPARAM) this, "Sink callback failed, hr=%x", hr);
  7466. fResult = FALSE;
  7467. goto cleanup;
  7468. }
  7469. fResult = ProcessAndSendResponse();
  7470. if ( FALSE == fResult) {
  7471. ErrorTrace( ( LPARAM) this, "ProcessAndSendResponse failed");
  7472. goto cleanup;
  7473. }
  7474. if ( m_CInboundContext.m_dwCommandStatus & EXPE_BLOB_DONE) {
  7475. m_fPeBlobReady = FALSE;
  7476. m_pPeBlobCallback->Release();
  7477. m_pPeBlobCallback = NULL;
  7478. }
  7479. cleanup:
  7480. if ( fResult == FALSE) {
  7481. m_fPeBlobReady = FALSE;
  7482. if ( NULL != m_pPeBlobCallback) {
  7483. m_pPeBlobCallback->Release();
  7484. m_pPeBlobCallback = NULL;
  7485. }
  7486. FormatSmtpMessage( SMTP_RESP_ERROR, NULL, " %s\r\n", "Sink problem processing blob");
  7487. SendSmtpResponse();
  7488. }
  7489. return fResult;
  7490. }
  7491. /*++
  7492. Name :
  7493. BOOL SMTP_CONNECTION::PE_FormatSmtpMessage
  7494. Description:
  7495. If we are processing non-native or extended native command, caches response in
  7496. context object, otherwise passes it along to native FormatSmtpMessage.
  7497. Arguments:
  7498. As in FormatSmtpMessage.
  7499. Returns:
  7500. TRUE always
  7501. --*/
  7502. BOOL SMTP_CONNECTION::PE_FormatSmtpMessage(IN const char * Format, ...)
  7503. {
  7504. va_list arglist;
  7505. char Buffer[MAX_NATIVE_RESPONSE_SIZE];
  7506. int BytesWritten;
  7507. DWORD AvailableBytes = MAX_NATIVE_RESPONSE_SIZE;
  7508. va_start(arglist, Format);
  7509. BytesWritten = _vsnprintf (Buffer, AvailableBytes, Format, arglist);
  7510. va_end(arglist);
  7511. if (!m_fIsPeUnderway)
  7512. {
  7513. //pass in 0 for the code and NULL for the enhanced staus code
  7514. //
  7515. FormatSmtpMessage(0,NULL,"%s", Buffer);
  7516. }
  7517. else
  7518. {
  7519. m_CInboundContext.AppendNativeResponse(Buffer, strlen(Buffer) + 1);
  7520. }
  7521. return TRUE;
  7522. }
  7523. /*++
  7524. Name :
  7525. BOOL SMTP_CONNECTION::PE_CdFormatSmtpMessage
  7526. Description:
  7527. As PE_FormatSmtpMessage above, except intercepts the SMTP return code.
  7528. Arguments:
  7529. As in PE_FormatSmtpMessage above, except add'l slot for SMTP return code.
  7530. Returns:
  7531. TRUE always
  7532. --*/
  7533. BOOL SMTP_CONNECTION::PE_CdFormatSmtpMessage(DWORD dwCode, const char * szEnhancedCodes, IN const char * Format,...)
  7534. {
  7535. va_list arglist;
  7536. char Buffer[MAX_NATIVE_RESPONSE_SIZE];
  7537. int BytesWritten;
  7538. DWORD AvailableBytes = MAX_NATIVE_RESPONSE_SIZE;
  7539. char RealFormat[MAX_PATH];
  7540. if(m_pInstance->AllowEnhancedCodes() && szEnhancedCodes)
  7541. {
  7542. sprintf(RealFormat,"%d %s",dwCode,szEnhancedCodes);
  7543. }
  7544. else
  7545. sprintf(RealFormat,"%d",dwCode);
  7546. strcat(RealFormat,Format);
  7547. va_start(arglist, Format);
  7548. BytesWritten = _vsnprintf (Buffer, AvailableBytes, (const char * )RealFormat, arglist);
  7549. if(BytesWritten == -1) {
  7550. //
  7551. // If response is too long, forcibly truncate it
  7552. // This might mean that the human readable part of the response may be
  7553. // incomplete... but there's nothing we can do about that easily.
  7554. //
  7555. Buffer[MAX_NATIVE_RESPONSE_SIZE-3] = '\r';
  7556. Buffer[MAX_NATIVE_RESPONSE_SIZE-2] = '\n';
  7557. Buffer[MAX_NATIVE_RESPONSE_SIZE-1] = '\0';
  7558. }
  7559. if (m_fIsPeUnderway)
  7560. {
  7561. PE_FormatSmtpMessage("%s",Buffer);
  7562. m_CInboundContext.SetSmtpStatusCode(dwCode);
  7563. }
  7564. else
  7565. {
  7566. //pass in 0 for the code and NULL for the enhanced staus code
  7567. //
  7568. FormatSmtpMessage(0,NULL,"%s",Buffer);
  7569. }
  7570. va_end(arglist);
  7571. return TRUE;
  7572. }
  7573. /*++
  7574. Name :
  7575. BOOL SMTP_CONNECTION::PE_DisconnectClient
  7576. Description:
  7577. If we are processing non-native or extended native command, caches DisconnectClient
  7578. calls' occurences in context object, otherwise passes it along to native
  7579. DisconnectClient.
  7580. Arguments:
  7581. none
  7582. Returns:
  7583. TRUE always
  7584. --*/
  7585. BOOL SMTP_CONNECTION::PE_DisconnectClient()
  7586. {
  7587. if (m_fIsPeUnderway)
  7588. {
  7589. m_CInboundContext.m_dwCommandStatus = m_CInboundContext.m_dwCommandStatus | EXPE_DROP_SESSION;
  7590. }
  7591. else
  7592. {
  7593. DisconnectClient();
  7594. }
  7595. return TRUE;
  7596. }
  7597. /*++
  7598. Name :
  7599. BOOL SMTP_CONNECTION::PE_SendSmtpResponse
  7600. Description:
  7601. If we are processing non-native or extended native command, caches SendSmtpResponse
  7602. calls' occurences in context object, otherwise passes it along to native
  7603. SendSmtpResponse.
  7604. Arguments:
  7605. none
  7606. Returns:
  7607. TRUE always if we're called from GlueDispatch callee's
  7608. SendSmtpResponse return otherwise
  7609. --*/
  7610. BOOL SMTP_CONNECTION::PE_SendSmtpResponse()
  7611. {
  7612. if (m_fIsPeUnderway)
  7613. {
  7614. m_CInboundContext.m_dwCommandStatus = m_CInboundContext.m_dwCommandStatus & (~EXPE_PIPELINED);
  7615. return TRUE;
  7616. }
  7617. else
  7618. {
  7619. return SendSmtpResponse();
  7620. };
  7621. }
  7622. //////////////////////////////////////////////////////////////////////////////
  7623. //---[ SMTP_CONNECTION::GetAndPersistRFC822Headers ]--------------------------
  7624. //
  7625. //
  7626. // Description:
  7627. // Parses and RFC822 headers, and promotes them to the P2 if neccessary
  7628. // Parameters:
  7629. // IN InputLine String with start of link (processed by ChompHeader)
  7630. // IN psValueBuf String with value of header
  7631. // Returns:
  7632. // -
  7633. // History:
  7634. // 2/8/99 - MikeSwa Updated - moved received header parsing to this function
  7635. //
  7636. //-----------------------------------------------------------------------------
  7637. void SMTP_CONNECTION::GetAndPersistRFC822Headers(
  7638. char* InputLine,
  7639. char* pszValueBuf
  7640. )
  7641. {
  7642. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNECTION::GetAndPersistRFC822Headers");
  7643. HRESULT hr = S_OK;
  7644. //count the number of received lines for hop count
  7645. //analysis later
  7646. if(!strncasecmp(InputLine, "Received:", strlen("Received:")) ||
  7647. !strncasecmp(InputLine, "Received :", strlen("Received :")))
  7648. {
  7649. m_HopCount++;
  7650. CHAR szText[2024];
  7651. CHAR * pszTemp;
  7652. // Check if this string represents the local server - if it does we might be
  7653. // looping around and we need to keep track of how many times this server has
  7654. // seen the message
  7655. sprintf(szText,szFormatReceivedServer, m_pInstance->GetFQDomainName());
  7656. pszTemp = strstr(InputLine, szText);
  7657. if (pszTemp)
  7658. {
  7659. // we found the string, make sure we also find "with Microsoft SMTPSVC"
  7660. sprintf(szText,szFormatReceivedService);
  7661. pszTemp = strstr(InputLine, szText);
  7662. if (pszTemp)
  7663. {
  7664. // we found that string too - we've been here before,
  7665. // increment m_LocalHopCount
  7666. m_LocalHopCount++;
  7667. }
  7668. }
  7669. }
  7670. //
  7671. // Get the message ID and persist it
  7672. //
  7673. if( !m_fSeenRFC822MsgId &&
  7674. ( !strncasecmp( InputLine, "Message-ID:", strlen("Message-ID:") ) ||
  7675. !strncasecmp( InputLine, "Message-ID :", strlen("Message-ID :") ) ) )
  7676. {
  7677. m_fSeenRFC822MsgId = TRUE;
  7678. if( pszValueBuf )
  7679. {
  7680. //
  7681. // Some MTAs fold Message-IDs. Embedded CRLFs in a Message-ID can cause trouble since
  7682. // we use it for message tracking, and in the "Queued" response, so we unfold the
  7683. // Message-ID prior to using it.
  7684. //
  7685. CHAR *pszUnfolded = NULL;
  7686. if(!UnfoldHeader(pszValueBuf, &pszUnfolded))
  7687. {
  7688. m_MailBodyError = E_OUTOFMEMORY;
  7689. TraceFunctLeaveEx((LPARAM) this);
  7690. return;
  7691. }
  7692. if(pszUnfolded)
  7693. pszValueBuf = pszUnfolded;
  7694. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_ID, pszValueBuf ) ) )
  7695. m_MailBodyError = hr;
  7696. if(pszUnfolded)
  7697. FreeUnfoldedHeader(pszUnfolded);
  7698. TraceFunctLeaveEx((LPARAM) this);
  7699. return;
  7700. }
  7701. }
  7702. //
  7703. // get the Subject: & persist it
  7704. //
  7705. if( !m_fSeenRFC822Subject &&
  7706. ( !strncasecmp( InputLine, "Subject:", strlen("Subject:") ) ||
  7707. !strncasecmp( InputLine, "Subject :", strlen("Subject :") ) ) )
  7708. {
  7709. m_fSeenRFC822Subject = TRUE;
  7710. if( pszValueBuf )
  7711. {
  7712. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_SUBJECT, pszValueBuf ) ) )
  7713. m_MailBodyError = hr;
  7714. }
  7715. }
  7716. //
  7717. // get the To: address & persist it
  7718. //
  7719. if( !m_fSeenRFC822ToAddress &&
  7720. ( !strncasecmp( InputLine, "To:", strlen("To:") ) ||
  7721. !strncasecmp( InputLine, "To :", strlen("To :") ) ) )
  7722. {
  7723. m_fSeenRFC822ToAddress = TRUE;
  7724. if( pszValueBuf )
  7725. {
  7726. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_RFC822_TO_ADDRESS, pszValueBuf ) ) )
  7727. m_MailBodyError = hr;
  7728. }
  7729. }
  7730. //
  7731. // get the Cc: address & persist it
  7732. //
  7733. if( !m_fSeenRFC822CcAddress &&
  7734. ( !strncasecmp( InputLine, "Cc:", strlen("Cc:") ) ||
  7735. !strncasecmp( InputLine, "Cc :", strlen("Cc :") ) ) )
  7736. {
  7737. m_fSeenRFC822CcAddress = TRUE;
  7738. if( pszValueBuf )
  7739. {
  7740. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_RFC822_CC_ADDRESS, pszValueBuf ) ) )
  7741. m_MailBodyError = hr;
  7742. }
  7743. }
  7744. //
  7745. // get the Bcc: address & persist it
  7746. //
  7747. if( !m_fSeenRFC822BccAddress &&
  7748. ( !strncasecmp( InputLine, "Bcc:", strlen("Bcc:") ) ||
  7749. !strncasecmp( InputLine, "Bcc :", strlen("Bcc :") ) ) )
  7750. {
  7751. m_fSeenRFC822BccAddress = TRUE;
  7752. if( pszValueBuf )
  7753. {
  7754. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_RFC822_BCC_ADDRESS, pszValueBuf ) ) )
  7755. m_MailBodyError = hr;
  7756. }
  7757. }
  7758. //
  7759. // get the From: address & persist it
  7760. //
  7761. if( !m_fSeenRFC822FromAddress &&
  7762. ( !strncasecmp( InputLine, "From:", strlen("From:") ) ||
  7763. !strncasecmp( InputLine, "From :", strlen("From :") ) ) )
  7764. {
  7765. m_fSeenRFC822FromAddress = TRUE;
  7766. if( pszValueBuf )
  7767. {
  7768. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_RFC822_FROM_ADDRESS, pszValueBuf ) ) )
  7769. m_MailBodyError = hr;
  7770. char szSmtpFromAddress[MAX_INTERNET_NAME + 6] = "smtp:";
  7771. char *pszDomainOffset;
  7772. DWORD cAddr;
  7773. if (CAddr::ExtractCleanEmailName(szSmtpFromAddress + 5, &pszDomainOffset, &cAddr, pszValueBuf)) {
  7774. if (FAILED(hr = m_pIMsg->PutStringA(IMMPID_MP_FROM_ADDRESS, szSmtpFromAddress))) {
  7775. m_MailBodyError = hr;
  7776. }
  7777. }
  7778. }
  7779. }
  7780. if( !m_fSeenRFC822SenderAddress &&
  7781. ( !strncasecmp( InputLine, "Sender:", strlen("Sender:") ) ||
  7782. !strncasecmp( InputLine, "Sender :", strlen("Sender :") ) ) )
  7783. {
  7784. m_fSeenRFC822SenderAddress = TRUE;
  7785. if( pszValueBuf )
  7786. {
  7787. char szSmtpSenderAddress[MAX_INTERNET_NAME + 6] = "smtp:";
  7788. char *pszDomainOffset;
  7789. DWORD cAddr;
  7790. if (CAddr::ExtractCleanEmailName(szSmtpSenderAddress + 5, &pszDomainOffset, &cAddr, pszValueBuf)) {
  7791. if (FAILED(hr = m_pIMsg->PutStringA(IMMPID_MP_SENDER_ADDRESS, szSmtpSenderAddress))) {
  7792. m_MailBodyError = hr;
  7793. }
  7794. }
  7795. }
  7796. }
  7797. //
  7798. // get the X-Priority & persist it
  7799. //
  7800. if( !m_fSeenXPriority &&
  7801. ( !strncasecmp( InputLine, "X-Priority:", strlen("X-Priority:") ) ||
  7802. !strncasecmp( InputLine, "X-Priority :", strlen("X-Priority :") ) ) )
  7803. {
  7804. m_fSeenXPriority = TRUE;
  7805. if( pszValueBuf )
  7806. {
  7807. DWORD dwPri = (DWORD)atoi( pszValueBuf );
  7808. if( FAILED( hr = m_pIMsg->PutDWORD( IMMPID_MP_X_PRIORITY, dwPri ) ) )
  7809. m_MailBodyError = hr;
  7810. }
  7811. }
  7812. //
  7813. // get the X-OriginalArrivalTime & persist it
  7814. //
  7815. if( !m_fSeenXOriginalArrivalTime &&
  7816. ( !strncasecmp( InputLine, "X-OriginalArrivalTime:", strlen("X-OriginalArrivalTime:") ) ||
  7817. !strncasecmp( InputLine, "X-OriginalArrivalTime :", strlen("X-OriginalArrivalTime :") ) ) )
  7818. {
  7819. m_fSeenXOriginalArrivalTime = TRUE;
  7820. if( pszValueBuf )
  7821. {
  7822. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_ORIGINAL_ARRIVAL_TIME, pszValueBuf ) ) )
  7823. m_MailBodyError = hr;
  7824. }
  7825. }
  7826. //
  7827. // get the content type & persist it
  7828. //
  7829. if( !m_fSeenContentType &&
  7830. ( !strncasecmp( InputLine, "Content-Type:", strlen("Content-Type:") ) ||
  7831. !strncasecmp( InputLine, "Content-Type :", strlen("Content-Type :") ) ) )
  7832. {
  7833. m_fSeenContentType = TRUE;
  7834. m_fSetContentType = TRUE;
  7835. DWORD dwContentType = 0;
  7836. if( pszValueBuf )
  7837. {
  7838. if( !strncasecmp( pszValueBuf, "multipart/signed", strlen("multipart/signed") ) )
  7839. {
  7840. dwContentType = 1;
  7841. }
  7842. else if( !strncasecmp( pszValueBuf, "application/x-pkcs7-mime", strlen("application/x-pkcs7-mime") ) ||
  7843. !strncasecmp( pszValueBuf, "application/pkcs7-mime", strlen("application/pkcs7-mime") ) )
  7844. {
  7845. dwContentType = 2;
  7846. }
  7847. if( FAILED( hr = m_pIMsg->PutStringA( IMMPID_MP_CONTENT_TYPE, pszValueBuf ) ) )
  7848. m_MailBodyError = hr;
  7849. }
  7850. if( FAILED( hr = m_pIMsg->PutDWORD( IMMPID_MP_ENCRYPTION_TYPE, dwContentType ) ) )
  7851. m_MailBodyError = hr;
  7852. }
  7853. if( !m_fSetContentType )
  7854. {
  7855. m_fSetContentType = TRUE;
  7856. if( FAILED( hr = m_pIMsg->PutDWORD( IMMPID_MP_ENCRYPTION_TYPE, 0 ) ) )
  7857. m_MailBodyError = hr;
  7858. }
  7859. TraceFunctLeaveEx((LPARAM) this);
  7860. }
  7861. //////////////////////////////////////////////////////////////////////////////
  7862. HRESULT SMTP_CONNECTION::SetAvailableMailMsgProperties()
  7863. {
  7864. //set IPaddress that is already available
  7865. HRESULT hr = m_pIMsg->PutStringA(IMMPID_MP_CONNECTION_IP_ADDRESS, QueryClientHostName());
  7866. if(FAILED(hr))
  7867. {
  7868. return( hr );
  7869. }
  7870. hr = m_pIMsg->PutStringA(IMMPID_MP_CONNECTION_SERVER_IP_ADDRESS, QueryLocalHostName());
  7871. if(FAILED(hr))
  7872. {
  7873. return( hr );
  7874. }
  7875. hr = m_pIMsg->PutStringA(IMMPID_MP_SERVER_NAME, g_ComputerName);
  7876. if(FAILED(hr))
  7877. {
  7878. return( hr );
  7879. }
  7880. hr = m_pIMsg->PutStringA(IMMPID_MP_SERVER_VERSION, g_VersionString);
  7881. if(FAILED(hr))
  7882. {
  7883. return( hr );
  7884. }
  7885. if (QueryLocalPortName())
  7886. {
  7887. DWORD dwPortNum = atoi(QueryLocalPortName());
  7888. hr = m_pIMsg->PutDWORD(IMMPID_MP_CONNECTION_SERVER_PORT, dwPortNum);
  7889. if (FAILED(hr))
  7890. return ( hr );
  7891. }
  7892. if (m_fAuthenticated)
  7893. {
  7894. //Get username
  7895. if (m_securityCtx.QueryUserName())
  7896. {
  7897. hr = m_pIMsg->PutStringA(IMMPID_MP_CLIENT_AUTH_USER,
  7898. m_securityCtx.QueryUserName());
  7899. if (FAILED(hr))
  7900. return hr;
  7901. }
  7902. //Get type of authentication
  7903. if (m_szUsedAuthKeyword[0])
  7904. {
  7905. hr = m_pIMsg->PutStringA(IMMPID_MP_CLIENT_AUTH_TYPE, m_szUsedAuthKeyword);
  7906. if (FAILED(hr))
  7907. return hr;
  7908. }
  7909. }
  7910. return( hr );
  7911. }
  7912. //-----------------------------------------------------------------------------
  7913. // Description:
  7914. // If a failure occurs after a client issues the BDAT command, SMTP cannot
  7915. // respond with a failure code (since the RFC forbids a BDAT response). So
  7916. // SMTP calls this function which will read BDAT chunks from the socket
  7917. // and discard them. When the chunk has been received, and it is time to
  7918. // ACK the chunk, this function calls DoBDATErrorProcessing() to send the
  7919. // correct error response.
  7920. //
  7921. // Arguments:
  7922. // IN const char *InputLine - Ptr to data from client
  7923. // IN DWORD UndecryptedTailSize - # of undecrypted bytes in buffer (TLS)
  7924. //
  7925. // OUT BOOL *pfAsyncOp - If this function succeeded, this is set to TRUE
  7926. // if all the data in the input buffer was consumed and we pended a
  7927. // read to pick up more. If this is FALSE, and the function succeeded
  7928. // there is extra (pipelined) data in the input buffer, and it must
  7929. // be parsed as an SMTP command.
  7930. //
  7931. // Notes:
  7932. // If there is data left in the input buffer after this function is done,
  7933. // that data should be parsed as an SMTP command.
  7934. //
  7935. // Returns:
  7936. // TRUE - Keep connection alive.
  7937. // FALSE - Drop connection, there was a fatal error.
  7938. //
  7939. //-----------------------------------------------------------------------------
  7940. BOOL SMTP_CONNECTION::AcceptAndDiscardBDAT(
  7941. const char *InputLine,
  7942. DWORD UndecryptedTailSize,
  7943. BOOL *pfAsyncOp)
  7944. {
  7945. BOOL fReturn = FALSE;
  7946. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::AcceptAndDiscardBDAT");
  7947. _ASSERT(m_MailBodyDiagnostic != ERR_NONE);
  7948. _ASSERT(m_nBytesRemainingInChunk > 0 && "No chunk data");
  7949. *pfAsyncOp = FALSE;
  7950. if((DWORD)m_nBytesRemainingInChunk <= m_cbParsable)
  7951. {
  7952. //
  7953. // Chunk completely received, discard it, do error processing.
  7954. //
  7955. DebugTrace((LPARAM) this, "Chunk completed");
  7956. MoveMemory((PVOID) InputLine, InputLine + m_nBytesRemainingInChunk,
  7957. m_cbReceived - m_nBytesRemainingInChunk);
  7958. m_cbParsable -= m_nBytesRemainingInChunk;
  7959. m_cbReceived -= m_nBytesRemainingInChunk;
  7960. m_nBytesRemainingInChunk = 0;
  7961. m_fIsChunkComplete = TRUE;
  7962. if(!MailBodyErrorProcessing()) {
  7963. fReturn = FALSE;
  7964. goto Exit;
  7965. }
  7966. //
  7967. // If we're done with all BDAT chunks, reset state. If this is NOT the last
  7968. // BDAT chunk, then either more BDAT commands/chunks are already buffered
  7969. // in the input buffer due to pipelining, or they're on their way. We need
  7970. // to accept and discard all of them. m_fIsLastChunk is set by DoBDATCommand.
  7971. //
  7972. if(m_fIsLastChunk)
  7973. {
  7974. DebugTrace((LPARAM) this, "Last chunk");
  7975. //
  7976. // Note: This doesn't flush the input buffer, so any pipelined data is
  7977. // still kept around. Thus the check for m_cbParsable below is valid.
  7978. //
  7979. ReInitClassVariables();
  7980. }
  7981. #if 0
  7982. //
  7983. // All data in buffer was consumed. Pend a read to pick up more.
  7984. // NOTE: Don't really have to do this... the parsing code already does
  7985. // this for us.
  7986. //
  7987. if(m_cbParsable == 0)
  7988. {
  7989. fReturn = PendReadIO(UndecryptedTailSize);
  7990. *pfAsyncOp = fReturn;
  7991. goto Exit;
  7992. }
  7993. #endif
  7994. //
  7995. // If we got here, there is additional data left in the buffer. This must
  7996. // be pipelined data sent by the client, along with the just discarded
  7997. // chunk.
  7998. //
  7999. *pfAsyncOp = FALSE;
  8000. fReturn = TRUE;
  8001. }
  8002. else
  8003. {
  8004. //
  8005. // The complete chunk has not been received, discard whatever we got, update
  8006. // the counts of bytes received/expected and pend a read to pick up more.
  8007. //
  8008. DebugTrace((LPARAM) this, "Processing partial chunk");
  8009. MoveMemory((PVOID) InputLine, InputLine + m_cbParsable, m_cbReceived - m_cbParsable);
  8010. m_nBytesRemainingInChunk -= m_cbParsable;
  8011. m_cbReceived -= m_cbParsable;
  8012. m_cbParsable = 0;
  8013. _ASSERT(!m_fIsChunkComplete);
  8014. fReturn = PendReadIO(UndecryptedTailSize);
  8015. *pfAsyncOp = fReturn;
  8016. goto Exit;
  8017. }
  8018. Exit:
  8019. //
  8020. // If we succeeded, one of the following is true:
  8021. // (1) Either there wasn't enough data to complete the chunk and we pended a read.
  8022. // (2) There was enough data to complete the chunk, and the remaining bytes are
  8023. // to be interpreted as an SMTP command (m_nBytesRemainingInChunk == 0).
  8024. //
  8025. if(fReturn && !*pfAsyncOp)
  8026. _ASSERT(m_nBytesRemainingInChunk == 0);
  8027. TraceFunctLeaveEx((LPARAM) this);
  8028. return fReturn;
  8029. }
  8030. BOOL SMTP_CONNECTION::AcceptAndDiscardDATA(
  8031. const char *InputLine,
  8032. DWORD UndecryptedTailSize,
  8033. BOOL *pfAsyncOp)
  8034. {
  8035. DWORD IntermediateSize = 0;
  8036. BOOL fRet = FALSE;
  8037. char *pszSearch = NULL;
  8038. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::AcceptAndDiscardDATA");
  8039. _ASSERT(m_MailBodyDiagnostic != ERR_NONE);
  8040. *pfAsyncOp = FALSE;
  8041. //
  8042. // Discard data line by line until we hit an incomplete line. Then break
  8043. // out of loop and pend a read to pick up more data. If we hit a line that
  8044. // has only a "." in it, that is the end of the mailbody. Kick off error
  8045. // processing, reinit the state and return.
  8046. //
  8047. while(pszSearch = IsLineComplete(InputLine, m_cbParsable))
  8048. {
  8049. IntermediateSize = (DWORD) (pszSearch - InputLine);
  8050. if(IntermediateSize == 1 &&
  8051. InputLine[0] == '.' && InputLine[1] == '\r' && InputLine[2] == '\n')
  8052. {
  8053. m_cbReceived -= IntermediateSize + 2; // +2 for CRLF since pszSearch points->CR
  8054. m_cbParsable -= IntermediateSize + 2;
  8055. MoveMemory((PVOID *)InputLine, pszSearch + 2, m_cbReceived);
  8056. if(MailBodyErrorProcessing())
  8057. {
  8058. ReInitClassVariables();
  8059. fRet = TRUE;
  8060. }
  8061. TraceFunctLeaveEx((LPARAM) this);
  8062. return fRet;
  8063. }
  8064. m_cbReceived -= IntermediateSize + 2; // +2 for CRLF since pszSearch points->CR
  8065. m_cbParsable -= IntermediateSize + 2;
  8066. MoveMemory((PVOID *)InputLine, pszSearch + 2, m_cbReceived);
  8067. }
  8068. //
  8069. // Pend a read to pick up rest of the mail body.
  8070. //
  8071. fRet = PendReadIO(UndecryptedTailSize);
  8072. *pfAsyncOp = fRet;
  8073. TraceFunctLeaveEx((LPARAM) this);
  8074. return fRet;
  8075. }
  8076. //-----------------------------------------------------------------------------
  8077. // Description:
  8078. // Based on m_MailBodyDiagnostic, this function performs error processing for
  8079. // errors that occurred during BDAT. BDAT errors that occur when BDAT
  8080. // is issued are not processed as soon as the error occurs, but after
  8081. // SMTP has accepted the BDAT chunk (see AcceptAndDiscardBDAT()).
  8082. // Arguments:
  8083. // None.
  8084. // Returns:
  8085. // TRUE - Success, error response sent.
  8086. // FALSE - Drop connection. Either there was an error, or error processing
  8087. // requires dropping the connection (This is an RFC violation if there
  8088. // is pipelined data in the input buffer).
  8089. //-----------------------------------------------------------------------------
  8090. BOOL SMTP_CONNECTION::MailBodyErrorProcessing()
  8091. {
  8092. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNECTION::DoBDATErrorProcessing");
  8093. switch(m_MailBodyDiagnostic) {
  8094. case ERR_RETRY:
  8095. case ERR_OUT_OF_MEMORY:
  8096. PE_CdFormatSmtpMessage(SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_MEMORY);
  8097. break;
  8098. case ERR_MAX_MSG_SIZE:
  8099. PE_CdFormatSmtpMessage(SMTP_RESP_NOSTORAGE, ENO_RESOURCES, " %s\r\n", SMTP_MAX_MSG_SIZE_EXCEEDED_MSG );
  8100. break;
  8101. case ERR_AUTH_NEEDED:
  8102. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY, " %s\r\n", "Client was not authenticated");
  8103. break;
  8104. case ERR_TLS_NEEDED:
  8105. PE_CdFormatSmtpMessage (SMTP_RESP_MUST_SECURE, ENO_SECURITY, " %s\r\n", SMTP_MSG_MUST_SECURE);
  8106. break;
  8107. case ERR_HELO_NEEDED:
  8108. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", "Send hello first" );
  8109. break;
  8110. case ERR_MAIL_NEEDED:
  8111. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", "Need mail command." );
  8112. break;
  8113. case ERR_RCPT_NEEDED:
  8114. PE_CdFormatSmtpMessage (SMTP_RESP_BAD_SEQ, ESYNTAX_ERROR," %s\r\n", "Need Rcpt command." );
  8115. break;
  8116. case ERR_NO_VALID_RCPTS:
  8117. PE_CdFormatSmtpMessage (SMTP_RESP_TRANS_FAILED, ESYNTAX_ERROR," %s\r\n", SMTP_NO_VALID_RECIPS );
  8118. break;
  8119. default:
  8120. _ASSERT(0 && "Bad err value");
  8121. PE_CdFormatSmtpMessage(SMTP_RESP_NORESOURCES, ENO_RESOURCES, " %s\r\n",SMTP_NO_MEMORY);
  8122. ErrorTrace((LPARAM) this, "BUG: bad value");
  8123. }
  8124. TraceFunctLeaveEx((LPARAM) this);
  8125. return SendSmtpResponse(); // Error responses are not pipelined
  8126. }
  8127. //////////////////////////////////////////////////////////////////////////////
  8128. BOOL SMTP_CONNECTION::WriteLine (char * TextToWrite, DWORD TextSize)
  8129. {
  8130. /*#if 0
  8131. char * SavedLine = TextToWrite;
  8132. char * EndOfLine = &SavedLine[TextSize];
  8133. DWORD LineSize = 0;
  8134. while( (LineSize = (EndOfLine - SavedLine)) > 78)
  8135. {
  8136. char * RealEndOfLine = &TextToWrite[78 - 2];
  8137. if(!WriteMailFile(TextToWrite, RealEndOfLine - SavedLine))
  8138. return FALSE;
  8139. m_TotalMsgSize += (RealEndOfLine - SavedLine);
  8140. if(!WriteMailFile("\r\n", 2))
  8141. return FALSE;
  8142. m_TotalMsgSize += 2;
  8143. if(!WriteMailFile("\t", 1))
  8144. return FALSE;
  8145. m_TotalMsgSize += 1;
  8146. SavedLine = RealEndOfLine;
  8147. }
  8148. if(LineSize > 0)
  8149. {
  8150. if(!WriteMailFile(TextToWrite, LineSize))
  8151. return FALSE;
  8152. m_TotalMsgSize += LineSize;
  8153. }
  8154. if(!WriteMailFile("\r\n", 2))
  8155. return FALSE;
  8156. m_TotalMsgSize += 2;
  8157. return TRUE;
  8158. #endif*/
  8159. return FALSE;
  8160. }
  8161. BOOL SMTP_CONNECTION::AddToField(void)
  8162. {
  8163. /*#if 0
  8164. char szWriteBuffer [1000];
  8165. char szNameBuf[MAX_INTERNET_NAME + 100];
  8166. int LineSize = 0;
  8167. int PrevLineSize = 0;
  8168. DWORD NumAddress = 0;
  8169. DWORD BuffOffSet = 0;
  8170. DWORD MaxLineSize = 78;
  8171. CAddr * pCAddr = NULL;
  8172. PLIST_ENTRY pentry = NULL;
  8173. char * HeadChar ="";
  8174. //write the "To: " first. This includes the space
  8175. BuffOffSet = (DWORD) wsprintf(szWriteBuffer, "To: ");
  8176. //get the first address
  8177. pCAddr = MailInfo->GetFirstAddress(&pentry);
  8178. while(pCAddr != NULL)
  8179. {
  8180. if(NumAddress)
  8181. {
  8182. HeadChar = ", ";
  8183. }
  8184. LineSize = wsprintf(szNameBuf, "%s<%s>", HeadChar, pCAddr->GetAddress());
  8185. if( (DWORD)(LineSize + PrevLineSize) < MaxLineSize)
  8186. {
  8187. CopyMemory(&szWriteBuffer[BuffOffSet], szNameBuf, LineSize);
  8188. PrevLineSize += LineSize;
  8189. BuffOffSet += LineSize;
  8190. }
  8191. else
  8192. {
  8193. szWriteBuffer[BuffOffSet++] = CR;
  8194. szWriteBuffer[BuffOffSet++] = LF;
  8195. if(!WriteMailFile(szWriteBuffer, BuffOffSet))
  8196. return FALSE;
  8197. m_TotalMsgSize += BuffOffSet;
  8198. szWriteBuffer[0] = '\t';
  8199. CopyMemory(&szWriteBuffer[1], szNameBuf, LineSize);
  8200. BuffOffSet = LineSize + 1;
  8201. PrevLineSize = BuffOffSet;
  8202. }
  8203. NumAddress++;
  8204. //get the first address
  8205. pCAddr = MailInfo->GetNextAddress(&pentry);
  8206. }
  8207. szWriteBuffer[BuffOffSet++] = CR;
  8208. szWriteBuffer[BuffOffSet++] = LF;
  8209. if(WriteMailFile(szWriteBuffer, BuffOffSet))
  8210. {
  8211. m_TotalMsgSize += BuffOffSet ;
  8212. return TRUE;
  8213. }
  8214. return FALSE;
  8215. #endif*/
  8216. return TRUE;
  8217. }