Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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