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.

560 lines
18 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File:
  5. // smtpconn.cpp
  6. // Description:
  7. // Implementation of CSMTPConn
  8. // Author: Mike Swafford (MikeSwa)
  9. //
  10. // History:
  11. //
  12. // Copyright (C) 1998 Microsoft Corporation
  13. //
  14. //-----------------------------------------------------------------------------
  15. #include "aqprecmp.h"
  16. #include "SMTPConn.h"
  17. #include "connmgr.h"
  18. #include "domcfg.h"
  19. CPool CSMTPConn::s_SMTPConnPool;
  20. //---[ CSMTPConn::CSMTPConn() ]------------------------------------------------
  21. //
  22. //
  23. // Description:
  24. // CSMTPConn constructor
  25. // Parameters:
  26. // IN pConnMgr Ptr to instance connection manager
  27. // IN plmq Ptr to link for this connection
  28. // IN cMaxMessagesPerConnection Max messages to send per connection
  29. // 0 implies unlimited
  30. // Returns:
  31. // -
  32. //
  33. //-----------------------------------------------------------------------------
  34. CSMTPConn::CSMTPConn(CConnMgr *pConnMgr, CLinkMsgQueue *plmq,
  35. DWORD cMaxMessagesPerConnection)
  36. {
  37. _ASSERT(pConnMgr);
  38. _ASSERT(plmq);
  39. m_dwSignature = SMTP_CONNECTION_SIG;
  40. m_pConnMgr = pConnMgr;
  41. m_pIntDomainInfo = NULL;
  42. m_plmq = plmq;
  43. m_cFailedMsgs = 0;
  44. m_cTriedMsgs = 0;
  45. m_cMaxMessagesPerConnection = cMaxMessagesPerConnection;
  46. m_dwConnectionStatus = CONNECTION_STATUS_OK;
  47. m_szDomainName = NULL;
  48. m_cbDomainName = 0;
  49. m_liConnections.Flink = NULL;
  50. m_liConnections.Blink = NULL;
  51. m_cAcks = 0;
  52. m_dwTickCountOfLastAck = 0;
  53. ZeroMemory(m_szConnectedIPAddress, sizeof(m_szConnectedIPAddress));
  54. if (plmq)
  55. {
  56. plmq->AddRef();
  57. }
  58. }
  59. //---[ CSMTPConn::~CSMTPConn() ]-----------------------------------------------
  60. //
  61. //
  62. // Description:
  63. // CSMTPConn default destructor
  64. // Parameters:
  65. // -
  66. // Returns:
  67. // -
  68. //
  69. //-----------------------------------------------------------------------------
  70. CSMTPConn::~CSMTPConn()
  71. {
  72. HRESULT hrConnectionStatus = S_OK;
  73. BOOL fForceCheckForDSNGeneration = FALSE;
  74. _ASSERT(m_cAcks == m_cTriedMsgs);
  75. if (m_plmq != NULL)
  76. {
  77. _ASSERT(m_pConnMgr);
  78. m_pConnMgr->ReleaseConnection(this, &fForceCheckForDSNGeneration);
  79. switch(m_dwConnectionStatus)
  80. {
  81. case CONNECTION_STATUS_OK:
  82. hrConnectionStatus = S_OK;
  83. break;
  84. case CONNECTION_STATUS_FAILED:
  85. hrConnectionStatus = AQUEUE_E_HOST_NOT_RESPONDING;
  86. break;
  87. case CONNECTION_STATUS_DROPPED:
  88. hrConnectionStatus = AQUEUE_E_CONNECTION_DROPPED;
  89. break;
  90. case CONNECTION_STATUS_FAILED_LOOPBACK:
  91. hrConnectionStatus = AQUEUE_E_LOOPBACK_DETECTED;
  92. break;
  93. case CONNECTION_STATUS_FAILED_NDR_UNDELIVERED:
  94. hrConnectionStatus = AQUEUE_E_SMTP_GENERIC_ERROR;
  95. break;
  96. default:
  97. _ASSERT(0 && "Undefined Connection Status");
  98. hrConnectionStatus = S_OK;
  99. }
  100. m_plmq->SetLastConnectionFailure(hrConnectionStatus);
  101. m_plmq->RemoveConnection(this, fForceCheckForDSNGeneration);
  102. m_plmq->Release();
  103. //We should kick the connection manager, because if we were generating
  104. //DSNs, no connection could be made
  105. m_pConnMgr->KickConnections();
  106. }
  107. if (m_pIntDomainInfo)
  108. m_pIntDomainInfo->Release();
  109. }
  110. //---[ CSMTPConn::QueryInterface ]------------------------------------------
  111. //
  112. //
  113. // Description:
  114. // QueryInterface for IAdvQueue
  115. // Parameters:
  116. //
  117. // Returns:
  118. // S_OK on success
  119. //
  120. // Notes:
  121. // This implementation makes it possible for any server component to get
  122. // the IAdvQueueConfig interface.
  123. //
  124. // History:
  125. // 11/27/2001 - MikeSwa copied from CAQSvrInst
  126. //
  127. //-----------------------------------------------------------------------------
  128. STDMETHODIMP CSMTPConn::QueryInterface(REFIID riid, LPVOID * ppvObj)
  129. {
  130. HRESULT hr = S_OK;
  131. if (!ppvObj)
  132. {
  133. hr = E_INVALIDARG;
  134. goto Exit;
  135. }
  136. if (IID_IUnknown == riid)
  137. {
  138. *ppvObj = static_cast<ISMTPConnection *>(this);
  139. }
  140. else if (IID_ISMTPConnection == riid)
  141. {
  142. *ppvObj = static_cast<ISMTPConnection *>(this);
  143. }
  144. else if (IID_IConnectionPropertyManagement == riid)
  145. {
  146. *ppvObj = static_cast<IConnectionPropertyManagement *>(this);
  147. }
  148. else
  149. {
  150. *ppvObj = NULL;
  151. hr = E_NOINTERFACE;
  152. goto Exit;
  153. }
  154. static_cast<IUnknown *>(*ppvObj)->AddRef();
  155. Exit:
  156. return hr;
  157. }
  158. //---[ CSMTPConn::GetNextMessage ]---------------------------------------------
  159. //
  160. //
  161. // Description:
  162. // Implementation of ISMTPConnection::GetNextMsg.
  163. // Gets the next message queued for this connection and determines which
  164. // recipients should be delivered for this connection.
  165. // Parameters:
  166. // OUT ppimsg New IMsg top be delivered
  167. // OUT pdwMsgContext A 32-bit Context that needs to be provided in the
  168. // message ack.
  169. // OUT pcIndexes The number of index in prgdwRecipIndex
  170. // OUT prgdwRecipIndex Recipient indexes that the caller is responsible
  171. // for attempting delivery to.
  172. // Returns:
  173. //
  174. //
  175. //-----------------------------------------------------------------------------
  176. STDMETHODIMP CSMTPConn::GetNextMessage(
  177. OUT IMailMsgProperties **ppIMailMsgProperties,
  178. OUT DWORD ** ppvMsgContext,
  179. OUT DWORD * pcIndexes,
  180. OUT DWORD ** prgdwRecipIndex)
  181. {
  182. TraceFunctEnterEx((LPARAM) this, "CSMTPConn::GetNextMessage");
  183. HRESULT hr = S_OK;
  184. //We get the next message only if we are under the batch limit
  185. if(m_cMaxMessagesPerConnection &&
  186. (m_cTriedMsgs >= m_cMaxMessagesPerConnection) &&
  187. (!m_pIntDomainInfo ||
  188. !((DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY) &
  189. m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags)))
  190. {
  191. //SMTP does not check - but we may need a specific error for this case
  192. hr = AQUEUE_E_QUEUE_EMPTY;
  193. goto Exit;
  194. }
  195. if (m_pConnMgr && m_pConnMgr->fConnectionsStoppedByAdmin())
  196. {
  197. //Admin has requested that all outbound connections stop
  198. hr = AQUEUE_E_QUEUE_EMPTY;
  199. goto Exit;
  200. }
  201. hr = m_plmq->HrGetNextMsg(&m_dcntxtCurrentDeliveryContext, ppIMailMsgProperties,
  202. pcIndexes, prgdwRecipIndex);
  203. if (FAILED(hr))
  204. goto Exit;
  205. //this will automagically catch the queue empty case...
  206. //If the Link has no more messages it will return AQUEUE_E_QUEUE_EMPTY, which
  207. //should cause the caller to Release() and query GetNextConnection again.
  208. *ppvMsgContext = (DWORD *) &m_dcntxtCurrentDeliveryContext;
  209. //increment the messages served
  210. InterlockedIncrement((PLONG)&m_cTriedMsgs);
  211. Exit:
  212. if (!m_cTriedMsgs)
  213. DebugTrace((LPARAM) this, "GetNextMessage called, but no messages tried for this connection");
  214. //rewrite error for SMTPSVC
  215. if (AQUEUE_E_QUEUE_EMPTY == hr)
  216. hr = HRESULT_FROM_WIN32(ERROR_EMPTY);
  217. TraceFunctLeave();
  218. return hr;
  219. }
  220. //---[ CSMTPConn::AckMessage ]-------------------------------------------------
  221. //
  222. //
  223. // Description:
  224. // Acknowledges the delivery of a message (success/error codes are put in
  225. // the envelope by the transport).
  226. //
  227. // Implements ISMTPConnection::AckMessage();
  228. // Parameters:
  229. // IN pIMsg IMsg to acknowledge
  230. // IN dwMsgContext Context that was returned by GetNextMessage
  231. // IN eMsgStatus Status of message
  232. // Returns:
  233. // S_OK on success
  234. // E_INVALIDARG if dwMsgContext is invalid
  235. //
  236. //-----------------------------------------------------------------------------
  237. STDMETHODIMP CSMTPConn::AckMessage(/*[in]*/ MessageAck *pMsgAck)
  238. {
  239. HRESULT hr = S_OK;
  240. DWORD dwTickCount = GetTickCount();
  241. _ASSERT(m_plmq);
  242. _ASSERT(pMsgAck);
  243. if (!(pMsgAck->dwMsgStatus & MESSAGE_STATUS_ALL_DELIVERED))
  244. {
  245. m_cFailedMsgs++;
  246. }
  247. InterlockedIncrement((PLONG)&m_cAcks);
  248. _ASSERT(m_cAcks == m_cTriedMsgs);
  249. hr = m_plmq->HrAckMsg(pMsgAck);
  250. m_dwTickCountOfLastAck = dwTickCount; //Set after assert so we can compare
  251. return hr;
  252. }
  253. //---[ CSMTPConn::GetSMTPDomain ]----------------------------------------------
  254. //
  255. //
  256. // Description:
  257. // Returns the SMTPDomain of the link associated with this connections.
  258. //
  259. // $$REVIEW:
  260. // This method does not allocate new memory for this string, but instead
  261. // relies on the good intentions of the SMTP stack (or test driver) to
  262. // not overwrite this memory. If we ever expose this interface externally,
  263. // then we should revert to allocating memory and doing a buffer copy
  264. //
  265. // Implements ISMTPConnection::GetSMTPDomain
  266. // Parameters:
  267. // IN OUT pDomainInfo Ptr to DomainInfo struct supplied by caller
  268. // and filled in here
  269. // Returns:
  270. // S_OK on success
  271. //
  272. //-----------------------------------------------------------------------------
  273. STDMETHODIMP CSMTPConn::GetDomainInfo(IN OUT DomainInfo *pDomainInfo)
  274. {
  275. HRESULT hr = S_OK;
  276. _ASSERT(pDomainInfo->cbVersion >= sizeof(DomainInfo));
  277. _ASSERT(pDomainInfo);
  278. if (NULL == m_plmq)
  279. {
  280. hr = AQUEUE_E_LINK_INVALID;
  281. goto Exit;
  282. }
  283. if (!m_pIntDomainInfo)
  284. {
  285. //Try to get domain info
  286. hr = m_plmq->HrGetDomainInfo(&m_cbDomainName, &m_szDomainName,
  287. &m_pIntDomainInfo);
  288. if (FAILED(hr))
  289. {
  290. m_pIntDomainInfo = NULL;
  291. _ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
  292. goto Exit;
  293. }
  294. }
  295. _ASSERT(m_pIntDomainInfo);
  296. _ASSERT(m_cbDomainName);
  297. _ASSERT(m_szDomainName);
  298. // Is it OK to send client side commands on this connection
  299. // If not, we reset those domain info flags so SMTp cannot see them
  300. if(!m_plmq->fCanSendCmd())
  301. {
  302. m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &= ~(DOMAIN_INFO_SEND_TURN | DOMAIN_INFO_SEND_ETRN);
  303. }
  304. // If SMTP doesn't have the DOMAIN_INFO_TURN_ON_EMPTY then it is the older,
  305. // broken SMTP and we shouldn't allow TURN on empty to work.
  306. if ((m_plmq->cGetTotalMsgCount() == 0) &&
  307. !(m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
  308. DOMAIN_INFO_TURN_ON_EMPTY))
  309. {
  310. m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &= ~DOMAIN_INFO_SEND_TURN;
  311. }
  312. //copy everything but size
  313. memcpy(&(pDomainInfo->dwDomainInfoFlags),
  314. &(m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags),
  315. sizeof(DomainInfo) - sizeof(DWORD));
  316. //make sure our assumptions about the struct of DomainInfo are valid
  317. _ASSERT(1 == ((DWORD *) &(pDomainInfo->dwDomainInfoFlags)) - ((DWORD *) pDomainInfo));
  318. //we've filled pDomainInfo with the info for our Domain
  319. if (pDomainInfo->szDomainName[0] == '*')
  320. {
  321. //we matched a wildcard domain... substitute our domain name
  322. pDomainInfo->cbDomainNameLength = m_cbDomainName;
  323. pDomainInfo->szDomainName = m_szDomainName;
  324. }
  325. else
  326. {
  327. //if it wasn't a wildcard match... strings should match!
  328. _ASSERT(0 == _stricmp(m_szDomainName, pDomainInfo->szDomainName));
  329. }
  330. Exit:
  331. return hr;
  332. }
  333. //---[ CSMTPConn::SetDiagnosticInfo ]------------------------------------------
  334. //
  335. //
  336. // Description:
  337. // Sets the extra diagnostic information for this connection.
  338. // Parameters:
  339. // IN hrDiagnosticError Error code... if SUCCESS we thow away
  340. // the rest of the information
  341. // IN szDiagnosticVerb String pointing to the protocol
  342. // verb that caused the failure.
  343. // IN szDiagnosticResponse String that contains the remote
  344. // servers response.
  345. // Returns:
  346. // S_OK always
  347. // History:
  348. // 2/18/99 - MikeSwa Created
  349. //
  350. //-----------------------------------------------------------------------------
  351. STDMETHODIMP CSMTPConn::SetDiagnosticInfo(
  352. IN HRESULT hrDiagnosticError,
  353. IN LPCSTR szDiagnosticVerb,
  354. IN LPCSTR szDiagnosticResponse)
  355. {
  356. TraceFunctEnterEx((LPARAM) this, "CSMTPConn::SetDiagnosticInfo");
  357. if (m_plmq && FAILED(hrDiagnosticError))
  358. {
  359. m_plmq->SetDiagnosticInfo(hrDiagnosticError, szDiagnosticVerb,
  360. szDiagnosticResponse);
  361. }
  362. TraceFunctLeave();
  363. return S_OK; //always return S_OK
  364. }
  365. //---[ CSMTPConn::CopyQueuePropertiesToSession ]-------------------------------
  366. //
  367. //
  368. // Description:
  369. // Copies the set of propties that queuing owns into the SMTP session
  370. // object. In some cases, these properties are required for security
  371. // reasons (ie - a sink wants to know who we think we are connecting to
  372. // instead of who the remote side says they are).
  373. // Parameters:
  374. // IN IUnknown SMTP Session object to copy properties to
  375. // NOTE: We need to resist the urge to AddRef and keep this around
  376. // later use. AddRefs on this object are actually ignored as the
  377. // lifetime is controlled by either the SMTP connection object
  378. // or the stack.
  379. //
  380. // Returns:
  381. // S_OK always
  382. // History:
  383. // 11/27/2001 - MikeSwa Created
  384. //
  385. //-----------------------------------------------------------------------------
  386. STDMETHODIMP CSMTPConn::CopyQueuePropertiesToSession(IN IUnknown *pISession)
  387. {
  388. TraceFunctEnterEx((LPARAM) this, "SMTPConn::CopyQueuePropertiesToSession");
  389. HRESULT hr = S_OK;
  390. IMailMsgPropertyBag *pISessionProperties = NULL;
  391. LPSTR szConnectorName = NULL;
  392. if (!pISession) {
  393. ErrorTrace((LPARAM) this, "NULL ISession - bailing");
  394. hr = E_POINTER;
  395. goto Exit;
  396. }
  397. //
  398. // Get the property bag object
  399. //
  400. hr = pISession->QueryInterface(IID_IMailMsgPropertyBag,
  401. (PVOID *) &pISessionProperties);
  402. if (FAILED(hr)) {
  403. ErrorTrace((LPARAM) this,
  404. "QI for IID_IMailMsgPropertyBag failed 0x%08X", hr);
  405. pISessionProperties = NULL;
  406. goto Exit;
  407. }
  408. //
  409. // Copy the next hop name into the session property bag
  410. //
  411. hr = pISessionProperties->PutStringA(ISESSION_PID_OUT_ROUTE_ADDRESS,
  412. m_szDomainName);
  413. if (FAILED(hr)) {
  414. ErrorTrace((LPARAM) this,
  415. "Unable to write ISESSION_PID_OUT_ROUTE_ADDRESS hr - 0x%08X", hr);
  416. }
  417. if (m_plmq)
  418. szConnectorName = m_plmq->szGetConnectorName();
  419. if (szConnectorName) {
  420. hr = pISessionProperties->PutStringA(ISESSION_PID_OUT_CONNECTOR_NAME,
  421. szConnectorName);
  422. if (FAILED(hr)) {
  423. ErrorTrace((LPARAM) this,
  424. "Unable to write ISESSION_PID_OUT_CONNECTOR_NAME 0x%08X", hr);
  425. }
  426. }
  427. else {
  428. DebugTrace((LPARAM) this,
  429. "szConnectorName is NULL... not writing to ISession");
  430. }
  431. Exit:
  432. if (pISessionProperties)
  433. pISessionProperties->Release();
  434. TraceFunctLeave();
  435. return S_OK;
  436. }
  437. //---[ CSMTPConn::CopySessionPropertiesToQueue ]-------------------------------
  438. //
  439. //
  440. // Description:
  441. // Copies the set of propties that the protocol owns into the queue
  442. // object. In some cases, these properties are required for diagnostic
  443. // reasons (ie - an admin wants to know which IP address we connected to).
  444. // Parameters:
  445. // IN IUnknown SMTP Session object to copy properties to
  446. // NOTE: We need to resist the urge to AddRef and keep this around
  447. // later use. AddRefs on this object are actually ignored as the
  448. // lifetime is controlled by either the SMTP connection object
  449. // or the stack.
  450. //
  451. // Returns:
  452. // S_OK always
  453. // History:
  454. // 11/27/2001 - MikeSwa Created
  455. //
  456. //-----------------------------------------------------------------------------
  457. STDMETHODIMP CSMTPConn::CopySessionPropertiesToQueue(IN IUnknown *pISession)
  458. {
  459. TraceFunctEnterEx((LPARAM) this, "SMTPConn::CopySessionPropertiesToQueue");
  460. HRESULT hr = S_OK;
  461. IMailMsgPropertyBag *pISessionProperties = NULL;
  462. if (!pISession) {
  463. ErrorTrace((LPARAM) this, "NULL ISession - bailing");
  464. hr = E_POINTER;
  465. goto Exit;
  466. }
  467. //
  468. // Get the property bag object
  469. //
  470. hr = pISession->QueryInterface(IID_IMailMsgPropertyBag,
  471. (PVOID *) &pISessionProperties);
  472. if (FAILED(hr)) {
  473. ErrorTrace((LPARAM) this,
  474. "QI for IID_IMailMsgPropertyBag failed 0x%08X", hr);
  475. pISessionProperties = NULL;
  476. goto Exit;
  477. }
  478. hr = pISessionProperties->GetStringA(ISESSION_PID_REMOTE_IP_ADDRESS,
  479. sizeof(m_szConnectedIPAddress)-1,
  480. (CHAR *) &m_szConnectedIPAddress);
  481. if (FAILED(hr)) {
  482. DebugTrace((LPARAM) this,
  483. "Unable to read ISESSION_PID_REMOTE_IP_ADDRESS - 0x%08X", hr);
  484. m_szConnectedIPAddress[0] = '\0';
  485. }
  486. Exit:
  487. if (pISessionProperties)
  488. pISessionProperties->Release();
  489. TraceFunctLeave();
  490. return S_OK;
  491. }