/*++ 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 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 // // 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. } 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; // // Reference count // ULONG NlDnsNameRefCount; // // 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; // // Context describing a series of DNS registrations or deregistrations // typedef struct _NL_DNS_CONTEXT { ULONG StartTime; #define NL_DNS_THRESHOLD (30*1000) // 30 seconds } NL_DNS_CONTEXT, *PNL_DNS_CONTEXT; // // 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. // VOID NlDnsScavengeOne( IN PNL_DNS_CONTEXT NlDnsContext, IN PNL_DNS_NAME NlDnsName ); // // 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; } #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 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 NlDnsDereferenceEntry( PNL_DNS_NAME NlDnsName ) /*++ Routine Description: Dereference a DNS name entry on the global list of DNS names Arguments: NlDnsName - The DNS name entry Return Value: None --*/ { EnterCriticalSection( &NlGlobalDnsCritSect ); NlAssert( NlDnsName->NlDnsNameRefCount > 0 ); NlDnsName->NlDnsNameRefCount --; // // If the ref count reaches zero, // delink this entry and free it. // if ( NlDnsName->NlDnsNameRefCount == 0 ) { NlPrintDns(( NL_DNS, NlDnsName, "NlDnsDereferenceEntry: Deleting DNS name" )); NlAssert( NlDnsName->State == DeleteMe ); RemoveEntryList( &NlDnsName->Next ); LocalFree( NlDnsName ); } LeaveCriticalSection( &NlGlobalDnsCritSect ); } VOID NlDnsSetState( PNL_DNS_NAME NlDnsName, NL_DNS_NAME_STATE State ) /*++ Routine Description: Set the state of the entry. If the state is DeleteMe, NlDnsName must have at least 2 references: one for being on the global list and one reference by the caller. For the deleted state, this routine will dereference the name removing the reference for being on the global list. When the caller derefereces this entry after the return from this routine and removes the caller's reference, this entry will be delinked and freed if ref count reaches 0. 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; // // If this entry is to be deleted, // derefernce it // } else if ( NlDnsName->State == DeleteMe ) { // // There must be at least 2 references // NlAssert( NlDnsName->NlDnsNameRefCount > 1 ); NlDnsDereferenceEntry( NlDnsName ); } } 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; } // // There is a reference for being on the global list // NlDnsName->NlDnsNameRefCount = 1; 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; DNS_RECORD DnsRecord; LPWSTR MsgStrings[3]; ULONG DnsUpdateFlags = DNS_UPDATE_SECURITY_USE_DEFAULT; DNS_FAILED_UPDATE_INFO DnsFailedUpdateInfo; WCHAR DnsServerIpAddressString[NL_IP_ADDRESS_LENGTH+1]; 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_NetlogonNoDynamicDns, EVENTLOG_WARNING_TYPE, (LPBYTE) &NetStatus, sizeof(NetStatus), 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; } // // 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, // All DNS servers NULL ); // reserved } else { NetStatus = DnsModifyRecordsInSet_UTF8( &DnsRecord, // Add record NULL, // No delete records DnsUpdateFlags, NULL, // No context handle NULL, // All DNS servers NULL ); // reserved } } else { NetStatus = DnsModifyRecordsInSet_UTF8( NULL, // No add records &DnsRecord, // Delete this record DnsUpdateFlags, NULL, // No context handle NULL, // All DNS servers NULL ); // reserved } // // 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 ) { NlDnsName->NlDnsNameLastStatus = NetStatus; NlpWriteEventlog( NELOG_NetlogonDynamicDnsServerFailure, EVENTLOG_WARNING_TYPE, (LPBYTE) &NetStatus, sizeof(NetStatus), NULL, 0 ); } 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; RtlZeroMemory( &DnsFailedUpdateInfo, sizeof(DnsFailedUpdateInfo) ); RtlZeroMemory( DnsServerIpAddressString, sizeof(DnsServerIpAddressString) ); DnsGetLastFailedUpdateInfo( &DnsFailedUpdateInfo ); if ( DnsFailedUpdateInfo.Ip4Address != 0 ) { NetpIpAddressToWStr( DnsFailedUpdateInfo.Ip4Address, DnsServerIpAddressString ); } else { wcsncpy( DnsServerIpAddressString, L"", sizeof(DnsServerIpAddressString)/sizeof(WCHAR) - 1 ); } MsgStrings[0] = DnsServerIpAddressString; // Old server that doesn't understand dynamic DNS if ( NetStatus == DNS_ERROR_RCODE_NOT_IMPLEMENTED ) { NlpWriteEventlog( NELOG_NetlogonNoDynamicDns, EVENTLOG_WARNING_TYPE, (LPBYTE) &DnsFailedUpdateInfo.Rcode, sizeof(DnsFailedUpdateInfo.Rcode), MsgStrings, 1 ); NetStatus = ERROR_DYNAMIC_DNS_NOT_SUPPORTED; // All other errors } else { MsgStrings[1] = NlDnsNameToWStr( NlDnsName ); if ( MsgStrings[1] != NULL ) { MsgStrings[2] = (LPWSTR) UlongToPtr( NetStatus ); NlpWriteEventlog( Register ? NELOG_NetlogonDynamicDnsRegisterFailure : NELOG_NetlogonDynamicDnsDeregisterFailure, EVENTLOG_ERROR_TYPE, (LPBYTE) &DnsFailedUpdateInfo.Rcode, sizeof(DnsFailedUpdateInfo.Rcode), MsgStrings, 3 | NETP_LAST_MESSAGE_IS_NETSTATUS ); NetApiBufferFree( MsgStrings[1] ); } } break; } // // Compute when we want to try this name again. // Cleanup: 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; } // // If this period is shorter than that of the scavenger timer, // reset the scavenger timer. // EnterCriticalSection( &NlGlobalScavengerCritSect ); if ( NlDnsName->ScavengeTimer.Period < NlGlobalDnsScavengerTimer.Period ) { NlGlobalDnsScavengerTimer = NlDnsName->ScavengeTimer; NlPrint(( NL_DNS_MORE, "NlDnsUpdate: Set DNS scavenger to run in %ld minutes (%ld).\n", (NlGlobalDnsScavengerTimer.Period+59999)/60000, NlGlobalDnsScavengerTimer.Period )); if ( !SetEvent( NlGlobalTimerEvent ) ) { NlPrint(( NL_CRITICAL, "NlDnsUpdate: SetEvent failed %ld\n", GetLastError() )); } } LeaveCriticalSection( &NlGlobalScavengerCritSect ); return NetStatus; } NET_API_STATUS NlDnsRegisterOne( IN PNL_DNS_CONTEXT NlDnsContext, IN PNL_DNS_NAME NlDnsName ) /*++ Routine Description: This routine registers a SRV record for a particular name with DNS. Name must be referenced by the caller Arguments: NlDnsContext - Context describing a series of registrations or deregistration NlDnsName - Structure describing name to register. Return Value: NO_ERROR: The name was registered --*/ { NET_API_STATUS NetStatus; // // If Netlogon is shutting down, // or Netlogon is starting and the name was added before anyway, // and we've already spent a long time doing this batch of DNS records, // just give up. // if ( (NlGlobalTerminate || (NlGlobalChangeLogNetlogonState == NetlogonStarting && (NlDnsName->Flags & NL_DNS_REGISTERED_ONCE) != 0 ) ) && NetpDcElapsedTime(NlDnsContext->StartTime) > NL_DNS_THRESHOLD ) { NlPrintDns(( NL_DNS, NlDnsName, "NlDnsRegisterOne: DNS really slow during startup. (Registration skipped) %ld", NetpDcElapsedTime(NlDnsContext->StartTime) )); return NO_ERROR; } // // Register the name with DNS // NetStatus = NlDnsUpdate( NlDnsName, TRUE ); if ( NetStatus == NO_ERROR ) { // // Mark that the name is really registered. // NlDnsSetState( NlDnsName, 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 NlDnsRegisterName( IN PNL_DNS_CONTEXT NlDnsContext, IN PDOMAIN_INFO DomainInfo, IN NL_DNS_NAME_TYPE NlDnsNameType, IN LPWSTR SiteName, IN ULONG IpAddress ) /*++ Routine Description: This routine registers a particular name with DNS. Arguments: NlDnsContext - Context describing a series of registrations or deregistration 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, "NlDnsRegisterName: %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, "NlDnsRegisterName: %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. // EnterCriticalSection( &NlGlobalDnsCritSect ); for ( ListEntry = NlGlobalDnsList.Flink ; ListEntry != &NlGlobalDnsList ; ) { NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next ); // // If this entry is marked for deletion, // skip it // if ( NlDnsName->State == DeleteMe ) { ListEntry = ListEntry->Flink; continue; } // // Increase the ref count because we // potentially mark this entry for deletion below // NlDnsName->NlDnsNameRefCount ++; // // 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, "NlDnsRegisterName: 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, "NlDnsRegisterName: Priority not equal. %ld %ld", NlDnsName->Priority, Priority )); } else if ( NlDnsName->Port != Port ) { Identical = FALSE; NlPrintDns(( NL_DNS_MORE, NlDnsName, "NlDnsRegisterName: Port not equal. %ld %ld", NlDnsName->Port, Port )); } else if ( NlDnsName->Weight != Weight ) { Identical = FALSE; NlPrintDns(( NL_DNS_MORE, NlDnsName, "NlDnsRegisterName: Weight not equal. %ld %ld", NlDnsName->Weight, Weight )); } else if ( !NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsHostName, NlDnsName->DnsHostName )) { Identical = FALSE; NlPrintDns(( NL_DNS_MORE, NlDnsName, "NlDnsRegisterName: 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, "NlDnsRegisterName: 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, "NlDnsRegisterName: 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 NlDnsRegisterDomain 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, "NlDnsRegisterName: Similar entry found and marked for deregistration" )); } } // // Grab the pointer to the next entry // before dereferencing this entry as we // may delink this entry upon dereferencing // ListEntry = ListEntry->Flink; NlDnsDereferenceEntry( NlDnsName ); } // // If the name was found, // use it. if ( FoundNlDnsName != NULL ) { NlDnsName = FoundNlDnsName; // // 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 )); LeaveCriticalSection( &NlGlobalDnsCritSect ); return ERROR_NOT_ENOUGH_MEMORY; } NlDnsName->DomainInfo = DomainInfo; NlPrintDns(( NL_DNS, NlDnsName, "NlDnsRegister: registering name" )); } // // Increase the ref count so that this entry // doesn't get deleted behind our back // NlDnsName->NlDnsNameRefCount ++; // // Scavenge now to attempt to register the name. // // Avoid having crit sect locked while doing network IO // LeaveCriticalSection( &NlGlobalDnsCritSect ); NlDnsScavengeOne( NlDnsContext, NlDnsName ); // // Now it's safe to remove our reference // NlDnsDereferenceEntry( NlDnsName ); } return NO_ERROR; } NET_API_STATUS NlDnsDeregisterOne( IN PNL_DNS_CONTEXT NlDnsContext, IN PNL_DNS_NAME NlDnsName ) /*++ Routine Description: This routine deregisters a the SRV record for a particular name with DNS. Name must be referenced by the caller Arguments: NlDnsContext - Context describing a series of registrations or deregistration NlDnsName - Structure describing name to deregister. Return Value: NO_ERROR: The name was deregistered Otherwise, the name was not deregistered. The operation should be retried. --*/ { NET_API_STATUS NetStatus; BOOL EntryDeleted = FALSE; // // If Netlogon is shutting down or starting, // and we've already spent a long time doing this batch of DNS records, // just give up. // if ( (NlGlobalTerminate || NlGlobalChangeLogNetlogonState == NetlogonStarting ) && NetpDcElapsedTime(NlDnsContext->StartTime) > NL_DNS_THRESHOLD ) { NlPrintDns(( NL_DNS, NlDnsName, "NlDnsDeregisterOne: DNS really slow during shutdown. (Deregistration skipped) %ld", NetpDcElapsedTime(NlDnsContext->StartTime) )); return ERROR_DNS_NOT_AVAILABLE; } // // 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 )); NlDnsSetState( NlDnsName, DeleteMe ); EntryDeleted = TRUE; 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)" )); NlDnsSetState( NlDnsName, DeleteMe ); EntryDeleted = TRUE; 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 ( !EntryDeleted ) { 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" )); NlDnsSetState( NlDnsName, DeleteMe ); EntryDeleted = TRUE; NetStatus = NO_ERROR; } } LeaveCriticalSection( &NlGlobalDnsCritSect ); return NetStatus; } VOID NlDnsScavengeOne( IN PNL_DNS_CONTEXT NlDnsContext, IN PNL_DNS_NAME NlDnsName ) /*++ Routine Description: Register or Deregister any DNS names that need it. Enter with NlDnsName referenced at least twice. If this name gets deregistered successfully it will be marked for deletion and dereferenced. The caller will dereference the name, too, and if the ref count reaches 0 the entry will be delinked and freed by the caller. Thus, if there is less than 2 references on input, we may delink and free the entry in this routine resulting in AV if the caller attempts to use the freed name upon return from this routine. Arguments: NlDnsContext - Context describing a series of registrations or deregistration NlDnsName - Name to scavenge. This structure will be marked for deletion if it is no longer needed. Return Value: None. --*/ { NET_API_STATUS NetStatus; BOOLEAN EntryNeeded = FALSE; LARGE_INTEGER TimeNow; ULONG Timeout; // // There must be at least 2 references on this entry: // one for beign on the global list and one reference // by the caller. // NlAssert( NlDnsName->NlDnsNameRefCount > 1 ); // // 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( NlDnsContext, NlDnsName ); break; // // If the name needs to be registered, // do it now. // case RegisterMe: case Registered: NlDnsRegisterOne( NlDnsContext, NlDnsName ); break; } } } VOID NlDnsScavenge( IN LPVOID ScavengerParam ) /*++ Routine Description: Register or Deregister any DNS names that need it. Arguments: None Return Value: None. --*/ { PLIST_ENTRY ListEntry; PNL_DNS_NAME NlDnsName; LARGE_INTEGER TimeNow; ULONG Timeout; NL_DNS_CONTEXT NlDnsContext; ULONG Flags = NL_DNSPNP_SITECOV_CHANGE_ONLY; // // The DNS scavenger has good backoff characteristics so // take this opportunity to ensure the DomainName<1B> names are // properly registered. // (VOID) NlEnumerateDomains( FALSE, NlBrowserFixAllNames, NULL ); // // Initialization // NlPrint(( NL_DNS, "NlDnsScavenge: Starting scavenge of DNS names.\n" )); // // 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(); // // Fix up the registrations if the site coverage changed // // Avoid having crit sect locked while doing network IO // LeaveCriticalSection( &NlGlobalDnsCritSect ); NlEnumerateDomains( TRUE, NlDnsRegisterDomainOnPnp, &Flags ); EnterCriticalSection( &NlGlobalDnsCritSect ); // // Loop through the list of DNS names finding any that need attention // NlDnsContext.StartTime = GetTickCount(); for ( ListEntry = NlGlobalDnsList.Flink ; ListEntry != &NlGlobalDnsList ; ) { NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next ); if ( NlGlobalTerminate ) { break; } // // If this entry is marked for deletion, // skip it // if ( NlDnsName->State == DeleteMe ) { ListEntry = ListEntry->Flink; continue; } // // Increase the ref count so that this entry // doesn't get deleted behind our back // NlDnsName->NlDnsNameRefCount ++; // // Scavenge this entry // // Avoid having crit sect locked while doing network IO // LeaveCriticalSection( &NlGlobalDnsCritSect ); NlDnsScavengeOne( &NlDnsContext, NlDnsName ); EnterCriticalSection( &NlGlobalDnsCritSect ); // // Grab the pointer to the next entry // before dereferencing this entry as we // may delink this entry upon dereferencing // ListEntry = ListEntry->Flink; NlDnsDereferenceEntry( NlDnsName ); } // // In all cases, // flush any changes to disk. // NlDnsWriteLog(); // // Determine when we should scavenge next // Timeout = (DWORD) -1; NlQuerySystemTime( &TimeNow ); 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; } if ( TimerExpired( &NlDnsName->ScavengeTimer, &TimeNow, &Timeout)) { Timeout = 0; break; } } EnterCriticalSection( &NlGlobalScavengerCritSect ); NlGlobalDnsScavengerTimer.StartTime.QuadPart = TimeNow.QuadPart; NlGlobalDnsScavengerTimer.Period = Timeout; NlPrint(( NL_DNS_MORE, "NlDnsScavenge: Set DNS scavenger to run in %ld minutes (%ld).\n", (NlGlobalDnsScavengerTimer.Period+59999)/60000, NlGlobalDnsScavengerTimer.Period )); // 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. NlGlobalDnsScavengerTimer.Period += 2000; // // Indicate that we're done running. // NlGlobalDnsScavengerIsRunning = FALSE; LeaveCriticalSection( &NlGlobalScavengerCritSect ); LeaveCriticalSection( &NlGlobalDnsCritSect ); UNREFERENCED_PARAMETER( ScavengerParam ); } VOID NlDnsPnp( BOOL TransportChanged ) /*++ Routine Description: When each new IP transport is PNP'ed, this routine attempts to register all the DNS names again. Arguments: TransportChanged - TRUE if transport was changed. FALSE if names have change. Return Value: None. --*/ { PLIST_ENTRY ListEntry; PNL_DNS_NAME NlDnsName; DWORD DomFlags = 0; // // Nothing to register on a workstation // if ( NlGlobalMemberWorkstation ) { return; } // // Mark every DNS name for re-registration. // // The behavior of dynamic DNS is unclear when there are multiple // adapters, each with its own DNS server. If each DNS server supports // dynamic DNS for the names we register, do the names actually get // registered on both DNS server. I'm assuming YES. In that case, // when a new transport comes online, I re-register all names to allow // the registration to occur on the new transport. // // ?? If later the transport is removed, I'll have no opportunity to remove // the name. Even worse, if I try to remove the name, I'll succeed because // I'll remove the name from the transport that's still on-line. It seems // that dynamic DNS needs some conceptual work done here... // if ( TransportChanged ) { NlPrint(( NL_DNS, "NlDnsPnp: Mark all DNS names for re-registration.\n" )); DomFlags = DOM_DNSPNPREREG_UPDATE_NEEDED; } else { DomFlags = DOM_DNSPNP_UPDATE_NEEDED; } // // 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(); // // Do the updates for each domain/forest/NDNC in the domain threads // NlEnumerateDomains( TRUE, NlStartDomainThread, &DomFlags ); return; } NET_API_STATUS NlDnsNtdsDsaDeleteOne( IN PNL_DNS_CONTEXT NlDnsContext, 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 deletes a single DNS record. Arguments: NlDnsContext - Context describing a series of registrations or deregistration 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" )); // // Increase the ref count so that this entry // doesn't get deleted behind our back // NlDnsName->NlDnsNameRefCount ++; // // Try once to delete it now. // // Avoid having crit sect locked while doing network IO // LeaveCriticalSection( &NlGlobalDnsCritSect ); NlDnsScavengeOne( NlDnsContext, NlDnsName ); EnterCriticalSection( &NlGlobalDnsCritSect ); NlDnsDereferenceEntry( NlDnsName ); // // 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" )); // // Increase the ref count so that this entry // doesn't get deleted behind our back // NlDnsName->NlDnsNameRefCount ++; // // Try once to delete it now. // // Avoid having crit sect locked while doing network IO // LeaveCriticalSection( &NlGlobalDnsCritSect ); NlDnsScavengeOne( NlDnsContext, NlDnsName ); EnterCriticalSection( &NlGlobalDnsCritSect ); NlDnsDereferenceEntry( NlDnsName ); } 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; NL_DNS_CONTEXT NlDnsContext; ULONG i; ULONG NameIndex; NlDnsContext.StartTime = GetTickCount(); // // 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( &NlDnsContext, (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; iSiteCount; i++ ) { NetStatus = NlDnsNtdsDsaDeleteOne( &NlDnsContext, (NL_DNS_NAME_TYPE) NameIndex, LocalGuid, SiteInformation->Sites[i].SiteName.Buffer, LocalDomainName, Utf8DnsHostName ); if ( NetStatus != NO_ERROR ) { goto Cleanup; } } } } 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 NlDnsRegisterDomainOnPnp( IN PDOMAIN_INFO DomainInfo, IN PULONG Flags ) /*++ Routine Description: This routine registers all of the DNS names that are supposed to be registered for a particular domain. It deregisters 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_DNSPNP_SITECOV_CHANGE_ONLY - Register only if site coverage changes NL_DNSPNP_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 SiteCount = 0; 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, "NlDnsRegisterDomainOnPnp: 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 ) ) { SiteCount = SiteConnect->cNumSites; } } // // Ensure that ntdsapi.dll is loaded // Status = NlLoadNtDsApiDll(); if ( !NT_SUCCESS(Status) ) { NlPrintDom(( NL_CRITICAL, DomainInfo, "NlDnsRegisterDomainOnPnp: 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, "NlDnsRegisterDomainOnPnp: 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 ); } // // Finally register all records for this domain // if ( ((*Flags) & NL_DNSPNP_SITECOV_CHANGE_ONLY) == 0 || SiteCoverageChanged ) { NetStatus = NlDnsRegisterDomain( DomainInfo, *Flags ); if ( NetStatus != NO_ERROR ) { NlPrintDom(( NL_CRITICAL, DomainInfo, "NlDnsRegisterDomainOnPnp: Cannot NlDnsRegisterDomain 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 NlDnsRegisterDomain( IN PDOMAIN_INFO DomainInfo, IN ULONG Flags ) /*++ Routine Description: This routine registers all of the DNS names that are supposed to be registered for a particular domain. It deregisters 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_DNSPNP_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; NL_DNS_CONTEXT NlDnsContext; PLIST_ENTRY ListEntry; ULONG DomainFlags; ULONG i; ULONG SocketAddressCount; PSOCKET_ADDRESS SocketAddresses = NULL; ULONG BufferSize; /* PUNICODE_STRING DcSiteNames = NULL; ULONG DcSiteCount; */ PNL_SITE_NAME_ARRAY DcSiteNames = NULL; /* PUNICODE_STRING GcSiteNames = NULL; ULONG GcSiteCount; */ PNL_SITE_NAME_ARRAY GcSiteNames = NULL; ULONG SiteIndex; ULONG NameIndex; EnterCriticalSection( &DomainInfo->DomDnsRegisterCritSect ); // // Get the list of IP Addresses for this machine. // NlDnsContext.StartTime = GetTickCount(); 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_DNSPNP_FORCE_REREGISTER) != 0 ) { if ( NlDnsName->State == Registered ) { NlDnsSetState ( NlDnsName, RegisterMe ); } } } } LeaveCriticalSection( &NlGlobalDnsCritSect ); // // 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_INIT, 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_INIT, DomainInfo, "NlDnsRegisterDomain: 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; isa_family != AF_INET ) { continue; } IpAddress = ((PSOCKADDR_IN) SocketAddresses[i].lpSockaddr)->sin_addr.S_un.S_addr; NetStatus = NlDnsRegisterName( &NlDnsContext, 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_INIT, DomainInfo, "Couldn't NlDnsRegisterName (%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 = NlDnsRegisterName( &NlDnsContext, DomainInfo, (NL_DNS_NAME_TYPE)NameIndex, NULL, 0 ); if ( NetStatus != NERR_Success ) { NlPrintDom((NL_INIT, DomainInfo, "Couldn't NlDnsRegisterName (%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 = NlDnsRegisterName( &NlDnsContext, DomainInfo, (NL_DNS_NAME_TYPE)NameIndex, SiteNames[SiteIndex].Buffer, 0 ); if ( NetStatus != NERR_Success ) { NlPrintDom((NL_INIT, DomainInfo, "Couldn't NlDnsRegisterName (%ws %ws) %ld 0x%lx.\n", NlDcDnsNameTypeDesc[NameIndex].Name, SiteNames[SiteIndex].Buffer, NetStatus, NetStatus )); SaveNetStatus = NetStatus; } } } } } } // // Any names that are still marked should be deleted. // // However, do not deregister on shutdown (when DOM_DELETED is set) // unless we are forced to do it (when the DC is beign demotted, // in particular). // EnterCriticalSection( &NlGlobalDnsCritSect ); if ( NlGlobalDcDemotionInProgress || !(NlGlobalParameters.AvoidDnsDeregOnShutdown) || (DomainInfo->DomFlags & DOM_DELETED) == 0 ) { for ( ListEntry = NlGlobalDnsList.Flink ; ListEntry != &NlGlobalDnsList ; ) { NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next ); // // If the entry is marked for deletion, // skip it // if ( NlDnsName->State == DeleteMe ) { ListEntry = ListEntry->Flink; continue; } // // If entry is still marked, // deregister it as needed. // if ( NlDnsName->DomainInfo == DomainInfo && (NlDnsName->Flags & NL_DNS_REGISTER_DOMAIN) != 0 ) { NlPrintDns(( NL_DNS, NlDnsName, "NlDnsRegisterDomain: being deregistered" )); NlDnsSetState( NlDnsName, DeregisterMe ); // // Increase the ref count so that this entry // doesn't get deleted behind our back // NlDnsName->NlDnsNameRefCount ++; // // Avoid having crit sect locked while doing network IO // LeaveCriticalSection( &NlGlobalDnsCritSect ); NlDnsScavengeOne( &NlDnsContext, NlDnsName ); EnterCriticalSection( &NlGlobalDnsCritSect ); // // This entry might be delinked below. // So grab the pointer to the next entry // before dereferencing this entry. // ListEntry = ListEntry->Flink; NlDnsDereferenceEntry( NlDnsName ); // // Otherwise, proceed with the next entry // } else { ListEntry = ListEntry->Flink; } } } // // Ditch any dangling pointer to the domain info structure. // if ( DomainInfo->DomFlags & DOM_DELETED ) { for ( ListEntry = NlGlobalDnsList.Flink ; ListEntry != &NlGlobalDnsList ; ListEntry = ListEntry->Flink ) { // // If entry didn't deregister, // simply ditch the DomainInfo field. // // This leaves the entry around so we'll deregister it sooner or // later. (Perhaps after the next boot.) // NlDnsName = CONTAINING_RECORD( ListEntry, NL_DNS_NAME, Next ); if ( NlDnsName->DomainInfo == DomainInfo ) { NlPrintDns(( NL_DNS, NlDnsName, "NlDnsDeregisterDomain: Name didn't get deregistered (try later)" )); NlDnsName->DomainInfo = NULL; } } } // // In all cases, // flush any changes to disk. // NlDnsWriteLog(); LeaveCriticalSection( &NlGlobalDnsCritSect ); if ( SocketAddresses != NULL ) { LocalFree( SocketAddresses ); } if ( DcSiteNames != NULL ) { NetApiBufferFree( DcSiteNames ); } if ( GcSiteNames != NULL ) { NetApiBufferFree( GcSiteNames ); } LeaveCriticalSection( &DomainInfo->DomDnsRegisterCritSect ); 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; NlDnsInitCount ++; // // That's it for a workstation. // if ( NlGlobalMemberWorkstation ) { NetStatus = NO_ERROR; goto Cleanup; } // // 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 ++; // // 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; } // // 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; // // Ensure we're flushed. // NlDnsWriteLog(); // // Loop deleting all the entries. // EnterCriticalSection( &NlGlobalDnsCritSect ); while ( !IsListEmpty( &NlGlobalDnsList ) ) { // // All of the domains are now cleaned up. // NlDnsName = CONTAINING_RECORD( NlGlobalDnsList.Flink, NL_DNS_NAME, Next ); NlAssert( NlDnsName->DomainInfo == NULL ); // // Mark the name for deletion // NlDnsName->NlDnsNameRefCount ++; NlDnsSetState( NlDnsName, DeleteMe ); // // Wait for any other references to disappear // while ( NlDnsName->NlDnsNameRefCount != 1 ) { LeaveCriticalSection( &NlGlobalDnsCritSect ); NlPrint(( NL_CRITICAL, "NlDnsShutdown: Sleeping a second waiting for RefCount to one.\n")); Sleep( 1000 ); EnterCriticalSection( &NlGlobalDnsCritSect ); } // // Actually delink and delete structure by removing the last reference // NlAssert( NlDnsName->NlDnsNameRefCount == 1 ); NlDnsDereferenceEntry( 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; }