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.
 
 
 
 
 
 

681 lines
21 KiB

#include "dnsincs.h"
#include <stdlib.h>
extern void DeleteDnsRec(PSMTPDNS_RECS pDnsRec);
CAsyncMxDns::CAsyncMxDns(char *MyFQDN)
{
lstrcpyn(m_FQDNToDrop, MyFQDN, sizeof(m_FQDNToDrop));
m_fUsingMx = TRUE;
m_Index = 0;
m_LocalPref = 256;
m_SeenLocal = FALSE;
m_AuxList = NULL;
m_fMxLoopBack = FALSE;
ZeroMemory (m_Weight, sizeof(m_Weight));
ZeroMemory (m_Prefer, sizeof(m_Prefer));
}
//-----------------------------------------------------------------------------
// 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 CAsyncMxDns::GetIpFromDns(PSMTPDNS_RECS pDnsRec, DWORD Index)
{
MXIPLIST_ENTRY * pEntry = NULL;
BOOL fReturn = FALSE;
DWORD dwStatus = ERROR_SUCCESS;
DWORD rgdwIpAddresses[SMTP_MAX_DNS_ENTRIES];
DWORD cIpAddresses = SMTP_MAX_DNS_ENTRIES;
PIP_ARRAY pipDnsList = NULL;
TraceFunctEnterEx((LPARAM) this, "CAsyncMxDns::GetIpFromDns");
fReturn = GetDnsIpArrayCopy(&pipDnsList);
if(!fReturn)
{
ErrorTrace((LPARAM) this, "Unable to get DNS server list copy");
TraceFunctLeaveEx((LPARAM) this);
return FALSE;
}
dwStatus = ResolveHost(
pDnsRec->DnsArray[Index]->DnsName,
pipDnsList,
DNS_QUERY_STANDARD,
rgdwIpAddresses,
&cIpAddresses);
if(dwStatus == ERROR_SUCCESS)
{
fReturn = TRUE;
for (DWORD Loop = 0; !IsShuttingDown() && Loop < cIpAddresses; Loop++)
{
pEntry = new MXIPLIST_ENTRY;
if(pEntry != NULL)
{
pDnsRec->DnsArray[Index]->NumEntries++;
CopyMemory(&pEntry->IpAddress, &rgdwIpAddresses[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);
}
ReleaseDnsIpArray(pipDnsList);
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 CAsyncMxDns::GetMissingIpAddresses(PSMTPDNS_RECS pDnsRec)
{
DWORD Count = 0;
DWORD Error = 0;
BOOL fSucceededOnce = FALSE;
if(pDnsRec == NULL)
{
return FALSE;
}
while(!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 );
}
int MxRand(char * host)
{
int hfunc = 0;
unsigned int seed = 0;;
seed = rand() & 0xffff;
hfunc = seed;
while (*host != '\0')
{
int c = *host++;
if (isascii((UCHAR)c) && isupper((UCHAR)c))
c = tolower(c);
hfunc = ((hfunc << 1) ^ c) % 2003;
}
hfunc &= 0xff;
return hfunc;
}
BOOL CAsyncMxDns::CheckList(void)
{
MXIPLIST_ENTRY * pEntry = NULL;
BOOL fRet = TRUE;
DWORD dwStatus = ERROR_SUCCESS;
DWORD rgdwIpAddresses[SMTP_MAX_DNS_ENTRIES];
DWORD cIpAddresses = SMTP_MAX_DNS_ENTRIES;
PIP_ARRAY pipDnsList = NULL;
TraceFunctEnterEx((LPARAM) this, "CAsyncDns::CheckList");
if(m_Index == 0)
{
DebugTrace((LPARAM) this, "m_Index == 0 in CheckList");
m_fUsingMx = FALSE;
DeleteDnsRec(m_AuxList);
m_AuxList = new SMTPDNS_RECS;
if(m_AuxList == NULL)
{
ErrorTrace((LPARAM) this, "m_AuxList = new SMTPDNS_RECS failed");
TraceFunctLeaveEx((LPARAM)this);
return FALSE;
}
ZeroMemory(m_AuxList, sizeof(SMTPDNS_RECS));
m_AuxList->NumRecords = 1;
m_AuxList->DnsArray[0] = new MX_NAMES;
if(m_AuxList->DnsArray[0] == NULL)
{
ErrorTrace((LPARAM) this, "m_AuxList->DnsArray[0] = new MX_NAMES failed");
TraceFunctLeaveEx((LPARAM)this);
return FALSE;
}
m_AuxList->DnsArray[0]->NumEntries = 0;
InitializeListHead(&m_AuxList->DnsArray[0]->IpListHead);
lstrcpyn(m_AuxList->DnsArray[0]->DnsName, m_HostName,
sizeof(m_AuxList->DnsArray[m_Index]->DnsName));
fRet = GetDnsIpArrayCopy(&pipDnsList);
if(!fRet)
{
ErrorTrace((LPARAM) this, "Unable to get DNS server list copy");
TraceFunctLeaveEx((LPARAM) this);
return FALSE;
}
dwStatus = ResolveHost(
m_HostName,
pipDnsList,
DNS_QUERY_STANDARD,
rgdwIpAddresses,
&cIpAddresses);
if(dwStatus == ERROR_SUCCESS && cIpAddresses)
{
for (DWORD Loop = 0; Loop < cIpAddresses; Loop++)
{
pEntry = new MXIPLIST_ENTRY;
if(pEntry != NULL)
{
m_AuxList->DnsArray[0]->NumEntries++;
CopyMemory(&pEntry->IpAddress, &rgdwIpAddresses[Loop], 4);
InsertTailList(&m_AuxList->DnsArray[0]->IpListHead, &pEntry->ListEntry);
}
else
{
fRet = FALSE;
ErrorTrace((LPARAM) this, "pEntry = new MXIPLIST_ENTRY failed in CheckList");
break;
}
}
}
else
{
fRet = FALSE;
}
ReleaseDnsIpArray(pipDnsList);
}
TraceFunctLeaveEx((LPARAM)this);
return fRet;
}
BOOL CAsyncMxDns::SortMxList(void)
{
BOOL fRet = TRUE;
/* sort the records */
for (DWORD i = 0; i < m_Index; i++)
{
for (DWORD j = i + 1; j < m_Index; j++)
{
if (m_Prefer[i] > m_Prefer[j] ||
(m_Prefer[i] == m_Prefer[j] && m_Weight[i] > m_Weight[j]))
{
DWORD temp;
MX_NAMES *temp1;
temp = m_Prefer[i];
m_Prefer[i] = m_Prefer[j];
m_Prefer[j] = temp;
temp1 = m_AuxList->DnsArray[i];
m_AuxList->DnsArray[i] = m_AuxList->DnsArray[j];
m_AuxList->DnsArray[j] = temp1;
temp = m_Weight[i];
m_Weight[i] = m_Weight[j];
m_Weight[j] = temp;
}
}
if (m_SeenLocal && m_Prefer[i] >= m_LocalPref)
{
/* truncate higher preference part of list */
m_Index = i;
}
}
m_AuxList->NumRecords = m_Index;
if(!CheckList())
{
DeleteDnsRec(m_AuxList);
m_AuxList = NULL;
fRet = FALSE;
}
return fRet;
}
void CAsyncMxDns::ProcessMxRecord(PDNS_RECORD pnewRR)
{
DWORD Len = 0;
TraceFunctEnterEx((LPARAM) this, "CAsyncDns::ProcessMxRecord");
//
// Leave room for NULL-termination of array
//
if(m_Index >= SMTP_MAX_DNS_ENTRIES-1)
{
DebugTrace((LPARAM) this, "SMTP_MAX_DNS_ENTRIES reached for %s", m_HostName);
TraceFunctLeaveEx((LPARAM)this);
return;
}
if((pnewRR->wType == DNS_TYPE_MX) && pnewRR->Data.MX.nameExchange)
{
Len = lstrlen(pnewRR->Data.MX.nameExchange);
if(pnewRR->Data.MX.nameExchange[Len - 1] == '.')
{
pnewRR->Data.MX.nameExchange[Len - 1] = '\0';
}
DebugTrace((LPARAM) this, "Received MX rec %s with priority %d for %s", pnewRR->Data.MX.nameExchange, pnewRR->Data.MX.wPreference, m_HostName);
if(lstrcmpi(pnewRR->Data.MX.nameExchange, m_FQDNToDrop))
{
m_AuxList->DnsArray[m_Index] = new MX_NAMES;
if(m_AuxList->DnsArray[m_Index])
{
m_AuxList->DnsArray[m_Index]->NumEntries = 0;;
InitializeListHead(&m_AuxList->DnsArray[m_Index]->IpListHead);
lstrcpyn(m_AuxList->DnsArray[m_Index]->DnsName,pnewRR->Data.MX.nameExchange, sizeof(m_AuxList->DnsArray[m_Index]->DnsName));
m_Weight[m_Index] = MxRand (m_AuxList->DnsArray[m_Index]->DnsName);
m_Prefer[m_Index] = pnewRR->Data.MX.wPreference;
m_Index++;
}
else
{
DebugTrace((LPARAM) this, "Out of memory allocating MX_NAMES for %s", m_HostName);
}
}
else
{
if (!m_SeenLocal || pnewRR->Data.MX.wPreference < m_LocalPref)
m_LocalPref = pnewRR->Data.MX.wPreference;
m_SeenLocal = TRUE;
}
}
else if(pnewRR->wType == DNS_TYPE_A)
{
MXIPLIST_ENTRY * pEntry = NULL;
for(DWORD i = 0; i < m_Index; i++)
{
if(lstrcmpi(pnewRR->nameOwner, m_AuxList->DnsArray[i]->DnsName) == 0)
{
pEntry = new MXIPLIST_ENTRY;
if(pEntry != NULL)
{
m_AuxList->DnsArray[i]->NumEntries++;;
pEntry->IpAddress = pnewRR->Data.A.ipAddress;
InsertTailList(&m_AuxList->DnsArray[i]->IpListHead, &pEntry->ListEntry);
}
break;
}
}
}
TraceFunctLeaveEx((LPARAM)this);
}
void CAsyncMxDns::ProcessARecord(PDNS_RECORD pnewRR)
{
MXIPLIST_ENTRY * pEntry = NULL;
if(pnewRR->wType == DNS_TYPE_A)
{
pEntry = new MXIPLIST_ENTRY;
if(pEntry != NULL)
{
pEntry->IpAddress = pnewRR->Data.A.ipAddress;
InsertTailList(&m_AuxList->DnsArray[0]->IpListHead, &pEntry->ListEntry);
}
}
}
//-----------------------------------------------------------------------------
// Description:
// Checks to see if any of the IP addresses returned by DNS belong to this
// machine. This is a common configuration when there are backup mail
// spoolers. To avoid mail looping, we should delete all MX records that
// are less preferred than the record containing the local IP address.
//
// Arguments:
// None.
// Returns:
// TRUE if no loopback
// FALSE if loopback detected
//-----------------------------------------------------------------------------
BOOL CAsyncMxDns::CheckMxLoopback()
{
ULONG i = 0;
ULONG cLocalIndex = 0;
BOOL fSeenLocal = TRUE;
DWORD dwIpAddress = INADDR_NONE;
DWORD dwLocalPref = 256;
PLIST_ENTRY pListHead = NULL;
PLIST_ENTRY pListTail = NULL;
PLIST_ENTRY pListCurrent = NULL;
PMXIPLIST_ENTRY pMxIpListEntry = NULL;
TraceFunctEnterEx((LPARAM)this, "CAsyncMxDns::CheckMxLoopback");
if(!m_AuxList)
{
TraceFunctLeaveEx((LPARAM)this);
return TRUE;
}
//
// m_AuxList is a sorted list of MX records. Scan through it searching
// for an MX record with a local-IP address. cLocalIndex is set to the
// index, within m_AuxList, of this record.
//
while(m_AuxList->DnsArray[cLocalIndex] != NULL)
{
pListTail = &(m_AuxList->DnsArray[cLocalIndex]->IpListHead);
pListHead = m_AuxList->DnsArray[cLocalIndex]->IpListHead.Flink;
pListCurrent = pListHead;
while(pListCurrent != pListTail)
{
pMxIpListEntry = CONTAINING_RECORD(pListCurrent, MXIPLIST_ENTRY, ListEntry);
dwIpAddress = pMxIpListEntry->IpAddress;
if(IsAddressMine(dwIpAddress))
{
DNS_PRINTF_MSG("Local host's IP is one of the target IPs.\n");
DNS_PRINTF_MSG("Discarding all equally or less-preferred IP addresses.\n");
DebugTrace((LPARAM)this, "Local record found in MX list, name=%s, pref=%d, ip=%08x",
m_AuxList->DnsArray[cLocalIndex]->DnsName,
m_Prefer[cLocalIndex],
dwIpAddress);
// All records with preference > m_Prefer[cLocalIndex] should be deleted. Since
// m_AuxList is sorted by preference, we need to delete everthing with index >
// cLocalIndex. However since there may be some records with preference == local-
// preference, which occur before cLocalIndex, we walk backwards till we find
// the first record with preference = m_Prefer[cLocalIndex].
dwLocalPref = m_Prefer[cLocalIndex];
while(cLocalIndex > 0 && dwLocalPref == m_Prefer[cLocalIndex])
cLocalIndex--;
if(dwLocalPref != m_Prefer[cLocalIndex])
cLocalIndex++;
fSeenLocal = TRUE;
// All records > cLocalIndex are even less preferred than this one,
// (since m_AuxList already sorted) and will be deleted.
goto END_SEARCH;
}
pListCurrent = pListCurrent->Flink;
}
cLocalIndex++;
}
END_SEARCH:
//
// If a local-IP address was found, delete all less-preferred records
//
if(fSeenLocal)
{
DebugTrace((LPARAM)this,
"Deleting all MX records with lower preference than %d", m_Prefer[cLocalIndex]);
for(i = cLocalIndex; m_AuxList->DnsArray[i] != NULL; i++)
{
if(!m_AuxList->DnsArray[i]->DnsName[0])
continue;
while(!IsListEmpty(&(m_AuxList->DnsArray[i]->IpListHead)))
{
pListCurrent = RemoveHeadList(&(m_AuxList->DnsArray[i]->IpListHead));
pMxIpListEntry = CONTAINING_RECORD(pListCurrent, MXIPLIST_ENTRY, ListEntry);
delete pMxIpListEntry;
}
delete m_AuxList->DnsArray[i];
m_AuxList->DnsArray[i] = NULL;
}
m_AuxList->NumRecords = cLocalIndex;
// No records left
if(m_AuxList->NumRecords == 0)
{
DNS_PRINTF_ERR("DNS configuration error (loopback), messages will be NDRed.\n");
DNS_PRINTF_ERR("Local host's IP address is the most preferred MX record.\n");
ErrorTrace((LPARAM)this, "Possible misconfiguration: most preferred MX record is loopback");
_ASSERT(m_AuxList->pMailMsgObj == NULL);
delete m_AuxList;
m_AuxList = NULL;
return FALSE;
}
}
TraceFunctLeaveEx((LPARAM)this);
return TRUE;
}
void CAsyncMxDns::DnsProcessReply(
DWORD status,
PDNS_RECORD pRecordList)
{
TraceFunctEnterEx((LPARAM) this, "CAsyncDns::DnsParseMessage");
PDNS_RECORD pTmp = NULL;
m_SeenLocal = FALSE;
m_LocalPref = 256;
m_AuxList = new SMTPDNS_RECS;
if(!m_AuxList)
{
return;
}
ZeroMemory(m_AuxList, sizeof(SMTPDNS_RECS));
//
// Due to Raid #122555 m_fUsingMx is always TRUE in this function
// - hence we will always go a GetHostByName() if there is no MX
// record. It would be better Perf if we did a A record lookup.
//
DebugTrace((LPARAM) this, "Parsed DNS record for %s. status = 0x%08x", m_HostName, status);
switch(status)
{
case ERROR_SUCCESS:
//
// Got the DNS record we want.
//
DNS_PRINTF_MSG("Processing MX/A records in reply.\n");
DebugTrace((LPARAM) this, "Success: DNS record parsed");
pTmp = pRecordList;
while( pTmp )
{
if( m_fUsingMx )
{
ProcessMxRecord( pTmp );
}
else
{
ProcessARecord( pTmp );
}
pTmp = pTmp->pNext;
}
if(m_fUsingMx)
{
//
// SortMxList sorts the MX records by preference and calls
// gethostbyname() to resolve A records for Mail Exchangers
// if needed (when the A records are not returned in the
// supplementary info).
//
DNS_PRINTF_MSG("Sorting MX records by priority.\n");
if(SortMxList())
{
status = ERROR_SUCCESS;
DebugTrace((LPARAM) this, "SortMxList() succeeded.");
}
else
{
status = ERROR_RETRY;
ErrorTrace((LPARAM) this, "SortMxList() failed. Message will stay queued.");
}
}
break;
case DNS_ERROR_RCODE_NAME_ERROR:
// Fall through to using gethostbyname()
case DNS_INFO_NO_RECORDS:
// Non authoritative host not found.
// Fall through to using gethostbyname()
default:
DebugTrace((LPARAM) this, "Error in query: status = 0x%08x.", status);
//
// Use gethostbyname to resolve the hostname:
// One issue with our approach is that sometimes we will NDR the message
// on non-permanent errors, "like WINS server down", when gethostbyname
// fails. However, there's no way around it --- gethostbyname doesn't
// report errors in a reliable manner, so it's not possible to distinguish
// between permanent and temporary errors.
//
if (!CheckList ()) {
if(status == DNS_ERROR_RCODE_NAME_ERROR) {
DNS_PRINTF_ERR("Host does not exist in DNS. Messages will be NDRed.\n");
ErrorTrace((LPARAM) this, "Authoritative error");
status = ERROR_NOT_FOUND;
} else {
DNS_PRINTF_ERR("Host could not be resolved. Messages will be retried later.\n");
ErrorTrace((LPARAM) this, "Retryable error");
status = ERROR_RETRY;
}
} else {
DebugTrace ((LPARAM) this, "Successfully resolved using gethostbyname");
status = ERROR_SUCCESS;
}
break;
}
//
// Make a last ditch effort to fill in the IP addresses for any hosts
// that are still unresolved.
//
if(m_AuxList && status == ERROR_SUCCESS)
{
if(!GetMissingIpAddresses(m_AuxList))
{
DeleteDnsRec(m_AuxList);
m_AuxList = NULL;
status = ERROR_RETRY;
goto Exit;
}
if(!CheckMxLoopback())
{
m_fMxLoopBack = TRUE;
DeleteDnsRec(m_AuxList);
m_AuxList = NULL;
TraceFunctLeaveEx((LPARAM) this);
return;
}
}
//
// End of resolve: HandleCompleted data examines the DnsStatus and results, and sets up
// member variables of CAsyncMxDns to either NDR messages, connect to the remote host
// or ack this queue for retry when the object is deleted.
//
Exit:
HandleCompletedData(status);
return;
}