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.
 
 
 
 
 
 

5020 lines
144 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
dns.c
Abstract:
Routines to register DNS names.
Author:
Cliff Van Dyke (CliffV) 28-May-1996
Revision History:
--*/
//
// Common include files.
//
#include "logonsrv.h" // Include files common to entire service
#pragma hdrstop
BOOL NlGlobalDnsScavengeNeeded = FALSE;
BOOL NlGlobalDnsScavengingInProgress = FALSE;
ULONG NlGlobalDnsScavengeFlags = 0;
WORKER_ITEM NlGlobalDnsScavengeWorkItem;
BOOL NlDnsWriteServerFailureEventLog = FALSE;
ULONG NlDnsInitCount = 0; // The number of times we have been started
//
// The timeout after our start when it's OK to write DNS errors into
// the event log. We postpone error output because the DNS server
// (if it runs locally) may not have started yet.
//
#define NL_DNS_EVENTLOG_TIMEOUT (2 * 60 * 1000) // 2 minutes
//
// Max time that a name update should reasonably take.
// We will indicate in netlogon.log if a given update
// takes longer than this threshold.
//
#define NL_DNS_ONE_THRESHOLD (15*1000) // 15 seconds
//
// Max time that DNS deregistrations are allowed to take on shutdown.
// We will abort deregistration cycle on shutdown if total deregisrtaion
// time for all records takes longer than this timeout.
//
#define NL_DNS_SHUTDOWN_THRESHOLD 60000 // 1 minute
//
// Max number of times we will restart concurrent DNS scavenging
// before giving up.
//
#define NL_DNS_MAX_SCAVENGE_RESTART 10 // 10 times
//
// State of a DNS name.
//
typedef enum {
RegisterMe, // Name needs to be registered
Registered, // Name is registered
DeregisterMe, // Name needs to be deregistered
DelayedDeregister, // Name will be marked for deregistration in the future
DeleteMe, // This entry should be deleted.
DnsNameStateInvalid // State is invalid
} NL_DNS_NAME_STATE;
//
// Structure representing an added DNS name.
// (All fields serialized by NlGlobalDnsCritSect)
//
typedef struct _NL_DNS_NAME {
//
// Link in list of all such structures headed by NlGlobalDnsList
//
LIST_ENTRY Next;
//
// Type of name registed.
//
NL_DNS_NAME_TYPE NlDnsNameType;
//
// Domain this entry refers to.
//
PDOMAIN_INFO DomainInfo;
//
// Flags describing the entry.
//
ULONG Flags;
#define NL_DNS_REGISTER_DOMAIN 0x0001 // All names for domain being registered.
#define NL_DNS_REGISTERED_ONCE 0x0002 // Name has been registered at least once
//
// The time of the first failure to deregister this name.
// Reset to zero on successful deregistration.
//
LARGE_INTEGER FirstDeregFailureTime;
//
// Each regisration is periodically re-done (whether successful or not).
// This timer indicates when the next re-registration should be done.
//
// The initial re-registration is done after 5 minute. The period then
// doubles until it reaches a maximum of DnsRefreshInterval.
//
TIMER ScavengeTimer;
#define ORIG_DNS_SCAVENGE_PERIOD (5*60*1000) // 5 minute
//
// Actual DNS name registered.
//
LPSTR DnsRecordName;
//
// Data for the SRV record
//
ULONG Priority;
ULONG Weight;
ULONG Port;
LPSTR DnsHostName;
//
// Data for the A record
//
ULONG IpAddress;
//
// State of this entry.
//
NL_DNS_NAME_STATE State;
//
// Last DNS update status for this name
//
NET_API_STATUS NlDnsNameLastStatus;
} NL_DNS_NAME, *PNL_DNS_NAME;
//
// Header for binary Dns log file.
//
typedef struct _NL_DNSLOG_HEADER {
ULONG Version;
} NL_DNSLOG_HEADER, *PNL_DNSLOG_HEADER;
#define NL_DNSLOG_VERSION 1
//
// Entry in the binary Dns log file.
//
typedef struct _NL_DNSLOG_ENTRY {
//
// Size (in bytes) of this entry
//
ULONG EntrySize;
//
// Type of name registed.
//
NL_DNS_NAME_TYPE NlDnsNameType;
//
// Data for the SRV record
//
ULONG Priority;
ULONG Weight;
ULONG Port;
//
// Data for the A record
//
ULONG IpAddress;
} NL_DNSLOG_ENTRY, *PNL_DNSLOG_ENTRY;
//
// Globals specific to this .c file.
//
//
// True if the DNS list needs to be output to netlogon.dns
//
BOOLEAN NlGlobalDnsListDirty;
//
// True if the initial cleanup of previously registered names has been done.
//
BOOLEAN NlGlobalDnsInitialCleanupDone;
//
// Time when netlogon was started.
//
DWORD NlGlobalDnsStartTime;
#define NL_DNS_INITIAL_CLEANUP_TIME (10 * 60 * 1000) // 10 minutes
VOID
NlDnsNameToStr(
IN PNL_DNS_NAME NlDnsName,
OUT CHAR Utf8DnsRecord[NL_DNS_RECORD_STRING_SIZE]
)
/*++
Routine Description:
This routine builds a textual representation of NlDnsName
Arguments:
NlDnsName - Name to register or deregister.
Utf8DnsRecord - Preallocated buffer to build the text string into.
The built record is a UTF-8 zero terminated string.
The string is concatenated to this buffer.
Return Value:
None.
--*/
{
CHAR Number[33];
//
// Write the record name
//
strcat( Utf8DnsRecord, NlDnsName->DnsRecordName );
//
// Concatenate the TTL
//
_ltoa( NlGlobalParameters.DnsTtl, Number, 10 );
strcat( Utf8DnsRecord, " " );
strcat( Utf8DnsRecord, Number );
//
// Build an A record.
//
if ( NlDnsARecord( NlDnsName->NlDnsNameType ) ) {
CHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1];
strcat( Utf8DnsRecord, NL_DNS_A_RR_VALUE_1 );
NetpIpAddressToStr( NlDnsName->IpAddress, IpAddressString );
strcat( Utf8DnsRecord, IpAddressString );
//
// Build a CNAME record
//
} else if ( NlDnsCnameRecord( NlDnsName->NlDnsNameType ) ) {
strcat( Utf8DnsRecord, NL_DNS_CNAME_RR_VALUE_1 );
strcat( Utf8DnsRecord, NlDnsName->DnsHostName );
strcat( Utf8DnsRecord, "." );
//
// Build a SRV record
//
} else {
strcat( Utf8DnsRecord, NL_DNS_SRV_RR_VALUE_1 );
_ltoa( NlDnsName->Priority, Number, 10 );
strcat( Utf8DnsRecord, Number );
strcat( Utf8DnsRecord, " " );
_ltoa( NlDnsName->Weight, Number, 10 );
strcat( Utf8DnsRecord, Number );
strcat( Utf8DnsRecord, " " );
_ltoa( NlDnsName->Port, Number, 10 );
strcat( Utf8DnsRecord, Number );
strcat( Utf8DnsRecord, " " );
strcat( Utf8DnsRecord, NlDnsName->DnsHostName );
strcat( Utf8DnsRecord, "." );
}
}
LPWSTR
NlDnsNameToWStr(
IN PNL_DNS_NAME NlDnsName
)
/*++
Routine Description:
This routine builds a textual representation of NlDnsName
Arguments:
NlDnsName - Name to register or deregister.
Utf8DnsRecord - Preallocated buffer to build the text string into.
The built record is a UTF-8 zero terminated string.
The string is concatenated to this buffer.
Return Value:
Buffer containing a textual representation of NlDnsName
NULL: Buffer could not be allocated
Buffer should be free by calling NetApiBufferFree();
--*/
{
LPSTR DnsRecord = NULL;
LPWSTR UnicodeDnsRecord;
//
// Allocate a buffer for the UTF-8 version of the string.
//
DnsRecord = LocalAlloc( 0, NL_DNS_RECORD_STRING_SIZE + 1 );
if ( DnsRecord == NULL ) {
return NULL;
}
DnsRecord[0] = '\0';
//
// Create the text string in UTF-8
//
NlDnsNameToStr( NlDnsName, DnsRecord );
//
// Convert to Unicode
//
UnicodeDnsRecord = NetpAllocWStrFromUtf8Str( DnsRecord );
LocalFree( DnsRecord );
return UnicodeDnsRecord;
}
LPWSTR
NlDnsNameToDomainName(
IN PNL_DNS_NAME NlDnsName
)
/*++
Routine Description:
This routine parses the record and returns the DNS name
of the domain the record belongs to.
Note that we have to parse the name because we may not have
the DomainInfo structure hanging off NlDnsName if this is a
deregistration of no longer hosted domain.
Note that this routine relies on a particular structure
of DNS records that netlogon registers. If that structure
changes, this routine will have to change accordingly.
Arguments:
NlDnsName - Name to register or deregister.
Return Value:
Pointer to the alocated DNS domain name. Must be freed
by calling NetApiBufferFree.
--*/
{
LPWSTR UnicodeRecordName = NULL;
LPWSTR UnicodeDnsDomainName = NULL;
LPWSTR Ptr = NULL;
LPWSTR DotPtr = NULL;
//
// The record name has a structure where the label immediately
// before the domain name has a leading underscore. There might
// be no label before the domain name for A records -- we will
// treat the absence of underscore in the name as an indication
// of that the record name is the domain name itself.
//
UnicodeRecordName = NetpAllocWStrFromUtf8Str( NlDnsName->DnsRecordName );
if ( UnicodeRecordName == NULL || *UnicodeRecordName == UNICODE_NULL ) {
goto Cleanup;
}
//
// Starting from the last character and going to the front,
// search for the characters of interest.
//
Ptr = UnicodeRecordName + wcslen( UnicodeRecordName ) - 1;
while ( Ptr != UnicodeRecordName ) {
//
// If this is the next dot going from the end,
// remember its location
//
if ( *Ptr == NL_DNS_DOT ) {
DotPtr = Ptr;
}
//
// If this is the first underscore going from the end,
// break from the loop: the domain name is immediately
// after the previous dot
//
if ( *Ptr == NL_DNS_UNDERSCORE ) {
NlAssert( DotPtr != NULL );
break;
}
Ptr --;
}
//
// If there is no underscore in the name,
// the domain name is the record name itself.
// Otherwise, the domain name follows immediately
// after the last dot.
//
if ( Ptr == UnicodeRecordName ) {
UnicodeDnsDomainName = NetpAllocWStrFromWStr( UnicodeRecordName );
} else if ( DotPtr != NULL ) {
UnicodeDnsDomainName = NetpAllocWStrFromWStr( DotPtr + 1 );
}
Cleanup:
if ( UnicodeRecordName != NULL ) {
NetApiBufferFree( UnicodeRecordName );
}
return UnicodeDnsDomainName;
}
#if NETLOGONDBG
#define NlPrintDns(_x_) NlPrintDnsRoutine _x_
#else
#define NlPrintDns(_x_)
#endif // NETLOGONDBG
#if NETLOGONDBG
VOID
NlPrintDnsRoutine(
IN DWORD DebugFlag,
IN PNL_DNS_NAME NlDnsName,
IN LPSTR Format,
...
)
{
va_list arglist;
CHAR Utf8DnsRecord[NL_DNS_RECORD_STRING_SIZE];
//
// vsprintf isn't multithreaded + we don't want to intermingle output
// from different threads.
//
EnterCriticalSection( &NlGlobalLogFileCritSect );
//
// Prefix the printed line with the domain name
//
if ( NlGlobalServicedDomainCount > 1 ) {
if ( NlDnsName->DomainInfo == NULL ) {
NlPrint(( DebugFlag, "%ws: ", L"[Unknown]" ));
} else if ( NlDnsName->DomainInfo->DomUnicodeDomainName != NULL &&
*(NlDnsName->DomainInfo->DomUnicodeDomainName) != UNICODE_NULL ) {
NlPrint(( DebugFlag, "%ws: ", NlDnsName->DomainInfo->DomUnicodeDomainName ));
} else {
NlPrint(( DebugFlag, "%ws: ", NlDnsName->DomainInfo->DomUnicodeDnsDomainName ));
}
}
//
// Simply change arguments to va_list form and call NlPrintRoutineV
//
va_start(arglist, Format);
NlPrintRoutineV( DebugFlag, Format, arglist );
va_end(arglist);
//
// Finally print a description of the DNS record in question.
//
Utf8DnsRecord[0] = '\0';
NlDnsNameToStr( NlDnsName, Utf8DnsRecord );
NlPrint(( DebugFlag,
": %ws: %s\n",
NlDcDnsNameTypeDesc[NlDnsName->NlDnsNameType].Name,
Utf8DnsRecord ));
LeaveCriticalSection( &NlGlobalLogFileCritSect );
}
#endif // NETLOGONDBG
BOOL
NlDnsSetAvoidRegisterNameParam(
IN LPTSTR_ARRAY NewDnsAvoidRegisterRecords
)
/*++
Routine Description:
This routine sets the names of DNS records this DC should avoid registering.
Arguments:
NewSiteCoverage - Specifies the new list of names to avoid registering
Return Value:
TRUE: iff the list of names to avoid registering changed
--*/
{
BOOL DnsAvoidRegisterRecordsChanged = FALSE;
EnterCriticalSection( &NlGlobalParametersCritSect );
//
// Handle DnsAvoidRegisterRecords changing
//
DnsAvoidRegisterRecordsChanged = !NetpEqualTStrArrays(
NlGlobalParameters.DnsAvoidRegisterRecords,
NewDnsAvoidRegisterRecords );
if ( DnsAvoidRegisterRecordsChanged ) {
//
// Swap in the new value.
(VOID) NetApiBufferFree( NlGlobalParameters.DnsAvoidRegisterRecords );
NlGlobalParameters.DnsAvoidRegisterRecords = NewDnsAvoidRegisterRecords;
}
LeaveCriticalSection( &NlGlobalParametersCritSect );
return DnsAvoidRegisterRecordsChanged;
}
NET_API_STATUS
NlGetConfiguredDnsDomainName(
OUT LPWSTR *DnsDomainName
)
/*++
Routine Description:
This routine gets the DNS domain name of domain as configured by DNS or DHCP
NOTE: THIS ROUTINE IS CURRENTLY UNUSED
Arguments:
DnsDomainName - Returns the DNS domain name of the domain.
The returned name has a trailing . since the name is an absolute name.
The allocated buffer must be freed via NetApiBufferFree.
Returns NO_ERROR and a pointer to a NULL buffer if there is no
domain name configured.
Return Value:
Status of the operation.
--*/
{
NET_API_STATUS NetStatus;
WCHAR LocalDnsDomainNameBuffer[NL_MAX_DNS_LENGTH+1];
LPWSTR LocalDnsDomainName = NULL;
LPNET_CONFIG_HANDLE SectionHandle = NULL;
*DnsDomainName = NULL;
//
// Get the domain name from the registery
//
NetStatus = NetpOpenConfigData(
&SectionHandle,
NULL, // no server name.
SERVICE_TCPIP,
TRUE ); // we only want readonly access
if ( NetStatus != NO_ERROR ) {
//
// Simply return success if TCP/IP isn't configured.
//
if ( NetStatus == NERR_CfgCompNotFound ) {
NetStatus = NO_ERROR;
}
SectionHandle = NULL;
goto Cleanup;
}
//
// Get the "Domain" parameter from the TCPIP service.
//
NetStatus = NetpGetConfigValue (
SectionHandle,
L"Domain", // key wanted
&LocalDnsDomainName ); // Must be freed by NetApiBufferFree().
if ( NetStatus == NO_ERROR && *LocalDnsDomainName == L'\0' ) {
NetStatus = NERR_CfgParamNotFound;
NetApiBufferFree( LocalDnsDomainName );
LocalDnsDomainName = NULL;
}
if (NetStatus != NERR_CfgParamNotFound ) {
goto Cleanup;
}
//
// Fall back to the "DhcpDomain" parameter from the TCPIP service.
//
NetStatus = NetpGetConfigValue (
SectionHandle,
L"DhcpDomain", // key wanted
&LocalDnsDomainName ); // Must be freed by NetApiBufferFree().
if ( NetStatus == NO_ERROR && *LocalDnsDomainName == L'\0' ) {
NetStatus = NERR_CfgParamNotFound;
NetApiBufferFree( LocalDnsDomainName );
LocalDnsDomainName = NULL;
}
if (NetStatus == NERR_CfgParamNotFound ) {
NetStatus = NO_ERROR;
}
Cleanup:
if ( NetStatus == NO_ERROR ) {
if ( LocalDnsDomainName != NULL ) {
ULONG LocalDnsDomainNameLen = wcslen(LocalDnsDomainName);
if ( LocalDnsDomainNameLen != 0 ) {
if ( LocalDnsDomainNameLen > NL_MAX_DNS_LENGTH-1 ) {
NetStatus = ERROR_INVALID_DOMAINNAME;
} else {
NetStatus = NetapipBufferAllocate(
(LocalDnsDomainNameLen + 2) * sizeof(WCHAR),
DnsDomainName );
if ( NetStatus == NO_ERROR ) {
wcscpy( *DnsDomainName, LocalDnsDomainName );
if ( (*DnsDomainName)[LocalDnsDomainNameLen-1] != L'.' ) {
wcscat( *DnsDomainName, L"." );
}
}
}
}
}
}
if ( SectionHandle != NULL ) {
(VOID) NetpCloseConfigData( SectionHandle );
}
if ( LocalDnsDomainName != NULL ) {
NetApiBufferFree( LocalDnsDomainName );
}
return NetStatus;
}
VOID
NlDnsSetState(
PNL_DNS_NAME NlDnsName,
NL_DNS_NAME_STATE State
)
/*++
Routine Description:
Set the state of the entry.
Arguments:
NlDnsName - Structure describing name.
State - New state for the name.
Return Value:
None.
--*/
{
EnterCriticalSection( &NlGlobalDnsCritSect );
//
// If this name got registered,
// remember that fact
//
if ( State == Registered ) {
NlDnsName->Flags |= NL_DNS_REGISTERED_ONCE;
}
//
// If the state changes, do appropriate updates
//
if ( NlDnsName->State != State ) {
NlDnsName->State = State;
NlGlobalDnsListDirty = TRUE;
//
// If the new state says I need to update the DNS server,
// set the retry period to indicate to do that now.
//
if ( NlDnsName->State == RegisterMe ||
NlDnsName->State == DeregisterMe ) {
NlDnsName->ScavengeTimer.StartTime.QuadPart = 0;
NlDnsName->ScavengeTimer.Period = 0;
}
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
}
NET_API_STATUS
NlDnsBuildName(
IN PDOMAIN_INFO DomainInfo,
IN NL_DNS_NAME_TYPE NlDnsNameType,
IN LPWSTR SiteName,
IN BOOL DnsNameAlias,
OUT char DnsName[NL_MAX_DNS_LENGTH+1]
)
/*++
Routine Description:
This routine returns the textual DNS name for a particular domain and
name type.
Arguments:
DomainInfo - Domain the name is for.
NlDnsNameType - The specific type of name.
SiteName - If NlDnsNameType is any of the *AtSite values,
the site name of the site this name is registered for.
DnsNameAlias - If TRUE, the built name should correspond to the
alias of the domain/forest name.
DnsName - Textual representation of the name. If the name is not
applicable (DnsNameAlias is TRUE but there is no alias for
the name), the returned string will be empty.
Return Value:
NO_ERROR: The name was returned;
ERROR_NO_SUCH_DOMAIN: No (active) domain name is known for this domain.
ERROR_INVALID_DOMAINNAME: Domain's name is too long. Additional labels
cannot be concatenated.
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
GUID DomainGuid;
LPSTR DnsDomainName = NULL;
BOOLEAN UseForestName = FALSE;
//
// Initialization
//
RtlZeroMemory( DnsName, (NL_MAX_DNS_LENGTH+1)*sizeof(char) );
//
// Get the Domain GUID for the case where the DC domain name.
// The Domain GUID is registered at the TreeName
//
EnterCriticalSection(&NlGlobalDomainCritSect);
if ( NlDnsDcGuid( NlDnsNameType ) ) {
if ( DomainInfo->DomDomainGuid == NULL ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlDnsBuildName: Domain has no GUID.\n" ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
goto Cleanup;
}
DomainGuid = *(DomainInfo->DomDomainGuid);
UseForestName = TRUE;
//
// Get the DSA Guid for the case where the DC is renamed.
//
} else if ( NlDnsCnameRecord( NlDnsNameType) ) {
if ( IsEqualGUID( &NlGlobalDsaGuid, &NlGlobalZeroGuid) ) {
NlPrintDom((NL_DNS, DomainInfo,
"NlDnsBuildName: DSA has no GUID.\n" ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
goto Cleanup;
}
DomainGuid = NlGlobalDsaGuid;
UseForestName = TRUE;
}
//
// Ensure site specific names have been passed a site name
//
if ( NlDcDnsNameTypeDesc[NlDnsNameType].IsSiteSpecific ) {
if ( SiteName == NULL ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlDnsBuildName: DC has no Site Name.\n" ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
goto Cleanup;
}
}
//
// GC's are registered at the Forest name.
//
if ( NlDnsGcName( NlDnsNameType ) ) {
UseForestName = TRUE;
}
//
// Pick up the ForestName or DomainName as flagged above.
//
if ( UseForestName ) {
if ( !DnsNameAlias ) {
DnsDomainName = NlGlobalUtf8DnsForestName;
//
// We must have an active forest name
//
if ( NlGlobalUtf8DnsForestName == NULL ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlDnsBuildName: Domain has no Forest Name.\n" ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
goto Cleanup;
}
} else {
DnsDomainName = NlGlobalUtf8DnsForestNameAlias;
}
} else {
if ( !DnsNameAlias ) {
DnsDomainName = DomainInfo->DomUtf8DnsDomainName;
//
// We must have an active domain name
//
if ( DomainInfo->DomUtf8DnsDomainName == NULL ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"NlDnsBuildName: Domain has no Domain Name.\n" ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
goto Cleanup;
}
} else {
DnsDomainName = DomainInfo->DomUtf8DnsDomainNameAlias;
}
}
//
// Build the appropriate name as applicable
//
if ( DnsDomainName != NULL ) {
NetStatus = NetpDcBuildDnsName( NlDnsNameType,
&DomainGuid,
SiteName,
DnsDomainName,
DnsName );
}
Cleanup:
LeaveCriticalSection(&NlGlobalDomainCritSect);
return NetStatus;
}
HKEY
NlOpenNetlogonKey(
LPSTR KeyName
)
/*++
Routine Description:
Create/Open the Netlogon key in the registry.
Arguments:
KeyName - Name of the key to open
Return Value:
Return a handle to the key. NULL means the key couldn't be opened.
--*/
{
LONG RegStatus;
HKEY ParmHandle = NULL;
ULONG Disposition;
//
// Open the key for Netlogon\Parameters
//
RegStatus = RegCreateKeyExA(
HKEY_LOCAL_MACHINE,
KeyName,
0, //Reserved
NULL, // Class
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_NOTIFY,
NULL, // Security descriptor
&ParmHandle,
&Disposition );
if ( RegStatus != ERROR_SUCCESS ) {
NlPrint(( NL_CRITICAL,
"NlOpenNetlogonKey: Cannot create registy key %s %ld.\n",
KeyName,
RegStatus ));
return NULL;
}
return ParmHandle;
}
VOID
NlDnsWriteBinaryLog(
VOID
)
/*++
Routine Description:
Write the list of registered DNS names to the registry.
Arguments:
None
Return Value:
None.
--*/
{
NET_API_STATUS NetStatus;
PLIST_ENTRY ListEntry;
PNL_DNS_NAME NlDnsName;
ULONG DnsRecordBufferSize;
PNL_DNSLOG_HEADER DnsRecordBuffer = NULL;
PNL_DNSLOG_ENTRY DnsLogEntry;
ULONG CurrentSize;
LPBYTE Where;
//
// Compute the size of the buffer to allocate.
//
DnsRecordBufferSize = ROUND_UP_COUNT( sizeof(NL_DNSLOG_HEADER), ALIGN_WORST );
EnterCriticalSection( &NlGlobalDnsCritSect );
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
//
// If this entry is marked for deletion,
// skip it
//
if ( NlDnsName->State == DeleteMe ) {
continue;
}
//
// Only do entries that have been registered.
//
// The whole purpose of this log is to keep track of names that
// need to be deregistered sooner or later.
//
if ( NlDnsName->Flags & NL_DNS_REGISTERED_ONCE ) {
//
// Compute the size of this entry.
//
CurrentSize = sizeof(NL_DNSLOG_ENTRY);
CurrentSize += strlen( NlDnsName->DnsRecordName ) + 1;
if ( NlDnsName->DnsHostName != NULL ) {
CurrentSize += strlen( NlDnsName->DnsHostName ) + 1;
}
CurrentSize = ROUND_UP_COUNT( CurrentSize, ALIGN_WORST );
//
// Add it to the size needed for the file.
//
DnsRecordBufferSize += CurrentSize;
}
}
//
// Allocate a block to build the binary log into.
// (and the build the file name in)
//
DnsRecordBuffer = LocalAlloc( LMEM_ZEROINIT, DnsRecordBufferSize );
if ( DnsRecordBuffer == NULL ) {
LeaveCriticalSection( &NlGlobalDnsCritSect );
goto Cleanup;
}
DnsRecordBuffer->Version = NL_DNSLOG_VERSION;
DnsLogEntry = (PNL_DNSLOG_ENTRY)ROUND_UP_POINTER( (DnsRecordBuffer + 1), ALIGN_WORST );
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
ULONG DnsRecordNameSize;
ULONG DnsHostNameSize;
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
//
// If this entry is marked for deletion,
// skip it
//
if ( NlDnsName->State == DeleteMe ) {
continue;
}
//
// Only do entries that have been registered.
//
// The whole purpose of this log is to keep track of names that
// need to be deregistered sooner or later.
//
if ( NlDnsName->Flags & NL_DNS_REGISTERED_ONCE ) {
//
// Compute the size of this entry.
//
DnsRecordNameSize = strlen( NlDnsName->DnsRecordName ) + 1;
CurrentSize = sizeof(NL_DNSLOG_ENTRY) + DnsRecordNameSize;
if ( NlDnsName->DnsHostName != NULL ) {
DnsHostNameSize = strlen( NlDnsName->DnsHostName ) + 1;
CurrentSize += DnsHostNameSize;
}
CurrentSize = ROUND_UP_COUNT( CurrentSize, ALIGN_WORST );
//
// Put the constant size fields in the buffer.
//
DnsLogEntry->EntrySize = CurrentSize;
DnsLogEntry->NlDnsNameType = NlDnsName->NlDnsNameType;
DnsLogEntry->IpAddress = NlDnsName->IpAddress;
DnsLogEntry->Priority = NlDnsName->Priority;
DnsLogEntry->Weight = NlDnsName->Weight;
DnsLogEntry->Port = NlDnsName->Port;
//
// Copy the variable length entries.
//
Where = (LPBYTE) (DnsLogEntry+1);
strcpy( Where, NlDnsName->DnsRecordName );
Where += DnsRecordNameSize;
if ( NlDnsName->DnsHostName != NULL ) {
strcpy( Where, NlDnsName->DnsHostName );
Where += DnsHostNameSize;
}
Where = ROUND_UP_POINTER( Where, ALIGN_WORST );
NlAssert( (ULONG)(Where-(LPBYTE)DnsLogEntry) == CurrentSize );
NlAssert( (ULONG)(Where-(LPBYTE)DnsRecordBuffer) <= DnsRecordBufferSize );
//
// Move on to the next entry.
//
DnsLogEntry = (PNL_DNSLOG_ENTRY)Where;
} else {
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsWriteBinaryLog: not written to binary log file." ));
}
}
//
// Write the buffer to the file.
//
NetStatus = NlWriteBinaryLog(
NL_DNS_BINARY_LOG_FILE,
(LPBYTE) DnsRecordBuffer,
DnsRecordBufferSize );
LeaveCriticalSection( &NlGlobalDnsCritSect );
//
// Write event log on error
//
if ( NetStatus != NO_ERROR ) {
LPWSTR MsgStrings[2];
MsgStrings[0] = NL_DNS_BINARY_LOG_FILE;
MsgStrings[1] = (LPWSTR) UlongToPtr( NetStatus );
NlpWriteEventlog (NELOG_NetlogonFailedFileCreate,
EVENTLOG_ERROR_TYPE,
(LPBYTE) &NetStatus,
sizeof(NetStatus),
MsgStrings,
2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
}
Cleanup:
if ( DnsRecordBuffer != NULL ) {
LocalFree( DnsRecordBuffer );
}
return;
}
PNL_DNS_NAME
NlDnsAllocateEntry(
IN NL_DNS_NAME_TYPE NlDnsNameType,
IN LPSTR DnsRecordName,
IN ULONG Priority,
IN ULONG Weight,
IN ULONG Port,
IN LPCSTR DnsHostName OPTIONAL,
IN ULONG IpAddress,
IN NL_DNS_NAME_STATE State
)
/*++
Routine Description:
Allocate and initialize a DNS name entry.
Arguments:
Fields of the structure.
Return Value:
Pointer to the allocated structure.
NULL: not enough memory to allocate the structure
--*/
{
PNL_DNS_NAME NlDnsName;
ULONG Utf8DnsHostNameSize;
ULONG DnsRecordNameSize;
LPBYTE Where;
//
// Allocate a structure to represent this name.
//
if ( NlDnsARecord( NlDnsNameType ) ) {
Utf8DnsHostNameSize = 0;
} else {
Utf8DnsHostNameSize = strlen(DnsHostName) + 1;
}
DnsRecordNameSize = strlen( DnsRecordName ) + 1;
NlDnsName = LocalAlloc( LMEM_ZEROINIT, sizeof( NL_DNS_NAME ) +
Utf8DnsHostNameSize +
DnsRecordNameSize );
if ( NlDnsName == NULL ) {
return NULL;
}
Where = (LPBYTE)(NlDnsName+1);
//
// Initialize it and link it in.
//
NlDnsName->NlDnsNameType = NlDnsNameType;
NlDnsName->DnsRecordName = Where;
RtlCopyMemory( Where, DnsRecordName, DnsRecordNameSize );
Where += DnsRecordNameSize;
if ( NlDnsARecord( NlDnsNameType ) ) {
NlDnsName->IpAddress = IpAddress;
} else if ( NlDnsCnameRecord( NlDnsNameType ) ) {
NlDnsName->DnsHostName = Where;
RtlCopyMemory( Where, DnsHostName, Utf8DnsHostNameSize );
// Where += Utf8DnsHostNameSize;
} else {
NlDnsName->Priority = Priority;
NlDnsName->Port = Port;
NlDnsName->Weight = Weight;
NlDnsName->DnsHostName = Where;
RtlCopyMemory( Where, DnsHostName, Utf8DnsHostNameSize );
// Where += Utf8DnsHostNameSize;
}
NlDnsName->State = State;
NlGlobalDnsListDirty = TRUE;
EnterCriticalSection( &NlGlobalDnsCritSect );
InsertTailList(&NlGlobalDnsList, &NlDnsName->Next);
LeaveCriticalSection( &NlGlobalDnsCritSect );
return NlDnsName;
}
VOID
NlDnsWriteLog(
VOID
)
/*++
Routine Description:
Write the list of registered DNS names to
%SystemRoot%\System32\Config\netlogon.dns.
Arguments:
None
Return Value:
None.
--*/
{
PLIST_ENTRY ListEntry;
PNL_DNS_NAME NlDnsName;
NET_API_STATUS NetStatus;
LPWSTR AllocatedBuffer = NULL;
LPWSTR FileName;
LPSTR DnsRecord;
LPSTR DnsName;
UINT WindowsDirectoryLength;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
EnterCriticalSection( &NlGlobalDnsCritSect );
if ( !NlGlobalDnsListDirty ) {
LeaveCriticalSection( &NlGlobalDnsCritSect );
return;
}
//
// Allocate a buffer for storage local to this procedure.
// (Don't put it on the stack since we don't want to commit a huge stack.)
//
AllocatedBuffer = LocalAlloc( 0, sizeof(WCHAR) * (MAX_PATH+1) +
NL_MAX_DNS_LENGTH+1 +
NL_DNS_RECORD_STRING_SIZE + 1 );
if ( AllocatedBuffer == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
FileName = AllocatedBuffer;
DnsName = (LPSTR)(&AllocatedBuffer[MAX_PATH+1]);
DnsRecord = &DnsName[NL_MAX_DNS_LENGTH+1];
//
// Write the binary version of the log first.
//
NlDnsWriteBinaryLog();
//
// Build the name of the log file
//
WindowsDirectoryLength = GetSystemWindowsDirectoryW(
FileName,
sizeof(WCHAR) * (MAX_PATH+1) );
if ( WindowsDirectoryLength == 0 ) {
NetStatus = GetLastError();
NlPrint(( NL_CRITICAL,
"NlDnsWriteLog: Unable to GetSystemWindowsDirectoryW (%ld)\n",
NetStatus ));
goto Cleanup;
}
if ( WindowsDirectoryLength * sizeof(WCHAR) +
sizeof(WCHAR) +
sizeof(NL_DNS_LOG_FILE)
>= sizeof(WCHAR) * MAX_PATH ) {
NlPrint((NL_CRITICAL,
"NlDnsWriteLog: file name length is too long \n" ));
goto Cleanup;
}
wcscat( FileName, NL_DNS_LOG_FILE );
//
// Create a file to write to.
// If it exists already then truncate it.
//
FileHandle = CreateFileW(
FileName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, // allow backups and debugging
NULL, // Supply better security ??
CREATE_ALWAYS, // Overwrites always
FILE_ATTRIBUTE_NORMAL,
NULL ); // No template
if ( FileHandle == INVALID_HANDLE_VALUE) {
LPWSTR MsgStrings[2];
NetStatus = GetLastError();
NlPrint((NL_CRITICAL,
"NlDnsWriteLog: %ws: Unable to create file: %ld \n",
FileName,
NetStatus));
MsgStrings[0] = FileName;
MsgStrings[1] = (LPWSTR) UlongToPtr( NetStatus );
NlpWriteEventlog (NELOG_NetlogonFailedFileCreate,
EVENTLOG_ERROR_TYPE,
(LPBYTE) &NetStatus,
sizeof(NetStatus),
MsgStrings,
2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
goto Cleanup;
}
//
// Loop through the list of DNS names writing each one to the log
//
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
ULONG DnsRecordLength;
ULONG BytesWritten;
//
// If this entry really doesn't exist,
// comment it out.
//
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
//
// If this entry is marked for deletion,
// skip it (we must have successfully
// deregistered it and only need to
// delink and free this entry).
//
if ( NlDnsName->State == DeleteMe ) {
continue;
}
DnsRecord[0] = '\0';
switch (NlDnsName->State) {
case RegisterMe:
case Registered:
break;
default:
NlPrint(( NL_CRITICAL,
"NlDnsWriteLog: %ld: Invalid state\n",
NlDnsName->State ));
/* Drop through */
case DeregisterMe:
case DelayedDeregister:
strcat( DnsRecord, "; " );
break;
}
//
// Create the text string to write.
//
NlDnsNameToStr( NlDnsName, DnsRecord );
strcat( DnsRecord, NL_DNS_RR_EOL );
//
// Write the record to the file.
//
DnsRecordLength = strlen( DnsRecord );
if ( !WriteFile( FileHandle,
DnsRecord,
DnsRecordLength,
&BytesWritten,
NULL ) ) { // Not Overlapped
NetStatus = GetLastError();
NlPrint(( NL_CRITICAL,
"NlDnsWriteLog: %ws: Unable to WriteFile. %ld\n",
FileName,
NetStatus ));
goto Cleanup;
}
if ( BytesWritten != DnsRecordLength) {
NlPrint((NL_CRITICAL,
"NlDnsWriteLog: %ws: Write bad byte count %ld s.b. %ld\n",
FileName,
BytesWritten,
DnsRecordLength ));
goto Cleanup;
}
}
NlGlobalDnsListDirty = FALSE;
Cleanup:
if ( FileHandle != INVALID_HANDLE_VALUE ) {
CloseHandle( FileHandle );
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
if ( AllocatedBuffer != NULL ) {
LocalFree(AllocatedBuffer);
}
return;
}
BOOLEAN
NlDnsHasDnsServers(
VOID
)
/*++
Routine Description:
Returns TRUE if this machine has one or more DNS servers configured.
If FALSE, it is highly unlikely that DNS name resolution will work.
Arguments:
None.
Return Value:
TRUE: This machine has one or more DNS servers configured.
--*/
{
BOOLEAN RetVal;
NET_API_STATUS NetStatus;
PDNS_RECORD DnsARecords = NULL;
//
// If there are no IP addresses,
// there are no DNS servers.
//
if ( NlGlobalWinsockPnpAddresses == NULL ) {
RetVal = FALSE;
} else {
//
// Try getting the A records for the DNS servers from DNS
//
// REVIEW: consider having DNS notify us when the DNS server state changes.
// Then we wouldn't have to bother DNS each time we need to know.
//
NetStatus = DnsQuery_UTF8(
"", // Ask for addresses of the DNS servers
DNS_TYPE_A,
0, // No special flags
NULL, // No list of DNS servers
&DnsARecords,
NULL );
if ( NetStatus != NO_ERROR ) {
RetVal = FALSE;
} else {
RetVal = (DnsARecords != NULL);
}
if ( DnsARecords != NULL ) {
DnsRecordListFree( DnsARecords, DnsFreeRecordListDeep );
}
}
return RetVal;
}
BOOL
NlDnsCheckLastStatus(
VOID
)
/*++
Routine Description:
Query the status of DNS updates for all records as they
were registered/deregistered last time.
Arguments:
None
Return Value:
Returns TRUE if there was no error for last DNS updates
for all records. Otherwise returns FALSE.
--*/
{
PLIST_ENTRY ListEntry;
PNL_DNS_NAME NlDnsName;
BOOL Result = TRUE;
EnterCriticalSection( &NlGlobalDnsCritSect );
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
if ( NlDnsName->State != DeleteMe &&
NlDnsName->NlDnsNameLastStatus != NO_ERROR ) {
Result = FALSE;
break;
}
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
return Result;
}
VOID
NlDnsServerFailureOutputCheck(
VOID
)
/*++
Routine Description:
Check if it's OK to write DNS server failure event logs
Arguments:
None
Return Value:
None.
--*/
{
SC_HANDLE ScManagerHandle = NULL;
SC_HANDLE ServiceHandle = NULL;
//
// If we have already determined on any previous
// start on this boot that we should write the
// event log, there is nothing we need to check.
//
if ( NlDnsWriteServerFailureEventLog ) {
return;
}
//
// Query the service controller to see
// whether the DNS service exists
//
ScManagerHandle = OpenSCManager(
NULL,
NULL,
SC_MANAGER_CONNECT );
//
// If we couldn't open the SC,
// proceed with checking the timeout
//
if ( ScManagerHandle == NULL ) {
NlPrint(( NL_CRITICAL,
"NlDnsServerFailureOutputCheck: OpenSCManager failed: 0x%lx\n",
GetLastError()));
} else {
ServiceHandle = OpenService(
ScManagerHandle,
L"DNS",
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
(VOID) CloseServiceHandle( ScManagerHandle );
//
// If DNS service does not exits locally,
// we should write DNS server failure errors
//
if ( ServiceHandle == NULL ) {
if ( GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST ) {
NlDnsWriteServerFailureEventLog = TRUE;
return;
}
//
// Service exists. Proceed with checking the timeout
//
} else {
(VOID) CloseServiceHandle( ServiceHandle );
}
}
//
// If this is not the first time we have been started or
// the timeout has elapsed, it is time to write event errors
//
if ( NlDnsInitCount > 1 ||
NetpDcElapsedTime(NlGlobalDnsStartTime) > NL_DNS_EVENTLOG_TIMEOUT ) {
NlDnsWriteServerFailureEventLog = TRUE;
}
return;
}
NET_API_STATUS
NlDnsUpdate(
IN PNL_DNS_NAME NlDnsName,
IN BOOLEAN Register
)
/*++
Routine Description:
This routine does the actual call to DNS to register or deregister a name.
Arguments:
NlDnsName - Name to register or deregister.
Register - True to register the name.
False to deregister the name.
Return Value:
NO_ERROR: The name was registered or deregistered.
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
DNS_RECORD DnsRecord;
LPWSTR MsgStrings[5] = {NULL};
ULONG DnsUpdateFlags = DNS_UPDATE_SECURITY_USE_DEFAULT;
DNS_UPDATE_EXTRA_INFO DnsUpdateExtraInfo = {0};
WCHAR DnsServerIpAddressString[NL_IP_ADDRESS_LENGTH+1];
WCHAR RcodeString[] = L"<UNAVAILABLE>";
WCHAR StatusString[] = L"<UNAVAILABLE>";
static BOOL NetlogonNoDynamicDnsLogged = FALSE;
//
// Don't let the service controller think we've hung.
//
if ( !GiveInstallHints( FALSE ) ) {
NetStatus = ERROR_DNS_NOT_CONFIGURED;
goto Cleanup;
}
//
// If dynamic DNS is manually disabled,
// warn the user to update DNS manually.
// But do not abuse the event log, write only once
//
if ( !NlGlobalParameters.UseDynamicDns ) {
NetStatus = ERROR_DYNAMIC_DNS_NOT_SUPPORTED;
if ( !NetlogonNoDynamicDnsLogged ) {
NlpWriteEventlog( NELOG_NetlogonNoDynamicDnsManual,
EVENTLOG_WARNING_TYPE,
NULL,
0,
NULL,
0 );
NetlogonNoDynamicDnsLogged = TRUE;
}
goto Cleanup;
//
// Otherwise, reset the boolean to account for the case when
// dynamic DNS being disabled gets enabled and then disabled again.
//
} else {
NetlogonNoDynamicDnsLogged = FALSE;
}
//
// Build the common parts of the RR.
//
RtlZeroMemory( &DnsRecord, sizeof(DnsRecord) );
DnsRecord.pNext = NULL;
DnsRecord.pName = (LPTSTR) NlDnsName->DnsRecordName;
DnsRecord.dwTtl = NlGlobalParameters.DnsTtl;
//
// Build an A RR
//
if ( NlDnsARecord( NlDnsName->NlDnsNameType ) ) {
DnsRecord.wType = DNS_TYPE_A;
DnsRecord.wDataLength = sizeof( DNS_A_DATA );
DnsRecord.Data.A.IpAddress = NlDnsName->IpAddress;
//
// Build a CNAME RR
//
} else if ( NlDnsCnameRecord( NlDnsName->NlDnsNameType ) ) {
DnsRecord.wType = DNS_TYPE_CNAME;
DnsRecord.wDataLength = sizeof( DNS_PTR_DATA );
DnsRecord.Data.CNAME.pNameHost = (LPTSTR) NlDnsName->DnsHostName;
//
// Build a SRV RR
//
} else {
DnsRecord.wType = DNS_TYPE_SRV;
DnsRecord.wDataLength = sizeof( DNS_SRV_DATA );
DnsRecord.Data.SRV.pNameTarget = (LPTSTR) NlDnsName->DnsHostName;
DnsRecord.Data.SRV.wPriority = (WORD) NlDnsName->Priority;
DnsRecord.Data.SRV.wWeight = (WORD) NlDnsName->Weight;
DnsRecord.Data.SRV.wPort = (WORD) NlDnsName->Port;
}
//
// Tell DNS to skip adapters where dynamic DNS updates
// are disabled unless we are instructed otherwise
//
if ( !NlGlobalParameters.DnsUpdateOnAllAdapters ) {
DnsUpdateFlags |= DNS_UPDATE_SKIP_NO_UPDATE_ADAPTERS;
}
//
// If it's a CNAME record (used by the DS replication),
// tell DNS to register on a remote server (in addition
// to the local server if this machine is a DNS server)
// to avoid the following so called island problem (chicken
// & egg problem): Other DCs (i.e replication destinations)
// can't locate this replication source because their DNS database
// doesn't contain this CNAME record, and their DNS database doesn't
// contain this CNAME record because the the destination DCs can't
// locate the source and can't replicate this record.
//
if ( NlDnsCnameRecord(NlDnsName->NlDnsNameType) ) {
DnsUpdateFlags |= DNS_UPDATE_REMOTE_SERVER;
}
//
// Ask DNS to return the debug info
//
DnsUpdateExtraInfo.Id = DNS_UPDATE_INFO_ID_RESULT_INFO;
//
// Call DNS to do the update.
//
if ( Register ) {
// According to RFC 2136 (and bug 173936) we need to replace the RRSet for
// CNAME records to avoid an error if other records exist by the
// same name.
//
// Note that the dynamic DNS RFC says that CNAME records ALWAYS overwrite the
// existing single record (ignoring the DNS_UPDATE_SHARED).
//
// Also, replace the record if this is a PDC name (there should be only one PDC)
//
if ( NlDnsCnameRecord( NlDnsName->NlDnsNameType ) ||
NlDnsPdcName( NlDnsName->NlDnsNameType ) ) {
NetStatus = DnsReplaceRecordSetUTF8(
&DnsRecord, // New record set
DnsUpdateFlags,
NULL, // No context handle
NULL, // DNS will choose the servers
&DnsUpdateExtraInfo );
} else {
NetStatus = DnsModifyRecordsInSet_UTF8(
&DnsRecord, // Add record
NULL, // No delete records
DnsUpdateFlags,
NULL, // No context handle
NULL, // DNS will choose the servers
&DnsUpdateExtraInfo );
}
} else {
NetStatus = DnsModifyRecordsInSet_UTF8(
NULL, // No add records
&DnsRecord, // Delete this record
DnsUpdateFlags,
NULL, // No context handle
NULL, // DNS will choose the servers
&DnsUpdateExtraInfo );
}
//
// Convert the status codes to ones we understand.
//
switch ( NetStatus ) {
case NO_ERROR:
NlDnsName->NlDnsNameLastStatus = NetStatus;
break;
case ERROR_TIMEOUT: // DNS server isn't available
case DNS_ERROR_RCODE_SERVER_FAILURE: // Server failed
//
// Don't log an error specific to the DnsRecordName since all of them
// are probably going to fail.
//
if ( NlDnsWriteServerFailureEventLog ) {
LPWSTR LocalDnsDomainName = NULL;
//
// Remember the status of the failure
//
NlDnsName->NlDnsNameLastStatus = NetStatus;
//
// Get the name of the domain that this record belongs to.
//
LocalDnsDomainName = NlDnsNameToDomainName( NlDnsName );
if ( LocalDnsDomainName != NULL ) {
MsgStrings[0] = LocalDnsDomainName;
NlpWriteEventlog( NELOG_NetlogonDynamicDnsServerFailure,
EVENTLOG_WARNING_TYPE,
(LPBYTE) &NetStatus,
sizeof(NetStatus),
MsgStrings,
1 );
NetApiBufferFree( LocalDnsDomainName );
}
}
NetStatus = ERROR_DNS_NOT_AVAILABLE;
break;
case DNS_ERROR_NO_TCPIP: // TCP/IP not configured
case DNS_ERROR_NO_DNS_SERVERS: // DNS not configured
case WSAEAFNOSUPPORT: // Winsock Address Family not supported ??
NlDnsName->NlDnsNameLastStatus = NetStatus;
MsgStrings[0] = (LPWSTR) UlongToPtr( NetStatus );
// Don't log an error specific to the DnsRecordName since all of them
// are probably going to fail.
NlpWriteEventlog( NELOG_NetlogonDynamicDnsFailure,
EVENTLOG_WARNING_TYPE,
(LPBYTE) &NetStatus,
sizeof(NetStatus),
MsgStrings,
1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
NetStatus = ERROR_DNS_NOT_CONFIGURED;
break;
default:
NlDnsName->NlDnsNameLastStatus = NetStatus;
//
// Get the IP address
//
if ( DnsUpdateExtraInfo.U.Results.ServerIp4 != 0 ) {
NetpIpAddressToWStr( DnsUpdateExtraInfo.U.Results.ServerIp4,
DnsServerIpAddressString );
} else {
wcscpy( DnsServerIpAddressString, L"<UNAVAILABLE>" );
}
//
// Get the RCODE. Rcode is a WORD, but let's be careful to avoid
// buffer overrun problems if they change it to __int64 one day.
// Max DWORD in decimal is "4294967295" which is 11 characters
// long, so we've got enough storage for it in RcodeString.
//
if ( DnsUpdateExtraInfo.U.Results.Rcode <= MAXULONG ) {
swprintf( RcodeString, L"%lu", DnsUpdateExtraInfo.U.Results.Rcode );
}
//
// Get the status string. Above comment applies here, too.
//
if ( DnsUpdateExtraInfo.U.Results.Status <= MAXULONG ) {
swprintf( StatusString, L"%lu", DnsUpdateExtraInfo.U.Results.Status );
}
// Old server that doesn't understand dynamic DNS
if ( NetStatus == DNS_ERROR_RCODE_NOT_IMPLEMENTED ) {
MsgStrings[0] = DnsServerIpAddressString;
MsgStrings[1] = RcodeString;
MsgStrings[2] = StatusString;
NlpWriteEventlog( NELOG_NetlogonNoDynamicDns,
EVENTLOG_WARNING_TYPE,
(LPBYTE) &DnsUpdateExtraInfo.U.Results.Rcode,
sizeof(DnsUpdateExtraInfo.U.Results.Rcode),
MsgStrings,
3 );
NetStatus = ERROR_DYNAMIC_DNS_NOT_SUPPORTED;
// All other errors
} else {
MsgStrings[0] = NlDnsNameToWStr( NlDnsName );
if ( MsgStrings[0] != NULL ) {
MsgStrings[1] = (LPWSTR) UlongToPtr( NetStatus );
MsgStrings[2] = DnsServerIpAddressString;
MsgStrings[3] = RcodeString;
MsgStrings[4] = StatusString;
NlpWriteEventlogEx(
Register ?
NELOG_NetlogonDynamicDnsRegisterFailure :
NELOG_NetlogonDynamicDnsDeregisterFailure,
EVENTLOG_ERROR_TYPE,
(LPBYTE) &DnsUpdateExtraInfo.U.Results.Rcode,
sizeof(DnsUpdateExtraInfo.U.Results.Rcode),
MsgStrings,
5 | NETP_LAST_MESSAGE_IS_NETSTATUS,
1 ); // status message index
NetApiBufferFree( MsgStrings[0] );
}
}
break;
}
Cleanup:
//
// Compute when we want to try this name again
//
NlQuerySystemTime( &NlDnsName->ScavengeTimer.StartTime );
if ( NlDnsName->ScavengeTimer.Period == 0 ) {
NlDnsName->ScavengeTimer.Period = min( ORIG_DNS_SCAVENGE_PERIOD, NlGlobalParameters.DnsRefreshIntervalPeriod );
} else if ( NlDnsName->ScavengeTimer.Period < NlGlobalParameters.DnsRefreshIntervalPeriod / 2 ) {
NlDnsName->ScavengeTimer.Period *= 2;
} else {
NlDnsName->ScavengeTimer.Period = NlGlobalParameters.DnsRefreshIntervalPeriod;
}
return NetStatus;
}
NET_API_STATUS
NlDnsRegisterOne(
IN PNL_DNS_NAME NlDnsName,
OUT NL_DNS_NAME_STATE *ResultingState
)
/*++
Routine Description:
This routine registers a SRV record for a particular name with DNS.
Arguments:
NlDnsName - Structure describing name to register.
ResultingState - The state of the entry after it has been scavenged.
May be undefined if we couldn't scavenge the entry for some reason.
Return Value:
NO_ERROR: The name was registered
--*/
{
NET_API_STATUS NetStatus;
//
// Initialization
//
*ResultingState = DnsNameStateInvalid;
//
// Register the name with DNS
//
NetStatus = NlDnsUpdate( NlDnsName, TRUE );
if ( NetStatus == NO_ERROR ) {
//
// Mark that the name is really registered.
//
*ResultingState = Registered;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsRegisterOne: registered (success)" ));
//
// If DNS is not configured on this machine,
// silently ignore the error.
//
} else if ( NetStatus == ERROR_DNS_NOT_CONFIGURED ) {
NetStatus = NO_ERROR;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsRegisterOne: not registered (dns not configured)" ));
//
// If the DNS server cannot be reached at this time,
// simply don't mark the name as registered. We'll register it later.
//
} else if ( NetStatus == ERROR_DNS_NOT_AVAILABLE ) {
NetStatus = NO_ERROR;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsRegisterOne: not registered (dns server not available)" ));
//
// If Dynamic Dns is not supported,
// complain so the names can be added manually.
//
} else if ( NetStatus == ERROR_DYNAMIC_DNS_NOT_SUPPORTED ) {
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsRegisterOne: not registered (dynamic dns not supported)" ));
NetStatus = NO_ERROR;
}
return NetStatus;
}
NET_API_STATUS
NlDnsAddName(
IN PDOMAIN_INFO DomainInfo,
IN NL_DNS_NAME_TYPE NlDnsNameType,
IN LPWSTR SiteName,
IN ULONG IpAddress
)
/*++
Routine Description:
This routine adds a particular DNS name to the global list
of all DNS names registed by this DC.
Enter with NlGlobalDnsCritSect locked
Arguments:
DomainInfo - Domain the name is to be registered for.
NlDnsNameType - The specific type of name to be registered.
SiteName - If NlDnsNameType is any of the *AtSite values,
the site name of the site this name is registered for.
IpAddress - If NlDnsNameType is NlDnsLdapIpAddress or NlDnsGcIpAddress,
the IP address of the DC.
Return Value:
NO_ERROR: The name was registered or queued to be registered.
--*/
{
NET_API_STATUS NetStatus;
CHAR DnsRecordName[NL_MAX_DNS_LENGTH+1];
PNL_DNS_NAME NlDnsName = NULL;
PNL_DNS_NAME FoundNlDnsName = NULL;
ULONG Weight;
ULONG Port;
ULONG Priority;
ULONG LoopCount;
PLIST_ENTRY ListEntry;
//
// If there is no DNS domain name for this domain,
// silently return;
//
if ( DomainInfo->DomUtf8DnsDomainName == NULL ) {
NlPrintDom(( NL_DNS, DomainInfo,
"NlDnsRegister: %ws: Domain has no DNS domain name (silently return)\n",
NlDcDnsNameTypeDesc[NlDnsNameType].Name ));
return NO_ERROR;
}
//
// If this is a SRV or CNAME record,
// require that there is a dns host name.
//
if ( (NlDnsSrvRecord( NlDnsNameType ) || NlDnsCnameRecord( NlDnsNameType ) ) &&
DomainInfo->DomUtf8DnsHostName == NULL ) {
NlPrintDom(( NL_DNS, DomainInfo,
"NlDnsRegister: %ws: Domain has no DNS host name (silently return)\n",
NlDcDnsNameTypeDesc[NlDnsNameType].Name ));
return NO_ERROR;
}
//
// Grab the parameters we're going to register.
//
Priority = NlGlobalParameters.LdapSrvPriority;
Weight = NlGlobalParameters.LdapSrvWeight;
if ( NlDnsGcName( NlDnsNameType ) ) {
Port = NlGlobalParameters.LdapGcSrvPort;
} else if ( NlDnsKpwdRecord( NlDnsNameType )) {
Port = 464;
} else if ( NlDnsKdcRecord( NlDnsNameType ) ) {
Port = NlGlobalParameters.KdcSrvPort;
} else {
Port = NlGlobalParameters.LdapSrvPort;
}
//
// Register the record for the name and for the name alias, if any
//
for ( LoopCount = 0; LoopCount < 2; LoopCount++ ) {
NlDnsName = NULL;
FoundNlDnsName = NULL;
//
// Build the name of this DNS record.
//
// On the first loop iteration, build the active name.
// On the second loop iteration, build the name alias, if any.
//
NetStatus = NlDnsBuildName( DomainInfo,
NlDnsNameType,
SiteName,
(LoopCount == 0) ?
FALSE : // active name
TRUE, // name alias
DnsRecordName );
if ( NetStatus != NO_ERROR ) {
//
// If the domain has no DNS domain name,
// simply bypass the name registration forever.
//
if ( NetStatus == ERROR_NO_SUCH_DOMAIN ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsAddName: %ws: NlDnsBuildName indicates something is missing and this DNS name cannot be built (ignored)\n",
NlDcDnsNameTypeDesc[NlDnsNameType].Name ));
return NO_ERROR;
} else {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsAddName: %ws: Cannot NlDnsBuildName %ld\n",
NlDcDnsNameTypeDesc[NlDnsNameType].Name,
NetStatus ));
return NetStatus;
}
}
//
// If this name doesn't exist, skip it
//
if ( *DnsRecordName == '\0' ) {
continue;
}
//
// Loop through the list of DNS names finding any that match the one we're
// about to register.
//
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
//
// If this entry is marked for deletion,
// skip it
//
if ( NlDnsName->State == DeleteMe ) {
continue;
}
//
// The names will only be equal if the name types are equal,
// the domains are compatible (equal or not specified),
// and the DnsRecordName is identical.
//
// This first test sees if the record "identifies" the same record.
//
//
if ( NlDnsName->NlDnsNameType == NlDnsNameType &&
(NlDnsName->DomainInfo == DomainInfo ||
NlDnsName->DomainInfo == NULL ) &&
NlDnsName->IpAddress == IpAddress &&
NlEqualDnsNameUtf8( DnsRecordName, NlDnsName->DnsRecordName ) ) {
BOOLEAN Identical;
BOOLEAN DeleteIt;
//
// Assume the records are identical.
//
// This second test sees if any of the "data" portion of the record
// changes.
//
// The Dynamic DNS RFC says that the Ttl field isn't used to
// distiguish the record. So, ignore it here knowing we'll
// simply re-register with the new value if the Ttl has changed.
//
DeleteIt = FALSE;
Identical = TRUE;
// Compare A records
if ( NlDnsARecord( NlDnsNameType ) ) {
// Nothing else to compare
// Compare CNAME records
} else if ( NlDnsCnameRecord( NlDnsNameType ) ) {
//
// The Dynamic DNS RFC says that the host name part of the
// CNAME record isn't used for comparison purposes. There
// can only be one record for a particular name.
// So, if the host name is different, simply ditch this entry and
// allocate a new one with the right host name.
//
if ( !NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsHostName, NlDnsName->DnsHostName )) {
DeleteIt = TRUE;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsAddName: CNAME Host not equal. %s %s",
DomainInfo->DomUtf8DnsHostName,
NlDnsName->DnsHostName ));
}
// Compare SRV records
} else {
if ( NlDnsName->Priority != Priority ) {
Identical = FALSE;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsAddName: Priority not equal. %ld %ld",
NlDnsName->Priority,
Priority ));
} else if ( NlDnsName->Port != Port ) {
Identical = FALSE;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsAddName: Port not equal. %ld %ld",
NlDnsName->Port,
Port ));
} else if ( NlDnsName->Weight != Weight ) {
Identical = FALSE;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsAddName: Weight not equal. %ld %ld",
NlDnsName->Weight,
Weight ));
} else if ( !NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsHostName, NlDnsName->DnsHostName )) {
Identical = FALSE;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsAddName: Host not equal. %s %s",
DomainInfo->DomUtf8DnsHostName,
NlDnsName->DnsHostName ));
}
}
//
// If the entry should simply be deleted,
// do so now.
//
if ( DeleteIt ) {
NlDnsSetState( NlDnsName, DeleteMe );
NlPrintDns(( NL_CRITICAL, NlDnsName,
"NlDnsAddName: Annoying entry found (recovering)" ));
//
// If this is the exact record,
// simply mark it for registration.
//
} else if ( Identical ) {
//
// If this is the second such entry we've found,
// this is an internal error.
// But recover by deleting the entry.
//
if ( FoundNlDnsName != NULL ) {
NlDnsSetState( NlDnsName, DeleteMe );
NlPrintDns(( NL_CRITICAL, NlDnsName,
"NlDnsAddName: Duplicate entry found (recovering)" ));
} else {
if ( NlDnsName->State != Registered ) {
NlDnsSetState( NlDnsName, RegisterMe );
}
//
// DomainInfo might be NULL if this was a record marked for
// deletion.
//
NlDnsName->DomainInfo = DomainInfo;
//
// Cooperate with NlDnsAddDomainRecords and tell it that
// this entry can be kept.
//
NlDnsName->Flags &= ~NL_DNS_REGISTER_DOMAIN;
FoundNlDnsName = NlDnsName;
}
//
// If this record isn't exact,
// deregister the previous value.
//
// Don't scavenge yet. We'll pick this up when the scavenger gets
// around to running.
//
} else {
NlDnsSetState( NlDnsName, DeregisterMe );
NlPrintDns(( NL_CRITICAL, NlDnsName,
"NlDnsAddName: Similar entry found and marked for deregistration" ));
}
}
}
//
// If the name was found,
// use it.
if ( FoundNlDnsName != NULL ) {
NlDnsName = FoundNlDnsName;
NlPrintDns(( NL_DNS_MORE, NlDnsName,
"NlDnsAddName: Name already on the list" ));
//
// If not,
// allocate the structure now.
//
} else {
NlDnsName = NlDnsAllocateEntry(
NlDnsNameType,
DnsRecordName,
Priority,
Weight,
Port,
DomainInfo->DomUtf8DnsHostName,
IpAddress,
RegisterMe );
if ( NlDnsName == NULL ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsRegister: %ws: Cannot allocate DnsName structure\n",
NlDcDnsNameTypeDesc[NlDnsNameType].Name ));
return ERROR_NOT_ENOUGH_MEMORY;
}
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsAddName: New name added to the list" ));
NlDnsName->DomainInfo = DomainInfo;
}
}
return NO_ERROR;
}
NET_API_STATUS
NlDnsDeregisterOne(
IN PNL_DNS_NAME NlDnsName,
OUT NL_DNS_NAME_STATE *ResultingState
)
/*++
Routine Description:
This routine deregisters a the SRV record for a particular name with DNS.
Arguments:
NlDnsName - Structure describing name to deregister.
ResultingState - The state of the entry after it has been scavenged.
May be undefined if we couldn't scavenge the entry for some reason.
Return Value:
NO_ERROR: The name was deregistered
Otherwise, the name was not deregistered. The operation should be retried.
--*/
{
NET_API_STATUS NetStatus;
//
// Initialization
//
*ResultingState = DnsNameStateInvalid;
//
// Deregister the name with DNS
//
NetStatus = NlDnsUpdate( NlDnsName, FALSE );
//
// If the name has been removed for all practical purposes,
// Indicate this routine was successful.
//
if ( NetStatus == NO_ERROR ||
NetStatus == ERROR_DYNAMIC_DNS_NOT_SUPPORTED ) {
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsDeregisterOne: being deregistered (success) %ld",
NetStatus ));
*ResultingState = DeleteMe;
NetStatus = NO_ERROR;
//
// If the DNS server cannot be reached at this time,
// we'll deregister it later.
//
} else if ( NetStatus == ERROR_DNS_NOT_AVAILABLE ) {
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsDeregisterOne: being deregistered (DNS server not available)" ));
//
// If the DNS server is not configured,
// we'll deregister it later.
//
// DNS was available when we registered the name. So this is probably
// a temporary condition (such as we temporarily don't have an IP address).
//
} else if ( NetStatus == ERROR_DNS_NOT_CONFIGURED ) {
//
// If it's never really been registered,
// ditch the name
//
if ( NlDnsName->Flags & NL_DNS_REGISTERED_ONCE ) {
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsDeregisterOne: being deregistered (DNS not configured) (Try later)" ));
} else {
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsDeregisterOne: being deregistered (DNS not configured) (Ditch it)" ));
*ResultingState = DeleteMe;
NetStatus = NO_ERROR;
}
}
//
// If we successfully deregistered,
// reset the first deregistration failure time stamp
//
EnterCriticalSection( &NlGlobalDnsCritSect );
if ( NetStatus == NO_ERROR ) {
NlDnsName->FirstDeregFailureTime.QuadPart = 0;
//
// If we failed to deregister and we postponed it until later,
// check if it's time to give up on this entry
//
} else if ( *ResultingState != DeleteMe ) {
ULONG LocalDnsFailedDeregisterTimeout;
BOOLEAN FirstFailure = FALSE;
//
// Set the first deregistration failure time stamp
//
if ( NlDnsName->FirstDeregFailureTime.QuadPart == 0 ) {
NlQuerySystemTime( &NlDnsName->FirstDeregFailureTime );
FirstFailure = TRUE;
}
//
// Get the reg value for failed deregistration timeout (in seconds)
// and convert it to milliseconds
//
LocalDnsFailedDeregisterTimeout = NlGlobalParameters.DnsFailedDeregisterTimeout;
// if the value converted into milliseconds fits into a ULONG, use it
if ( LocalDnsFailedDeregisterTimeout <= MAXULONG/1000 ) {
LocalDnsFailedDeregisterTimeout *= 1000; // convert into milliseconds
// otherwise, use the max ULONG
} else {
LocalDnsFailedDeregisterTimeout = MAXULONG; // infinity
}
//
// Determine if it's time to give up on this entry
//
// If timeout is zero we are to delete immediately
// after the first failure
// Otherwise, if this is not the first failure,
// check the timestamp
//
if ( LocalDnsFailedDeregisterTimeout == 0 ||
(!FirstFailure &&
NetpLogonTimeHasElapsed(NlDnsName->FirstDeregFailureTime,
LocalDnsFailedDeregisterTimeout)) ) {
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsDeregisterOne: Ditch it due to time expire" ));
*ResultingState = DeleteMe;
NetStatus = NO_ERROR;
}
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
return NetStatus;
}
VOID
NlDnsScavengeOne(
IN PNL_DNS_NAME NlDnsName,
OUT NL_DNS_NAME_STATE *ResultingState
)
/*++
Routine Description:
Register or Deregister any DNS names that need it.
Arguments:
NlDnsName - Name to scavenge. This structure will be marked for deletion
if it is no longer needed.
ResultingState - The state of the entry after it has been scavenged.
May be undefined if we couldn't scavenge the entry for some reason.
Return Value:
None.
--*/
{
LARGE_INTEGER TimeNow;
ULONG Timeout;
//
// Only scavenge this entry if its timer has expired
//
Timeout = (DWORD) -1;
NlQuerySystemTime( &TimeNow );
if ( TimerExpired( &NlDnsName->ScavengeTimer, &TimeNow, &Timeout)) {
//
// If the name needs to be deregistered,
// do it now.
//
switch ( NlDnsName->State ) {
case DeregisterMe:
NlDnsDeregisterOne( NlDnsName, ResultingState );
break;
//
// If the name needs to be registered,
// do it now.
//
case RegisterMe:
case Registered:
NlDnsRegisterOne( NlDnsName, ResultingState );
break;
}
}
}
VOID
NlDnsScavengeWorker(
IN LPVOID ScavengeRecordsParam
)
/*++
Routine Description:
Scavenge through the list of all DNS records and
register/deregisteer each record as apprropriate.
Arguments:
ScavengeRecordsParam - not used
Return Value:
None.
--*/
{
ULONG LoopCount = 0;
ULONG StartTime = 0;
ULONG CycleStartTime = 0;
ULONG Timeout = MAXULONG;
ULONG Flags = 0;
LARGE_INTEGER TimeNow;
BOOL ReasonLogged = FALSE;
BOOL ScavengeAbortedOnShutdown = FALSE;
PLIST_ENTRY ListEntry = NULL;
PNL_DNS_NAME NlDnsName = NULL;
NL_DNS_NAME_STATE ResultingState = DnsNameStateInvalid;
EnterCriticalSection( &NlGlobalDnsCritSect );
while ( NlGlobalDnsScavengeNeeded ) {
NlGlobalDnsScavengeNeeded = FALSE;
CycleStartTime = GetTickCount();
//
// Guard against infinite loop processing
//
LoopCount ++;
if ( LoopCount > NL_DNS_MAX_SCAVENGE_RESTART ) {
NlPrint(( NL_CRITICAL,
"NlDnsScavengeWorker: Avoid ReStarting DNS scavenge after too many times %ld\n",
LoopCount ));
break;
}
//
// If required,
// ensure the DomainName<1B> names are properly registered.
// Doing this only once is enough.
//
// Avoid having a crit sect locked while doing network I/O
//
if ( NlGlobalDnsScavengeFlags & NL_DNS_FIX_BROWSER_NAMES ) {
NlGlobalDnsScavengeFlags &= ~NL_DNS_FIX_BROWSER_NAMES;
LeaveCriticalSection( &NlGlobalDnsCritSect );
(VOID) NlEnumerateDomains( FALSE, NlBrowserFixAllNames, NULL );
EnterCriticalSection( &NlGlobalDnsCritSect );
}
//
// Restart the scavenge if some other thread indicated so
// and we still can do another loop. If we have no loop
// left, just press on and finish this loop -- we will
// remember this fact and retry scavenging in 5 minutes.
//
if ( NlGlobalDnsScavengeNeeded &&
LoopCount < NL_DNS_MAX_SCAVENGE_RESTART ) {
NlPrint(( NL_DNS,
"NlDnsScavengeWorker: ReStarting scavenge %ld (after adding <1B> names)\n",
LoopCount ));
continue;
}
//
// Refresh records for all domains/NDNCs in the global list
// as needed.
//
// Avoid having a crit sect locked while doing network I/O.
//
if ( NlGlobalDnsScavengeFlags & NL_DNS_REFRESH_DOMAIN_RECORDS ||
NlGlobalDnsScavengeFlags & NL_DNS_FORCE_REFRESH_DOMAIN_RECORDS ) {
Flags = NlGlobalDnsScavengeFlags;
LeaveCriticalSection( &NlGlobalDnsCritSect );
NlEnumerateDomains( TRUE, NlDnsAddDomainRecordsWithSiteRefresh, &Flags );
EnterCriticalSection( &NlGlobalDnsCritSect );
}
//
// Restart the scavenge if some other thread indicated so
// and we still can do another loop. If we have no loop
// left, just press on and finish this loop -- we will
// remember this fact and retry scavenging in 5 minutes.
//
if ( NlGlobalDnsScavengeNeeded &&
LoopCount < NL_DNS_MAX_SCAVENGE_RESTART ) {
NlPrint(( NL_DNS,
"NlDnsScavengeWorker: ReStarting scavenge %ld (after adding domain records)\n",
LoopCount ));
continue;
}
//
// Now that the global list has been updated,
// register/deregister each record as appropriate
//
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
//
// If this entry is marked for deletion,
// skip it -- we will remove it below
//
if ( NlDnsName->State == DeleteMe ) {
continue;
}
//
// Abort the scavenge if we are terminating
// unless we are configured otherwise
//
if ( NlGlobalTerminate ) {
ULONG TotalElapsedTime = NetpDcElapsedTime( CycleStartTime );
//
// Continue if we are in the process of demotion
//
if ( NlGlobalDcDemotionInProgress ) {
if ( !ReasonLogged ) {
NlPrint(( NL_DNS,
"NlDnsScavengeWorker: Continue DNS scavenge on demotion\n" ));
ReasonLogged = TRUE;
}
//
// Continue if we are configured to deregister on shutdown
//
} else if ( !NlGlobalParameters.AvoidDnsDeregOnShutdown ) {
if ( !ReasonLogged ) {
NlPrint(( NL_DNS,
"NlDnsScavengeWorker: Continue DNS scavenge on shutdown (config)\n" ));
ReasonLogged = TRUE;
}
//
// Otherwise, abort the scavenge cycle
//
} else {
NlPrint(( NL_DNS_MORE,
"NlDnsScavengeWorker: Avoiding DNS scavenge on shutdown\n" ));
break;
}
//
// If we have spent all the time alloted for DNS deregistartions
// on shutdown, abort the scavenge cycle as we can't wait.
// Don't be tempted to measure the time spent on the entire
// routine rather than this cycle because previous cycles
// (if any) might not be due to a shutdown deregistration cleanup.
//
if ( TotalElapsedTime > NL_DNS_SHUTDOWN_THRESHOLD ) {
ScavengeAbortedOnShutdown = TRUE;
NlPrint(( NL_CRITICAL,
"NlDnsScavengeWorker: Abort DNS scavenge on shutdown because DNS is too slow %lu\n",
TotalElapsedTime ));
break;
}
//
// We continue the deregistration scavenge on shutdown.
// Set this entry state to deregister it.
//
NlDnsSetState( NlDnsName, DeregisterMe );
}
//
// Scavenge this entry
//
// Avoid having crit sect locked while doing network IO
//
NlPrintDns(( NL_DNS_MORE, NlDnsName, "NlDnsScavengeWorker: scavenging name" ));
StartTime = GetTickCount();
LeaveCriticalSection( &NlGlobalDnsCritSect );
ResultingState = DnsNameStateInvalid;
NlDnsScavengeOne( NlDnsName, &ResultingState );
EnterCriticalSection( &NlGlobalDnsCritSect );
//
// Report if we've spent a long time on this DNS record
//
if ( NetpDcElapsedTime(StartTime) > NL_DNS_ONE_THRESHOLD ) {
NlPrintDns(( NL_CRITICAL, NlDnsName,
"NlDnsScavengeWorker: DNS is really slow: %ld",
NetpDcElapsedTime(StartTime) ));
}
//
// Restart the scavenge if some other thread indicated so
// and we still can do another loop. If we have no loop
// left, just press on and finish this loop -- we will
// remember this fact and retry scavenging in 5 minutes.
//
if ( NlGlobalDnsScavengeNeeded &&
LoopCount < NL_DNS_MAX_SCAVENGE_RESTART ) {
NlPrint(( NL_DNS,
"NlDnsScavengeWorker: ReStarting scavenge %ld of DNS names\n",
LoopCount ));
break;
}
//
// Set the state of this entry if it's known.
//
// Note that we set the state only if the record list was
// intact while we release the crit sect doing the network
// I/O. We do this to avoid reseting the new state to
// preserve the updated knowledge about what needs to be
// done with this record on the next cycle (which is just
// about to start).
//
if ( ResultingState != DnsNameStateInvalid ) {
NlDnsSetState( NlDnsName, ResultingState );
}
}
}
//
// Do a pass through all records to:
//
// * Delete names which we successfully deregistered above
// * Determine when we should scavenge next
//
NlQuerySystemTime( &TimeNow );
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
) {
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
ListEntry = ListEntry->Flink;
//
// Remove this entry if it has been deregistered successfully
//
if ( NlDnsName->State == DeleteMe ) {
RemoveEntryList( &NlDnsName->Next );
LocalFree( NlDnsName );
continue;
}
//
// Determine when this entry should be scavenged next
//
// Note that the only way the timer can expire here is when it took
// exceptionally long to update through all names so that by the
// end of the update cycle it's time to restart the cycle again.
//
if ( TimerExpired(&NlDnsName->ScavengeTimer, &TimeNow, &Timeout) ) {
Timeout = 0;
}
}
//
// Wait a couple of extra seconds. There are multiple DNS entries. They
// won't all expire at the same time. They typically expire within
// a couple of seconds of one another. Doing this will increase the
// likelihood that all DNS names will get scavenged in a single
// scavenge cycle.
//
if ( Timeout < (MAXULONG - 2000) ) {
Timeout += 2000;
} else {
Timeout = MAXULONG;
}
//
// Check whether the auto site coverage scavenging needs to happen earlier.
//
// If it does, we will do site coverage refresh (as part of DNS scavenging)
// earlier but we will not update records in DNS (because records will
// not timeout) unless site coverages changes.
//
if ( NlGlobalParameters.AutoSiteCoverage &&
NlGlobalParameters.SiteCoverageRefreshInterval*1000 < Timeout ) {
Timeout = NlGlobalParameters.SiteCoverageRefreshInterval * 1000;
}
//
// Now reset the scavenger timer unless we are terminating
//
if ( !NlGlobalTerminate ) {
NlGlobalDnsScavengerTimer.StartTime.QuadPart = TimeNow.QuadPart;
//
// If we had to abort the scavenge due to too many concurrent
// requests, backoff for 5 minutes
//
if ( NlGlobalDnsScavengeNeeded ) {
NlGlobalDnsScavengerTimer.Period = ORIG_DNS_SCAVENGE_PERIOD;
} else {
NlGlobalDnsScavengerTimer.Period = Timeout;
}
NlPrint(( NL_DNS,
"NlDnsScavengeWorker: Set DNS scavenger to run in %ld minutes (%ld).\n",
(NlGlobalDnsScavengerTimer.Period+59999)/60000,
NlGlobalDnsScavengerTimer.Period ));
if ( !SetEvent(NlGlobalTimerEvent) ) {
NlPrint(( NL_CRITICAL,
"NlDnsScavengeWorker: SetEvent failed %ld\n",
GetLastError() ));
}
}
//
// In all cases, flush any changes to disk
//
NlDnsWriteLog();
//
// If we had to abort the scavenge on demotion,
// log the event log to that effect. Do this
// after flushing the changes to the log file
// as the file is referenced in the event message.
//
if ( NlGlobalDcDemotionInProgress && ScavengeAbortedOnShutdown ) {
NlpWriteEventlog( NELOG_NetlogonDnsDeregAborted,
EVENTLOG_ERROR_TYPE,
NULL,
0,
NULL,
0 );
}
//
// If we had to abort the scavenge due to too many concurrent
// requests, preserve the flags so that we redo what's needed
// in 5 minutes. Set the bit to avoid forced DNS scavenge within
// these 5 minutes.
//
if ( NlGlobalDnsScavengeNeeded ) {
NlGlobalDnsScavengeFlags |= NL_DNS_AVOID_FORCED_SCAVENGE;
NlPrint(( NL_CRITICAL,
"NlDnsScavengeWorker: Preserving the scavenge flags for future re-do: %ld\n",
NlGlobalDnsScavengeFlags ));
} else {
NlGlobalDnsScavengeFlags = 0;
}
//
// Indicate that we are done
//
NlGlobalDnsScavengingInProgress = FALSE;
LeaveCriticalSection( &NlGlobalDnsCritSect );
UNREFERENCED_PARAMETER( ScavengeRecordsParam );
}
VOID
NlDnsScavenge(
IN BOOL NormalScavenge,
IN BOOL RefreshDomainRecords,
IN BOOL ForceRefreshDomainRecords,
IN BOOL ForceReregister
)
/*++
Routine Description:
Register or Deregister any DNS (and <1B>) names that need it.
Arguments:
NormalScavenge -- Indicates whether this is a normal periodic
scavenge vs. scavenge forced by some external event like PnP
event. For normal scavenge, we will do periodic browser <1B>
name refresh.
RefreshDomainRecords -- Indicates whether domain records should be
refreshed in the global list before doing the DNS updates
ForceRefreshDomainRecords -- Indicates whether the refresh is forced,
that is whether we should refresh even if there is no site coverage
change. Ignored if RefreshDomainRecords is FALSE.
ForceReregister -- TRUE if records that have been already
registered should be re-registered even if their scavenge
timer hasn't expired yet.
Return Value:
None.
--*/
{
PLIST_ENTRY ListEntry;
PNL_DNS_NAME NlDnsName;
//
// Nothing to register on a workstation
//
if ( NlGlobalMemberWorkstation ) {
return;
}
NlPrint(( NL_DNS,
"NlDnsScavenge: Starting DNS scavenge with: %s %s %s %s\n",
(NormalScavenge ? "Normal" : "Force"),
(RefreshDomainRecords ? "RefreshDomainRecords" : "0"),
(ForceRefreshDomainRecords ? "ForceRefreshDomainRecords" : "0"),
(ForceReregister ? "ForceReregister" : "0") ));
//
// If Netlogon has been started for a long time,
// deregister any names that have been registered in
// a previous incarnation, but have not been registered in this incarnation.
//
// We wait a while to do these deregistrations because:
//
// * Some of the registrations done by netlogon are a function of multiple
// hosted domains. The initialization of such domains are done asynchronously.
// * Some of the registrations done by netlogon are done as a function of
// other processes telling us that a name needs registration. (e.g., the
// GC name is registered only after the DS starts completely.)
//
// So, it is better to wait a long time to deregister these old registrations
// than to risk deregistering them then immediately re-registering them.
//
EnterCriticalSection( &NlGlobalDnsCritSect );
if ( !NlGlobalDnsInitialCleanupDone &&
NetpDcElapsedTime(NlGlobalDnsStartTime) > NL_DNS_INITIAL_CLEANUP_TIME ) {
NlPrint(( NL_DNS,
"NlDnsScavenge: Mark all delayed deregistrations for deregistration.\n" ));
//
// Mark all delayed deregistrations to deregister now.
//
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
if ( NlDnsName->State == DelayedDeregister ) {
NlDnsSetState( NlDnsName, DeregisterMe );
}
}
NlGlobalDnsInitialCleanupDone = TRUE;
}
//
// Check if it's time to log "DNS server failure" errors.
// We do this check before the series of updates so that
// we don't miss an error for any given name we register.
//
NlDnsServerFailureOutputCheck();
//
// Indicate that a new scavenge cycle is needed
//
NlGlobalDnsScavengeNeeded = TRUE;
//
// Set the scavenge flags. Don't clear any flags as there might
// already be an outstanding scavenge running that requires
// a bigger work. This way we will do all the work required
// in the last of the oustanding scavenging cycles. The scavenge
// worker will clear all the bits when it's done.
//
// Indicate whether domain records should be refreshed in
// the global list before doing the DNS updates
//
if ( RefreshDomainRecords ) {
//
// Indicate whether the refresh if forced even if there is
// no site coverage change
//
if ( ForceRefreshDomainRecords ) {
NlGlobalDnsScavengeFlags |= NL_DNS_FORCE_REFRESH_DOMAIN_RECORDS;
} else {
NlGlobalDnsScavengeFlags |= NL_DNS_REFRESH_DOMAIN_RECORDS;
}
}
//
// Indicate wether we should re-register all those records which
// have already been registered in the previous cycle even if
// their timers have not expired yet.
//
if ( ForceReregister ) {
NlGlobalDnsScavengeFlags |= NL_DNS_FORCE_RECORD_REREGISTER;
}
//
// The periodic DNS scavenger has good backoff characteristics so
// take this opportunity to ensure the DomainName<1B> names are
// properly registered.
//
if ( NormalScavenge ) {
NlGlobalDnsScavengeFlags |= NL_DNS_FIX_BROWSER_NAMES;
}
//
// Start a worker thread if it's not already running
//
if ( !NlGlobalDnsScavengingInProgress ) {
//
// Avoid starting the worker if this scavenge is forced by some external
// event while we are backing off due to high scavenging load.
// Preserve all the flags we set above so that we do all the work
// requested later when normal scavenging kicks off that should
// happen within 5 minutes.
//
if ( !NormalScavenge &&
(NlGlobalDnsScavengeFlags & NL_DNS_AVOID_FORCED_SCAVENGE) != 0 ) {
NlPrint(( NL_CRITICAL, "NlDnsScavengeRecords: avoid forced scavenge due to high load\n" ));
} else {
if ( NlQueueWorkItem( &NlGlobalDnsScavengeWorkItem, TRUE, FALSE ) ) {
NlGlobalDnsScavengingInProgress = TRUE;
} else {
NlPrint(( NL_CRITICAL, "NlDnsScavengeRecords: couldn't queue DNS scavenging\n" ));
}
}
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
}
VOID
NlDnsForceScavenge(
IN BOOL RefreshDomainRecords,
IN BOOL ForceReregister
)
/*++
Routine Description:
Thing wrapper around NlDnsScavenge to pass proper
arguments for induced scavenging
Arguments:
RefreshDomainRecords -- If TRUE, domain records will be
refreshed and refreshed with force (even if site coverege
doesn't change) before doing DNS updates
ForceReregister -- TRUE if records that have been already
registered should be re-registered even if their scavenge
timer hasn't expired yet.
Return Value:
None.
--*/
{
BOOL LocalRefreshDomainRecords = FALSE;
BOOL ForceRefreshDomainRecords = FALSE;
//
// Indicate that we should refresh domain records
// in the global list with force unless we are
// instructed otherwise
//
if ( RefreshDomainRecords ) {
LocalRefreshDomainRecords = TRUE;
ForceRefreshDomainRecords = TRUE;
}
//
// Do the work
//
NlDnsScavenge( FALSE, // not a normal periodic scavenge
LocalRefreshDomainRecords,
ForceRefreshDomainRecords,
ForceReregister );
}
NET_API_STATUS
NlDnsNtdsDsaDeleteOne(
IN NL_DNS_NAME_TYPE NlDnsNameType,
IN GUID *DomainGuid OPTIONAL,
IN LPCWSTR SiteName OPTIONAL,
IN LPCSTR DnsDomainName,
IN LPCSTR DnsHostName OPTIONAL
)
/*++
Routine Description:
This routine adds a single DNS entry associated with a particular
NtDsDsa object and/or a particular DNS host name to the global
list of all DNS records registered/deregistered by this DC. The
entry is marked for deregistration.
Arguments:
NlDnsNameType - The specific type of name.
DomainGuid - Guid to append to DNS name.
For NlDnsDcByGuid, this is the GUID of the domain being located.
For NlDnsDsaCname, this is the GUID of the DSA being located.
SiteName - Name of the site to append to DNS name.
If NlDnsNameType is any of the *AtSite values,
this is the name of the site the DC is in.
DnsDomainName - Specifies the DNS domain for the name.
For NlDnsDcByGuid or any of the GC names,
this is the DNS domain name of the domain at the root of the tree of
domains.
For all others, this is the DNS domain for the DC.
DnsHostName - Specifies the DnsHostName for the record.
For SRV and CNAME records, this name must be specified
For A records, this name is ignored
Return Value:
NO_ERROR: The name was returned;
ERROR_INVALID_DOMAINNAME: Domain's name is too long. Additional labels
cannot be concatenated.
ERROR_NOT_ENOUGH_MEMORY: Not enough memory to complete the operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_DNS_NAME NlDnsName;
ULONG Port;
ULONG DefaultPort;
char DnsRecordName[NL_MAX_DNS_LENGTH+1];
//
// Build the name of the record to delete
//
NetStatus = NetpDcBuildDnsName( NlDnsNameType,
DomainGuid,
SiteName,
DnsDomainName,
DnsRecordName );
if ( NetStatus != NO_ERROR ) {
return NetStatus;
}
//
// Compute the port number for this SRV record
//
if ( NlDnsGcName( NlDnsNameType ) ) {
Port = NlGlobalParameters.LdapGcSrvPort;
DefaultPort = DEFAULT_LDAPGCSRVPORT;
} else if ( NlDnsKpwdRecord( NlDnsNameType )) {
Port = 464;
DefaultPort = 464;
} else if ( NlDnsKdcRecord( NlDnsNameType )) {
Port = NlGlobalParameters.KdcSrvPort;
DefaultPort = DEFAULT_KDCSRVPORT;
} else {
Port = NlGlobalParameters.LdapSrvPort;
DefaultPort = DEFAULT_LDAPSRVPORT;
}
//
// Queue the entry for deletion.
//
EnterCriticalSection( &NlGlobalDnsCritSect );
NlDnsName = NlDnsAllocateEntry( NlDnsNameType,
DnsRecordName,
NlGlobalParameters.LdapSrvPriority, // Priority
NlGlobalParameters.LdapSrvWeight, // Weight
Port, // Port
DnsHostName,
0, // IpAddress
DeregisterMe );
if ( NlDnsName == NULL ) {
LeaveCriticalSection( &NlGlobalDnsCritSect );
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Persist this entry so we try to delete it after a reboot
//
NlDnsName->Flags |= NL_DNS_REGISTERED_ONCE;
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsNtdsDsaDelete: Name queued for deletion" ));
//
// If any of the parameters configured on this machine aren't the default values,
// try the defaults, too
//
if ( NlGlobalParameters.LdapSrvPriority != DEFAULT_LDAPSRVPRIORITY ||
NlGlobalParameters.LdapSrvWeight != DEFAULT_LDAPSRVWEIGHT ||
Port != DefaultPort ) {
//
// Queue the entry for deletion.
//
NlDnsName = NlDnsAllocateEntry( NlDnsNameType,
DnsRecordName,
DEFAULT_LDAPSRVPRIORITY, // Priority
DEFAULT_LDAPSRVWEIGHT, // Weight
DefaultPort, // Port
DnsHostName,
0, // IpAddress
DeregisterMe );
if ( NlDnsName == NULL ) {
LeaveCriticalSection( &NlGlobalDnsCritSect );
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Persist this entry so we try to delete it after a reboot
//
NlDnsName->Flags |= NL_DNS_REGISTERED_ONCE;
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsNtdsDsaDelete: Name queued for deletion" ));
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
return NO_ERROR;
}
NTSTATUS
NlDnsNtdsDsaDeletion (
IN LPWSTR DnsDomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN GUID *DsaGuid OPTIONAL,
IN LPWSTR DnsHostName
)
/*++
Routine Description:
This function deletes all DNS entries associated with a particular
NtDsDsa object and/or a particular DNS host name.
This routine does NOT delete A records registered by the DC. We have
no way of finding out the IP addresses of the long gone DC.
Arguments:
DnsDomainName - DNS domain name of the domain the DC was in.
This need not be a domain hosted by this DC.
If NULL, it is implied to be the DnsHostName with the leftmost label
removed.
DomainGuid - Domain Guid of the domain specified by DnsDomainName
If NULL, GUID specific names will not be removed.
DsaGuid - GUID of the NtdsDsa object that is being deleted.
DnsHostName - DNS host name of the DC whose NTDS-DSA object is being deleted.
Return Value:
Status of the operation.
--*/
{
NET_API_STATUS NetStatus;
NTSTATUS Status;
LPSTR Utf8DnsDomainName = NULL;
LPSTR Utf8DnsHostName = NULL;
PLSAP_SITE_INFO SiteInformation = NULL;
ULONG i;
ULONG NameIndex;
//
// Validate passed parameters
//
if ( DnsHostName == NULL ||
!NetpDcValidDnsDomain(DnsHostName) ) {
NetStatus = ERROR_INVALID_NAME;
goto Cleanup;
}
//
// If DNS domain name isn't specified,
// infer it from the DnsHostName
//
if ( DnsDomainName == NULL ) {
DnsDomainName = wcschr( DnsHostName, L'.' );
if ( DnsDomainName == NULL ) {
NetStatus = ERROR_INVALID_NAME;
goto Cleanup;
}
DnsDomainName ++;
if ( *DnsDomainName == '\0' ) {
NetStatus = ERROR_INVALID_NAME;
goto Cleanup;
}
} else if ( !NetpDcValidDnsDomain(DnsDomainName) ) {
NetStatus = ERROR_INVALID_NAME;
goto Cleanup;
}
//
// Initialization
//
Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( DnsDomainName );
if ( Utf8DnsDomainName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
Utf8DnsHostName = NetpAllocUtf8StrFromWStr( DnsHostName );
if ( Utf8DnsHostName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Enumerate the sites supported by this forest so we can delete
// the records that are named by site.
//
// We need to delete the records for all sites since we don't know which
// sites are "covered" by the removed DC.
//
Status = LsaIQuerySiteInfo( &SiteInformation );
if ( !NT_SUCCESS(Status) ) {
NlPrint(( NL_CRITICAL,
"NlDnsNtdsDsaDeletion: Cannot LsaIQuerySiteInfo 0x%lx\n", Status ));
NetStatus = NetpNtStatusToApiStatus( Status );
goto Cleanup;
}
//
// Loop through the list of names Netlogon understands deleting them all
//
for ( NameIndex = 0;
NameIndex < NL_DNS_NAME_TYPE_COUNT;
NameIndex++) {
LPSTR LocalDomainName;
GUID *LocalGuid;
//
// If the name is obsolete,
// ignore it.
//
if ( NlDcDnsNameTypeDesc[NameIndex].DsGetDcFlags == 0 ) {
continue;
}
//
// We don't know how to delete the A records since we don't know the IP address.
//
// We'd ask DNS what the IP address is, but the name might not exist. If it does
// we don't know whether the IP address has already been assign to another DC.
//
if ( NlDnsARecord( NameIndex ) ) {
continue;
}
//
// Use either the DomainName or ForestName
//
if ( NlDcDnsNameTypeDesc[NameIndex].IsForestRelative ) {
LocalDomainName = NlGlobalUtf8DnsForestName;
} else {
LocalDomainName = Utf8DnsDomainName;
}
//
// Figure out which GUID to use for this name.
//
if ( NlDnsCnameRecord( NameIndex ) ) {
//
// If we don't know the Dsa GUID,
// ignore names that need it.
//
if ( DsaGuid == NULL || IsEqualGUID( DsaGuid, &NlGlobalZeroGuid) ) {
continue;
}
LocalGuid = DsaGuid;
} else if ( NlDnsDcGuid( NameIndex )) {
//
// If we don't know the Domain GUID,
// ignore names that need it.
//
if ( DomainGuid == NULL || IsEqualGUID( DomainGuid, &NlGlobalZeroGuid) ) {
continue;
}
LocalGuid = DomainGuid;
} else {
LocalGuid = NULL;
}
//
// If the name isn't site specific,
// just delete the one name.
//
if ( !NlDcDnsNameTypeDesc[NameIndex].IsSiteSpecific ) {
NetStatus = NlDnsNtdsDsaDeleteOne( (NL_DNS_NAME_TYPE) NameIndex,
LocalGuid,
NULL, // No site name
LocalDomainName,
Utf8DnsHostName );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
//
// If the name is site specific,
// we need to delete the records for all sites since we don't know which
// sites are "covered" by the removed DC.
//
} else {
//
// Loop deleting entries for each Site
//
for ( i=0; i<SiteInformation->SiteCount; i++ ) {
NetStatus = NlDnsNtdsDsaDeleteOne( (NL_DNS_NAME_TYPE) NameIndex,
LocalGuid,
SiteInformation->Sites[i].SiteName.Buffer,
LocalDomainName,
Utf8DnsHostName );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
}
}
}
//
// Now that the entries are on the list,
// scavenge through the list and delete
// the entries (in a worker thread)
//
NlDnsForceScavenge( FALSE, // don't refresh domain records
FALSE ); // don't force re-register
NetStatus = NO_ERROR;
//
// Clean up locally used resources.
//
Cleanup:
if ( Utf8DnsDomainName != NULL ) {
NetpMemoryFree( Utf8DnsDomainName );
}
if ( Utf8DnsHostName != NULL ) {
NetpMemoryFree( Utf8DnsHostName );
}
if ( SiteInformation != NULL ) {
LsaIFree_LSAP_SITE_INFO( SiteInformation );
}
//
// Flush the log
//
NlDnsWriteLog();
return NetStatus;
}
NET_API_STATUS
NlDnsAddDomainRecordsWithSiteRefresh(
IN PDOMAIN_INFO DomainInfo,
IN PULONG Flags
)
/*++
Routine Description:
This routine refreshes site coverage for a particular domain and then
it adds all of the DNS names that are supposed to be registered for
that domain to the global list of all records. It marks for deregister
any name that should not be registered for that domain.
?? This routine should be called when ANY of the information changing the
registration changes. For instance, if the domain name changes, simply
call this routine.
Arguments:
DomainInfo - Domain the names are to be registered for.
Flags - Indicates the actions to take:
NL_DNS_FORCED_SCAVENGE - Register even if site coverage doesn't change
NL_DNS_FORCE_REREGISTER - Force re-registration of all previously
registered records
Return Value:
NO_ERROR: All names could be registered
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
NTSTATUS Status = STATUS_SUCCESS;
WCHAR CapturedSiteName[NL_MAX_DNS_LABEL_LENGTH+1];
PISM_CONNECTIVITY SiteConnect = NULL;
ULONG ThisSiteIndex = 0xFFFFFFFF;
PSOCKET_ADDRESS SocketAddresses = NULL;
ULONG SocketAddressCount = 0;
ULONG BufferSize;
PNL_SITE_ENTRY SiteEntry = NULL;
LPWSTR IpAddressList = NULL;
ULONG Index;
BOOLEAN SiteCoverageChanged = FALSE;
HANDLE DsHandle = NULL;
//
// This operation is meaningless on a workstation
//
if ( NlGlobalMemberWorkstation ) {
goto Cleanup;
}
//
// Capture the name of the site this machine is in.
//
if ( !NlCaptureSiteName( CapturedSiteName ) ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsAddDomainRecordsWithSiteRefresh: Cannot NlCaptureSiteName.\n" ));
goto Cleanup;
}
//
// If we are to automatically determine site coverage,
// get the site link costs.
//
if ( NlGlobalParameters.AutoSiteCoverage ) {
if ( !NlSitesGetIsmConnect(CapturedSiteName,
&SiteConnect,
&ThisSiteIndex) ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsAddDomainRecordsWithSiteRefresh: NlSitesGetIsmConnect failed\n" ));
}
}
//
// Ensure that ntdsapi.dll is loaded
//
Status = NlLoadNtDsApiDll();
if ( !NT_SUCCESS(Status) ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsAddDomainRecordsWithSiteRefresh: Cannot NlLoadNtDsApiDll 0x%lx.\n",
Status ));
DsHandle = NULL;
} else {
//
// Bind to the DS
//
NetStatus = (*NlGlobalpDsBindW)(
// L"localhost",
DomainInfo->DomUnicodeComputerNameString.Buffer,
NULL,
&DsHandle );
if ( NetStatus != NO_ERROR ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsAddDomainRecordsWithSiteRefresh: Cannot DsBindW %ld.\n",
NetStatus ));
DsHandle = NULL;
}
}
//
// Update the site coverage for each role we play in
// this forest/domain/NDNC
//
if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
NlSitesUpdateSiteCoverageForRole( DomainInfo,
DOM_FOREST,
DsHandle,
SiteConnect,
CapturedSiteName,
ThisSiteIndex,
&SiteCoverageChanged );
NlSitesUpdateSiteCoverageForRole( DomainInfo,
DOM_REAL_DOMAIN,
DsHandle,
SiteConnect,
CapturedSiteName,
ThisSiteIndex,
&SiteCoverageChanged );
}
if ( DomainInfo->DomFlags & DOM_NON_DOMAIN_NC ) {
NlSitesUpdateSiteCoverageForRole( DomainInfo,
DOM_NON_DOMAIN_NC,
DsHandle,
SiteConnect,
CapturedSiteName,
ThisSiteIndex,
&SiteCoverageChanged );
}
//
// If the site coverage changed or we are forced to refresh
// domain records even if site coverage hasn't changed,
// do so
//
if ( ((*Flags) & NL_DNS_FORCE_REFRESH_DOMAIN_RECORDS) != 0 ||
SiteCoverageChanged ) {
NetStatus = NlDnsAddDomainRecords( DomainInfo, *Flags );
if ( NetStatus != NO_ERROR ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"NlDnsAddDomainRecordsWithSiteRefresh: Cannot NlDnsAddDomainRecords 0x%lx.\n",
NetStatus ));
}
}
//
// Inform the user if none of our IP addresses maps to our site.
// Do it only once (for the primary domain processing).
// ?? When we support multihosting, our site will be different depending
// on the forest of a particular domain we host. So we will need to do
// this check for each site.
//
if ( DomainInfo->DomFlags & DOM_PRIMARY_DOMAIN ) {
SocketAddressCount = NlTransportGetIpAddresses(
0, // No special header,
FALSE, // Return pointers
&SocketAddresses,
&BufferSize );
for ( Index = 0; Index < SocketAddressCount; Index++ ) {
SiteEntry = NlFindSiteEntryBySockAddr( SocketAddresses[Index].lpSockaddr );
if ( SiteEntry != NULL ) {
if ( _wcsicmp(SiteEntry->SiteName, CapturedSiteName) == 0 ) {
break;
}
NlDerefSiteEntry( SiteEntry );
SiteEntry = NULL;
}
}
//
// Log the error
//
if ( SiteEntry == NULL && SocketAddressCount != 0 ) {
LPWSTR MsgStrings[2];
//
// Form the list of IP addresses for event log output
//
IpAddressList = LocalAlloc( LMEM_ZEROINIT,
SocketAddressCount * (NL_SOCK_ADDRESS_LENGTH+1) * sizeof(WCHAR) );
if ( IpAddressList == NULL ) {
goto Cleanup;
}
//
// Loop adding all addresses to the list
//
for ( Index = 0; Index < SocketAddressCount; Index++ ) {
WCHAR IpAddressString[NL_SOCK_ADDRESS_LENGTH+1] = {0};
NetStatus = NetpSockAddrToWStr(
SocketAddresses[Index].lpSockaddr,
SocketAddresses[Index].iSockaddrLength,
IpAddressString );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
//
// If this is not the first address on the list,
// separate addresses by space
//
if ( *IpAddressList != UNICODE_NULL ) {
wcscat( IpAddressList, L" " );
}
//
// Add this address to the list
//
wcscat( IpAddressList, IpAddressString );
}
//
// Now write the event
//
MsgStrings[0] = CapturedSiteName;
MsgStrings[1] = IpAddressList;
NlpWriteEventlog( NELOG_NetlogonNoAddressToSiteMapping,
EVENTLOG_WARNING_TYPE,
NULL,
0,
MsgStrings,
2 );
}
}
Cleanup:
if ( DsHandle != NULL ) {
(*NlGlobalpDsUnBindW)( &DsHandle );
}
if ( SiteConnect != NULL ) {
I_ISMFree( SiteConnect );
}
if ( SocketAddresses != NULL ) {
LocalFree( SocketAddresses );
}
if ( IpAddressList != NULL ) {
LocalFree( IpAddressList );
}
if ( SiteEntry != NULL ) {
NlDerefSiteEntry( SiteEntry );
}
return NO_ERROR;
}
NET_API_STATUS
NlDnsAddDomainRecords(
IN PDOMAIN_INFO DomainInfo,
IN ULONG Flags
)
/*++
Routine Description:
This routine adds all of the DNS names that are supposed to be
registered for a particular domain to the global list of all records.
It marks for deregister any name that should not be registered.
?? This routine should be called when ANY of the information changing the
registration changes. For instance, if the domain name changes, simply
call this routine.
Arguments:
DomainInfo - Domain the names are to be registered for.
Flags - Indicates the actions to take:
NL_DNS_FORCE_REREGISTER - Force re-registration of all previously
registered records
Return Value:
NO_ERROR: All names could be registered
--*/
{
NET_API_STATUS NetStatus;
NET_API_STATUS SaveNetStatus = NO_ERROR;
PNL_DNS_NAME NlDnsName = NULL;
PLIST_ENTRY ListEntry;
ULONG DomainFlags;
ULONG i;
ULONG SocketAddressCount;
PSOCKET_ADDRESS SocketAddresses = NULL;
ULONG BufferSize;
PNL_SITE_NAME_ARRAY DcSiteNames = NULL;
PNL_SITE_NAME_ARRAY GcSiteNames = NULL;
ULONG SiteIndex;
ULONG NameIndex;
//
// Get the list of IP Addresses for this machine.
//
SocketAddressCount = NlTransportGetIpAddresses(
0, // No special header,
FALSE, // Return pointers
&SocketAddresses,
&BufferSize );
//
// Loop marking all of the current entries for this domain.
//
EnterCriticalSection( &NlGlobalDnsCritSect );
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
//
// If entry is for this domain,
// mark it.
//
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
if ( NlDnsName->DomainInfo == DomainInfo ) {
NlDnsName->Flags |= NL_DNS_REGISTER_DOMAIN;
//
// If we are to force the re-registration of this record,
// mark the name as RegisterMe to force us to try again
//
if ( (Flags & NL_DNS_FORCE_RECORD_REREGISTER) != 0 ) {
if ( NlDnsName->State == Registered ) {
NlDnsSetState ( NlDnsName, RegisterMe );
}
}
}
}
//
// If the hosted domain still exists,
// register all of the appropriate names.
//
if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 ) {
//
// Determine which names should be registered now.
//
DomainFlags = NlGetDomainFlags( DomainInfo );
// Always register the DS names regardless of whether the DS is
// actually running.
//
// We actually think this will always be a no-op except during
// installation and when booted to use the registry version of SAM.
//
if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
DomainFlags |= DS_DS_FLAG;
}
//
// Get the list of Sites covered by this DC/NDNC
//
NetStatus = NlSitesGetCloseSites( DomainInfo,
DOM_REAL_DOMAIN | DOM_NON_DOMAIN_NC,
&DcSiteNames );
if ( NetStatus != NERR_Success ) {
NlPrintDom((NL_INIT, DomainInfo,
"Couldn't NlSitesGetCloseSites %ld 0x%lx.\n",
NetStatus, NetStatus ));
SaveNetStatus = NetStatus;
}
//
// Get the list of Sites covered by this GC
//
NetStatus = NlSitesGetCloseSites( DomainInfo,
DOM_FOREST,
&GcSiteNames );
if ( NetStatus != NERR_Success ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"Couldn't NlSitesGetCloseSites (GcAtSite) %ld 0x%lx.\n",
NetStatus, NetStatus ));
SaveNetStatus = NetStatus;
}
//
// Loop through each name seeing if it needs to be registered.
//
for ( NameIndex = 0;
NameIndex < NL_DNS_NAME_TYPE_COUNT;
NameIndex++) {
//
// If this DC is playing the role described by this name,
// register the name.
//
if ( DomainFlags & NlDcDnsNameTypeDesc[NameIndex].DsGetDcFlags ) {
BOOL SkipName = FALSE;
//
// Don't register this name if we are to avoid it
//
EnterCriticalSection( &NlGlobalParametersCritSect );
if ( NlGlobalParameters.DnsAvoidRegisterRecords != NULL ) {
LPTSTR_ARRAY TStrArray;
TStrArray = NlGlobalParameters.DnsAvoidRegisterRecords;
while ( !NetpIsTStrArrayEmpty(TStrArray) ) {
if ( _wcsicmp(TStrArray,
NlDcDnsNameTypeDesc[NameIndex].Name + NL_DNS_NAME_PREFIX_LENGTH) == 0 ) {
SkipName = TRUE;
break;
}
TStrArray = NetpNextTStrArrayEntry(TStrArray);
}
}
LeaveCriticalSection( &NlGlobalParametersCritSect );
if ( SkipName ) {
NlPrintDom(( NL_DNS, DomainInfo,
"NlDnsAddDomainRecords: Skipping name %ws (per registry)\n",
NlDcDnsNameTypeDesc[NameIndex].Name ));
continue;
}
//
// Do other checks since we have to support the old
// ways of disabling DNS registrations
//
// If the name registers an A record,
// register it for each IP address.
//
if ( NlDnsARecord( NameIndex) ) {
//
// If we aren't supposed to register A records,
// Just skip this name
//
if ( !NlGlobalParameters.RegisterDnsARecords ) {
continue;
}
//
// Register the domain name for each IP address of the machine.
//
for ( i=0; i<SocketAddressCount; i++ ) {
ULONG IpAddress;
//
// Require AF_INET for now
//
if ( SocketAddresses[i].lpSockaddr->sa_family != AF_INET ) {
continue;
}
IpAddress =
((PSOCKADDR_IN) SocketAddresses[i].lpSockaddr)->sin_addr.S_un.S_addr;
NetStatus = NlDnsAddName( DomainInfo,
(NL_DNS_NAME_TYPE)NameIndex,
NULL,
IpAddress );
if ( NetStatus != NERR_Success ) {
#if NETLOGONDBG
CHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1];
NetpIpAddressToStr( IpAddress, IpAddressString );
NlPrintDom((NL_CRITICAL, DomainInfo,
"Couldn't NlDnsAddName (%ws %s) %ld 0x%lx.\n",
NlDcDnsNameTypeDesc[NameIndex].Name,
IpAddressString,
NetStatus, NetStatus ));
#endif // NETLOGONDBG
SaveNetStatus = NetStatus;
}
}
//
// If the name isn't site specific,
// just register the single name.
//
} else if ( !NlDcDnsNameTypeDesc[NameIndex].IsSiteSpecific ) {
NetStatus = NlDnsAddName( DomainInfo,
(NL_DNS_NAME_TYPE)NameIndex,
NULL,
0 );
if ( NetStatus != NERR_Success ) {
NlPrintDom((NL_CRITICAL, DomainInfo,
"Couldn't NlDnsAddName (%ws) %ld 0x%lx.\n",
NlDcDnsNameTypeDesc[NameIndex].Name,
NetStatus, NetStatus ));
SaveNetStatus = NetStatus;
}
//
// If the name is site specific,
// register the name for each covered site.
//
} else {
PUNICODE_STRING SiteNames;
ULONG SiteCount;
//
// Use a different site coverage list depending on the role.
//
if ( NlDnsGcName( NameIndex) ) {
if ( GcSiteNames != NULL ) {
SiteNames = GcSiteNames->SiteNames;
SiteCount = GcSiteNames->EntryCount;
} else {
SiteNames = NULL;
SiteCount = 0;
}
//
// Use the domain/NDNC specific sites
//
} else {
// ???: Should KDCs have their own site coverage list?
if ( DcSiteNames != NULL ) {
SiteNames = DcSiteNames->SiteNames;
SiteCount = DcSiteNames->EntryCount;
} else {
SiteNames = NULL;
SiteCount = 0;
}
}
//
// Loop through the list of sites.
//
for ( SiteIndex=0; SiteIndex < SiteCount; SiteIndex ++) {
NetStatus = NlDnsAddName( DomainInfo,
(NL_DNS_NAME_TYPE)NameIndex,
SiteNames[SiteIndex].Buffer,
0 );
if ( NetStatus != NERR_Success ) {
NlPrintDom((NL_INIT, DomainInfo,
"Couldn't NlDnsAddName (%ws %ws) %ld 0x%lx.\n",
NlDcDnsNameTypeDesc[NameIndex].Name,
SiteNames[SiteIndex].Buffer,
NetStatus, NetStatus ));
SaveNetStatus = NetStatus;
}
}
}
}
}
}
//
// Do the second pass through records for this domain
// and process those which need our attention.
//
// * Any names that are still marked should be deleted.
// * If domain is being deleted, indicate that the record
// doesn't belong to any domain anymore.
//
for ( ListEntry = NlGlobalDnsList.Flink ;
ListEntry != &NlGlobalDnsList ;
ListEntry = ListEntry->Flink ) {
NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next );
//
// If the entry is marked for deletion,
// skip it
//
if ( NlDnsName->State == DeleteMe ) {
continue;
}
if ( NlDnsName->DomainInfo == DomainInfo ) {
//
// If entry is still marked, deregister it
//
if ( (NlDnsName->Flags & NL_DNS_REGISTER_DOMAIN) != 0 ) {
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsAddDomainRecords: marked for deregister" ));
NlDnsSetState( NlDnsName, DeregisterMe );
}
//
// If the domain is being deleted,
// ditch the dangling pointer to the domain info structure.
//
if ( DomainInfo->DomFlags & DOM_DELETED ) {
NlDnsName->DomainInfo = NULL;
}
}
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
if ( SocketAddresses != NULL ) {
LocalFree( SocketAddresses );
}
if ( DcSiteNames != NULL ) {
NetApiBufferFree( DcSiteNames );
}
if ( GcSiteNames != NULL ) {
NetApiBufferFree( GcSiteNames );
}
return SaveNetStatus;
}
NET_API_STATUS
NlDnsInitialize(
VOID
)
/*++
Routine Description:
Initialize the dynamic DNS code.
Read the list of registered DNS names from the binary log file. Put each entry in the
list of registered DNS names. The names will be marked as DelayedDeregister.
Such names will be marked for deleting if they aren't re-registered
during the Netlogon startup process.
Arguments:
None
Return Value:
None.
--*/
{
NET_API_STATUS NetStatus;
PLIST_ENTRY ListEntry;
PNL_DNS_NAME NlDnsName;
ULONG DnsRecordBufferSize;
PNL_DNSLOG_HEADER DnsRecordBuffer = NULL;
LPBYTE DnsRecordBufferEnd;
PNL_DNSLOG_ENTRY DnsLogEntry;
ULONG CurrentSize;
LPBYTE Where;
//
// Initialization
//
EnterCriticalSection( &NlGlobalDnsCritSect );
NlGlobalDnsStartTime = GetTickCount();
NlGlobalDnsInitialCleanupDone = FALSE;
NlGlobalDnsListDirty = FALSE;
NlGlobalDnsScavengeNeeded = FALSE;
NlGlobalDnsScavengingInProgress = FALSE;
NlGlobalDnsScavengeFlags = 0;
NlDnsInitCount ++;
//
// That's it for a workstation.
//
if ( NlGlobalMemberWorkstation ) {
NetStatus = NO_ERROR;
goto Cleanup;
}
NlInitializeWorkItem( &NlGlobalDnsScavengeWorkItem, NlDnsScavengeWorker, NULL );
//
// Set the DNS scavenger timer
//
NlQuerySystemTime( &NlGlobalDnsScavengerTimer.StartTime );
NlGlobalDnsScavengerTimer.Period = min( ORIG_DNS_SCAVENGE_PERIOD, NlGlobalParameters.DnsRefreshIntervalPeriod );
//
// Read the file into a buffer.
//
NetStatus = NlReadBinaryLog(
NL_DNS_BINARY_LOG_FILE,
FALSE, // Don't delete the file
(LPBYTE *) &DnsRecordBuffer,
&DnsRecordBufferSize );
if ( NetStatus != NO_ERROR ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: error reading binary log: %ld.\n",
NL_DNS_BINARY_LOG_FILE,
DnsRecordBufferSize ));
goto Cleanup;
}
//
// Validate the returned data.
//
if ( DnsRecordBufferSize < sizeof(NL_DNSLOG_HEADER) ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: %ws: size too small: %ld.\n",
NL_DNS_BINARY_LOG_FILE,
DnsRecordBufferSize ));
NetStatus = NO_ERROR;
goto Cleanup;
}
if ( DnsRecordBuffer->Version != NL_DNSLOG_VERSION ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: %ws: Version wrong: %ld.\n",
NL_DNS_BINARY_LOG_FILE,
DnsRecordBuffer->Version ));
NetStatus = NO_ERROR;
goto Cleanup;
}
//
// Loop through each log entry.
//
DnsRecordBufferEnd = ((LPBYTE)DnsRecordBuffer) + DnsRecordBufferSize;
DnsLogEntry = (PNL_DNSLOG_ENTRY)ROUND_UP_POINTER( (DnsRecordBuffer + 1), ALIGN_WORST );
while ( (LPBYTE)(DnsLogEntry+1) <= DnsRecordBufferEnd ) {
LPSTR DnsRecordName;
LPSTR DnsHostName;
LPBYTE DnsLogEntryEnd;
DnsLogEntryEnd = ((LPBYTE)DnsLogEntry) + DnsLogEntry->EntrySize;
//
// Ensure this entry is entirely within the allocated buffer.
//
if ( DnsLogEntryEnd > DnsRecordBufferEnd ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: Entry too big: %lx %lx.\n",
((LPBYTE)DnsLogEntry)-((LPBYTE)DnsRecordBuffer),
DnsLogEntry->EntrySize ));
break;
}
//
// Validate the entry
//
if ( !COUNT_IS_ALIGNED(DnsLogEntry->EntrySize, ALIGN_DWORD) ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: size not aligned %lx.\n",
DnsLogEntry->EntrySize ));
break;
}
if ( DnsLogEntry->NlDnsNameType < 0 ||
DnsLogEntry->NlDnsNameType >= NlDnsInvalid ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: Bogus DnsNameType: %lx.\n",
DnsLogEntry->NlDnsNameType ));
break;
}
if ( DnsLogEntry->Priority > 0xFFFF ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: Bogus priority: %lx.\n",
DnsLogEntry->Priority ));
break;
}
if ( DnsLogEntry->Weight > 0xFFFF ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: Bogus weight %lx.\n",
DnsLogEntry->Weight ));
break;
}
if ( DnsLogEntry->Port > 0xFFFF ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: Bogus port %lx.\n",
DnsLogEntry->Port ));
break;
}
//
// Grab the DnsRecordName from the entry.
//
Where = (LPBYTE) (DnsLogEntry+1);
if ( Where >= DnsLogEntryEnd ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: DnsRecordName missing: %lx\n",
((LPBYTE)DnsLogEntry)-((LPBYTE)DnsRecordBuffer) ));
break;
}
DnsRecordName = Where;
while ( *Where != '\0' && Where < DnsLogEntryEnd ) {
Where ++;
}
if ( Where >= DnsLogEntryEnd ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: DnsRecordName has no trailing 0: %lx\n",
((LPBYTE)DnsLogEntry)-((LPBYTE)DnsRecordBuffer) ));
break;
}
Where ++;
//
// Validate the record name is syntactically valid
//
NetStatus = DnsValidateName_UTF8( DnsRecordName, DnsNameDomain );
if ( NetStatus != ERROR_SUCCESS && NetStatus != DNS_ERROR_NON_RFC_NAME ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: Bad DNS record name encountered: %s\n",
DnsRecordName ));
break;
}
//
// Grab the DnsHostName from the entry.
//
if ( !NlDnsARecord( DnsLogEntry->NlDnsNameType ) ) {
if ( Where >= DnsLogEntryEnd ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: DnsHostName missing: %lx\n",
((LPBYTE)DnsLogEntry)-((LPBYTE)DnsRecordBuffer) ));
break;
}
DnsHostName = Where;
while ( *Where != '\0' && Where < DnsLogEntryEnd ) {
Where ++;
}
if ( Where >= DnsLogEntryEnd ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: DnsHostName has no trailing 0: %lx\n",
((LPBYTE)DnsLogEntry)-((LPBYTE)DnsRecordBuffer) ));
break;
}
Where ++;
} else {
DnsHostName = NULL;
}
//
// Validate the host name is syntactically valid
//
if ( DnsHostName != NULL ) {
NetStatus = DnsValidateName_UTF8( DnsHostName, DnsNameHostnameFull );
if ( NetStatus != ERROR_SUCCESS && NetStatus != DNS_ERROR_NON_RFC_NAME ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: Bad DNS host name encountered: %s\n",
DnsHostName ));
break;
}
}
//
// Allocate the entry and mark it as DelayedDeregister.
//
NlDnsName = NlDnsAllocateEntry(
DnsLogEntry->NlDnsNameType,
DnsRecordName,
DnsLogEntry->Priority,
DnsLogEntry->Weight,
DnsLogEntry->Port,
DnsHostName,
DnsLogEntry->IpAddress,
DelayedDeregister );
if ( NlDnsName == NULL ) {
NlPrint(( NL_CRITICAL,
"NlDnsInitialize: %s: Cannot allocate DnsName structure %lx\n",
((LPBYTE)DnsLogEntry)-((LPBYTE)DnsRecordBuffer) ));
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// This name has been registered once or it wouldn't be here.
//
NlDnsName->Flags |= NL_DNS_REGISTERED_ONCE;
NlPrintDns(( NL_DNS, NlDnsName,
"NlDnsInitialize: Previously registered name noticed" ));
//
// Move to the next entry.
//
DnsLogEntry = (PNL_DNSLOG_ENTRY)(((LPBYTE)DnsLogEntry) + DnsLogEntry->EntrySize);
}
NetStatus = NO_ERROR;
//
// Be tidy.
//
Cleanup:
if ( DnsRecordBuffer != NULL ) {
LocalFree( DnsRecordBuffer );
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
return NetStatus;
}
VOID
NlDnsShutdown(
VOID
)
/*++
Routine Description:
Cleanup DNS names upon shutdown.
Arguments:
None.
Return Value:
None
--*/
{
NET_API_STATUS NetStatus;
PNL_DNS_NAME NlDnsName = NULL;
PLIST_ENTRY ListEntry;
EnterCriticalSection( &NlGlobalDnsCritSect );
//
// Deregister records on shutdown as needed.
// Do the work in this thread as we are shutting down.
//
NlGlobalDnsScavengeNeeded = TRUE;
//
// Clear all options,
// just deregister records that are already on the list as appropriate
//
NlGlobalDnsScavengeFlags = 0;
NlDnsScavengeWorker( NULL );
//
// Loop deleting all the entries.
//
while ( !IsListEmpty( &NlGlobalDnsList ) ) {
NlDnsName = CONTAINING_RECORD( NlGlobalDnsList.Flink, NL_DNS_NAME, Next );
RemoveEntryList( &NlDnsName->Next );
LocalFree( NlDnsName );
}
LeaveCriticalSection( &NlGlobalDnsCritSect );
return;
}
NET_API_STATUS
NlSetDnsForestName(
IN PUNICODE_STRING DnsForestName OPTIONAL,
OUT PBOOLEAN DnsForestNameChanged OPTIONAL
)
/*++
Routine Description:
Set the DNS tree name in the appropriate globals.
Arguments:
DnsForestName: of the tree this machine is in.
DnsForestNameChanged: Returns TRUE if the tree name changed.
Return Value:
NO_ERROR - String was saved successfully.
--*/
{
NET_API_STATUS NetStatus;
ULONG DnsForestNameLength;
LPWSTR LocalUnicodeDnsForestName = NULL;
ULONG LocalUnicodeDnsForestNameLen = 0;
LPSTR LocalUtf8DnsForestName = NULL;
BOOLEAN LocalDnsForestNameChanged = FALSE;
//
// If a tree name is specified,
// allocate buffers for them.
//
EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
if ( DnsForestName != NULL && DnsForestName->Length != 0 ) {
//
// If the tree name hasn't changed,
// avoid setting it.
//
if ( NlGlobalUnicodeDnsForestNameString.Length != 0 ) {
if ( NlEqualDnsNameU( &NlGlobalUnicodeDnsForestNameString, DnsForestName ) ) {
NetStatus = NO_ERROR;
goto Cleanup;
}
}
NlPrint(( NL_DNS,
"Set DnsForestName to: %wZ\n",
DnsForestName ));
//
// Save the . terminated Unicode version of the string.
//
LocalUnicodeDnsForestNameLen = DnsForestName->Length / sizeof(WCHAR);
if ( LocalUnicodeDnsForestNameLen > NL_MAX_DNS_LENGTH ) {
NetStatus = ERROR_INVALID_NAME;
goto Cleanup;
}
LocalUnicodeDnsForestName = NetpMemoryAllocate( (LocalUnicodeDnsForestNameLen+2) * sizeof(WCHAR));
if ( LocalUnicodeDnsForestName == NULL) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
RtlCopyMemory( LocalUnicodeDnsForestName,
DnsForestName->Buffer,
LocalUnicodeDnsForestNameLen*sizeof(WCHAR) );
if ( LocalUnicodeDnsForestName[LocalUnicodeDnsForestNameLen-1] != L'.' ) {
LocalUnicodeDnsForestName[LocalUnicodeDnsForestNameLen++] = L'.';
}
LocalUnicodeDnsForestName[LocalUnicodeDnsForestNameLen] = L'\0';
//
// Convert it to zero terminated UTF-8
//
LocalUtf8DnsForestName = NetpAllocUtf8StrFromWStr( LocalUnicodeDnsForestName );
if (LocalUtf8DnsForestName == NULL) {
NetpMemoryFree( LocalUnicodeDnsForestName );
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
if ( strlen(LocalUtf8DnsForestName) > NL_MAX_DNS_LENGTH ) {
NetpMemoryFree( LocalUnicodeDnsForestName );
NetpMemoryFree( LocalUtf8DnsForestName );
NetStatus = ERROR_INVALID_NAME;
goto Cleanup;
}
//
// Indicate the the name has changed.
//
LocalDnsForestNameChanged = TRUE;
}
//
// Free any existing global tree name.
//
if ( NlGlobalUnicodeDnsForestName != NULL ) {
NetApiBufferFree( NlGlobalUnicodeDnsForestName );
}
if ( NlGlobalUtf8DnsForestName != NULL ) {
NetpMemoryFree( NlGlobalUtf8DnsForestName );
}
//
// Save the new names in the globals.
//
NlGlobalUnicodeDnsForestName = LocalUnicodeDnsForestName;
NlGlobalUnicodeDnsForestNameLen = LocalUnicodeDnsForestNameLen;
NlGlobalUnicodeDnsForestNameString.Buffer = LocalUnicodeDnsForestName;
NlGlobalUnicodeDnsForestNameString.Length = (USHORT)(LocalUnicodeDnsForestNameLen*sizeof(WCHAR));
NlGlobalUnicodeDnsForestNameString.MaximumLength = (USHORT)((LocalUnicodeDnsForestNameLen+1)*sizeof(WCHAR));
NlGlobalUtf8DnsForestName = LocalUtf8DnsForestName;
NetStatus = NO_ERROR;
Cleanup:
LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
//
// If the name changed,
// recompute the DOM_FOREST_ROOT bit on all domains.
//
if ( LocalDnsForestNameChanged ) {
(VOID) NlEnumerateDomains( FALSE, NlSetDomainForestRoot, NULL );
}
if ( ARGUMENT_PRESENT( DnsForestNameChanged) ) {
*DnsForestNameChanged = LocalDnsForestNameChanged;
}
return NetStatus;
}
VOID
NlCaptureDnsForestName(
OUT WCHAR DnsForestName[NL_MAX_DNS_LENGTH+1]
)
/*++
Routine Description:
Captures a copy of the DnsForestName for this machine.
Arguments:
DnsForestName - Returns the DNS name of the tree this machine is in.
If there is none, an empty string is returned.
Return Value:
None.
--*/
{
EnterCriticalSection(&NlGlobalDnsForestNameCritSect);
if ( NlGlobalUnicodeDnsForestName == NULL ) {
*DnsForestName = L'\0';
} else {
wcscpy( DnsForestName, NlGlobalUnicodeDnsForestName );
}
LeaveCriticalSection(&NlGlobalDnsForestNameCritSect);
return;
}