windows-nt/Source/XPSP1/NT/net/rras/ip/nathlp/dns/dnslookup.cpp
2020-09-26 16:20:57 +08:00

2137 lines
48 KiB
C++

/*++
Copyright (c) 1999, Microsoft Corporation
Module Name:
dnslookup.c
Abstract:
This module contains code for the DNS component's name-lookup mechanism.
Author:
Tom Brown (tbrown) 10/21/99
Revision History:
Raghu Gatta (rgatta) 21-Oct-2000
Rewrite + Cleanup + New Functions
--*/
#include "precomp.h"
#pragma hdrstop
#define DNS_HOMENET_DOT L"."
ULONG g_PrivateIPAddr = 0;
CRITICAL_SECTION DnsTableLock; // protects both tables
RTL_GENERIC_TABLE g_DnsTable,
g_ReverseDnsTable;
//
// FORWARD DECLARATIONS
//
ULONG
DhcpGetPrivateInterfaceAddress(
VOID
);
RTL_GENERIC_COMPARE_RESULTS
TableNameCompareRoutine(
PRTL_GENERIC_TABLE Table,
PVOID FirstStruct,
PVOID SecondStruct
)
/*++
Routine Description:
This is a callback routine to compare two DNS_ENTRY structures.
It is used by the RTL table implementation.
Arguments:
Table - pointer to the RTL table. Not used.
FirstStruct - the first DNS_ENTRY structure
SecondStruct - the second DNS_ENTRY structure
Return Value:
One of GenericLessThan, GenericGreaterThan, or GenericEqual,
depending on the relative values of the parameters.
Environment:
Called back by the Rtl table lookup routines.
--*/
{
INT iCompareResults;
BOOL fNamesAreEqual;
WCHAR *pszFirstName, *pszSecondName;
PROFILE("TableNameCompareRoutine");
pszFirstName = ((PDNS_ENTRY)FirstStruct)->pszName;
pszSecondName = ((PDNS_ENTRY)SecondStruct)->pszName;
fNamesAreEqual = DnsNameCompare_W(pszFirstName, pszSecondName);
if (fNamesAreEqual)
{
iCompareResults = 0;
}
else
{
iCompareResults = _wcsicmp(pszFirstName, pszSecondName);
}
if (iCompareResults < 0)
{
return GenericLessThan;
}
else if (iCompareResults > 0)
{
return GenericGreaterThan;
}
else
{
return GenericEqual;
}
}
RTL_GENERIC_COMPARE_RESULTS
TableAddressCompareRoutine(
PRTL_GENERIC_TABLE Table,
PVOID FirstStruct,
PVOID SecondStruct
)
/*++
Routine Description:
This is a callback routine to compare two REVERSE_DNS_ENTRY structures.
It is used by the RTL table implementation.
Arguments:
Table - pointer to the RTL table. Not used.
FirstStruct - the first REVERSE_DNS_ENTRY structure
SecondStruct - the second REVERSE_DNS_ENTRY structure
Return Value:
One of GenericLessThan, GenericGreaterThan, or GenericEqual,
depending on the relative values of the parameters.
Environment:
Called back by the Rtl table lookup routines.
--*/
{
DNS_ADDRESS Address1, Address2;
PROFILE("TableAddressCompareRoutine");
Address1 = ((PREVERSE_DNS_ENTRY)FirstStruct)->ulAddress;
Address2 = ((PREVERSE_DNS_ENTRY)SecondStruct)->ulAddress;
if (Address1 > Address2)
{
return GenericGreaterThan;
}
else if (Address1 < Address2)
{
return GenericLessThan;
}
else
{
return GenericEqual;
}
}
PVOID
TableAllocateRoutine(
PRTL_GENERIC_TABLE Table,
CLONG ByteSize
)
/*++
Routine Description:
This is a callback routine to allocate memory for an Rtl table.
Arguments:
Table - pointer to the RTL table. Not used.
ByteSize - the number of bytes to allocate
SecondStruct - the second DNS_ENTRY structure
Return Value:
A pointer to the allocated memory.
Environment:
Called back by the Rtl table lookup routines.
--*/
{
return NH_ALLOCATE(ByteSize);
}
VOID
TableFreeRoutine(
PRTL_GENERIC_TABLE Table,
PVOID pBuffer
)
/*++
Routine Description:
This is a callback routine to free memory allocated by TableAllocateRoutine.
Arguments:
Table - pointer to the RTL table. Not used.
pBuffer - pointer to the buffer to free
Return Value:
None
Environment:
Called back by the Rtl table lookup routines.
--*/
{
NH_FREE(pBuffer);
}
ULONG
DnsInitializeTableManagement(
VOID
)
/*++
Routine Description:
This is a public function that must be called before any of the other Dns
table functions. It initializes the various tables used by the server.
Arguments:
None
Return Value:
None
Environment:
Arbitrary.
--*/
{
ULONG Error = NO_ERROR;
PROFILE("DnsInitializeTableManagement");
__try {
InitializeCriticalSection(&DnsTableLock);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
NhTrace(
TRACE_FLAG_DNS,
"DnsInitializeTableManagement: exception %d creating lock",
Error = GetExceptionCode()
);
}
RtlInitializeGenericTable(
&g_DnsTable,
TableNameCompareRoutine,
TableAllocateRoutine,
TableFreeRoutine,
NULL
);
RtlInitializeGenericTable(
&g_ReverseDnsTable,
TableAddressCompareRoutine,
TableAllocateRoutine,
TableFreeRoutine,
NULL
);
return Error;
} // DnsInitializeTableManagement
VOID
DnsShutdownTableManagement(
VOID
)
/*++
Routine Description:
This routine is called to shutdown the table management module.
Arguments:
none.
Return Value:
none.
Environment:
Invoked in an arbitrary thread context.
--*/
{
PROFILE("DnsShutdownTableManagement");
DnsEmptyTables();
DeleteCriticalSection(&DnsTableLock);
} // DnsShutdownTableManagement
VOID
DnsEmptyTables(
VOID
)
/*++
Routine Description:
This routine is called to empty the DNS tables.
Arguments:
none.
Return Value:
none.
Environment:
Invoked in an arbitrary thread context.
--*/
{
ULONG i, count;
PDNS_ENTRY pDnsEntry;
REVERSE_DNS_ENTRY reverseEntry;
PREVERSE_DNS_ENTRY pRDnsEntry;
WCHAR *pszNameCopy;
PROFILE("DnsEmptyTables");
//
// for each entry in the forward table, delete all the entries in the
// reverse table
//
//
// emptying table in LIFO order
//
EnterCriticalSection(&DnsTableLock);
count = RtlNumberGenericTableElements(&g_DnsTable);
while (count)
{
pDnsEntry = (PDNS_ENTRY) RtlGetElementGenericTable(
&g_DnsTable,
--count
);
reverseEntry.pszName = NULL;
for (i = 0; i < pDnsEntry->cAddresses; i++)
{
reverseEntry.ulAddress = pDnsEntry->aAddressInfo[i].ulAddress;
RtlDeleteElementGenericTable(
&g_ReverseDnsTable,
&reverseEntry
);
}
pszNameCopy = pDnsEntry->pszName;
NH_FREE(pDnsEntry->aAddressInfo);
pDnsEntry->aAddressInfo = NULL;
RtlDeleteElementGenericTable(
&g_DnsTable,
pDnsEntry
);
NH_FREE(pszNameCopy);
}
//
// the forward table should be empty by now
//
ASSERT(RtlIsGenericTableEmpty(&g_DnsTable));
//
// ensure that the reverse table is also empty
//
count = RtlNumberGenericTableElements(&g_ReverseDnsTable);
while (count)
{
pRDnsEntry = (PREVERSE_DNS_ENTRY) RtlGetElementGenericTable(
&g_ReverseDnsTable,
--count
);
RtlDeleteElementGenericTable(
&g_ReverseDnsTable,
pRDnsEntry
);
}
LeaveCriticalSection(&DnsTableLock);
} // DnsEmptyTables
BOOL
DnsRegisterName(
WCHAR *pszName,
UINT cAddresses,
ADDRESS_INFO aAddressInfo[]
)
/*++
Routine Description:
Public function to register a DNS name in the server's table.
Arguments:
pszName - Name to register, in Unicode, dotted-name format.
cAddresses - Number of addresses associated with this name
aAddressInfo - Array of address information (addresses in network order)
Return Value:
TRUE if the registration was for a new name (the name did not already exist
in the table); FALSE if the name already existed and was replaced.
FALSE also if there was an error condition.
Environment:
Arbitrary
--*/
{
DNS_ENTRY dnsEntry;
DWORD cAddressesAllocated = 0;
REVERSE_DNS_ENTRY reverseDnsEntry;
BOOLEAN fNewElement = TRUE,
fNameIsNew = FALSE;
UINT i;
PROFILE("DnsRegisterName");
NhTrace(
TRACE_FLAG_DNS,
"DnsRegisterName: Registering name %S, with %d addresses",
pszName,
cAddresses
);
for (i = 0; i < cAddresses; i++)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsRegisterName: Address %d = %lx",
i,
aAddressInfo[i].ulAddress
);
}
dnsEntry.pszName = (PWCHAR) NH_ALLOCATE((wcslen(pszName) + 1) * sizeof(WCHAR));
if (!dnsEntry.pszName)
{
return fNameIsNew; // currently set to FALSE
}
wcscpy(dnsEntry.pszName, pszName);
if (cAddresses == 1)
{
// In general, all names will have one address; so if we're just registering
// one name, then only allocate enough space for one name.
cAddressesAllocated = 1;
}
else
{
// If we have more than one address, then allocate in increments of 5.
cAddressesAllocated = ((cAddresses + 4) / 5) * 5;
}
dnsEntry.aAddressInfo = (PADDRESS_INFO) NH_ALLOCATE(cAddressesAllocated * sizeof(ADDRESS_INFO));
if (!dnsEntry.aAddressInfo)
{
NH_FREE(dnsEntry.pszName);
return fNameIsNew; // currently set to FALSE
}
memcpy(dnsEntry.aAddressInfo, aAddressInfo, cAddresses * sizeof(ADDRESS_INFO));
dnsEntry.cAddresses = cAddresses;
dnsEntry.cAddressesAllocated = cAddressesAllocated;
EnterCriticalSection(&DnsTableLock);
RtlInsertElementGenericTable(
&g_DnsTable,
&dnsEntry,
sizeof(dnsEntry),
&fNameIsNew
);
reverseDnsEntry.pszName = dnsEntry.pszName;
for (i = 0; i < cAddresses; i++)
{
PREVERSE_DNS_ENTRY pEntry;
reverseDnsEntry.ulAddress = dnsEntry.aAddressInfo[i].ulAddress;
pEntry = (PREVERSE_DNS_ENTRY) RtlInsertElementGenericTable(
&g_ReverseDnsTable,
&reverseDnsEntry,
sizeof(reverseDnsEntry),
&fNewElement
);
// If this IP address is already in the reverse table, then replace it.
if (!fNewElement)
{
pEntry->pszName = dnsEntry.pszName;
}
}
LeaveCriticalSection(&DnsTableLock);
if (!fNewElement)
{
DnsCleanupTables();
}
return fNameIsNew;
} // DnsRegisterName
VOID
DnsAddAddressForName(
WCHAR *pszName,
DNS_ADDRESS ulAddress,
FILETIME ftExpires
)
/*++
Routine Description:
Public function to add an IP address for a name that potentially
already exists.
Arguments:
pszName - Name to register, in Unicode, dotted-name format.
ulAddress - New IP address to associate with this name,
in network order
Return Value:
None.
Environment:
Arbitrary
--*/
{
PDNS_ENTRY pEntry;
PROFILE("DnsAddAddressForName");
pEntry = DnsLookupAddress(pszName);
if (pEntry == NULL)
{
ADDRESS_INFO info;
info.ulAddress = ulAddress;
info.ftExpires = ftExpires;
//info.ulExpires = ulExpires;
DnsRegisterName(pszName, 1, &info);
}
else
{
UINT i;
REVERSE_DNS_ENTRY reverseDnsEntry;
PREVERSE_DNS_ENTRY pReverseEntry;
BOOLEAN fNewElement;
// first, let's make sure that this IP address isn't already associated with
// this name.
for (i = 0; i < pEntry->cAddresses; i++)
{
if (pEntry->aAddressInfo[i].ulAddress == ulAddress)
{
//
// simply update the expiry time
//
NhTrace(
TRACE_FLAG_DNS,
"DnsAddAddressForName: Refresh expiry time for %S",
pszName
);
pEntry->aAddressInfo[i].ftExpires = ftExpires;
return;
}
}
//
// we limit the number of addresses per machine name to one only
//
//
// guard against zero allocation
//
if (!pEntry->cAddressesAllocated)
{
pEntry->aAddressInfo = (PADDRESS_INFO) NH_ALLOCATE(1 * sizeof(ADDRESS_INFO));
if (pEntry->aAddressInfo)
{
pEntry->cAddressesAllocated = 1;
}
else
{
// no memory - return quitely
return;
}
}
//
// at least 1 block has been allocated
//
pEntry->cAddresses = 1;
pEntry->aAddressInfo[0].ulAddress = ulAddress;
pEntry->aAddressInfo[0].ftExpires = ftExpires;
reverseDnsEntry.ulAddress = ulAddress;
reverseDnsEntry.pszName = pEntry->pszName;
EnterCriticalSection(&DnsTableLock);
pReverseEntry = (PREVERSE_DNS_ENTRY) RtlInsertElementGenericTable(
&g_ReverseDnsTable,
&reverseDnsEntry,
sizeof(reverseDnsEntry),
&fNewElement
);
// If this IP address is already in the reverse table, then replace it.
if (!fNewElement)
{
pReverseEntry->pszName = pEntry->pszName;
}
LeaveCriticalSection(&DnsTableLock);
if (!fNewElement)
{
DnsCleanupTables();
}
}
} // DnsAddAddressForName
VOID
DnsDeleteAddressForName(
WCHAR *pszName,
DNS_ADDRESS ulAddress
)
/*++
Routine Description:
Public function to un-associate an IP address from a given name,
and potentially delete the record from the table if there are no more
IP addresses associated with the name.
Arguments:
pszName - Name, in Unicode, dotted-name format.
ulAddress - IP address to un-associate with the given name,
in network order
Return Value:
None.
Environment:
Arbitrary
--*/
{
PDNS_ENTRY pEntry;
REVERSE_DNS_ENTRY reverseEntry;
PROFILE("DnsDeleteAddressForName");
pEntry = DnsLookupAddress(pszName);
if (pEntry != NULL)
{
INT i, iLocation = -1;
// Find the index of the requested address
for (i = 0; i < (INT)pEntry->cAddresses; i++)
{
if (pEntry->aAddressInfo[i].ulAddress == ulAddress)
{
iLocation = i;
break;
}
}
if (iLocation > -1)
{
if (pEntry->cAddresses > 1)
{
// Move the rest of the array backwards
memcpy(&pEntry->aAddressInfo[iLocation],
&pEntry->aAddressInfo[iLocation + 1],
(pEntry->cAddresses - 1 - iLocation) * sizeof(ADDRESS_INFO));
pEntry->cAddresses--;
}
else
{
// Delete the whole entry - it no longer has any IP addresses associated
// with it.
DnsDeleteName(pszName);
}
}
}
reverseEntry.pszName = NULL;
reverseEntry.ulAddress = ulAddress;
EnterCriticalSection(&DnsTableLock);
RtlDeleteElementGenericTable(
&g_ReverseDnsTable,
&reverseEntry
);
LeaveCriticalSection(&DnsTableLock);
} // DnsDeleteAddressForName
PDNS_ENTRY
DnsPurgeExpiredNames(
PDNS_ENTRY pEntry
)
/*++
Routine Description:
TODO.
Arguments:
TODO
Return Value:
TODO
Environment:
TODO.
--*/
{
UINT i, j;
FILETIME ftTime;
REVERSE_DNS_ENTRY reverseEntry;
PROFILE("DnsPurgeExpiredNames");
GetSystemTimeAsFileTime(&ftTime);
reverseEntry.pszName = NULL;
for (j = 1; j < pEntry->cAddresses + 1; j++)
{
// j is 1-based so that we can safely subtract 1 from it below (it's unsigned).
// we really want the 0-based number, so we translate that to i immediately.
i = j - 1;
if (IsFileTimeExpired(&pEntry->aAddressInfo[i].ftExpires))
{
NhTrace(TRACE_FLAG_DNS, "DnsPurgeExpiredNames: Deleting address %lx for name %ls",
pEntry->aAddressInfo[i].ulAddress,
pEntry->pszName);
reverseEntry.ulAddress = pEntry->aAddressInfo[i].ulAddress;
RtlDeleteElementGenericTable(
&g_ReverseDnsTable,
&reverseEntry
);
memcpy(&pEntry->aAddressInfo[i], &pEntry->aAddressInfo[i+1],
(pEntry->cAddresses - i - 1) * sizeof(ADDRESS_INFO));
pEntry->cAddresses--;
j--;
}
}
if (pEntry->cAddresses == 0)
{
WCHAR *pszName;
pszName = pEntry->pszName;
NH_FREE(pEntry->aAddressInfo);
pEntry->aAddressInfo = NULL;
RtlDeleteElementGenericTable(
&g_DnsTable,
pEntry
);
NH_FREE(pszName);
pEntry = NULL;
}
return pEntry;
} // DnsPurgeExpiredNames
PDNS_ENTRY
DnsLookupAddress(
WCHAR *pszName
)
/*++
Routine Description:
Public function to look up the address of a given name.
Arguments:
pszName - Name to look up, in Unicode, dotted name format.
Return Value:
A pointer to the DNS_ENTRY value that is in the table. Note that
this is not a copy, so a) it should not be freed by the caller, and
b) any modifications made to the data will be reflected in the table.
If the name is not found, the function will return NULL.
Addresses are stored in network order.
Environment:
Arbitrary.
--*/
{
PDNS_ENTRY pEntry;
DNS_ENTRY dnsSearch;
PROFILE("DnsLookupAddress");
dnsSearch.pszName = pszName;
dnsSearch.cAddresses = 0;
EnterCriticalSection(&DnsTableLock);
pEntry = (PDNS_ENTRY) RtlLookupElementGenericTable(
&g_DnsTable,
&dnsSearch
);
if (pEntry)
{
pEntry = DnsPurgeExpiredNames(pEntry);
}
LeaveCriticalSection(&DnsTableLock);
return pEntry;
} // DnsLookupAddress
PREVERSE_DNS_ENTRY
DnsLookupName(
DNS_ADDRESS ulAddress
)
/*++
Routine Description:
Public function to look up the name of a given address.
Arguments:
ulAddress - network order address.
Return Value:
A pointer to the REVERSE_DNS_ENTRY value that is in the table. Note that
this is not a copy, so a) it should not be freed by the caller, and
b) any modifications made to the data will be reflected in the table.
If the address is not found, the function will return NULL.
Environment:
Arbitrary.
--*/
{
PREVERSE_DNS_ENTRY pEntry;
REVERSE_DNS_ENTRY dnsSearch;
PROFILE("DnsLookupName");
dnsSearch.ulAddress = ulAddress;
dnsSearch.pszName = NULL;
EnterCriticalSection(&DnsTableLock);
pEntry = (PREVERSE_DNS_ENTRY) RtlLookupElementGenericTable(
&g_ReverseDnsTable,
&dnsSearch
);
LeaveCriticalSection(&DnsTableLock);
return pEntry;
} // DnsLookupName
VOID
DnsDeleteName(
WCHAR *pszName
)
/*++
Routine Description:
Public function to delete a given name from the DNS table.
Arguments:
pszName - Name to delete.
Return Value:
None.
Environment:
Arbitrary.
--*/
{
PDNS_ENTRY pEntry;
REVERSE_DNS_ENTRY reverseEntry;
UINT i;
WCHAR *pszNameCopy;
PROFILE("DnsDeleteName");
pEntry = DnsLookupAddress(pszName);
reverseEntry.pszName = NULL;
EnterCriticalSection(&DnsTableLock);
for (i = 0; i < pEntry->cAddresses; i++)
{
reverseEntry.ulAddress = pEntry->aAddressInfo[i].ulAddress;
RtlDeleteElementGenericTable(
&g_ReverseDnsTable,
&reverseEntry
);
}
pszNameCopy = pEntry->pszName;
NH_FREE(pEntry->aAddressInfo);
pEntry->aAddressInfo = NULL;
RtlDeleteElementGenericTable(
&g_DnsTable,
pEntry
);
LeaveCriticalSection(&DnsTableLock);
NH_FREE(pszNameCopy);
} // DnsDeleteName
VOID
DnsUpdateName(
WCHAR *pszName,
DNS_ADDRESS ulAddress
)
/*++
Routine Description:
Public function to add an IP address for a name that potentially
already exists. If both name and address exist, we update the time
in the table for a fresh lease
Arguments:
pszName - Name to register, in Unicode, dotted-name format.
ulAddress - (possibly new) IP address to associate with this name,
in network order
Return Value:
None.
Environment:
Arbitrary
--*/
{
PDNS_ENTRY pEntry;
FILETIME ftExpires;
LARGE_INTEGER liExpires, liTime, liNow;
BOOL fWriteToStore = FALSE;
BOOLEAN fNewElement = TRUE; // refers to reverse table entry
GetSystemTimeAsFileTime(&ftExpires); // current UTC time
memcpy(&liNow, &ftExpires, sizeof(LARGE_INTEGER));
//
// current cache table expiry time is fixed - put in registry afterwards
//
liTime = RtlEnlargedIntegerMultiply(CACHE_ENTRY_EXPIRY, SYSTIME_UNITS_IN_1_SEC);
liExpires = RtlLargeIntegerAdd(liTime, liNow);;
memcpy(&ftExpires, &liExpires, sizeof(LARGE_INTEGER));
PROFILE("DnsUpdateName");
pEntry = DnsLookupAddress(pszName);
if (pEntry == NULL)
{
ADDRESS_INFO info;
info.ulAddress = ulAddress;
info.ftExpires = ftExpires;
DnsRegisterName(pszName, 1, &info);
fWriteToStore = TRUE;
}
else
{
UINT i;
REVERSE_DNS_ENTRY reverseDnsEntry;
PREVERSE_DNS_ENTRY pReverseEntry;
// first, let's make sure that this IP address isn't already associated with
// this name.
for (i = 0; i < pEntry->cAddresses; i++)
{
if (pEntry->aAddressInfo[i].ulAddress == ulAddress)
{
//
// simply update the expiry time
//
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdateName: Refresh expiry time for %S",
pszName
);
pEntry->aAddressInfo[i].ftExpires = ftExpires;
return;
}
}
//
// we limit the number of addresses per machine name to one only
//
//
// guard against zero allocation
//
if (!pEntry->cAddressesAllocated)
{
pEntry->aAddressInfo = (PADDRESS_INFO) NH_ALLOCATE(1 * sizeof(ADDRESS_INFO));
if (pEntry->aAddressInfo)
{
pEntry->cAddressesAllocated = 1;
}
else
{
// no memory - return quitely
return;
}
}
//
// at least 1 block has been allocated
//
pEntry->cAddresses = 1;
pEntry->aAddressInfo[0].ulAddress = ulAddress;
pEntry->aAddressInfo[0].ftExpires = ftExpires;
reverseDnsEntry.ulAddress = ulAddress;
reverseDnsEntry.pszName = pEntry->pszName;
EnterCriticalSection(&DnsTableLock);
pReverseEntry = (PREVERSE_DNS_ENTRY) RtlInsertElementGenericTable(
&g_ReverseDnsTable,
&reverseDnsEntry,
sizeof(reverseDnsEntry),
&fNewElement
);
// If this IP address is already in the reverse table, then replace it.
if (!fNewElement)
{
pReverseEntry->pszName = pEntry->pszName;
}
LeaveCriticalSection(&DnsTableLock);
if (!fNewElement)
{
DnsCleanupTables();
}
fWriteToStore = TRUE;
}
if (fWriteToStore)
{
SaveHostsIcsFile(FALSE);
}
} // DnsUpdateName
VOID
DnsUpdate(
CHAR *pName,
ULONG len,
ULONG ulAddress
)
/*++
Routine Description:
Called from the DHCP component to simulate Dynamic DNS.
Arguments:
pName - hostname to register, in wire format.
len - length of hostname
ulAddress - (possibly new) IP address to associate with this name,
in network order
Return Value:
None.
Environment:
Arbitrary
--*/
{
PROFILE("DnsUpdate");
//
// convert string to a Unicode string and update table
//
DWORD dwSize = 0;
DWORD Error = NO_ERROR;
LPVOID lpMsgBuf = NULL;
LPBYTE pszName = NULL;
PWCHAR pszUnicodeFQDN = NULL;
if (NULL == pName || 0 == len || '\0' == *pName)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: No Name present - discard DNS Update"
);
return;
}
do
{
EnterCriticalSection(&DnsGlobalInfoLock);
if (!DnsICSDomainSuffix)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: DnsICSDomainSuffix string not present - update failed!"
);
break;
}
//
// create a null terminated copy
//
dwSize = len + 4;
pszName = reinterpret_cast<LPBYTE>(NH_ALLOCATE(dwSize));
if (!pszName)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: allocation failed for hostname copy buffer"
);
break;
}
ZeroMemory(pszName, dwSize);
memcpy(pszName, pName, len);
pszName[len] = '\0';
//
// NOTE: the RFCs are unclear about how to handle hostname option.
// try out different codepage conversions to unicode in order of:
// OEM, ANSI, MAC and finally give UTF8 a try
// our default conversion is to use mbstowcs()
//
//
// try OEM to Unicode conversion
//
Error = DnsConvertHostNametoUnicode(
CP_OEMCP,
(PCHAR)pszName,
DnsICSDomainSuffix,
&pszUnicodeFQDN
);
if (Error)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: DnsConvertHostName(OEM)toUnicode failed with "
"Error %ld (0x%08x)",
Error,
Error
);
if (pszUnicodeFQDN)
{
NH_FREE(pszUnicodeFQDN);
pszUnicodeFQDN = NULL;
}
}
//
// try ANSI to Unicode conversion
//
if (!pszUnicodeFQDN)
{
Error = DnsConvertHostNametoUnicode(
CP_ACP,
(PCHAR)pszName,
DnsICSDomainSuffix,
&pszUnicodeFQDN
);
if (Error)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: DnsConvertHostName(ANSI)toUnicode failed with "
"Error %ld (0x%08x)",
Error,
Error
);
if (pszUnicodeFQDN)
{
NH_FREE(pszUnicodeFQDN);
pszUnicodeFQDN = NULL;
}
}
}
//
// try MAC to Unicode conversion
//
if (!pszUnicodeFQDN)
{
Error = DnsConvertHostNametoUnicode(
CP_MACCP,
(PCHAR)pszName,
DnsICSDomainSuffix,
&pszUnicodeFQDN
);
if (Error)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: DnsConvertHostName(MAC)toUnicode() failed with "
"Error %ld (0x%08x)",
Error,
Error
);
if (pszUnicodeFQDN)
{
NH_FREE(pszUnicodeFQDN);
pszUnicodeFQDN = NULL;
}
}
}
//
// try UTF8 to Unicode conversion
//
if (!pszUnicodeFQDN)
{
Error = DnsConvertHostNametoUnicode(
CP_UTF8,
(PCHAR)pszName,
DnsICSDomainSuffix,
&pszUnicodeFQDN
);
if (Error)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: DnsConvertHostName(UTF8)toUnicode() failed with "
"Error %ld (0x%08x)",
Error,
Error
);
if (pszUnicodeFQDN)
{
NH_FREE(pszUnicodeFQDN);
pszUnicodeFQDN = NULL;
}
}
}
//
// default conversion
//
if (!pszUnicodeFQDN)
{
dwSize = len +
wcslen(DNS_HOMENET_DOT) +
wcslen(DnsICSDomainSuffix) +
1;
pszUnicodeFQDN = reinterpret_cast<PWCHAR>(NH_ALLOCATE(sizeof(WCHAR) * dwSize));
if (!pszUnicodeFQDN)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsUpdate: allocation failed for client name"
);
break;
}
ZeroMemory(pszUnicodeFQDN, (sizeof(WCHAR) * dwSize));
mbstowcs(pszUnicodeFQDN, (char *)pszName, len);
wcscat(pszUnicodeFQDN, DNS_HOMENET_DOT); // add the dot
wcscat(pszUnicodeFQDN, DnsICSDomainSuffix); // add the suffix
}
LeaveCriticalSection(&DnsGlobalInfoLock);
DnsUpdateName(
pszUnicodeFQDN,
ulAddress
);
NH_FREE(pszName);
NH_FREE(pszUnicodeFQDN);
return;
} while (FALSE);
LeaveCriticalSection(&DnsGlobalInfoLock);
if (pszName)
{
NH_FREE(pszName);
}
if (pszUnicodeFQDN)
{
NH_FREE(pszUnicodeFQDN);
}
return;
} // DnsUpdate
VOID
DnsAddSelf(
VOID
)
/*++
Routine Description:
Called each time we do a load of the hosts.ics file
Arguments:
none.
Return Value:
None.
Environment:
Arbitrary
--*/
{
PROFILE("DnsAddSelf");
DWORD len = 512, dwSize = 0;
WCHAR pszCompNameBuf[512];
PWCHAR pszBuf = NULL;
LPVOID lpMsgBuf;
ULONG ulAddress = 0;
FILETIME ftExpires;
LARGE_INTEGER liExpires, liTime, liNow;
ZeroMemory(pszCompNameBuf, (sizeof(WCHAR) * len));
if (!GetComputerNameExW(
ComputerNameDnsHostname,//ComputerNameNetBIOS,
pszCompNameBuf,
&len
)
)
{
lpMsgBuf = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
NhTrace(
TRACE_FLAG_DNS,
"DnsAddSelf: GetComputerNameExW failed with message: %S",
lpMsgBuf
);
if (lpMsgBuf) LocalFree(lpMsgBuf);
}
else
{
//
// we query the DHCP component if it is active for an IP address
// because it has scope information also. if this fails, we revert
// to the DNS component's list of interface addresses
//
//
// check if DHCP component is active
//
if (REFERENCE_DHCP())
{
ulAddress = DhcpGetPrivateInterfaceAddress();
DEREFERENCE_DHCP();
}
if (!ulAddress)
{
ulAddress = DnsGetPrivateInterfaceAddress();
}
if (!ulAddress)
{
//
// could not retreive correct IP address - use cached address
//
ulAddress = g_PrivateIPAddr;
}
else
{
//
// got some valid address
//
g_PrivateIPAddr = ulAddress;
}
if (ulAddress)
{
if (DnsICSDomainSuffix)
{
EnterCriticalSection(&DnsGlobalInfoLock);
dwSize = len +
wcslen(DNS_HOMENET_DOT) +
wcslen(DnsICSDomainSuffix) +
1;
pszBuf = reinterpret_cast<PWCHAR>(
NH_ALLOCATE(sizeof(WCHAR) * dwSize)
);
if (!pszBuf)
{
LeaveCriticalSection(&DnsGlobalInfoLock);
NhTrace(
TRACE_FLAG_DNS,
"DnsAddSelf: allocation failed for client name"
);
return;
}
ZeroMemory(pszBuf, (sizeof(WCHAR) * dwSize));
wcscpy(pszBuf, pszCompNameBuf); // copy the name
wcscat(pszBuf, DNS_HOMENET_DOT); // add the dot
wcscat(pszBuf, DnsICSDomainSuffix); // add the suffix
LeaveCriticalSection(&DnsGlobalInfoLock);
GetSystemTimeAsFileTime(&ftExpires); // current UTC time
memcpy(&liNow, &ftExpires, sizeof(LARGE_INTEGER));
liTime = RtlEnlargedIntegerMultiply((5 * 365 * 24 * 60 * 60), SYSTIME_UNITS_IN_1_SEC);
liExpires = RtlLargeIntegerAdd(liTime, liNow);;
memcpy(&ftExpires, &liExpires, sizeof(LARGE_INTEGER));
DnsAddAddressForName(
pszBuf,
ulAddress,
ftExpires
);
NH_FREE(pszBuf);
}
else
{
NhTrace(
TRACE_FLAG_DNS,
"DnsAddSelf: DnsICSDomainSuffix string not present - update failed!"
);
}
}
}
return;
} // DnsAddSelf
VOID
DnsCleanupTables(
VOID
)
/*++
Routine Description:
Called each time we detect that there could be atleast one entry with
an IP address not belonging to it anymore.
Arguments:
none.
Return Value:
None.
Environment:
Arbitrary.
--*/
{
PDNS_ENTRY pFwdEntry;
PREVERSE_DNS_ENTRY pRevEntry;
DNS_ENTRY dnsFwdSearch;
REVERSE_DNS_ENTRY dnsRevSearch;
BOOL fDelEntry;
UINT i;
PWCHAR *GCArray = NULL;
DWORD GCCount = 0,
GCSize = 0;
//
// Enumerate through the forward DNS table - if the IP address(es)
// for each forward entry have an entry in the reverse DNS table
// and this reverse entry's name pointer does not point to us, then
// delete this IP address from this forward entry
//
EnterCriticalSection(&DnsTableLock);
pFwdEntry = (PDNS_ENTRY) RtlEnumerateGenericTable(&g_DnsTable, TRUE);
while (pFwdEntry != NULL)
{
for (i = 0; i < pFwdEntry->cAddresses; i++)
{
pRevEntry = NULL;
dnsRevSearch.ulAddress = pFwdEntry->aAddressInfo[i].ulAddress;
dnsRevSearch.pszName = NULL;
pRevEntry = (PREVERSE_DNS_ENTRY) RtlLookupElementGenericTable(
&g_ReverseDnsTable,
&dnsRevSearch
);
if ((!pRevEntry) ||
((pRevEntry) &&
(pRevEntry->pszName != pFwdEntry->pszName)))
{
//
// Remove this IP address from the forward entry address list
//
if (pFwdEntry->cAddresses > 1)
{
memcpy(&pFwdEntry->aAddressInfo[i],
&pFwdEntry->aAddressInfo[i + 1],
(pFwdEntry->cAddresses - 1 - i) * sizeof(ADDRESS_INFO));
pFwdEntry->cAddresses--;
}
else
{
//
// Single "invalid" IP address - zero the count
//
pFwdEntry->cAddresses = 0;
NH_FREE(pFwdEntry->aAddressInfo);
pFwdEntry->aAddressInfo = NULL;
break;
}
}
}
if (0 == pFwdEntry->cAddresses)
{
//
// Remember this entry name
//
if (GCSize <= GCCount)
{
PWCHAR *tmpGCArray = NULL;
DWORD tmpGCSize = 0;
// Allocate in increments of five
tmpGCSize = ((GCCount + 5) / 5) * 5;
tmpGCArray = (PWCHAR *) NH_ALLOCATE(tmpGCSize * sizeof(PWCHAR));
if (tmpGCArray)
{
if (GCArray)
{
memcpy(tmpGCArray, GCArray, (GCCount * sizeof(PWCHAR)));
NH_FREE(GCArray);
}
GCSize = tmpGCSize;
GCArray = tmpGCArray;
//
// add it to our array
//
GCArray[GCCount++] = pFwdEntry->pszName;
}
}
else
{
//
// add it to our array
//
GCArray[GCCount++] = pFwdEntry->pszName;
}
}
pFwdEntry = (PDNS_ENTRY) RtlEnumerateGenericTable(&g_DnsTable, FALSE);
}
//
// Garbage collect after complete enumeration
//
for (i = 0; i < GCCount; i++)
{
dnsFwdSearch.pszName = GCArray[i];
dnsFwdSearch.cAddresses = 0;
pFwdEntry = (PDNS_ENTRY) RtlLookupElementGenericTable(
&g_DnsTable,
&dnsFwdSearch
);
if (pFwdEntry)
{
//
// (1) we have a copy of pointer to name as in GCArray[i]
// (2) aAddressInfo has already been taken care of above
// (3) only need to get rid of FwdEntry struct from table
//
RtlDeleteElementGenericTable(
&g_DnsTable,
pFwdEntry
);
//
// done after the fwd entry was deleted from fwd DNS table
//
NH_FREE(GCArray[i]);
}
GCArray[i] = NULL;
}
LeaveCriticalSection(&DnsTableLock);
if (GCArray)
{
NH_FREE(GCArray);
}
return;
} // DnsCleanupTables
//
// Utility conversion routines
//
DWORD
DnsConvertHostNametoUnicode(
UINT CodePage,
CHAR *pszHostName,
PWCHAR DnsICSDomainSuffix,
PWCHAR *ppszUnicodeFQDN
)
{
PROFILE("DnsConvertHostNametoUnicode");
//
// make sure to free the returned UnicodeFQDN
// caller holds DnsGlobalInfoLock
//
DWORD dwSize = 0;
DWORD Error = NO_ERROR;
LPBYTE pszUtf8HostName = NULL; // copy of pszHostName in Utf8 format
PWCHAR pszUnicodeHostName = NULL;
PWCHAR pszUnicodeFQDN = NULL;
//
// convert the given hostname to a Unicode string
//
if (CP_UTF8 == CodePage)
{
pszUtf8HostName = (LPBYTE)pszHostName;
}
else
{
//
// now convert this into UTF8 format
//
if (!ConvertToUtf8(
CodePage,
(LPSTR)pszHostName,
(PCHAR *)&pszUtf8HostName,
&dwSize))
{
Error = GetLastError();
NhTrace(
TRACE_FLAG_DNS,
"DnsConvertHostNametoUnicode: conversion from "
"CodePage %d to UTF8 for hostname failed "
"with error %ld (0x%08x)",
CodePage,
Error,
Error
);
if (pszUtf8HostName)
{
NH_FREE(pszUtf8HostName);
}
return Error;
}
}
//
// now convert this into Unicode format
//
if (!ConvertUTF8ToUnicode(
pszUtf8HostName,
(LPWSTR *)&pszUnicodeHostName,
&dwSize))
{
Error = GetLastError();
NhTrace(
TRACE_FLAG_DNS,
"DnsConvertHostNametoUnicode: conversion from "
"UTF8 to Unicode for hostname failed "
"with error %ld (0x%08x)",
Error,
Error
);
if (CP_UTF8 != CodePage)
{
NH_FREE(pszUtf8HostName);
}
if (pszUnicodeHostName)
{
NH_FREE(pszUnicodeHostName);
}
return Error;
}
dwSize += sizeof(WCHAR)*(wcslen(DNS_HOMENET_DOT)+wcslen(DnsICSDomainSuffix)+1);
pszUnicodeFQDN = reinterpret_cast<PWCHAR>(NH_ALLOCATE(dwSize));
if (!pszUnicodeFQDN)
{
NhTrace(
TRACE_FLAG_DNS,
"DnsConvertHostNametoUnicode: allocation failed "
"for Unicode FQDN"
);
if (CP_UTF8 != CodePage)
{
NH_FREE(pszUtf8HostName);
}
NH_FREE(pszUnicodeHostName);
return ERROR_NOT_ENOUGH_MEMORY;
}
ZeroMemory(pszUnicodeFQDN, dwSize);
wcscpy(pszUnicodeFQDN, pszUnicodeHostName); // copy the name
wcscat(pszUnicodeFQDN, DNS_HOMENET_DOT); // add the dot
wcscat(pszUnicodeFQDN, DnsICSDomainSuffix); // add the suffix
*ppszUnicodeFQDN = pszUnicodeFQDN;
if (CP_UTF8 != CodePage)
{
NH_FREE(pszUtf8HostName);
}
NH_FREE(pszUnicodeHostName);
NhTrace(
TRACE_FLAG_DNS,
"DnsConvertHostNametoUnicode: succeeded! %S",
pszUnicodeFQDN
);
return Error;
} // DnsConvertHostNametoUnicode
BOOL
ConvertToUtf8(
IN UINT CodePage,
IN LPSTR pszName,
OUT PCHAR *ppszUtf8Name,
OUT ULONG *pUtf8NameSize
)
/*++
Routine Description:
This functions converts an specified CodePage string to Utf8 format.
Arguments:
pszName - Buffer to the hostname string which is null terminated.
ppszUtf8Name - receives Pointer to the buffer receiving Utf8 string.
BufSize - receives Length of the above buffer in bytes.
Return Value:
TRUE on successful conversion.
--*/
{
DWORD Error = NO_ERROR;
DWORD dwSize = 0;
PCHAR pszUtf8Name = NULL;
LPWSTR pBuf = NULL;
DWORD Count;
Count = MultiByteToWideChar(
CodePage,
MB_ERR_INVALID_CHARS,
pszName,
-1,
pBuf,
0
);
if(0 == Count)
{
Error = GetLastError();
NhTrace(
TRACE_FLAG_DNS,
"ConvertToUtf8: MultiByteToWideChar returned %ld (0x%08x)",
Error,
Error
);
return FALSE;
}
dwSize = Count * sizeof(WCHAR);
pBuf = reinterpret_cast<LPWSTR>(NH_ALLOCATE(dwSize));
if (!pBuf)
{
NhTrace(
TRACE_FLAG_DNS,
"ConvertToUtf8: allocation failed for temporary wide char buffer"
);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
ZeroMemory(pBuf, dwSize);
Count = MultiByteToWideChar(
CodePage,
MB_ERR_INVALID_CHARS,
pszName,
-1,
pBuf,
Count
);
if(0 == Count)
{
Error = GetLastError();
NhTrace(
TRACE_FLAG_DNS,
"ConvertToUtf8: MultiByteToWideChar returned %ld (0x%08x)",
Error,
Error
);
NH_FREE(pBuf);
return FALSE;
}
Count = WideCharToMultiByte(
CP_UTF8,
0,
pBuf,
-1,
pszUtf8Name,
0,
NULL,
NULL
);
dwSize = Count;
pszUtf8Name = reinterpret_cast<PCHAR>(NH_ALLOCATE(dwSize));
if (!pszUtf8Name)
{
NhTrace(
TRACE_FLAG_DNS,
"ConvertToUtf8: allocation failed for Utf8 char buffer"
);
NH_FREE(pBuf);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
ZeroMemory(pszUtf8Name, dwSize);
Count = WideCharToMultiByte(
CP_UTF8,
0,
pBuf,
-1,
pszUtf8Name,
Count,
NULL,
NULL
);
//
// N.B. Looks like there is no such thing as a default
// character for UTF8 - so we have to assume this
// succeeded..
// if any default characters were used, then it can't be
// converted actually.. so don't allow this
//
NH_FREE(pBuf);
*ppszUtf8Name = pszUtf8Name;
*pUtf8NameSize = dwSize;
return (Count != 0);
} // ConvertToUtf8
BOOL
ConvertUTF8ToUnicode(
IN LPBYTE UTF8String,
OUT LPWSTR *ppszUnicodeName,
OUT DWORD *pUnicodeNameSize
)
/*++
Routine Description:
This functions converts Utf8 format to Unicodestring.
Arguments:
UTF8String - Buffer to UTFString which is null terminated.
ppszUnicodeName - receives Pointer to the buffer receiving Unicode string.
pUnicodeLength - receives Length of the above buffer in bytes.
Return Value:
TRUE on successful conversion.
--*/
{
DWORD Count, dwSize = 0, Error = NO_ERROR;
LPWSTR pBuf = NULL;
Count = MultiByteToWideChar(
CP_UTF8,
0,
(LPCSTR)UTF8String,
-1,
pBuf,
0
);
if(0 == Count)
{
Error = GetLastError();
NhTrace(
TRACE_FLAG_DNS,
"ConvertUTF8ToUnicode: MultiByteToWideChar returned %ld (0x%08x)",
Error,
Error
);
return FALSE;
}
dwSize = Count * sizeof(WCHAR);
pBuf = reinterpret_cast<LPWSTR>(NH_ALLOCATE(dwSize));
if (!pBuf)
{
NhTrace(
TRACE_FLAG_DNS,
"ConvertUTF8ToUnicode: allocation failed for unicode string buffer"
);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
ZeroMemory(pBuf, dwSize);
Count = MultiByteToWideChar(
CP_UTF8,
0,
(LPCSTR)UTF8String,
-1,
pBuf,
Count
);
if(0 == Count)
{
Error = GetLastError();
NhTrace(
TRACE_FLAG_DNS,
"ConvertUTF8ToUnicode: MultiByteToWideChar returned %ld (0x%08x)",
Error,
Error
);
NH_FREE(pBuf);
return FALSE;
}
*ppszUnicodeName = pBuf;
*pUnicodeNameSize = dwSize;
return (Count != 0);
} // ConvertUTF8ToUnicode