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
560 lines
18 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// File:
|
|
// smtpconn.cpp
|
|
// Description:
|
|
// Implementation of CSMTPConn
|
|
// Author: Mike Swafford (MikeSwa)
|
|
//
|
|
// History:
|
|
//
|
|
// Copyright (C) 1998 Microsoft Corporation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "aqprecmp.h"
|
|
#include "SMTPConn.h"
|
|
#include "connmgr.h"
|
|
#include "domcfg.h"
|
|
|
|
CPool CSMTPConn::s_SMTPConnPool;
|
|
|
|
//---[ CSMTPConn::CSMTPConn() ]------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// CSMTPConn constructor
|
|
// Parameters:
|
|
// IN pConnMgr Ptr to instance connection manager
|
|
// IN plmq Ptr to link for this connection
|
|
// IN cMaxMessagesPerConnection Max messages to send per connection
|
|
// 0 implies unlimited
|
|
// Returns:
|
|
// -
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CSMTPConn::CSMTPConn(CConnMgr *pConnMgr, CLinkMsgQueue *plmq,
|
|
DWORD cMaxMessagesPerConnection)
|
|
{
|
|
_ASSERT(pConnMgr);
|
|
_ASSERT(plmq);
|
|
|
|
m_dwSignature = SMTP_CONNECTION_SIG;
|
|
m_pConnMgr = pConnMgr;
|
|
m_pIntDomainInfo = NULL;
|
|
m_plmq = plmq;
|
|
m_cFailedMsgs = 0;
|
|
m_cTriedMsgs = 0;
|
|
m_cMaxMessagesPerConnection = cMaxMessagesPerConnection;
|
|
m_dwConnectionStatus = CONNECTION_STATUS_OK;
|
|
m_szDomainName = NULL;
|
|
m_cbDomainName = 0;
|
|
m_liConnections.Flink = NULL;
|
|
m_liConnections.Blink = NULL;
|
|
m_cAcks = 0;
|
|
m_dwTickCountOfLastAck = 0;
|
|
|
|
ZeroMemory(m_szConnectedIPAddress, sizeof(m_szConnectedIPAddress));
|
|
if (plmq)
|
|
{
|
|
plmq->AddRef();
|
|
}
|
|
}
|
|
|
|
//---[ CSMTPConn::~CSMTPConn() ]-----------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// CSMTPConn default destructor
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CSMTPConn::~CSMTPConn()
|
|
{
|
|
HRESULT hrConnectionStatus = S_OK;
|
|
BOOL fForceCheckForDSNGeneration = FALSE;
|
|
_ASSERT(m_cAcks == m_cTriedMsgs);
|
|
if (m_plmq != NULL)
|
|
{
|
|
_ASSERT(m_pConnMgr);
|
|
m_pConnMgr->ReleaseConnection(this, &fForceCheckForDSNGeneration);
|
|
|
|
switch(m_dwConnectionStatus)
|
|
{
|
|
case CONNECTION_STATUS_OK:
|
|
hrConnectionStatus = S_OK;
|
|
break;
|
|
case CONNECTION_STATUS_FAILED:
|
|
hrConnectionStatus = AQUEUE_E_HOST_NOT_RESPONDING;
|
|
break;
|
|
case CONNECTION_STATUS_DROPPED:
|
|
hrConnectionStatus = AQUEUE_E_CONNECTION_DROPPED;
|
|
break;
|
|
case CONNECTION_STATUS_FAILED_LOOPBACK:
|
|
hrConnectionStatus = AQUEUE_E_LOOPBACK_DETECTED;
|
|
break;
|
|
case CONNECTION_STATUS_FAILED_NDR_UNDELIVERED:
|
|
hrConnectionStatus = AQUEUE_E_SMTP_GENERIC_ERROR;
|
|
break;
|
|
default:
|
|
_ASSERT(0 && "Undefined Connection Status");
|
|
hrConnectionStatus = S_OK;
|
|
}
|
|
|
|
m_plmq->SetLastConnectionFailure(hrConnectionStatus);
|
|
m_plmq->RemoveConnection(this, fForceCheckForDSNGeneration);
|
|
|
|
m_plmq->Release();
|
|
|
|
//We should kick the connection manager, because if we were generating
|
|
//DSNs, no connection could be made
|
|
m_pConnMgr->KickConnections();
|
|
}
|
|
|
|
if (m_pIntDomainInfo)
|
|
m_pIntDomainInfo->Release();
|
|
|
|
}
|
|
|
|
//---[ CSMTPConn::QueryInterface ]------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// QueryInterface for IAdvQueue
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
// S_OK on success
|
|
//
|
|
// Notes:
|
|
// This implementation makes it possible for any server component to get
|
|
// the IAdvQueueConfig interface.
|
|
//
|
|
// History:
|
|
// 11/27/2001 - MikeSwa copied from CAQSvrInst
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CSMTPConn::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!ppvObj)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
if (IID_IUnknown == riid)
|
|
{
|
|
*ppvObj = static_cast<ISMTPConnection *>(this);
|
|
}
|
|
else if (IID_ISMTPConnection == riid)
|
|
{
|
|
*ppvObj = static_cast<ISMTPConnection *>(this);
|
|
}
|
|
else if (IID_IConnectionPropertyManagement == riid)
|
|
{
|
|
*ppvObj = static_cast<IConnectionPropertyManagement *>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
hr = E_NOINTERFACE;
|
|
goto Exit;
|
|
}
|
|
|
|
static_cast<IUnknown *>(*ppvObj)->AddRef();
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
//---[ CSMTPConn::GetNextMessage ]---------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Implementation of ISMTPConnection::GetNextMsg.
|
|
// Gets the next message queued for this connection and determines which
|
|
// recipients should be delivered for this connection.
|
|
// Parameters:
|
|
// OUT ppimsg New IMsg top be delivered
|
|
// OUT pdwMsgContext A 32-bit Context that needs to be provided in the
|
|
// message ack.
|
|
// OUT pcIndexes The number of index in prgdwRecipIndex
|
|
// OUT prgdwRecipIndex Recipient indexes that the caller is responsible
|
|
// for attempting delivery to.
|
|
// Returns:
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CSMTPConn::GetNextMessage(
|
|
OUT IMailMsgProperties **ppIMailMsgProperties,
|
|
OUT DWORD ** ppvMsgContext,
|
|
OUT DWORD * pcIndexes,
|
|
OUT DWORD ** prgdwRecipIndex)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CSMTPConn::GetNextMessage");
|
|
HRESULT hr = S_OK;
|
|
|
|
//We get the next message only if we are under the batch limit
|
|
|
|
if(m_cMaxMessagesPerConnection &&
|
|
(m_cTriedMsgs >= m_cMaxMessagesPerConnection) &&
|
|
(!m_pIntDomainInfo ||
|
|
!((DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY) &
|
|
m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags)))
|
|
{
|
|
//SMTP does not check - but we may need a specific error for this case
|
|
hr = AQUEUE_E_QUEUE_EMPTY;
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_pConnMgr && m_pConnMgr->fConnectionsStoppedByAdmin())
|
|
{
|
|
//Admin has requested that all outbound connections stop
|
|
hr = AQUEUE_E_QUEUE_EMPTY;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = m_plmq->HrGetNextMsg(&m_dcntxtCurrentDeliveryContext, ppIMailMsgProperties,
|
|
pcIndexes, prgdwRecipIndex);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
//this will automagically catch the queue empty case...
|
|
//If the Link has no more messages it will return AQUEUE_E_QUEUE_EMPTY, which
|
|
//should cause the caller to Release() and query GetNextConnection again.
|
|
|
|
*ppvMsgContext = (DWORD *) &m_dcntxtCurrentDeliveryContext;
|
|
|
|
//increment the messages served
|
|
InterlockedIncrement((PLONG)&m_cTriedMsgs);
|
|
|
|
Exit:
|
|
if (!m_cTriedMsgs)
|
|
DebugTrace((LPARAM) this, "GetNextMessage called, but no messages tried for this connection");
|
|
|
|
//rewrite error for SMTPSVC
|
|
if (AQUEUE_E_QUEUE_EMPTY == hr)
|
|
hr = HRESULT_FROM_WIN32(ERROR_EMPTY);
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CSMTPConn::AckMessage ]-------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Acknowledges the delivery of a message (success/error codes are put in
|
|
// the envelope by the transport).
|
|
//
|
|
// Implements ISMTPConnection::AckMessage();
|
|
// Parameters:
|
|
// IN pIMsg IMsg to acknowledge
|
|
// IN dwMsgContext Context that was returned by GetNextMessage
|
|
// IN eMsgStatus Status of message
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_INVALIDARG if dwMsgContext is invalid
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CSMTPConn::AckMessage(/*[in]*/ MessageAck *pMsgAck)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwTickCount = GetTickCount();
|
|
_ASSERT(m_plmq);
|
|
_ASSERT(pMsgAck);
|
|
|
|
if (!(pMsgAck->dwMsgStatus & MESSAGE_STATUS_ALL_DELIVERED))
|
|
{
|
|
m_cFailedMsgs++;
|
|
}
|
|
|
|
|
|
InterlockedIncrement((PLONG)&m_cAcks);
|
|
_ASSERT(m_cAcks == m_cTriedMsgs);
|
|
hr = m_plmq->HrAckMsg(pMsgAck);
|
|
|
|
m_dwTickCountOfLastAck = dwTickCount; //Set after assert so we can compare
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---[ CSMTPConn::GetSMTPDomain ]----------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns the SMTPDomain of the link associated with this connections.
|
|
//
|
|
// $$REVIEW:
|
|
// This method does not allocate new memory for this string, but instead
|
|
// relies on the good intentions of the SMTP stack (or test driver) to
|
|
// not overwrite this memory. If we ever expose this interface externally,
|
|
// then we should revert to allocating memory and doing a buffer copy
|
|
//
|
|
// Implements ISMTPConnection::GetSMTPDomain
|
|
// Parameters:
|
|
// IN OUT pDomainInfo Ptr to DomainInfo struct supplied by caller
|
|
// and filled in here
|
|
// Returns:
|
|
// S_OK on success
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CSMTPConn::GetDomainInfo(IN OUT DomainInfo *pDomainInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
_ASSERT(pDomainInfo->cbVersion >= sizeof(DomainInfo));
|
|
_ASSERT(pDomainInfo);
|
|
|
|
if (NULL == m_plmq)
|
|
{
|
|
hr = AQUEUE_E_LINK_INVALID;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!m_pIntDomainInfo)
|
|
{
|
|
//Try to get domain info
|
|
hr = m_plmq->HrGetDomainInfo(&m_cbDomainName, &m_szDomainName,
|
|
&m_pIntDomainInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
m_pIntDomainInfo = NULL;
|
|
_ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
_ASSERT(m_pIntDomainInfo);
|
|
_ASSERT(m_cbDomainName);
|
|
_ASSERT(m_szDomainName);
|
|
|
|
// Is it OK to send client side commands on this connection
|
|
// If not, we reset those domain info flags so SMTp cannot see them
|
|
if(!m_plmq->fCanSendCmd())
|
|
{
|
|
m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &= ~(DOMAIN_INFO_SEND_TURN | DOMAIN_INFO_SEND_ETRN);
|
|
}
|
|
|
|
// If SMTP doesn't have the DOMAIN_INFO_TURN_ON_EMPTY then it is the older,
|
|
// broken SMTP and we shouldn't allow TURN on empty to work.
|
|
if ((m_plmq->cGetTotalMsgCount() == 0) &&
|
|
!(m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
|
|
DOMAIN_INFO_TURN_ON_EMPTY))
|
|
{
|
|
m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &= ~DOMAIN_INFO_SEND_TURN;
|
|
}
|
|
|
|
//copy everything but size
|
|
memcpy(&(pDomainInfo->dwDomainInfoFlags),
|
|
&(m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags),
|
|
sizeof(DomainInfo) - sizeof(DWORD));
|
|
|
|
//make sure our assumptions about the struct of DomainInfo are valid
|
|
_ASSERT(1 == ((DWORD *) &(pDomainInfo->dwDomainInfoFlags)) - ((DWORD *) pDomainInfo));
|
|
|
|
//we've filled pDomainInfo with the info for our Domain
|
|
if (pDomainInfo->szDomainName[0] == '*')
|
|
{
|
|
//we matched a wildcard domain... substitute our domain name
|
|
pDomainInfo->cbDomainNameLength = m_cbDomainName;
|
|
pDomainInfo->szDomainName = m_szDomainName;
|
|
}
|
|
else
|
|
{
|
|
//if it wasn't a wildcard match... strings should match!
|
|
_ASSERT(0 == _stricmp(m_szDomainName, pDomainInfo->szDomainName));
|
|
}
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---[ CSMTPConn::SetDiagnosticInfo ]------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Sets the extra diagnostic information for this connection.
|
|
// Parameters:
|
|
// IN hrDiagnosticError Error code... if SUCCESS we thow away
|
|
// the rest of the information
|
|
// IN szDiagnosticVerb String pointing to the protocol
|
|
// verb that caused the failure.
|
|
// IN szDiagnosticResponse String that contains the remote
|
|
// servers response.
|
|
// Returns:
|
|
// S_OK always
|
|
// History:
|
|
// 2/18/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CSMTPConn::SetDiagnosticInfo(
|
|
IN HRESULT hrDiagnosticError,
|
|
IN LPCSTR szDiagnosticVerb,
|
|
IN LPCSTR szDiagnosticResponse)
|
|
{
|
|
|
|
TraceFunctEnterEx((LPARAM) this, "CSMTPConn::SetDiagnosticInfo");
|
|
|
|
if (m_plmq && FAILED(hrDiagnosticError))
|
|
{
|
|
m_plmq->SetDiagnosticInfo(hrDiagnosticError, szDiagnosticVerb,
|
|
szDiagnosticResponse);
|
|
}
|
|
TraceFunctLeave();
|
|
return S_OK; //always return S_OK
|
|
}
|
|
|
|
//---[ CSMTPConn::CopyQueuePropertiesToSession ]-------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Copies the set of propties that queuing owns into the SMTP session
|
|
// object. In some cases, these properties are required for security
|
|
// reasons (ie - a sink wants to know who we think we are connecting to
|
|
// instead of who the remote side says they are).
|
|
// Parameters:
|
|
// IN IUnknown SMTP Session object to copy properties to
|
|
// NOTE: We need to resist the urge to AddRef and keep this around
|
|
// later use. AddRefs on this object are actually ignored as the
|
|
// lifetime is controlled by either the SMTP connection object
|
|
// or the stack.
|
|
//
|
|
// Returns:
|
|
// S_OK always
|
|
// History:
|
|
// 11/27/2001 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CSMTPConn::CopyQueuePropertiesToSession(IN IUnknown *pISession)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "SMTPConn::CopyQueuePropertiesToSession");
|
|
HRESULT hr = S_OK;
|
|
IMailMsgPropertyBag *pISessionProperties = NULL;
|
|
LPSTR szConnectorName = NULL;
|
|
|
|
if (!pISession) {
|
|
ErrorTrace((LPARAM) this, "NULL ISession - bailing");
|
|
hr = E_POINTER;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Get the property bag object
|
|
//
|
|
hr = pISession->QueryInterface(IID_IMailMsgPropertyBag,
|
|
(PVOID *) &pISessionProperties);
|
|
|
|
if (FAILED(hr)) {
|
|
ErrorTrace((LPARAM) this,
|
|
"QI for IID_IMailMsgPropertyBag failed 0x%08X", hr);
|
|
pISessionProperties = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Copy the next hop name into the session property bag
|
|
//
|
|
hr = pISessionProperties->PutStringA(ISESSION_PID_OUT_ROUTE_ADDRESS,
|
|
m_szDomainName);
|
|
|
|
if (FAILED(hr)) {
|
|
ErrorTrace((LPARAM) this,
|
|
"Unable to write ISESSION_PID_OUT_ROUTE_ADDRESS hr - 0x%08X", hr);
|
|
}
|
|
|
|
if (m_plmq)
|
|
szConnectorName = m_plmq->szGetConnectorName();
|
|
|
|
if (szConnectorName) {
|
|
hr = pISessionProperties->PutStringA(ISESSION_PID_OUT_CONNECTOR_NAME,
|
|
szConnectorName);
|
|
|
|
if (FAILED(hr)) {
|
|
ErrorTrace((LPARAM) this,
|
|
"Unable to write ISESSION_PID_OUT_CONNECTOR_NAME 0x%08X", hr);
|
|
}
|
|
|
|
}
|
|
else {
|
|
DebugTrace((LPARAM) this,
|
|
"szConnectorName is NULL... not writing to ISession");
|
|
}
|
|
|
|
Exit:
|
|
if (pISessionProperties)
|
|
pISessionProperties->Release();
|
|
|
|
TraceFunctLeave();
|
|
return S_OK;
|
|
}
|
|
|
|
//---[ CSMTPConn::CopySessionPropertiesToQueue ]-------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Copies the set of propties that the protocol owns into the queue
|
|
// object. In some cases, these properties are required for diagnostic
|
|
// reasons (ie - an admin wants to know which IP address we connected to).
|
|
// Parameters:
|
|
// IN IUnknown SMTP Session object to copy properties to
|
|
// NOTE: We need to resist the urge to AddRef and keep this around
|
|
// later use. AddRefs on this object are actually ignored as the
|
|
// lifetime is controlled by either the SMTP connection object
|
|
// or the stack.
|
|
//
|
|
// Returns:
|
|
// S_OK always
|
|
// History:
|
|
// 11/27/2001 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CSMTPConn::CopySessionPropertiesToQueue(IN IUnknown *pISession)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "SMTPConn::CopySessionPropertiesToQueue");
|
|
HRESULT hr = S_OK;
|
|
IMailMsgPropertyBag *pISessionProperties = NULL;
|
|
|
|
if (!pISession) {
|
|
ErrorTrace((LPARAM) this, "NULL ISession - bailing");
|
|
hr = E_POINTER;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Get the property bag object
|
|
//
|
|
hr = pISession->QueryInterface(IID_IMailMsgPropertyBag,
|
|
(PVOID *) &pISessionProperties);
|
|
|
|
if (FAILED(hr)) {
|
|
ErrorTrace((LPARAM) this,
|
|
"QI for IID_IMailMsgPropertyBag failed 0x%08X", hr);
|
|
pISessionProperties = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = pISessionProperties->GetStringA(ISESSION_PID_REMOTE_IP_ADDRESS,
|
|
sizeof(m_szConnectedIPAddress)-1,
|
|
(CHAR *) &m_szConnectedIPAddress);
|
|
|
|
if (FAILED(hr)) {
|
|
DebugTrace((LPARAM) this,
|
|
"Unable to read ISESSION_PID_REMOTE_IP_ADDRESS - 0x%08X", hr);
|
|
m_szConnectedIPAddress[0] = '\0';
|
|
}
|
|
|
|
Exit:
|
|
if (pISessionProperties)
|
|
pISessionProperties->Release();
|
|
|
|
TraceFunctLeave();
|
|
return S_OK;
|
|
}
|
|
|