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.
 
 
 
 
 
 

867 lines
31 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name :
dnsutils.cpp
Abstract:
This file defines the functions for resolving a name using
DNS by querying a specified set of DNS servers.
Author:
Gautam Pulla ( GPulla ) 05-Dec-2001
Project:
SMTP Server DLL
Revision History:
--*/
#include "dnsincs.h"
//-----------------------------------------------------------------------------
// Description:
// Resolves a host by querying a specified set of DNS servers. The cache
// is also queried if the servers parameter is NULL (i.e. the local DNS
// servers configured on the machine are used). If this fails we failover
// to the winsock version of gethostbyname() to resolve the name. This
// queries the default DNS servers on the box and also does other lookups
// like WINS and NetBIOS.
//
// Arguments:
// IN LPSTR pszHost - Host to resolve
//
// IN PIP_ARRAY pipDnsServers - DNS servers to use. Pass in NULL if the
// default list of DNS servers should be used.
//
// IN DWORD fOptions - Flags to pass in to the DNSAPI.
//
// OUT DWORD *rgdwIpAddresses - Pass in array which will be filled in
// with the IP addresses returned by the resolve.
//
// IN OUT DWORD *pcIpAddresses - Pass in number max number of IP
// addresses that can be returned in the array. On successful return
// this is set to the number of IP addresses found.
//
// Returns:
// ERROR_SUCCESS if the hostname was resolved.
// Win32 error if the hostname could not be found or there was some other
// error.
//-----------------------------------------------------------------------------
DWORD ResolveHost(
LPSTR pszHost,
PIP_ARRAY pipDnsServers,
DWORD fOptions,
DWORD *rgdwIpAddresses,
DWORD *pcIpAddresses)
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwAddr = INADDR_NONE;
INT i = 0;
INT cIpAddresses = 0;
INT cBufferSize = *pcIpAddresses;
HOSTENT *hp = NULL;
TraceFunctEnterEx((LPARAM) pszHost, "ResolveHost");
DebugTrace((LPARAM) pszHost, "Resolving host %s", pszHost);
_ASSERT(pszHost && rgdwIpAddresses && pcIpAddresses && *pcIpAddresses > 0);
dwAddr = inet_addr(pszHost);
if(dwAddr != INADDR_NONE) {
DebugTrace((LPARAM) pszHost, "Resolving %s as an address literal", pszHost);
DNS_PRINTF_MSG("%s is a literal IP address\n", pszHost);
if(!*pcIpAddresses) {
_ASSERT(0 && "Passing in zero length buffer!");
TraceFunctLeaveEx((LPARAM) pszHost);
return ERROR_RETRY;
}
*pcIpAddresses = 1;
rgdwIpAddresses[0] = dwAddr;
TraceFunctLeaveEx((LPARAM) pszHost);
return ERROR_SUCCESS;
}
dwStatus = GetHostByNameEx(
pszHost,
pipDnsServers,
fOptions,
rgdwIpAddresses,
pcIpAddresses);
if(dwStatus == ERROR_SUCCESS) {
DebugTrace((LPARAM) pszHost, "GetHostByNameEx resolved %s", pszHost);
TraceFunctLeaveEx((LPARAM) pszHost);
return ERROR_SUCCESS;
}
DebugTrace((LPARAM) pszHost, "GetHostByNameEx failed, trying gethostbyname");
DNS_PRINTF_MSG("Cannot resolve using DNS only, calling gethostbyname as last resort.\n");
DNS_PRINTF_MSG("This will query\n");
DNS_PRINTF_MSG("- Global DNS servers.\n");
DNS_PRINTF_MSG("- DNS cache.\n");
DNS_PRINTF_MSG("- WINS/NetBIOS.\n");
DNS_PRINTF_MSG("- .hosts file.\n");
// GetHostByNameEx failed, failover to gethostbyname
*pcIpAddresses = 0;
hp = gethostbyname(pszHost);
if(hp == NULL) {
DNS_PRINTF_DBG("Winsock's gethostbyname() failed.\n");
ErrorTrace((LPARAM) pszHost, "gethostbyname failed, Error: %d", GetLastError());
TraceFunctLeaveEx((LPARAM) pszHost);
return ERROR_RETRY;
}
// Copy results to return buffer
for(i = 0; hp->h_addr_list[i] != NULL; i++) {
if(cIpAddresses >= cBufferSize)
break;
CopyMemory(&rgdwIpAddresses[cIpAddresses], hp->h_addr_list[i], 4);
cIpAddresses++;
}
if(!cIpAddresses) {
ErrorTrace((LPARAM) pszHost, "No IP address returned by gethostbyname");
TraceFunctLeaveEx((LPARAM) pszHost);
return ERROR_RETRY;
}
DebugTrace((LPARAM) pszHost, "gethostbyname succeeded resolving: %s", pszHost);
*pcIpAddresses = cIpAddresses;
TraceFunctLeaveEx((LPARAM) pszHost);
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Description:
// Resolves a host by querying DNS and returns a list of IP addresses for
// the host. The host is resolved for CNAME and A records.
// Arguments:
// IN LPSTR pszHost --- Hostname to be resolved
// IN PIP_ARRAY pipDnsServers --- DNS servers to use
// IN DWORD fOptions --- Options to be passed into DnsQuery_A
// OUT DWORD *rgdwIpAddresses --- Buffer to write IP addresses to
// IN OUT DWORD *pcIpAddresses --- Pass in number of IP addresses that can
// be returned in rgdwIpAddresses. On successful return, this is set to the
// number of IP addresses obtained from the resolution.
// Returns:
// ERROR_SUCCESS if the resolution succeeded
// DNS_ERROR_RCODE_NAME_ERROR if the host does not exist
// DNS_INFO_NO_RECORDS if the host cannot be resolved
// ERROR_INVALID_ARGUMENT if a permanent error occurred, such as a
// configuration error on the DNS server (CNAME loop for instance).
// Win32 error code if there is some other problem.
//-----------------------------------------------------------------------------
DWORD GetHostByNameEx(
LPSTR pszHost,
PIP_ARRAY pipDnsServers,
DWORD fOptions,
DWORD *rgdwIpAddresses,
DWORD *pcIpAddresses)
{
PDNS_RECORD pDnsRecordList = NULL;
PDNS_RECORD pDnsRecordListTail = NULL;
DWORD dwStatus = ERROR_SUCCESS;
LPSTR pszChainTail = NULL;
DWORD cBufferSize = *pcIpAddresses;
TraceFunctEnterEx((LPARAM) pszHost, "GetHostByNameEx");
//
// Check for A records for pszHost.
//
_ASSERT(pszHost && rgdwIpAddresses && pcIpAddresses && *pcIpAddresses > 0);
DebugTrace((LPARAM) pszHost, "Querying for A records for %s", pszHost);
dwStatus = MyDnsQuery(
pszHost,
DNS_TYPE_A,
fOptions,
pipDnsServers,
&pDnsRecordList);
if(DNS_INFO_NO_RECORDS == dwStatus) {
//
// If this is hit, it's almost always because there are no
// records for pszHost on the server. Even if pszHost is an
// alias, the A record query will normally return CNAME records
// as well.
//
DNS_PRINTF_MSG("No A records for %s in DNS.\n", pszHost);
DNS_PRINTF_MSG("Querying for CNAME records instead.\n", pszHost);
DebugTrace((LPARAM) pszHost, "A query for %s failed, trying CNAME", pszHost);
dwStatus = MyDnsQuery(
pszHost,
DNS_TYPE_CNAME,
fOptions,
pipDnsServers,
&pDnsRecordList);
}
DebugTrace((LPARAM) pszHost, "Return status from last DNS query: %d", dwStatus);
//
// At this point:
//
// (1) If pszHost is an alias, the A query has returned all the records
// we need to resolve it; because good (read "all") DNS servers return
// the relevant CNAME records even when queried for A records.
// (2) If pszHost is an alias and the DNS server doesn't return CNAME records
// in a reply to the A query, we would have queried specifically for CNAME
// records.
// (3) If pszHost is a proper hostname, the A query has returned all the
// records we need to resolve it.
// (4) Or something has failed.
//
// So now we can drop down (if we haven't failed) and chain the CNAME records
// (if there are any).
//
DNS_LOG_RESPONSE(dwStatus, pDnsRecordList, NULL, 0);
if(ERROR_SUCCESS != dwStatus) {
ErrorTrace((LPARAM) pszHost,
"Query for %s failed, failing resolve. Status: %d", pszHost, dwStatus);
goto Exit;
}
_ASSERT(pDnsRecordList);
//
// ProcessCNAMEChain chains CNAME records and tries to get an IP address
// for pszHost by following the chain. Zero length chains, which correspond
// to the situation where there are no CNAME records, but a direct A record
// for pszHost is available are also handled.
//
// For legal configurations in DNS, ProcessCNAMEChain will return a list of
// IP addresses 99% of the time. pszChainTail will be set to the hostname
// of the CNAME chain tail. The rare exceptional case where an IP address
// is not returned even when the configuration is legal is described below.
//
// If ProcessCNAMEChain does not return an IP address for pszHost, then the
// DNS server did not return any records for the initial A query. It only
// returned CNAME records for the subsequent CNAME query. This can be if:
//
// (1) This is a weird DNS server. DNS servers will almost always return
// CNAME records for a hostname in the initial A query if it has them.
// i.e if you have.
//
// random.com CNAME mail.com
// mail.com A 10.10.10.10
//
// The the first A query for random.com returned no records. This is
// technically correct because there are no A records for random.com.
// However all sane DNS servers return both the CNAME and A records as
// additional records anyway, so this DNS server must be a bit funky.
//
// To be robust, we must chain the CNAME record to mail.com, and then
// re-query for an A record for mail.com.
//
// (2) pszHost has only CNAME records. So the initial A query will fail,
// and the subsequent CNAME query will return only CNAME records. i.e.
// if you have:
//
// random.com CNAME mail.com
// No A records for mail.com
//
// Then the A query for random.com may return no records and the CNAME
// query for random.com may return only the CNAME records. This is a
// actually an illegal configuration, but we must go through the
// complete process anyway in case situation (1) caused this.
//
// In either case, if ProcessCNAMEChain returns successfully, pszChainTail
// is set to the tail of the chain and we can query for A records for
// pszChainTail.
//
*pcIpAddresses = cBufferSize;
dwStatus = ProcessCNAMEChain(
pDnsRecordList,
pszHost,
&pszChainTail,
rgdwIpAddresses,
pcIpAddresses);
if(ERROR_SUCCESS != dwStatus) {
ErrorTrace((LPARAM) pszHost, "Failed to process CNAME chain: %d", dwStatus);
goto Exit;
}
// Success: IP address(es) obtained
if(*pcIpAddresses > 0) {
DebugTrace((LPARAM) pszHost,
"Got %d IP addresses, success resolve", *pcIpAddresses);
goto Exit;
}
DebugTrace((LPARAM) pszHost,
"Chained CNAME chain, but no A records available for for chain-tail: %s",
pszChainTail);
_ASSERT(*pcIpAddresses == 0);
//
// No CNAME chain was found, which means that pszHost should have a direct
// A record. But there is no such A record, or we would have an IP address
// from ProcessCNAMEChain. So the resolve has failed.
//
if(!pszChainTail) {
ErrorTrace((LPARAM) pszHost, "Chain tail NULL. Failed resolve");
dwStatus = DNS_INFO_NO_RECORDS;
goto Exit;
}
//
// At this point, a CNAME chain has been followed but no IP address was
// found for pszChainTail. This could be because of situation (1) or (2)
// described in the comment above for ProcessCNAMEChain. We need to query
// for an A record for the tail of the CNAME chain.
//
DebugTrace((LPARAM) pszHost, "Querying A records for chain tail: %s", pszChainTail);
dwStatus = MyDnsQuery(
pszChainTail,
DNS_TYPE_A,
fOptions,
pipDnsServers,
&pDnsRecordListTail);
DebugTrace((LPARAM) pszHost, "A query retstatus: %d", pszChainTail);
if(ERROR_SUCCESS == dwStatus) {
_ASSERT(pDnsRecordListTail);
*pcIpAddresses = cBufferSize;
FindARecord(
pszChainTail,
pDnsRecordListTail,
rgdwIpAddresses,
pcIpAddresses);
}
if(*pcIpAddresses == 0) {
ErrorTrace((LPARAM) pszHost, "No A records found for chain tail: %s", pszChainTail);
dwStatus = DNS_INFO_NO_RECORDS;
}
Exit:
if(pDnsRecordListTail) {
//DnsRecordListFree(pDnsRecordListTail, DnsFreeRecordListDeep);
DnsFreeRRSet(pDnsRecordListTail, TRUE);
}
if(pDnsRecordList) {
//DnsRecordListFree(pDnsRecordList, DnsFreeRecordListDeep);
DnsFreeRRSet(pDnsRecordList, TRUE);
}
TraceFunctLeaveEx((LPARAM) pszHost);
return dwStatus;
}
//-----------------------------------------------------------------------------
// Description:
// Given a list of records returned from a successful CNAME query for a
// DNS name, this function traverses the chain and obtains the IP addresses
// for the hosts at the end of the chain tail if present. The tail of the
// chain is also returned.
// Arguments:
// IN PDNS_RECORD pDnsRecordList - Recordlist from CNAME query
// IN LPSTR pszHost - Host to resolve (head of chain)
// OUT LPSTR *ppszChainTail - Tail of chain (points to memory within
// pDnsRecordList). If no IP addresses are returned, we must requery
// for A records for *ppszChainTail.
// OUT DWORD *rgdwIpAddresses - If the resolve worked, on return, this
// array is filled with the IP addresses for pszHost.
// IN OUT ULONG *pcIpAddresses - On calling this function pass in the
// number of IP addresses that can be accomodated into rgdwIpAddresses.
// On return, this is set to the number of IP addresses found for
// pszHost.
// Returns:
// ERROR_SUCCESS
// ERROR_INVALID_DATA
// This function always succeeds (no memory allocated)
//-----------------------------------------------------------------------------
DWORD ProcessCNAMEChain(
PDNS_RECORD pDnsRecordList,
LPSTR pszHost,
LPSTR *ppszChainTail,
DWORD *rgdwIpAddresses,
ULONG *pcIpAddresses)
{
DWORD dwReturn = ERROR_RETRY;
LPSTR pszRealHost = pszHost;
PDNS_RECORD pDnsRecord = pDnsRecordList;
PDNS_RECORD rgCNAMERecord[MAX_CNAME_RECORDS];
ULONG cCNAMERecord = 0;
INT i = 0;
INT j = 0;
TraceFunctEnterEx((LPARAM) pszHost, "ProcessCNAMEChain");
_ASSERT(ppszChainTail && rgdwIpAddresses && pcIpAddresses && *pcIpAddresses > 0);
//
// Filter out the CNAME records (if any) so that we can chain them
//
while(pDnsRecord) {
if(DNS_TYPE_CNAME == pDnsRecord->wType) {
DNS_PRINTF_MSG("Processing CNAME: %s CNAME %s\n",
pDnsRecord->nameOwner, pDnsRecord->Data.CNAME.nameHost);
DebugTrace((LPARAM) pszHost, "CNAME record: %s -> %s",
pDnsRecord->nameOwner, pDnsRecord->Data.CNAME.nameHost);
rgCNAMERecord[cCNAMERecord] = pDnsRecord;
cCNAMERecord++;
if(cCNAMERecord >= MAX_CNAME_RECORDS) {
DNS_PRINTF_ERR("Too many CNAME records to process\n");
ErrorTrace((LPARAM) pszHost, "Too many CNAME records (max=%d)."
" Failed resolve", MAX_CNAME_RECORDS);
TraceFunctLeaveEx((LPARAM) pszHost);
return ERROR_INVALID_DATA;
}
}
pDnsRecord = pDnsRecord->pNext;
}
if(cCNAMERecord > 0) {
DNS_PRINTF_DBG("CNAME records found. Chaining CNAMEs.\n");
DebugTrace((LPARAM) pszHost, "Chaining CNAME records to: %s", pszHost);
dwReturn = GetCNAMEChainTail(rgCNAMERecord, cCNAMERecord,
pszHost, ppszChainTail);
if(ERROR_INVALID_DATA == dwReturn) {
ErrorTrace((LPARAM) pszHost, "No chain tail from GetCNAMEChainTail: %d", dwReturn);
TraceFunctLeaveEx((LPARAM) pszHost);
goto Exit;
}
_ASSERT(*ppszChainTail);
} else {
DNS_PRINTF_DBG("No CNAME records in reply.\n");
}
DebugTrace((LPARAM) pszHost,
"GetCNAMEChainTail succeeded. Chain tail: %s", *ppszChainTail);
//
// If pszHost chains through some CNAME records to *ppszChainTail, then
// the host we should really be looking for is *ppszChainTail.
//
if(*ppszChainTail)
pszRealHost = *ppszChainTail;
FindARecord(pszRealHost, pDnsRecordList, rgdwIpAddresses, pcIpAddresses);
dwReturn = ERROR_SUCCESS;
Exit:
TraceFunctLeaveEx((LPARAM) pszHost);
return dwReturn;
}
//-----------------------------------------------------------------------------
// Description:
// Handles CNAME chaining. It is possible (although not recommended) to
// have chains of CNAME records. If the host pointed to by a CNAME record
// itself has a CNAME record pointing to another host for instance. In the
// interests of robustness we follow such CNAME chains to an extent even
// though CNAME chains are illegal according to the RFC.
//
// We run through all the CNAME records in an O(n^2) loop examining each
// pair of CNAME records for a "parent-child" relationship. If there is
// such a relationship, these records are "linked" to each other. We
// create a "directed graph" whose nodes are CNAME records and whose
// edges represent the "parent-child" relationship between CNAME records.
//
// Since the number of CNAME records allowed is small (MAX_CNAME_RECORDS),
// the cost of creating this graph is at most O(MAX_CNAME_RECORDS^2) which
// is still quite small.
//
// Once the graph has been created, we traverse every valid chain and
// return the tail of the first one we find that starts in pszHost.
//
// Arguments:
// IN PDNS_RECORD rgCNAMERecord --- Pass in an array of CNAME PDNS_RECORDS
// IN ULONG cCNAMERecord --- Number of PDNS_RECORDS in rgCNAMERecord
// IN LPSTR pszHost --- Hostname which we are trying to resolve
// OUT LPSTR *ppszChainTail --- Tail of CNAME chain starting at pszHost.
// This is NULL if the return value is not ERROR_SUCCESS.
// Returns:
// ERROR_SUCCESS - If we successfully built a CNAME chain in which case
// *ppszChainTail points to the tail of the chain.
// ERROR_INVALID_DATA - If a CNAME chain loop was detected, or a valid
// CNAME chain could not be built.
// Note:
// No memory is allocated in this function, and any errors returned are
// permanent.
//-----------------------------------------------------------------------------
DWORD GetCNAMEChainTail(
PDNS_RECORD *rgCNAMERecord,
ULONG cCNAMERecord,
LPSTR pszHost,
LPSTR *ppszChainTail)
{
DWORD dwReturn = ERROR_INVALID_DATA;
ULONG i = 0;
ULONG j = 0;
ULONG cCNAMELoopDetect = 0; // Keeps track of chain length
struct TREE_NODE
{
PDNS_RECORD pDnsRecord;
TREE_NODE *pParent;
TREE_NODE *rgChild[3];
ULONG cChild;
};
TREE_NODE rgNode[MAX_CNAME_RECORDS];
TREE_NODE *pNode = NULL;
TraceFunctEnterEx((LPARAM) pszHost, "GetCNAMEChainTail");
*ppszChainTail = NULL;
ZeroMemory(rgNode, sizeof(rgNode));
for(i = 0; i < cCNAMERecord; i++)
rgNode[i].pDnsRecord = rgCNAMERecord[i];
//
// If there are 2 nodes with the following CNAME records:
//
// foo.com CNAME bar.com
// bar.com CNAME foo.bar
//
// Then, we define the second one to be the child of the first -- i.e.
// they "chain up". We will build up a tree of such parent/child nodes.
//
// For all nodes i, check if any other node j is a child of i. If
// it is, add it as a child of i provided there is room (only 3 nodes
// may be added as children of a given node. Each node also has a back
// pointer, pointing to its parent node. If there are more than 3
// children we won't worry about it. You shouldn't have CNAME chains
// in the first place, let alone *multiple* CNAME chains involving the
// *same* record.
//
DebugTrace((LPARAM) pszHost, "Building chaining graph");
for(i = 0; i < cCNAMERecord; i++) {
for(j = 0; j < cCNAMERecord; j++) {
if(i == j)
continue;
DebugTrace((LPARAM) pszHost, "Comparing: %s and %s",
rgNode[i].pDnsRecord->Data.CNAME.nameHost,
rgNode[j].pDnsRecord->nameOwner);
if(DnsNameCompare_A(rgNode[i].pDnsRecord->Data.CNAME.nameHost,
rgNode[j].pDnsRecord->nameOwner)) {
if(rgNode[i].cChild > ARRAY_SIZE(rgNode[i].rgChild)) {
ErrorTrace((LPARAM) pszHost,
"Cannot chain (too many children): %s -> %s",
rgNode[i].pDnsRecord->Data.CNAME.nameHost,
rgNode[j].pDnsRecord->nameOwner);
DNS_PRINTF_DBG("The following record has too many aliases (max = 3).\n");
DNS_PRINTF_DBG("This is not a fatal error, but some aliases will be"
" ignored.\n");
DNS_PRINT_RECORD(rgNode[i].pDnsRecord);
continue;
}
DNS_PRINTF_MSG("%s is an alias for %s\n",
rgNode[i].pDnsRecord->Data.CNAME.nameHost,
rgNode[j].pDnsRecord->nameOwner);
DebugTrace((LPARAM) pszHost, "Chained: %s -> %s",
rgNode[i].pDnsRecord->Data.CNAME.nameHost,
rgNode[j].pDnsRecord->nameOwner);
rgNode[i].rgChild[rgNode[i].cChild] = &rgNode[j];
rgNode[j].pParent = &rgNode[i];
rgNode[i].cChild++;
break;
}
}
}
//
// For every leaf node, we traverse backwards till we hit the parent node
// at the root, and check if the parent node is a CNAME for pszHost. If it
// is, then the leaf node is the tail end of a valid CNAME chain.
//
// At the end of this loop, we have looked at every CNAME chain.
// (a) Either we found a valid CNAME chain or,
// (b) No valid CNAME chain exists or,
// (c) There is a loop in some CNAME chain.
//
for(i = 0; i < cCNAMERecord; i++) {
pNode = &rgNode[i];
//
// Not a leaf node
//
if(pNode->cChild > 0)
continue;
DebugTrace((LPARAM) pszHost, "Starting with CNAME record: (%s %s)",
pNode->pDnsRecord->nameOwner, pNode->pDnsRecord->Data.CNAME.nameHost);
//
// Traverse backwards till we hit a node without a parent.
//
while(pNode->pParent != NULL && cCNAMELoopDetect < cCNAMERecord) {
cCNAMELoopDetect++;
pNode = pNode->pParent;
DebugTrace((LPARAM) pszHost, "Next record in chain is: (%s %s)",
pNode->pDnsRecord->nameOwner,
pNode->pDnsRecord->Data.CNAME.nameHost);
}
//
// The CNAME chain can't be longer than the number of records unless
// there's a loop. In such a case error out with a permanent error.
//
if(cCNAMELoopDetect == cCNAMERecord) {
DNS_PRINTF_ERR("CNAME loop detected, abandoning resolution.\n");
ErrorTrace((LPARAM) pszHost, "CNAME loop detected\n");
dwReturn = ERROR_INVALID_DATA;
goto Exit;
}
//
// Does the root node (pNode) match *ppszHost? If so, we found a chain.
// Set *ppszHost to the tail of the CNAME chain.
//
if(MyDnsNameCompare(pszHost, pNode->pDnsRecord->nameOwner)) {
*ppszChainTail = rgNode[i].pDnsRecord->Data.CNAME.nameHost;
dwReturn = ERROR_SUCCESS;
DebugTrace((LPARAM) pszHost, "Found complete chain from %s to %s",
pszHost, pNode->pDnsRecord->Data.CNAME.nameHost);
DNS_PRINTF_MSG("%s is an alias for %s\n", pszHost, pNode->pDnsRecord->nameOwner);
break;
}
}
Exit:
TraceFunctLeaveEx((LPARAM) pszHost);
return dwReturn;
}
//-----------------------------------------------------------------------------
// Description:
// Given a hostname and a list of PDNS_RECORDs, this function searches
// the list for A records matching the hostname and returns the IP
// addresses in the A records.
// Arguments:
// IN LPSTR pszHost --- Host for which to look for A records. Can be
// a NetBIOS name, in which case we only try to match the "prefix".
// See MyDnsQuery and MyDnsNameCompare documentation for what prefixes
// are.
// IN PDNS_RECORD pDnsRecordList --- List of records to scan.
// OUT DWORD *rgdwIpAddresses --- Pass in an array which will be filled
// in with the IP addresses found.
// IN OUT ULONG *pcIpAddresses --- Pass in the number of IP addresses
// that can be stored in rgdwIpAddresses. On return, this is
// initialized to the number of IP addresses found. We will only
// return as many IP addresses as there is space for.
// Returns:
// Nothing.
// This function always succeeds (no memory allocated)
//-----------------------------------------------------------------------------
void FindARecord(
LPSTR pszHost,
PDNS_RECORD pDnsRecordList,
DWORD *rgdwIpAddresses,
ULONG *pcIpAddresses)
{
ULONG i = 0;
PDNS_RECORD pDnsRecord = pDnsRecordList;
TraceFunctEnterEx((LPARAM) pszHost, "FindARecord");
DNS_PRINTF_DBG("Checking reply for A record for %s\n", pszHost);
DebugTrace((LPARAM) pszHost, "Looking for A record for %s", pszHost);
_ASSERT(pszHost && rgdwIpAddresses && pcIpAddresses && *pcIpAddresses > 0);
while(pDnsRecord) {
if(DNS_TYPE_A == pDnsRecord->wType) {
DebugTrace((LPARAM) pszHost, "Comparing with %s",
pDnsRecord->nameOwner);
}
if(DNS_TYPE_A == pDnsRecord->wType &&
MyDnsNameCompare(pszHost, pDnsRecord->nameOwner)) {
if(i > *pcIpAddresses)
break;
rgdwIpAddresses[i] = pDnsRecord->Data.A.ipAddress;
i++;
}
pDnsRecord = pDnsRecord->pNext;
}
DNS_PRINTF_MSG("%d A record(s) found for %s\n", i, pszHost);
DebugTrace((LPARAM) pszHost, "Found %d matches", i);
*pcIpAddresses = i;
TraceFunctLeaveEx((LPARAM) pszHost);
}
//-----------------------------------------------------------------------------
// Description:
// When querying for DNS to resolve hostnames, SMTP may also be asked to
// resolve NetBIOS names. In this case we must append the DNS suffixes
// configured for this machine before sending queries to the DNS server.
// This option can be toggled by setting the DNS_QUERY_TREAT_AS_FQDN flag
// to DnsQuery. MyDnsQuery is a simple wrapper function that checks if the
// name we are querying for is a NetBIOS name, and if is, it turns on
// the DNS_QUERY_TREAT_AS_FQDN flag to try the suffixes. Of course, if
// we are using suffixes, the returned *ppDnsRecordList will contain
// records that may not match pszHost. So checking for matching records
// should be done using MyDnsNameCompare instead of straight string
// comparison.
// Arguments:
// Same as arguments to DnsQuery_A
// Returns:
// Same as return value from DnsQuery_A
//-----------------------------------------------------------------------------
DWORD MyDnsQuery(
LPSTR pszHost,
WORD wType,
DWORD fOptions,
PIP_ARRAY pipDnsServers,
PDNS_RECORD *ppDnsRecordList)
{
BOOL fGlobal = TRUE;
if(NULL != strchr(pszHost, '.'))
fOptions |= DNS_QUERY_TREAT_AS_FQDN;
else
_ASSERT(0 == (fOptions & DNS_QUERY_TREAT_AS_FQDN));
if(pipDnsServers)
fGlobal = FALSE;
DNS_LOG_API_QUERY(
pszHost, // Host to query for
DNS_TYPE_A, // Query type
fOptions, // Flags for DNSAPI
fGlobal, // Are the global DNS servers being used
pipDnsServers); // Serverlist
return DnsQuery_A(
pszHost,
wType,
fOptions,
pipDnsServers,
ppDnsRecordList,
NULL);
}
//-----------------------------------------------------------------------------
// Description:
// Comparing DNS names is more than just a simple string comparison. Since
// the DNS API may append suffixes to the query before sending it to DNS,
// the returned records may not contain the exact same name as the name
// passed into MyDnsQuery. Specifically, the returned records may contain
// the name we passed into MyDnsQuery plus a suffix name. Since MyDnsQuery
// appends a suffix if we are trying to resolve a NetBIOS name, the returned
// record may not exactly match the name passed into MyDnsQuery for NetBIOS
// names. This function checks if pszHost is a NetBIOS name, and if it is,
// we will try to match only the "prefix".
// Arguments:
// IN LPSTR pszHost --- NetBIOS-name/FQDN to match
// IN LPSTR pszFqdn --- FQDN from DNS reply to match against (possibly
// includes a suffix appended by DnsQuery)
// Returns:
// TRUE if strings match
// FALSE otherwise
//-----------------------------------------------------------------------------
BOOL MyDnsNameCompare(
LPSTR pszHost,
LPSTR pszFqdn)
{
int cbHost = 0;
int cbFqdn = 0;
CHAR ch = '\0';
BOOL fRet = FALSE;
// Not a NetBIOS name... can do a straight string comparison
if(NULL != strchr(pszHost, '.'))
return DnsNameCompare_A(pszHost, pszFqdn);
//
// If it is a NetBIOS name, pszFqdn must be pszHost + Suffix, otherwise the
// names don't match. First remove the suffix.
//
cbHost = lstrlen(pszHost);
cbFqdn = lstrlen(pszFqdn);
//
// If pszFqdn == (pszHost+Suffix) then cbFqdn) must be >= cbHost
//
if(cbFqdn < cbHost)
return 1;
// The prefix and suffix should be joined with a '.' in between
if(pszFqdn[cbHost] != '.' && pszFqdn[cbHost] != '\0')
return 1;
// Remove the suffix and compare prefixes.
ch = pszFqdn[cbHost];
pszFqdn[cbHost] = '\0';
fRet = !lstrcmpi(pszHost, pszFqdn);
pszFqdn[cbHost] = ch;
return fRet;
}