423 lines
14 KiB
C++
423 lines
14 KiB
C++
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
asynccon.cxx
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
--*/
|
|
|
|
#define INCL_INETSRV_INCS
|
|
#include "smtpinc.h"
|
|
#include "remoteq.hxx"
|
|
#include <asynccon.hxx>
|
|
#include <dnsreci.h>
|
|
#include <cdns.h>
|
|
#include "smtpdns.hxx"
|
|
#include "asyncmx.hxx"
|
|
#include "smtpmsg.h"
|
|
|
|
extern BOOL QueueCallBackFunction(PVOID ThisPtr, BOOLEAN fTimedOut);
|
|
extern void DeleteDnsRec(PSMTPDNS_RECS pDnsRec);
|
|
extern BOOL GetIpAddressFromDns(char * HostName, PSMTPDNS_RECS pDnsRec, DWORD Index);
|
|
|
|
CPool CAsyncSmtpDns::Pool(SMTP_ASYNCMX_SIGNATURE);
|
|
|
|
CAsyncSmtpDns::CAsyncSmtpDns(SMTP_SERVER_INSTANCE * pServiceInstance,
|
|
ISMTPConnection *pSmtpConnection)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::CAsyncSmtpDns");
|
|
DebugTrace((LPARAM) this, "Creating CAsyncSmtpDns object = 0x%08x", this);
|
|
|
|
m_Signature = SMTP_ASYNCMX_SIGNATURE;
|
|
m_DomainOptions = 0;
|
|
m_fConnectToSmartHost = FALSE;
|
|
m_pServiceInstance = pServiceInstance;
|
|
m_pISMTPConnection = pSmtpConnection;
|
|
m_pDNS_RESOLVER_RECORD = NULL;
|
|
m_fInitCalled = FALSE;
|
|
m_pszSSLVerificationName = NULL;
|
|
pServiceInstance->InsertAsyncDnsObject(this);
|
|
}
|
|
|
|
BOOL CAsyncSmtpDns::Init (LPSTR pszSSLVerificationName)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
TraceFunctEnterEx ((LPARAM) this, "CAsyncSmtpDns::Init");
|
|
|
|
m_fInitCalled = TRUE;
|
|
|
|
if (pszSSLVerificationName) {
|
|
m_pszSSLVerificationName = new char [lstrlen(pszSSLVerificationName) + 1];
|
|
if (!m_pszSSLVerificationName)
|
|
goto Exit;
|
|
|
|
lstrcpy (m_pszSSLVerificationName, pszSSLVerificationName);
|
|
}
|
|
|
|
fRet = TRUE;
|
|
Exit:
|
|
TraceFunctLeaveEx ((LPARAM) this);
|
|
return fRet;
|
|
}
|
|
|
|
CAsyncSmtpDns::~CAsyncSmtpDns()
|
|
{
|
|
DWORD dwAck = 0;
|
|
|
|
TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::~CAsyncSmtpDns");
|
|
|
|
DebugTrace((LPARAM) this, "Destructing CAsyncSmtpDns object = 0x%08x", this);
|
|
|
|
_ASSERT (m_fInitCalled && "Init not called on CAsyncSmtpDns");
|
|
|
|
//
|
|
// If we did not succeed, we need to ack the connection here (m_dwDiagnostic holds
|
|
// the error code to use). On the other hand, if we succeeded, then HandleCompletedData
|
|
// must have kicked off an async connection to the server SMTP, and the ISMTPConnection
|
|
// will be acked by the "async connect" code -- we don't need to do anything. The
|
|
// m_pISMTPConnection may also be legally set to NULL (see HandleCompletedData).
|
|
//
|
|
if(m_dwDiagnostic != ERROR_SUCCESS && m_pISMTPConnection)
|
|
{
|
|
if(AQUEUE_E_AUTHORITATIVE_HOST_NOT_FOUND == m_dwDiagnostic)
|
|
dwAck = CONNECTION_STATUS_FAILED_NDR_UNDELIVERED;
|
|
else
|
|
dwAck = CONNECTION_STATUS_FAILED;
|
|
|
|
DebugTrace((LPARAM) this, "Connection status: %d, Failure: %d", dwAck, m_dwDiagnostic);
|
|
m_pISMTPConnection->AckConnection(dwAck);
|
|
m_pISMTPConnection->SetDiagnosticInfo(m_dwDiagnostic, NULL, NULL);
|
|
m_pISMTPConnection->Release();
|
|
m_pISMTPConnection = NULL;
|
|
}
|
|
|
|
if(m_pDNS_RESOLVER_RECORD != NULL)
|
|
{
|
|
DebugTrace((LPARAM) this, "Deleting DNS_RESOLVER_RECORD in Async SMTP obj");
|
|
delete m_pDNS_RESOLVER_RECORD;
|
|
m_pDNS_RESOLVER_RECORD = NULL;
|
|
}
|
|
DBG_CODE(else DebugTrace((LPARAM) this, "No DNS_RESOLVER_RECORD set for Async SMTP obj"));
|
|
|
|
if(m_pszSSLVerificationName)
|
|
delete [] m_pszSSLVerificationName;
|
|
|
|
m_pServiceInstance->RemoveAsyncDnsObject(this);
|
|
TraceFunctLeaveEx((LPARAM) this);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Description:
|
|
// Given a pDnsRec (array of host IP pairs) and an index into it, this
|
|
// tries to resolve the host at the Index position. It is assumed that
|
|
// the caller (GetMissingIpAddresses) has checked that the host at that
|
|
// index lacks an IP address.
|
|
// Arguments:
|
|
// IN PSMTPDNS_RECS pDnsRec --- Array of (host, IP) pairs.
|
|
// IN DWORD Index --- Index of host in pDnsRec to set IP for.
|
|
// Returns:
|
|
// TRUE --- Success IP was filled in for host.
|
|
// FALSE --- Either the host was not resolved from DNS or an error
|
|
// occurred (like "out of memory").
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CAsyncSmtpDns::GetIpFromDns(PSMTPDNS_RECS pDnsRec, DWORD Index)
|
|
{
|
|
struct hostent *hp = NULL;
|
|
MXIPLIST_ENTRY * pEntry = NULL;
|
|
BOOL fReturn = FALSE;
|
|
|
|
TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::GetIpFromDns");
|
|
|
|
if(m_pServiceInstance->GetNameResolution() == RESOLUTION_UNCACHEDDNS)
|
|
{
|
|
fReturn = GetIpAddressFromDns(pDnsRec->DnsArray[Index]->DnsName, pDnsRec, Index);
|
|
TraceFunctLeaveEx((LPARAM) this);
|
|
return fReturn;
|
|
}
|
|
|
|
hp = gethostbyname (pDnsRec->DnsArray[Index]->DnsName);
|
|
if(hp != NULL)
|
|
{
|
|
fReturn = TRUE;
|
|
for (DWORD Loop = 0; !m_pServiceInstance->IsShuttingDown() && (hp->h_addr_list[Loop] != NULL); Loop++)
|
|
{
|
|
pEntry = new MXIPLIST_ENTRY;
|
|
if(pEntry != NULL)
|
|
{
|
|
pDnsRec->DnsArray[Index]->NumEntries++;
|
|
CopyMemory(&pEntry->IpAddress, hp->h_addr_list[Loop], 4);
|
|
InsertTailList(&pDnsRec->DnsArray[Index]->IpListHead, &pEntry->ListEntry);
|
|
}
|
|
else
|
|
{
|
|
fReturn = FALSE;
|
|
ErrorTrace((LPARAM) this, "Not enough memory");
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorTrace((LPARAM) this, "gethostbyname failed on %s", pDnsRec->DnsArray[Index]->DnsName);
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
}
|
|
|
|
TraceFunctLeaveEx((LPARAM) this);
|
|
return fReturn;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Description:
|
|
// This runs through the list of hosts (MX hosts, or if no MX records were
|
|
// returned, the single target host) and verifies that they all have been
|
|
// resolved to IP addresses. If any have been found that do not have IP
|
|
// addresses, it will call GetIpFromDns to resolve it.
|
|
// Arguments:
|
|
// IN PSMTPDNS_RECS pDnsRec -- Object containing Host-IP pairs. Hosts
|
|
// without and IP are filled in.
|
|
// Returns:
|
|
// TRUE -- Success, all hosts have IP addresses.
|
|
// FALSE -- Unable to resolve all hosts to IP addresses, or some internal
|
|
// error occurred (like "out of memory" or "shutdown in progress".
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CAsyncSmtpDns::GetMissingIpAddresses(PSMTPDNS_RECS pDnsRec)
|
|
{
|
|
DWORD Count = 0;
|
|
DWORD Error = 0;
|
|
BOOL fSucceededOnce = FALSE;
|
|
|
|
if(pDnsRec == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
while(!m_pServiceInstance->IsShuttingDown() && pDnsRec->DnsArray[Count] != NULL)
|
|
{
|
|
if(IsListEmpty(&pDnsRec->DnsArray[Count]->IpListHead))
|
|
{
|
|
SetLastError(NO_ERROR);
|
|
if(!GetIpFromDns(pDnsRec, Count))
|
|
{
|
|
Error = GetLastError();
|
|
if(Error != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fSucceededOnce = TRUE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
fSucceededOnce = TRUE;
|
|
}
|
|
|
|
|
|
Count++;
|
|
}
|
|
|
|
return ( fSucceededOnce );
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Description:
|
|
// HandleCompletedData is called when the DNS resolve is finished. It
|
|
// does the final processing after DNS is finished, and sets the
|
|
// m_dwDiagnostic flag appropriately. It does one of three things based
|
|
// on the DnsStatus and m_AuxList:
|
|
//
|
|
// (1) If the resolve was successful, it kicks off a connection to the
|
|
// server and set the m_dwDiagnostic to ERROR_SUCCESS.
|
|
// (2) If the resolve failed authoritatively, it set the m_dwDiagnostic
|
|
// to NDR the messages (after checking for a smarthost) ==
|
|
// AQUEUE_E_AUTHORITATIVE_HOST_NOT_FOUND.
|
|
// (3) If the resolve failed (from dwDnsStatus and m_AuxList) or if
|
|
// something fails during HandleCompletedData, the m_dwDiagnostic is
|
|
// not modified (it remains initialized to AQUEUE_E_DNS_FAILURE, the
|
|
// default error code).
|
|
//
|
|
// m_dwDiagnostic is examined in ~CAsyncSmtpDns.
|
|
// Arguments:
|
|
// DNS_STATUS dwDnsStatus - Status code from DnsParseMessage
|
|
// Returns:
|
|
// Nothing.
|
|
//-----------------------------------------------------------------------------
|
|
void CAsyncSmtpDns::HandleCompletedData(DNS_STATUS dwDnsStatus)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
PSMTPDNS_RECS TempList = NULL;
|
|
CAsyncMx * pAsyncCon = NULL;
|
|
MXPARAMS Params;
|
|
|
|
TempList = m_AuxList;
|
|
|
|
//
|
|
// The DNS lookup failed authoritatively. The messages will be NDR'ed unless there
|
|
// is a smarthost configured. If there is a smarthost, we will kick off a resolve
|
|
// for it.
|
|
//
|
|
if(ERROR_NOT_FOUND == dwDnsStatus)
|
|
{
|
|
if(m_fConnectToSmartHost)
|
|
{
|
|
char szSmartHost[MAX_PATH+1];
|
|
|
|
m_pServiceInstance->GetSmartHost(szSmartHost);
|
|
((REMOTE_QUEUE *)m_pServiceInstance->QueryRemoteQObj())->StartAsyncConnect(szSmartHost,
|
|
m_pISMTPConnection, m_DomainOptions, FALSE);
|
|
|
|
//Do not release this ISMTPConnection object! We passed it on to
|
|
//StartAsyncConnect so that it can try to associate this object with
|
|
//a connection with the smart host. We set it to null here so that we
|
|
//will not release it or ack it in the destructor of this object.
|
|
m_pISMTPConnection = NULL;
|
|
m_dwDiagnostic = ERROR_SUCCESS;
|
|
return;
|
|
} else {
|
|
//No smart host, messages will be NDR'ed. Return value is meaningless.
|
|
m_dwDiagnostic = AQUEUE_E_AUTHORITATIVE_HOST_NOT_FOUND;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Successful DNS lookup.
|
|
if(m_AuxList)
|
|
{
|
|
m_AuxList = NULL;
|
|
|
|
//
|
|
// Make a last ditch effort to fill in the IP addresses for any hosts
|
|
// that are still unresolved.
|
|
//
|
|
if( !GetMissingIpAddresses(TempList) )
|
|
{
|
|
m_dwDiagnostic = AQUEUE_E_HOST_NOT_FOUND;
|
|
DeleteDnsRec(TempList);
|
|
return;
|
|
}
|
|
|
|
|
|
Params.HostName = "";
|
|
Params.PortNum = m_pServiceInstance->GetRemoteSmtpPort();
|
|
Params.TimeOut = INFINITE;
|
|
Params.CallBack = QueueCallBackFunction;
|
|
Params.pISMTPConnection = m_pISMTPConnection;
|
|
Params.pInstance = m_pServiceInstance;
|
|
Params.pDnsRec = TempList;
|
|
Params.pDNS_RESOLVER_RECORD = m_pDNS_RESOLVER_RECORD;
|
|
|
|
pAsyncCon = new CAsyncMx (&Params);
|
|
if(pAsyncCon != NULL)
|
|
{
|
|
// Abdicate responsibility for deleting/releasing the dns resolver record
|
|
m_pDNS_RESOLVER_RECORD = NULL;
|
|
|
|
// Outbound SSL: Set name against which server cert. should matched
|
|
fRet = pAsyncCon->Init(m_pszSSLVerificationName);
|
|
if(!fRet)
|
|
{
|
|
delete pAsyncCon;
|
|
goto Exit;
|
|
}
|
|
|
|
if(!m_fConnectToSmartHost)
|
|
{
|
|
pAsyncCon->SetTriedOnFailHost();
|
|
}
|
|
|
|
pAsyncCon->SetDomainOptions(m_DomainOptions);
|
|
|
|
fRet = pAsyncCon->InitializeAsyncConnect();
|
|
if(!fRet)
|
|
{
|
|
delete pAsyncCon;
|
|
}
|
|
else
|
|
{
|
|
m_dwDiagnostic = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DeleteDnsRec(TempList);
|
|
}
|
|
}
|
|
Exit:
|
|
return;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Description:
|
|
// Simple wrapper function for DnsQueryAsync. This is a virtual function
|
|
// called from CAsyncDns but implemented in CAsyncSmtpDns. In order to retry
|
|
// a DNS query we need all the parameters of the old query. These are members
|
|
// of CAsyncSmtpDns. Thus the virtual function based implementation.
|
|
//
|
|
// Arguments:
|
|
// BOOL fUdp -- Use UDP as transport for retry query?
|
|
//
|
|
// Returns:
|
|
// TRUE on success. In this situation the ISMTPConnection ack (and release of
|
|
// pDNS_RESOLVER_RECORD) is handled by the new CAsyncSmtpDns object created
|
|
// by DnsQueryAsync. The diagnostic code of this object is cleared.
|
|
//
|
|
// FALSE on error. In this case, the cleanup for ISMTPConnection and
|
|
// pDNS_RESOLVER_RECORD must be done by "this" CAsyncSmtpDns. The
|
|
// diagnostic code is not touched.
|
|
//------------------------------------------------------------------------------
|
|
BOOL CAsyncSmtpDns::RetryAsyncDnsQuery(BOOL fUdp)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CAsyncSmtpDns::RetryAsyncDnsQuery");
|
|
BOOL fRet = FALSE;
|
|
|
|
//
|
|
// If we do not have a connection object, then the requery attempt
|
|
// is doomed to fail. This can happen when we disconnect and
|
|
// ATQ calls our completion function with ERROR_OPERATION_ABORTED
|
|
// If we don't have a connection object, there is no way to
|
|
// ack the connection or get messages to send.
|
|
//
|
|
if (!m_pISMTPConnection) {
|
|
DebugTrace((LPARAM) this,
|
|
"RetryAsyncDnsQuery called without connection object - aborting");
|
|
//should be cleared by same code path
|
|
_ASSERT(!m_pDNS_RESOLVER_RECORD);
|
|
fRet = FALSE; //there is nothing to Ack.
|
|
goto Exit;
|
|
}
|
|
fRet = DnsQueryAsync(
|
|
m_pServiceInstance,
|
|
m_HostName,
|
|
m_FQDNToDrop,
|
|
m_pISMTPConnection,
|
|
m_dwFlags,
|
|
m_DomainOptions,
|
|
m_fConnectToSmartHost,
|
|
m_pDNS_RESOLVER_RECORD,
|
|
m_pszSSLVerificationName,
|
|
fUdp);
|
|
|
|
if(fRet) {
|
|
m_pDNS_RESOLVER_RECORD = NULL;
|
|
m_pISMTPConnection = NULL;
|
|
m_dwDiagnostic = ERROR_SUCCESS;
|
|
}
|
|
|
|
Exit:
|
|
TraceFunctLeave();
|
|
return fRet;
|
|
}
|