|
|
/*++
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; }
|