Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4328 lines
119 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
nlsite.c
Abstract:
Routines to handle sites and subnets.
Author:
Cliff Van Dyke (CliffV) 1-May-1997
Revision History:
--*/
//
// Common include files.
//
#include "logonsrv.h" // Include files common to entire service
#pragma hdrstop
//
// Include files specific to this .c file
//
#include "ismapi.h"
//
// Structure describing several subnets.
//
// NlGlobalSubnetTree is the head of a tree of pointer to subnet.
// The most significant byte of an IP address is used to index into an array
// of SubTrees. Each Subtree entry has either a pointer to the next level of
// the tree (to be indexed into with the next byte of the IP address) or a
// pointer to an NL_SUBNET leaf identifying the subnet this IP address is on.
//
// Both pointers can be NULL indicating that the subnet isn't registered.
//
// Both pointers can be non-NULL indicating that both a non-specific and specific
// subnet may be available. The most specific subnet available for a particular
// IP address should be used.
//
//
// Multiple entries can point to the same NL_SUBNET leaf. If the subnet mask is
// not an even number of bytes long, all of the entries represent IP addresses
// that correspond to the subnet mask will point to the subnet mask.
//
typedef struct _NL_SUBNET_TREE_ENTRY {
//
// Link to the next level of the tree
//
struct _NL_SUBNET_TREE *Subtree;
//
// Pointer to the subnet itself.
//
struct _NL_SUBNET *Subnet;
} NL_SUBNET_TREE_ENTRY, *PNL_SUBNET_TREE_ENTRY;
typedef struct _NL_SUBNET_TREE {
NL_SUBNET_TREE_ENTRY Subtree[256];
} NL_SUBNET_TREE, *PNL_SUBNET_TREE;
//
// Structure describing a single subnet.
//
typedef struct _NL_SUBNET {
//
// Link for NlGlobalSubnetList
//
LIST_ENTRY Next;
//
// Subnet address. (Network bytes order)
//
ULONG SubnetAddress;
//
// Subnet mask. (Network byte order).
//
ULONG SubnetMask;
//
// Pointer to Site this subnet is in.
//
PNL_SITE_ENTRY SiteEntry;
//
// Reference Count.
//
ULONG ReferenceCount;
//
// Number of bits in the subnet mask
//
BYTE SubnetBitCount;
} NL_SUBNET, *PNL_SUBNET;
//
// Globals specific to this .c file.
//
BOOLEAN NlGlobalSiteInitialized = 0;
// List of all NL_SITE_ENTRY entries.
LIST_ENTRY NlGlobalSiteList;
// List of all NL_SUBNET entries
LIST_ENTRY NlGlobalSubnetList;
// Tree of subnets.
NL_SUBNET_TREE_ENTRY NlGlobalSubnetTree;
NL_SUBNET_TREE_ENTRY NlGlobalNewSubnetTree;
//
// Site Entry for the site this domain is a member of
//
PNL_SITE_ENTRY NlGlobalSiteEntry;
BOOLEAN NlGlobalOnlyOneSite;
VOID
NlRefSiteEntry(
IN PNL_SITE_ENTRY SiteEntry
)
/*++
Routine Description:
Reference a site entry.
NlGlobalSiteCritSect must be locked.
Arguments:
SiteEntry - Entry to be referenced.
Return Value:
None.
--*/
{
SiteEntry->ReferenceCount ++;
}
VOID
NlDerefSiteEntry(
IN PNL_SITE_ENTRY SiteEntry
)
/*++
Routine Description:
Dereference a site entry.
If the reference count goes to zero,
the site entry will be deleted.
Arguments:
SiteEntry - Entry to be referenced.
Return Value:
None.
--*/
{
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( (--(SiteEntry->ReferenceCount)) == 0 ) {
RemoveEntryList( &SiteEntry->Next );
LocalFree( SiteEntry );
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
}
BOOL
NetpEqualTStrArrays(
LPTSTR_ARRAY TStrArray1 OPTIONAL,
LPTSTR_ARRAY TStrArray2 OPTIONAL
)
/*++
Routine Description:
Compares to see if two TStrArray's are identical.
Arguments:
TStrArray1 - First array to compare
TStrArray2 - Second array to compare
Return Value:
TRUE - Arrays are identical
FALSE - Arrays are different
--*/
{
//
// Handle the NULL pointer cases.
//
if ( TStrArray1 == NULL && TStrArray2 == NULL ) {
return TRUE;
}
if ( TStrArray1 != NULL && TStrArray2 == NULL ) {
return FALSE;
}
if ( TStrArray1 == NULL && TStrArray2 != NULL ) {
return FALSE;
}
//
// Handle the case where both arrays exist
//
if ( NetpTStrArrayEntryCount ( TStrArray1 ) !=
NetpTStrArrayEntryCount ( TStrArray2 ) ) {
return FALSE;
}
while (!NetpIsTStrArrayEmpty(TStrArray1)) {
//
// Check if the entry is different.
//
// Do a case sensitive comparison to allow case changes to be detected.
//
if ( wcscmp( TStrArray1, TStrArray2 ) != 0 ) {
return FALSE;
}
// Move to the next element
TStrArray1 = NetpNextTStrArrayEntry(TStrArray1);
TStrArray2 = NetpNextTStrArrayEntry(TStrArray2);
}
return TRUE;
}
NET_API_STATUS
NlSitesGetCloseSites(
IN PDOMAIN_INFO DomainInfo,
IN ULONG ServerRole,
OUT PNL_SITE_NAME_ARRAY *SiteNames
)
/*++
Routine Description:
This routine returns the site names of all the sites this DC covers.
Arguments:
DomainInfo - Domain/Forest/NDNC info for which to return close sites
ServerRole - The role this server plays in the domain/forest/NDNC.
??: When we go multihosted, this parameter will not be needed
because the role will be uniquely identified in the DomainInfo.
SiteNames - Returns an array of pointers to site names.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_COVERED_SITE CoveredSiteArray;
ULONG CoveredSiteCount = 0;
ULONG EntryCount;
ULONG i;
ULONG Size;
PUNICODE_STRING Strings;
LPBYTE Where;
ULONG Index;
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( (ServerRole & DOM_FOREST) != 0 ) {
CoveredSiteArray = DomainInfo->GcCoveredSites;
CoveredSiteCount = DomainInfo->GcCoveredSitesCount;
} else if ( (ServerRole & DOM_REAL_DOMAIN) != 0 ||
(ServerRole & DOM_NON_DOMAIN_NC) != 0 ) {
CoveredSiteArray = DomainInfo->CoveredSites;
CoveredSiteCount = DomainInfo->CoveredSitesCount;
}
//
// Determine the length of the returned information
//
Size = sizeof(NL_SITE_NAME_ARRAY);
EntryCount = 0;
for ( Index = 0; Index < CoveredSiteCount; Index++ ) {
Size += sizeof(UNICODE_STRING) +
CoveredSiteArray[Index].CoveredSite->SiteNameString.Length + sizeof(WCHAR);
EntryCount++;
}
//
// Allocate the return buffer.
//
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
Strings = (PUNICODE_STRING) ((*SiteNames)+1);
(*SiteNames)->EntryCount = EntryCount;
(*SiteNames)->SiteNames = Strings;
Where = (LPBYTE) &Strings[EntryCount];
//
// Loop copying the names into the return buffer.
//
i = 0;
for ( Index = 0; Index < CoveredSiteCount; Index++ ) {
RtlCopyMemory( Where,
CoveredSiteArray[Index].CoveredSite->SiteName,
CoveredSiteArray[Index].CoveredSite->SiteNameString.Length + sizeof(WCHAR) );
Strings[i].Buffer = (LPWSTR)Where;
Strings[i].Length = CoveredSiteArray[Index].CoveredSite->SiteNameString.Length;
Strings[i].MaximumLength = Strings[i].Length + sizeof(WCHAR);
Where += Strings[i].Length + sizeof(WCHAR);
i++;
}
NetStatus = NO_ERROR;
NlPrint(( NL_MISC, "NlSitesGetCloseSites returns successfully\n" ));
Cleanup:
LeaveCriticalSection( &NlGlobalSiteCritSect );
if ( NetStatus != NO_ERROR ) {
if ( *SiteNames != NULL ) {
MIDL_user_free( *SiteNames );
*SiteNames = NULL;
}
NlPrint((NL_MISC, "NlSitesGetCloseSites returns unsuccessfully with status %ld.\n", NetStatus ));
}
return NetStatus;
}
BOOL
NlSitesSetSiteCoverageParam(
IN ULONG ServerRole,
IN LPTSTR_ARRAY NewSiteCoverage OPTIONAL
)
/*++
Routine Description:
This routine set the site names of all the sites this DC covers.
Arguments:
ServerRole - Specifies the role of the server (DC/GC/NDNC) for which
the registry site coverage is being set.
NewSiteCoverage - Specifies the new covered sites
Return Value:
TRUE: iff site coverage changed
--*/
{
LPTSTR_ARRAY *OldSiteCoverage = NULL;
BOOL SiteCoverageChanged;
PLIST_ENTRY ListEntry;
//
// If asking about the GC,
// use GC specific globals.
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( ServerRole & DOM_FOREST ) {
OldSiteCoverage = &NlGlobalParameters.GcSiteCoverage;
} else if ( ServerRole & DOM_REAL_DOMAIN ) {
OldSiteCoverage = &NlGlobalParameters.SiteCoverage;
} else if ( ServerRole & DOM_NON_DOMAIN_NC ) {
OldSiteCoverage = &NlGlobalParameters.NdncSiteCoverage;
}
NlAssert( OldSiteCoverage != NULL );
//
// Handle SiteCoverage changing
//
SiteCoverageChanged = !NetpEqualTStrArrays(
*OldSiteCoverage,
NewSiteCoverage );
if ( SiteCoverageChanged ) {
//
// Swap in the new value.
(VOID) NetApiBufferFree( *OldSiteCoverage );
*OldSiteCoverage = NewSiteCoverage;
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
return SiteCoverageChanged;
}
//
// Procedure forwards of procedures in ntdsapi.dll
//
typedef
DWORD
(*PDsGetDomainControllerInfoW)(
HANDLE hDs, // in
LPCWSTR DomainName, // in
DWORD InfoLevel, // in
DWORD *pcOut, // out
VOID **ppInfo); // out
PDsGetDomainControllerInfoW NlGlobalpDsGetDomainControllerInfoW;
typedef
VOID
(*PDsFreeDomainControllerInfoW)(
DWORD InfoLevel, // in
DWORD cInfo, // in
VOID *pInfo); // in
PDsFreeDomainControllerInfoW NlGlobalpDsFreeDomainControllerInfoW;
NTSTATUS
NlLoadNtDsApiDll(
VOID
)
/*++
Routine Description:
This function loads the ntdsaapi.dll module if it is not loaded
already.
Arguments:
None
Return Value:
NT Status code.
--*/
{
static NTSTATUS DllLoadStatus = STATUS_SUCCESS;
HANDLE DllHandle = NULL;
//
// If the DLL is already loaded,
// we're done.
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( NlGlobalDsApiDllHandle != NULL ) {
LeaveCriticalSection( &NlGlobalSiteCritSect );
return STATUS_SUCCESS;
}
//
// If we've tried to load the DLL before and it failed,
// return the same error code again.
//
if( DllLoadStatus != STATUS_SUCCESS ) {
goto Cleanup;
}
//
// Load the dlls
//
DllHandle = LoadLibraryA( "NtDsApi" );
if ( DllHandle == NULL ) {
DllLoadStatus = STATUS_DLL_NOT_FOUND;
goto Cleanup;
}
//
// Macro to grab the address of the named procedure from ntdsa.dll
//
#define GRAB_ADDRESS( _X ) \
NlGlobalp##_X = (P##_X) GetProcAddress( DllHandle, #_X ); \
\
if ( NlGlobalp##_X == NULL ) { \
DllLoadStatus = STATUS_PROCEDURE_NOT_FOUND;\
goto Cleanup; \
}
//
// Get the addresses of the required procedures.
//
GRAB_ADDRESS( DsBindW )
GRAB_ADDRESS( DsGetDomainControllerInfoW )
GRAB_ADDRESS( DsFreeDomainControllerInfoW )
GRAB_ADDRESS( DsUnBindW )
DllLoadStatus = STATUS_SUCCESS;
Cleanup:
if (DllLoadStatus == STATUS_SUCCESS) {
NlGlobalDsApiDllHandle = DllHandle;
} else {
if ( DllHandle != NULL ) {
FreeLibrary( DllHandle );
}
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
return( DllLoadStatus );
}
VOID
NlSitesAddCloseSite(
IN LPWSTR SiteName,
IN OUT PNL_COVERED_SITE CoveredSites,
IN OUT PULONG CoveredSitesCount,
IN BOOLEAN CoveredAuto
)
/*++
Routine Description:
Add a site entry to the list passed. If the site entry already
exists on the list, this is no-op. Otherwise, the site entry
is added to the list (and to the global list of sites if this
entry wasn't on the global list) and a reference on the site
entry in the global list in incremented.
Arguments:
SiteName - Name of site entry to add
CoveredSites - Array of covered site entries. The array
must be big enough to accomodate a new potential entry.
CoveredSiteCount - The current number of entries in CoveredSites.
May be incremented if a new entry is added.
CoveredAuto - If TRUE, this site is covered automatically.
Return Value:
None
--*/
{
PNL_SITE_ENTRY SiteEntry = NULL;
ULONG CoveredSiteIndex;
//
// Sanity check
//
if ( SiteName == NULL || *SiteName == UNICODE_NULL ) {
return;
}
//
// Find/Add the site to the global list of sites
//
SiteEntry = NlFindSiteEntry( SiteName );
if ( SiteEntry != NULL ) {
//
// If we already have this site on the current list of covered
// sites, just update the auto coverage boolean and dereference
// this site entry.
//
for ( CoveredSiteIndex = 0; CoveredSiteIndex < *CoveredSitesCount; CoveredSiteIndex++ ) {
if ( CoveredSites[CoveredSiteIndex].CoveredSite == SiteEntry ) {
CoveredSites[CoveredSiteIndex].CoveredAuto = CoveredAuto;
NlDerefSiteEntry( SiteEntry );
return;
}
}
//
// We don't have this site on the current list of covered
// sites. So add this site to the list, set the auto coverage
// boolean, and keep just added reference in the global list
// of sites.
//
CoveredSites[*CoveredSitesCount].CoveredSite = SiteEntry;
CoveredSites[*CoveredSitesCount].CoveredAuto = CoveredAuto;
(*CoveredSitesCount) ++;
}
}
BOOL
NlSitesGetIsmConnect(
IN LPWSTR SiteName,
OUT PISM_CONNECTIVITY *SiteConnect,
OUT PULONG ThisSite
)
/*++
Routine Description:
Get the site connection matrix from ISM.
Arguments:
SiteName - Site name of the site this DC is in
SiteConnect - Returns a pointer to the site connection matrix
Use I_ISMFree to free this data.
ThisSite - Return an index to SiteName within SiteConnect
0xFFFFFFFF - means this site is not in SiteConnect
Return Value:
TRUE on success
None.
--*/
{
NET_API_STATUS NetStatus;
NTSTATUS Status;
DWORD Length;
PDSNAME DsRoot = NULL;
LPWSTR IpName = NULL;
DWORD SiteIndex1;
DWORD SiteIndex2;
BOOLEAN RetVal = FALSE;
//
// Initialization
//
*SiteConnect = NULL;
*ThisSite = 0xFFFFFFFF;
//
// If netlogon isn't running,
// simply return since we don't want to wait for the ISM service to start
// while we're starting.
//
if ( NlGlobalChangeLogNetlogonState != NetlogonStarted ) {
NlPrint(( NL_SITE_MORE,
"NlSitesGetIsmConnect: Avoided during startup.\n" ));
goto Cleanup;
}
//
// Wait up to 45 seconds for the ISM service to start
//
Status = NlWaitForService( SERVICE_ISMSERV, 45, TRUE );
if ( Status != STATUS_SUCCESS ) {
NlPrint(( NL_SITE_MORE,
"NlSitesGetIsmConnect: ISM service not started.\n" ));
goto Cleanup;
}
//
// Build the name of the IP transport
//
#define ISM_IP_TRANSPORT L"CN=IP,CN=Inter-Site Transports,CN=Sites,"
Length = 0;
Status = NlGetConfigurationName( DSCONFIGNAME_CONFIGURATION, &Length, NULL );
NlAssert( Status == STATUS_BUFFER_TOO_SMALL );
if ( Status != STATUS_BUFFER_TOO_SMALL ) {
NlPrint(( NL_CRITICAL,
"NlSitesGetIsmConnect: Cannot GetConfigurationName 0x%lx.\n",
Status ));
goto Cleanup;
}
DsRoot = LocalAlloc( 0, Length );
if ( DsRoot == NULL ) {
goto Cleanup;
}
Status = NlGetConfigurationName( DSCONFIGNAME_CONFIGURATION, &Length, DsRoot );
if ( !NT_SUCCESS( Status ) ) {
NlPrint(( NL_CRITICAL,
"NlSitesGetIsmConnect: Cannot GetConfigurationName 0x%lx.\n",
Status ));
goto Cleanup;
}
IpName = LocalAlloc( 0, DsRoot->NameLen * sizeof(WCHAR) +
sizeof( ISM_IP_TRANSPORT ) );
if ( IpName == NULL ) {
goto Cleanup;
}
wcscpy( IpName, ISM_IP_TRANSPORT );
wcscat( IpName, DsRoot->StringName );
//
// Get the site link costs
//
NetStatus = I_ISMGetConnectivity( IpName, SiteConnect);
if ( NetStatus != NO_ERROR ) {
NlPrint(( NL_CRITICAL,
"NlSitesGetIsmConnect: Cannot I_ISMGetConnectivity %ld.\n",
NetStatus ));
goto Cleanup;
}
if ( *SiteConnect == NULL ) {
NlPrint(( NL_CRITICAL,
"NlSitesGetIsmConnect: I_ISMGetConnectivity returned NULL.\n" ));
goto Cleanup;
}
//
// Convert the returned site name to a canonical form
//
for (SiteIndex1 = 0; SiteIndex1 < (*SiteConnect)->cNumSites; SiteIndex1++) {
if ( _wcsnicmp( (*SiteConnect)->ppSiteDNs[SiteIndex1], L"CN=", 3 ) == 0 ) {
WCHAR *Comma;
(*SiteConnect)->ppSiteDNs[SiteIndex1] += 3;
Comma = wcschr( (*SiteConnect)->ppSiteDNs[SiteIndex1], L',' );
if ( Comma != NULL ) {
*Comma = L'\0';
}
}
//
// Remember which site this site is:
//
if ( _wcsicmp( SiteName,
(*SiteConnect)->ppSiteDNs[SiteIndex1] ) == 0 ) {
*ThisSite = SiteIndex1;
}
}
//
// Be verbose
//
#if NETLOGONDBG
EnterCriticalSection( &NlGlobalLogFileCritSect );
NlPrint(( NL_SITE_MORE,
"NlSitesGetIsmConnect: Site link costs for %ld sites:\n",
(*SiteConnect)->cNumSites ));
for (SiteIndex2 = 0; SiteIndex2 < (*SiteConnect)->cNumSites; SiteIndex2++) {
NlPrint(( NL_SITE_MORE,
"%s%5d",
SiteIndex2 ? "," : " ",
SiteIndex2 ));
}
NlPrint(( NL_SITE_MORE, "\n"));
for (SiteIndex1 = 0; SiteIndex1 < (*SiteConnect)->cNumSites; SiteIndex1++) {
if ( *ThisSite == SiteIndex1 ) {
NlPrint(( NL_SITE_MORE, "*" ));
} else {
NlPrint(( NL_SITE_MORE, " " ));
}
NlPrint(( NL_SITE_MORE,
"(%2d) %ws\n",
SiteIndex1,
(*SiteConnect)->ppSiteDNs[SiteIndex1]));
for (SiteIndex2 = 0; SiteIndex2 < (*SiteConnect)->cNumSites; SiteIndex2++) {
PISM_LINK pLink = &((*SiteConnect)->pLinkValues[SiteIndex1 * (*SiteConnect)->cNumSites + SiteIndex2]);
NlPrint(( NL_SITE_MORE,
"%s%5d",
SiteIndex2 ? "," : " ",
pLink->ulCost ));
}
NlPrint(( NL_SITE_MORE, "\n"));
}
LeaveCriticalSection( &NlGlobalLogFileCritSect );
#endif // NETLOGONDBG
RetVal = TRUE;
Cleanup:
if ( DsRoot != NULL ) {
LocalFree( DsRoot );
}
if ( IpName != NULL ) {
LocalFree( IpName );
}
return RetVal;
}
BOOL
NlValidateSiteName(
IN LPWSTR SiteName
)
/*++
Routine Description:
This routine validates the site name to be non-mangled and
valid for use as a label in a DNS name. A site with a
mangled name may be created in addition to the site with the
intended name as a result of a site name collision in the DS.
Arguments:
SiteName -- The site name to be validated
Return Value:
TRUE -- Site name is verified as valid for use
FALSE -- Site name is not verified as valid for use
--*/
{
NET_API_STATUS NetStatus;
//
// NULL site name is invalid
//
if ( SiteName == NULL ) {
NlPrint(( NL_CRITICAL,
"NlValidateSiteName: NULL site name is invalid\n" ));
return FALSE;
}
//
// Check if the site name is mangled
//
if ( NlIsMangledRDNExternal(SiteName, wcslen(SiteName), NULL) ) {
NlPrint(( NL_CRITICAL,
"NlValidateSiteName: Site name %ws is mangled\n",
SiteName ));
return FALSE;
}
//
// Sanity check that the site name can be
// used as a label in a DNS name
//
NetStatus = DnsValidateName_W( SiteName, DnsNameDomainLabel );
if ( NetStatus == NO_ERROR || NetStatus == DNS_ERROR_NON_RFC_NAME ) {
return TRUE;
} else {
NlPrint(( NL_CRITICAL,
"NlValidateSiteName: Site name %ws is not valid DNS label\n",
SiteName ));
return FALSE;
}
}
NET_API_STATUS
NlSitesUpdateSiteCoverageForRole(
IN PDOMAIN_INFO DomainInfo,
IN ULONG DomFlags,
IN HANDLE DsHandle,
IN PISM_CONNECTIVITY SiteConnect,
IN LPWSTR ThisSiteName,
IN ULONG ThisSiteIndex,
OUT PBOOLEAN SiteCoverageChanged OPTIONAL
)
/*++
Routine Description:
This routine recomputes the site coverage for this DC based on the costs
associated with the site links.
Basically, for each site that has no DCs for this domain, the site this DC
is in might be chosen to "cover" the site. The following criteria is used:
* Site link cost.
* For sites where the above is equal, the site having the most DCs is chosen.
* For sites where the above is equal, the site having the alphabetically least
name is chosen.
Arguments:
DomainInfo - Hosted domain whose site coverage is to be updated
DomFlags - The role for which we need to update the site coverage
DsHandle - Handle to the DS
SiteConnect - If specified, the site link costs info returned by NlSitesGetIsmConnect
ThisSiteName - The name of the site of this server
ThisSiteIndex - The index of the site of this server in the SiteConnect info
SiteCoverageChanged - If specified and the site covereage changes, returns TRUE.
Otherwise, left intact.
Return Value:
NO_ERROR
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
WCHAR DnsDomainName[NL_MAX_DNS_LENGTH+1];
WCHAR CapturedDnsForestName[NL_MAX_DNS_LENGTH+1];
ULONG SiteCount = 0;
ULONG TmpSiteCount = 0;
DWORD SiteIndex1;
DWORD SiteIndex2;
PULONG DcsInSite = NULL;
BOOLEAN LocalSiteCoverageChanged = FALSE;
PDS_DOMAIN_CONTROLLER_INFO_1W DcInfo = NULL;
ULONG DcInfoCount;
PDS_NAME_RESULT GcInfo = NULL;
SERVERSITEPAIR *ServerSitePairs = NULL;
PNL_SITE_ENTRY SiteEntry;
PLIST_ENTRY ListEntry;
LPSTR GcOrDcOrNdnc = NULL;
BOOLEAN AtleastOneDc = FALSE;
LPTSTR_ARRAY SiteCoverageParameter = NULL;
PNL_COVERED_SITE CoveredSites = NULL;
ULONG CoveredSitesCount = 0;
ULONG CoveredSitesIndex;
PNL_COVERED_SITE OldCoveredSites = NULL;
ULONG OldCoveredSitesCount = 0;
ULONG OldCoveredSitesIndex;
//
// Local variable initialization
//
if ( DomFlags & DOM_FOREST ) {
GcOrDcOrNdnc = "GC";
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
GcOrDcOrNdnc = "DC";
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
GcOrDcOrNdnc = "NDNC";
}
//
// Allocate a temporary storage for all possible site entries
//
// Count for the site link cost entries
//
if ( SiteConnect != NULL ) {
TmpSiteCount += SiteConnect->cNumSites;
}
//
// Count for the registry coverage parameter
//
if ( DomFlags & DOM_FOREST ) {
SiteCoverageParameter = NlGlobalParameters.GcSiteCoverage;
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
SiteCoverageParameter = NlGlobalParameters.SiteCoverage;
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
SiteCoverageParameter = NlGlobalParameters.NdncSiteCoverage;
}
if ( SiteCoverageParameter != NULL ) {
TmpSiteCount += NetpTStrArrayEntryCount( (LPTSTR_ARRAY) SiteCoverageParameter );
}
//
// Count for the site of this machine
//
TmpSiteCount ++;
//
// Allocate the storage
//
CoveredSites = LocalAlloc( LMEM_ZEROINIT, TmpSiteCount * sizeof(NL_COVERED_SITE) );
if ( CoveredSites == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Capture the dns domain name of the hosted
//
NlCaptureDomainInfo ( DomainInfo, DnsDomainName, NULL );
//
// Capture the forest name
//
NlCaptureDnsForestName( CapturedDnsForestName );
//
// If we are to automatically determine site coverage and
// we have the site link costs, build the coverage list
// corresponding to the specified role.
//
if ( NlGlobalParameters.AutoSiteCoverage &&
SiteConnect != NULL &&
SiteConnect->cNumSites != 0 ) {
SiteCount = SiteConnect->cNumSites;
//
// Allocate a buffer for temporary storage.
//
DcsInSite = LocalAlloc( LMEM_ZEROINIT, SiteCount * sizeof(ULONG) );
if ( DcsInSite == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Depending on the role, get the list of GCs/DCs/NDNCs and their sites
//
if ( DsHandle != NULL ) {
//
// Handle building the GC coverage list
//
if ( DomFlags & DOM_FOREST ) {
LPWSTR DummyName = L".";
//
// Get the list of GCs in the forest and their sites.
// Avoid this operation if we are not currently a GC in
// which case update the GC covered site info based
// on the registry setting below.
//
// ??: For multihosting, we will indicate whether we
// are a GC in the forest DOMAIN_INFO struct instead of
// the global as we currently use.
//
if ( (NlGetDomainFlags(DomainInfo) & DS_GC_FLAG) == 0 ) {
NlPrint(( NL_SITE_MORE,
"GC: This DC isn't a GC. So it doesn't auto cover any sites as a GC.\n" ));
} else {
NetStatus = DsCrackNamesW(
DsHandle,
0,
DS_LIST_GLOBAL_CATALOG_SERVERS,
DS_LIST_GLOBAL_CATALOG_SERVERS,
1,
&DummyName,
&GcInfo );
if ( NetStatus != NO_ERROR ) {
NlPrint(( NL_CRITICAL,
"NlSitesUpdateSiteCoverage: CrackNames failed: %ld\n",
NetStatus ));
} else {
ULONG GcIndex;
//
// Determine which sites are already covered by GCs.
//
for ( GcIndex=0; GcIndex < GcInfo->cItems; GcIndex++ ) {
LPWSTR GcSiteName = GcInfo->rItems[GcIndex].pName;
LPWSTR GcDnsHostName = GcInfo->rItems[GcIndex].pDomain;
NlPrint(( NL_SITE,
"GC list: %ws %ws\n",
GcSiteName,
GcDnsHostName ));
if ( GcInfo->rItems[GcIndex].status != DS_NAME_NO_ERROR ) {
NlPrint(( NL_CRITICAL,
"NlSitesUpdateSiteCoverage: CrackNameStatus bad: %ws %ws: %ld\n",
GcSiteName,
GcDnsHostName,
GcInfo->rItems[GcIndex].status ));
continue;
}
//
// Count the number of GCs in each site.
//
for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
if ( GcSiteName != NULL &&
_wcsicmp( GcSiteName,
SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
DcsInSite[SiteIndex1] ++;
AtleastOneDc = TRUE;
break;
}
}
//
// If this DC isn't in any known site,
// simply ignore it.
//
if ( SiteIndex1 >= SiteCount ) {
NlPrint(( NL_CRITICAL,
"GC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
GcSiteName,
GcDnsHostName ));
}
}
}
}
//
// Handle building the DC coverage list
//
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
ULONG DcIndex;
//
// Get the list of DCs in the domain and their sites
//
NetStatus = (*NlGlobalpDsGetDomainControllerInfoW)(
DsHandle,
DnsDomainName,
1,
&DcInfoCount,
&DcInfo );
if ( NetStatus != NO_ERROR ) {
NlPrint(( NL_CRITICAL,
"NlSitesUpdateSiteCoverage: Cannot DsGetDomainControllerInfoW %ld.\n",
NetStatus ));
DcInfoCount = 0;
}
//
// Determine which sites are already covered by DCs.
//
for ( DcIndex=0; DcIndex<DcInfoCount; DcIndex++ ) {
NlPrint(( NL_SITE,
"DC list: %ws %ws\n",
DcInfo[DcIndex].SiteName,
DcInfo[DcIndex].DnsHostName ));
//
// Count the number of DCs in each site.
//
for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
if ( DcInfo[DcIndex].SiteName != NULL &&
_wcsicmp( DcInfo[DcIndex].SiteName,
SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
DcsInSite[SiteIndex1] ++;
AtleastOneDc = TRUE;
break;
}
}
//
// If this DC isn't in any known site,
// simply ignore it.
//
if ( SiteIndex1 >= SiteCount ) {
NlPrint(( NL_CRITICAL,
"DC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
DcInfo[DcIndex].SiteName,
DcInfo[DcIndex].DnsHostName ));
}
}
//
// Handle building the NDNC coverage list
//
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
SERVERSITEPAIR *ServerSitePairEntry;
NetStatus = NlDsGetServersAndSitesForNetLogon( DnsDomainName,
&ServerSitePairs );
//
// Determine which sites are already covered by LDAP servers
//
if ( NetStatus == NO_ERROR ) {
ServerSitePairEntry = ServerSitePairs;
while ( ServerSitePairEntry != NULL &&
ServerSitePairEntry->wszDnsServer != NULL ) {
NlPrint(( NL_SITE,
"NDNC list: %ws %ws\n",
ServerSitePairEntry->wszSite,
ServerSitePairEntry->wszDnsServer ));
//
// Count the number of LDAP servers in each site.
//
for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
if ( ServerSitePairEntry->wszSite != NULL &&
_wcsicmp( ServerSitePairEntry->wszSite,
SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
DcsInSite[SiteIndex1] ++;
AtleastOneDc = TRUE;
break;
}
}
//
// If this LDAP server isn't in any known site,
// simply ignore it.
//
if ( SiteIndex1 >= SiteCount ) {
NlPrint(( NL_CRITICAL,
"NDNC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
ServerSitePairEntry->wszSite,
ServerSitePairEntry->wszDnsServer ));
}
ServerSitePairEntry ++;
}
}
}
}
}
//
// If we were able to build the DcsInSite array,
// Compute the auto site coverage.
//
if ( AtleastOneDc ) {
//
// For each site that has no DCs ...
//
for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
DWORD BestSite;
if ( DcsInSite[SiteIndex1] != 0 ) {
continue;
}
//
// Skip this site if it's not valid for DNS registrations
//
if ( !NlValidateSiteName(SiteConnect->ppSiteDNs[SiteIndex1]) ) {
continue;
}
NlPrint(( NL_SITE_MORE,
"%s: %ws: Site has no %ss\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
GcOrDcOrNdnc ));
//
// Pick the site that should cover that site
//
BestSite = 0xFFFFFFFF;
for (SiteIndex2 = 0; SiteIndex2 < SiteCount; SiteIndex2++) {
PISM_LINK Link2 = &(SiteConnect->pLinkValues[SiteIndex1 * SiteCount + SiteIndex2]);
//
// A site cannot auto cover itself.
//
if ( SiteIndex1 == SiteIndex2 ) {
#ifdef notdef
NlPrint(( NL_SITE_MORE,
"%s: %ws: Site ignoring itself.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1] ));
#endif // notdef
//
// An invalid site is not eligible
//
} else if ( !NlValidateSiteName(SiteConnect->ppSiteDNs[SiteIndex2]) ) {
NlPrint(( NL_SITE_MORE,
"%s: %ws: Site '%ws' is invalid.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[SiteIndex2] ));
//
// A site with an infinite cost cannot be reached.
// so don't consider it.
//
} else if ( Link2->ulCost == 0xFFFFFFFF ) {
NlPrint(( NL_SITE_MORE,
"%s: %ws: Site '%ws' has infinite cost.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[SiteIndex2] ));
//
// A site with no DCs cannot cover any site
// so don't consider it.
//
} else if ( DcsInSite[SiteIndex2] == 0 ) {
NlPrint(( NL_SITE_MORE,
"%s: %ws: Site '%ws' has no Dcs.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[SiteIndex2] ));
//
// If no best site has yet been picked,
// use this one.
//
} else if ( BestSite == 0xFFFFFFFF ) {
NlPrint(( NL_SITE_MORE,
"%s: %ws: Site '%ws' is the first valid site.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[SiteIndex2] ));
BestSite = SiteIndex2;
} else {
PISM_LINK LinkBest = &(SiteConnect->pLinkValues[SiteIndex1 * SiteCount + BestSite]);
//
// If the SiteLink cost is less than the current best,
// use it.
//
if ( Link2->ulCost < LinkBest->ulCost ) {
NlPrint(( NL_SITE_MORE,
"%s: %ws: '%ws' has cheaper link costs than '%ws'\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[SiteIndex2],
SiteConnect->ppSiteDNs[BestSite] ));
BestSite = SiteIndex2;
//
// If the SiteLink cose is equal ...
//
} else if ( Link2->ulCost == LinkBest->ulCost ) {
//
// ... then pick the site with the greater number of DCs
//
if ( DcsInSite[SiteIndex2] > DcsInSite[BestSite] ) {
NlPrint(( NL_SITE_MORE,
"%s: %ws: '%ws' has more DCs than '%ws'\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[SiteIndex2],
SiteConnect->ppSiteDNs[BestSite] ));
BestSite = SiteIndex2;
//
// If the number of DCs is equal ...
//
} else if ( DcsInSite[SiteIndex2] == DcsInSite[BestSite] ) {
//
// Break the tie by using the site with the alphabetically
// least name.
//
if ( _wcsicmp( SiteConnect->ppSiteDNs[SiteIndex2],
SiteConnect->ppSiteDNs[BestSite] ) < 0 ) {
NlPrint(( NL_SITE_MORE,
"%s: %ws: '%ws' is alphabetically before '%ws'\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[SiteIndex2],
SiteConnect->ppSiteDNs[BestSite] ));
BestSite = SiteIndex2;
}
}
}
}
}
//
// If the best site is the site this DC is in,
// then mark this site as covered.
//
if ( BestSite == ThisSiteIndex ) {
NlPrint(( NL_SITE,
"%s: %ws: Site is auto covered by our site.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1] ));
NlSitesAddCloseSite( SiteConnect->ppSiteDNs[SiteIndex1],
CoveredSites,
&CoveredSitesCount,
TRUE ); // auto covered
} else {
//
// Note if no site was found
//
if ( BestSite == 0xFFFFFFFF ) {
NlPrint(( NL_SITE,
"%s: %ws: Site is not auto covered by any site.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1] ));
} else {
NlPrint(( NL_SITE,
"%s: %ws: Site is auto covered by site '%ws'.\n",
GcOrDcOrNdnc,
SiteConnect->ppSiteDNs[SiteIndex1],
SiteConnect->ppSiteDNs[BestSite] ));
}
}
}
}
//
// Now that all of the information has been collected.
// Update the in memory information with the CritSect locked.
//
EnterCriticalSection( &NlGlobalSiteCritSect );
//
// Merge in the list of covered sites from the registry
//
if ( SiteCoverageParameter != NULL ) {
LPTSTR_ARRAY TStrArray;
LPWSTR BackSlash = NULL;
BOOLEAN SkipThisEntry;
TStrArray = SiteCoverageParameter;
while (!NetpIsTStrArrayEmpty(TStrArray)) {
SkipThisEntry = FALSE;
BackSlash = wcsstr(TStrArray, L"\\");
//
// If backslash is present, then the covered site is specified
// for a given domain/forest/NDNC. The domain/forest/NDNC name
// precedes the backslash while the site name follows after the
// backslash. If backslash is absent, the site is covered for
// all domains/forests/NDNCs.
//
if ( BackSlash != NULL ) {
*BackSlash = UNICODE_NULL;
//
// Check the appropriate name depending on whether this is
// a forest or a domain or an NDNC
//
if ( DomFlags & DOM_FOREST ) {
if ( !NlEqualDnsName(TStrArray, CapturedDnsForestName) ) {
SkipThisEntry = TRUE;
}
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
if ( !NlEqualDnsName(TStrArray, DnsDomainName) &&
NlNameCompare(TStrArray, DomainInfo->DomUnicodeDomainName, NAMETYPE_DOMAIN) != 0 ) {
SkipThisEntry = TRUE;
}
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
if ( !NlEqualDnsName(TStrArray, DnsDomainName) ) {
SkipThisEntry = TRUE;
}
}
}
//
// Add this site to the current list of covered sites
//
if ( !SkipThisEntry ) {
if ( BackSlash != NULL ) {
if ( *(BackSlash+1) != UNICODE_NULL ) {
NlPrint(( NL_SITE,
"%s: %ws: Site is covered by our site (regkey).\n",
GcOrDcOrNdnc,
BackSlash+1 ));
NlSitesAddCloseSite( BackSlash+1, CoveredSites, &CoveredSitesCount, FALSE );
}
} else {
NlPrint(( NL_SITE,
"%s: %ws: Site is covered by our site (regkey).\n",
GcOrDcOrNdnc,
TStrArray ));
NlSitesAddCloseSite( TStrArray, CoveredSites, &CoveredSitesCount, FALSE );
}
}
if ( BackSlash != NULL ) {
*BackSlash = L'\\';
}
TStrArray = NetpNextTStrArrayEntry(TStrArray);
}
}
//
// The site this DC is in is covered by definition
//
NlSitesAddCloseSite( ThisSiteName, CoveredSites, &CoveredSitesCount, FALSE );
//
// Determine if the site coverages changes.
// Log info if auto site coverage changes.
//
if ( (DomFlags & DOM_FOREST) != 0 ) {
OldCoveredSites = DomainInfo->GcCoveredSites;
OldCoveredSitesCount = DomainInfo->GcCoveredSitesCount;
} else if ( (DomFlags & DOM_REAL_DOMAIN) != 0 ||
(DomFlags & DOM_NON_DOMAIN_NC) != 0 ) {
OldCoveredSites = DomainInfo->CoveredSites;
OldCoveredSitesCount = DomainInfo->CoveredSitesCount;
}
//
// Determine if new sites get covered and log events for newly auto
// covered sites
//
for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
DWORD EventId = 0;
LPWSTR MsgStrings[3];
MsgStrings[0] = ThisSiteName;
MsgStrings[1] = CoveredSites[CoveredSitesIndex].CoveredSite->SiteName;
//
// Determine if we didn't cover this site in the past
//
for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
if ( RtlEqualUnicodeString( &CoveredSites[CoveredSitesIndex].CoveredSite->SiteNameString,
&OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteNameString,
TRUE ) ) {
break;
}
}
//
// Indicate if this is a new covered site
//
if ( OldCoveredSitesIndex == OldCoveredSitesCount ) {
LocalSiteCoverageChanged = TRUE;
//
// If the new site is auto covered, log the event
//
if ( CoveredSites[CoveredSitesIndex].CoveredAuto ) {
//
// Log for GC that this is a newly auto covered site
//
if ( DomFlags & DOM_FOREST ) {
EventId = NELOG_NetlogonGcSiteCovered;
MsgStrings[2] = CapturedDnsForestName;
//
// Log for DC that this is a newly auto covered site
//
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
EventId = NELOG_NetlogonDcSiteCovered;
MsgStrings[2] = DomainInfo->DomUnicodeDomainName;
//
// Log for NDNC that this is a newly auto covered site
//
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
EventId = NELOG_NetlogonNdncSiteCovered;
MsgStrings[2] = DnsDomainName;
}
}
//
// If we had this site not auto covered in the past and it is
// auto covered now, log the event
//
} else if ( CoveredSites[CoveredSitesIndex].CoveredAuto &&
!OldCoveredSites[OldCoveredSitesIndex].CoveredAuto ) {
//
// Log for GC that this old manually covered site is now auto covered
//
if ( DomFlags & DOM_FOREST ) {
EventId = NELOG_NetlogonGcOldSiteCovered;
MsgStrings[2] = CapturedDnsForestName;
//
// Log for DC that this old manually covered site is now auto covered
//
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
EventId = NELOG_NetlogonDcOldSiteCovered;
MsgStrings[2] = DomainInfo->DomUnicodeDomainName;
//
// Log for NDNC that this old manually covered site is now auto covered
//
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
EventId = NELOG_NetlogonNdncOldSiteCovered;
MsgStrings[2] = DnsDomainName;
}
}
//
// Log the event if needed
//
if ( EventId != 0 ) {
NlpWriteEventlog ( EventId,
EVENTLOG_INFORMATION_TYPE,
NULL,
0,
MsgStrings,
3 | NETP_ALLOW_DUPLICATE_EVENTS );
}
}
//
// Determine if old sites are nolonger covered and log events for old auto
// covered sites which are nolonger auto covered
//
for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
DWORD EventId = 0;
LPWSTR MsgStrings[2];
MsgStrings[0] = OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteName;
//
// Determine if we no longer cover this site
//
for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
if ( RtlEqualUnicodeString( &OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteNameString,
&CoveredSites[CoveredSitesIndex].CoveredSite->SiteNameString,
TRUE ) ) {
break;
}
}
//
// Indicate if this site is no longer covered
//
if ( CoveredSitesIndex == CoveredSitesCount ) {
LocalSiteCoverageChanged = TRUE;
//
// If the old site was auto covered, log the event
//
if ( OldCoveredSites[OldCoveredSitesIndex].CoveredAuto ) {
//
// Log for GC that this old auto covered site is no longer auto covered
//
if ( DomFlags & DOM_FOREST ) {
EventId = NELOG_NetlogonGcSiteNotCovered;
MsgStrings[1] = CapturedDnsForestName;
//
// Log for DC that this old auto covered site is no longer auto covered
//
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
EventId = NELOG_NetlogonDcSiteNotCovered;
MsgStrings[1] = DomainInfo->DomUnicodeDomainName;
//
// Log for NDNC that this old auto covered site is no longer auto covered
//
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
EventId = NELOG_NetlogonNdncSiteNotCovered;
MsgStrings[1] = DnsDomainName;
}
}
//
// If we had this site auto covered in the past and it is
// no longer auto covered, log the event
//
} else if ( OldCoveredSites[OldCoveredSitesIndex].CoveredAuto &&
!CoveredSites[CoveredSitesIndex].CoveredAuto ) {
//
// Log for GC that this old auto covered site is now manually covered
//
if ( DomFlags & DOM_FOREST ) {
EventId = NELOG_NetlogonGcSiteNotCoveredAuto;
MsgStrings[1] = CapturedDnsForestName;
//
// Log for DC that this old auto covered site is now manually covered
//
} else if ( DomFlags & DOM_REAL_DOMAIN ) {
EventId = NELOG_NetlogonDcSiteNotCoveredAuto;
MsgStrings[1] = DomainInfo->DomUnicodeDomainName;
//
// Log for NDNC that this old auto covered site is now manually covered
//
} else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
EventId = NELOG_NetlogonNdncSiteNotCoveredAuto;
MsgStrings[1] = DnsDomainName;
}
}
//
// Log the event if needed
//
if ( EventId != 0 ) {
NlPrint(( NL_SITE,
"%s: %ws: Site is no longer auto covered by our site.\n",
GcOrDcOrNdnc,
OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteName ));
NlpWriteEventlog ( EventId,
EVENTLOG_INFORMATION_TYPE,
NULL,
0,
MsgStrings,
2 | NETP_ALLOW_DUPLICATE_EVENTS );
}
}
//
// Dereference the old entries.
//
for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
NlDerefSiteEntry( OldCoveredSites[OldCoveredSitesIndex].CoveredSite );
}
//
// Finally update the site coverage list.
// Free the old list and allocate the new one.
//
if ( DomFlags & DOM_FOREST ) {
if ( DomainInfo->GcCoveredSites != NULL ) {
LocalFree( DomainInfo->GcCoveredSites );
DomainInfo->GcCoveredSites = NULL;
DomainInfo->GcCoveredSitesCount = 0;
}
if ( CoveredSitesCount != 0 ) {
DomainInfo->GcCoveredSites = LocalAlloc( 0, CoveredSitesCount * sizeof(NL_COVERED_SITE ) );
if ( DomainInfo->GcCoveredSites != NULL ) {
RtlCopyMemory( DomainInfo->GcCoveredSites,
CoveredSites,
CoveredSitesCount * sizeof(NL_COVERED_SITE) );
DomainInfo->GcCoveredSitesCount = CoveredSitesCount;
} else {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Reference the newly added entries, if any
//
for ( CoveredSitesIndex = 0; CoveredSitesIndex < DomainInfo->GcCoveredSitesCount; CoveredSitesIndex++ ) {
NlRefSiteEntry( (DomainInfo->GcCoveredSites)[CoveredSitesIndex].CoveredSite );
}
} else if ( (DomFlags & DOM_REAL_DOMAIN) != 0 ||
(DomFlags & DOM_NON_DOMAIN_NC) != 0 ) {
if ( DomainInfo->CoveredSites != NULL ) {
LocalFree( DomainInfo->CoveredSites );
DomainInfo->CoveredSites = NULL;
DomainInfo->CoveredSitesCount = 0;
}
if ( CoveredSitesCount != 0 ) {
DomainInfo->CoveredSites = LocalAlloc( 0, CoveredSitesCount * sizeof(NL_COVERED_SITE) );
if ( DomainInfo->CoveredSites != NULL ) {
RtlCopyMemory( DomainInfo->CoveredSites,
CoveredSites,
CoveredSitesCount * sizeof(NL_COVERED_SITE) );
DomainInfo->CoveredSitesCount = CoveredSitesCount;
} else {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Reference the newly added entries, if any
//
for ( CoveredSitesIndex = 0; CoveredSitesIndex < DomainInfo->CoveredSitesCount; CoveredSitesIndex++ ) {
NlRefSiteEntry( (DomainInfo->CoveredSites)[CoveredSitesIndex].CoveredSite );
}
}
//
// Now that the datebase is consistent, drop the lock for the next pass
//
LeaveCriticalSection( &NlGlobalSiteCritSect );
Cleanup:
if ( DcsInSite != NULL ) {
LocalFree( DcsInSite );
}
if ( DcInfo != NULL ) {
(*NlGlobalpDsFreeDomainControllerInfoW)( 1, DcInfoCount, DcInfo );
}
if ( GcInfo != NULL ) {
DsFreeNameResultW( GcInfo );
}
if ( ServerSitePairs != NULL ) {
NlDsFreeServersAndSitesForNetLogon( ServerSitePairs );
}
//
// Free the temprory list of covered sites.
// Deref each temp entry.
//
if ( CoveredSites != NULL ) {
for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
NlDerefSiteEntry( CoveredSites[CoveredSitesIndex].CoveredSite );
}
LocalFree( CoveredSites );
}
//
// Update the site coverage change info only if it indeed changed
//
if ( NetStatus == NO_ERROR && SiteCoverageChanged != NULL && LocalSiteCoverageChanged ) {
*SiteCoverageChanged = TRUE;
}
return NO_ERROR;
}
PNL_SITE_ENTRY
NlFindSiteEntry(
IN LPWSTR SiteName
)
/*++
Routine Description:
This routine finds a site entry for a particular site name. If one
does not exist, one is created.
Arguments:
SiteName - Name of the site.
Return Value:
Pointer to the Site entry for the site.
NULL: Memory could not be allocated.
--*/
{
PLIST_ENTRY ListEntry;
ULONG SiteNameSize;
PNL_SITE_ENTRY SiteEntry;
UNICODE_STRING SiteNameString;
//
// If the site entry already exists,
// return a pointer to it.
//
RtlInitUnicodeString( &SiteNameString, SiteName );
EnterCriticalSection( &NlGlobalSiteCritSect );
for ( ListEntry = NlGlobalSiteList.Flink ;
ListEntry != &NlGlobalSiteList;
ListEntry = ListEntry->Flink) {
SiteEntry =
CONTAINING_RECORD( ListEntry, NL_SITE_ENTRY, Next );
if ( RtlEqualUnicodeString( &SiteEntry->SiteNameString,
&SiteNameString,
TRUE ) ) {
NlRefSiteEntry( SiteEntry );
LeaveCriticalSection( &NlGlobalSiteCritSect );
return SiteEntry;
}
}
//
// If not,
// allocate one.
//
SiteNameSize = SiteNameString.Length + sizeof(WCHAR);
SiteEntry = LocalAlloc( 0, sizeof(NL_SITE_ENTRY) + SiteNameSize );
if ( SiteEntry == NULL ) {
LeaveCriticalSection( &NlGlobalSiteCritSect );
return NULL;
}
//
// Fill it in.
//
// Being in global list is not a reference.
SiteEntry->ReferenceCount = 1;
SiteEntry->SiteNameString.Length = SiteNameString.Length;
SiteEntry->SiteNameString.MaximumLength = SiteNameString.Length + sizeof(WCHAR);
SiteEntry->SiteNameString.Buffer = SiteEntry->SiteName;
RtlCopyMemory( &SiteEntry->SiteName, SiteName, SiteNameSize );
InsertHeadList( &NlGlobalSiteList, &SiteEntry->Next );
LeaveCriticalSection( &NlGlobalSiteCritSect );
return SiteEntry;
}
VOID
NlSitesRefSubnet(
IN PNL_SUBNET Subnet
)
/*++
Routine Description:
Reference a subnet
NlGlobalSiteCritSect must be locked.
Arguments:
Subnet - Entry to be Referenced.
Return Value:
None.
--*/
{
Subnet->ReferenceCount++;
}
PNL_SUBNET
NlFindSubnetEntry(
IN LPWSTR SiteName,
IN ULONG SubnetAddress,
IN ULONG SubnetMask,
IN BYTE SubnetBitCount
)
/*++
Routine Description:
This routine finds a subnet entry for a particular subnet name. If one
does not exist, one is created.
Arguments:
SiteName - Name of the site the subnet covers.
SubnetAddress - Subnet Address for the subnet to find.
SubnetMask - Subnet mask for the subnet to find.
SubnetBitCount - Subnet bit count for the subnet to find.
Return Value:
Pointer to the Subnet entry for the site.
Entry should be dereferenced using NlSitesDerefSubnet
NULL: Memory could not be allocated.
--*/
{
PLIST_ENTRY ListEntry;
ULONG SiteNameSize;
PNL_SUBNET Subnet;
//
// If the subnet entry already exists,
// return a pointer to it.
//
EnterCriticalSection( &NlGlobalSiteCritSect );
for ( ListEntry = NlGlobalSubnetList.Flink ;
ListEntry != &NlGlobalSubnetList;
ListEntry = ListEntry->Flink) {
Subnet =
CONTAINING_RECORD( ListEntry, NL_SUBNET, Next );
if ( Subnet->SubnetAddress == SubnetAddress &&
Subnet->SubnetBitCount == SubnetBitCount &&
Subnet->SubnetMask == SubnetMask &&
_wcsicmp( Subnet->SiteEntry->SiteName, SiteName ) == 0 ) {
#if NETLOGONDBG
{
CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
NlPrint(( NL_SITE, "%s/%ld: Re-adding Subnet for site '%ws'\n", IpAddress, Subnet->SubnetBitCount, SiteName ));
}
#endif // NETLOGONDBG
NlSitesRefSubnet( Subnet );
LeaveCriticalSection( &NlGlobalSiteCritSect );
return Subnet;
}
}
//
// If not,
// allocate one.
//
Subnet = LocalAlloc( 0, sizeof(NL_SUBNET) );
if ( Subnet == NULL ) {
LeaveCriticalSection( &NlGlobalSiteCritSect );
return NULL;
}
//
// Fill it in.
//
// Being in global list is not a reference.
Subnet->ReferenceCount = 1;
Subnet->SubnetAddress = SubnetAddress;
Subnet->SubnetMask = SubnetMask;
Subnet->SubnetBitCount = SubnetBitCount;
Subnet->SiteEntry = NlFindSiteEntry( SiteName );
if ( Subnet->SiteEntry == NULL ) {
LocalFree( Subnet );
LeaveCriticalSection( &NlGlobalSiteCritSect );
return NULL;
}
#if NETLOGONDBG
{
CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
NlPrint(( NL_SITE, "%s/%ld: Adding Subnet for site '%ws'\n",
IpAddress,
Subnet->SubnetBitCount,
SiteName ));
}
#endif // NETLOGONDBG
InsertHeadList( &NlGlobalSubnetList, &Subnet->Next );
LeaveCriticalSection( &NlGlobalSiteCritSect );
return Subnet;
}
VOID
NlSitesDerefSubnet(
IN PNL_SUBNET Subnet
)
/*++
Routine Description:
Dereference a subnet
If the reference count goes to zero,
the subnet entry will be deleted.
Arguments:
Subnet - Entry to be dereferenced.
Return Value:
None.
--*/
{
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( (--(Subnet->ReferenceCount)) == 0 ) {
#if NETLOGONDBG
CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
NlPrint(( NL_SITE, "%s/%ld: Subnet deleted\n", IpAddress, Subnet->SubnetBitCount ));
#endif // NETLOGONDBG
//
// If there is a site associated with this subnet,
// dereference it.
//
if ( Subnet->SiteEntry != NULL ) {
NlDerefSiteEntry( Subnet->SiteEntry );
}
//
// Remove the subnet from the global list
//
RemoveEntryList( &Subnet->Next );
//
// Free the Subnet entry itself.
//
LocalFree( Subnet );
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
}
VOID
NlSiteDeleteSubnetTree(
IN PNL_SUBNET_TREE_ENTRY SubnetTreeEntry
)
/*++
Routine Description:
Delete everything pointed to by this SubnetTreeEntry
Enter with NlGlobalSiteCritSect locked.
Arguments:
SubnetTreeEntry - SubnetTreeEntry to de-initialize
Return Value:
TRUE: SubnetTreeEntry is now empty
FALSE: SubnetTreeEntry still has entries.
--*/
{
//
// If there are children,
// delete them.
//
if ( SubnetTreeEntry->Subtree != NULL ) {
ULONG i;
for ( i=0; i<256; i++ ) {
NlSiteDeleteSubnetTree( &SubnetTreeEntry->Subtree->Subtree[i] );
}
NlPrint(( NL_SITE_MORE, "Deleting subtree\n" ));
LocalFree( SubnetTreeEntry->Subtree );
SubnetTreeEntry->Subtree = NULL;
}
//
// If there is a subnet,
// dereference it.
//
if ( SubnetTreeEntry->Subnet != NULL ) {
// NlPrint(( NL_SITE_MORE, "Derefing subnet upon tree deletion\n" ));
NlSitesDerefSubnet( SubnetTreeEntry->Subnet );
SubnetTreeEntry->Subnet = NULL;
}
return;
}
VOID
NlSitesEndSubnetEnum(
VOID
)
/*++
Routine Description:
This routine is called at the end of a set of NlSitesAddSubnet calls.
The sequence is:
loop for each subnet
NlSitesAddSubnet
NlSitesEndSubnetEnum
NlSiteAddSubnet adds the entries to a temporary tree. This routine
swaps the temporary tree into the permanent location. This mechanism
does the following:
a) Allows the old subnet tree to be used while the new tree is being built.
b) Allows me to not permanently grab the SiteCritSect for the entire
enumeration of subnet/site objects from the DS.
c) Reuse the in-memory subnet/site structures in the old and new tree. This
avoids re-allocation of these structures (or worse temporarily doubling
of memory usage).
Arguments:
SiteName - Name of the site the subnet is in.
SubnetName - subnet to be added
Return Value:
NO_ERROR: success
ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
--*/
{
//
// Free all old entries in NlGlobalSubnetTree.
//
NlPrint(( NL_SITE_MORE, "NlSitesEndSubnetEnum: Entered\n" ));
EnterCriticalSection( &NlGlobalSiteCritSect );
NlSiteDeleteSubnetTree( &NlGlobalSubnetTree );
//
// Make the "new" subnet tree the real subnet tree.
//
NlGlobalSubnetTree = NlGlobalNewSubnetTree;
RtlZeroMemory( &NlGlobalNewSubnetTree, sizeof(NlGlobalNewSubnetTree) );
LeaveCriticalSection( &NlGlobalSiteCritSect );
NlPrint(( NL_SITE_MORE, "NlSitesEndSubnetEnum: Exitted\n" ));
}
NET_API_STATUS
NlSitesAddSubnet(
IN LPWSTR SiteName,
IN LPWSTR SubnetName
)
/*++
Routine Description:
This routine adds a subnet to the tree of subnets.
Arguments:
SiteName - Name of the site the subnet is in.
SubnetName - subnet to be added
Return Value:
NO_ERROR: success
ERROR_INVALID_NAME: Subnet Name is not valid
ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
--*/
{
NET_API_STATUS NetStatus;
PNL_SUBNET Subnet = NULL;
PNL_SUBNET_TREE_ENTRY SubnetTreeEntry;
LPBYTE SubnetBytePointer;
ULONG i;
ULONG SubnetAddress;
ULONG SubnetMask;
BYTE SubnetBitCount;
//
// Parse the subnet name
//
EnterCriticalSection( &NlGlobalSiteCritSect );
NetStatus = NlParseSubnetString( SubnetName,
&SubnetAddress,
&SubnetMask,
&SubnetBitCount );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Find or allocate an entry for the subnet
//
Subnet = NlFindSubnetEntry( SiteName,
SubnetAddress,
SubnetMask,
SubnetBitCount );
if ( Subnet == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Loop for each byte in the subnet address
//
SubnetTreeEntry = &NlGlobalNewSubnetTree;
SubnetBytePointer = (LPBYTE) (&Subnet->SubnetAddress);
while ( SubnetBitCount != 0 ) {
NlPrint(( NL_SITE_MORE, "%ld: Doing byte\n", *SubnetBytePointer ));
//
// If there isn't a tree branch for the current node,
// create one.
//
if ( SubnetTreeEntry->Subtree == NULL ) {
NlPrint(( NL_SITE_MORE, "%ld: Creating subtree\n", *SubnetBytePointer ));
SubnetTreeEntry->Subtree = LocalAlloc( LMEM_ZEROINIT, sizeof(NL_SUBNET_TREE) );
if ( SubnetTreeEntry->Subtree == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
//
// If this is the last byte of the subnet address,
// link the subnet onto the tree here.
//
if ( SubnetBitCount <= 8 ) {
ULONG LoopCount;
//
// The caller indexes into this array with an IP address.
// Create a link to our subnet for each possible IP addresses
// that map onto this subnet.
//
// Between 1 and 128 IP addresses map onto this subnet address.
//
LoopCount = 1 << (8-SubnetBitCount);
for ( i=0; i<LoopCount; i++ ) {
PNL_SUBNET_TREE_ENTRY Subtree;
ULONG SubnetIndex;
//
// Compute which entry is to be updated.
//
SubnetIndex = (*SubnetBytePointer) + i;
NlPrint(( NL_SITE_MORE, "%ld: Doing sub-byte\n", SubnetIndex ));
NlAssert( SubnetIndex <= 255 );
Subtree = &SubnetTreeEntry->Subtree->Subtree[SubnetIndex];
//
// If there already is a subnet linked off the tree here,
// handle it.
//
if ( Subtree->Subnet != NULL ) {
NlPrint(( NL_SITE_MORE, "%ld: Subnet already exists %ld\n",
SubnetIndex,
Subtree->Subnet->SubnetBitCount ));
//
// If the entry is for a less specific subnet
// delete the current entry.
//
if ( Subtree->Subnet->SubnetBitCount < Subnet->SubnetBitCount ) {
NlPrint(( NL_SITE_MORE, "%ld: Deref previous subnet\n",
SubnetIndex ));
NlSitesDerefSubnet( Subtree->Subnet );
Subtree->Subnet = NULL;
//
// Otherwise,
// use the current entry since it is better than this one.
//
} else {
NlPrint(( NL_SITE_MORE, "%ld: Use previous subnet\n",
SubnetIndex ));
continue;
}
}
//
// Link the subnet into the tree.
// Increment the reference count.
//
NlSitesRefSubnet( Subnet );
Subtree->Subnet = Subnet;
}
break;
}
//
// Move on to the next byte of the subnet address
//
SubnetTreeEntry = &SubnetTreeEntry->Subtree->Subtree[*SubnetBytePointer];
SubnetBitCount -= 8;
SubnetBytePointer ++;
}
NetStatus = NO_ERROR;
//
// Free locally used resources.
//
Cleanup:
if ( Subnet != NULL ) {
NlSitesDerefSubnet( Subnet );
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
return NetStatus;
}
PNL_SITE_ENTRY
NlFindSiteEntryBySockAddrEx(
IN PSOCKADDR SockAddr,
OUT PNL_SUBNET *RetSubnet OPTIONAL
)
/*++
Routine Description:
This routine look up the specified socket address and translate it to a
site name.
Arguments:
SockAddr - Socket Address to lookup
RetSubnet - If specified, returns a pointer to the subnet object used to do
the mapping.
Might return NULL indicating a subnet object wasn't used.
Entry should be dereferenced using NlSitesDerefSubnet.
Return Value:
NULL: No site can be found for this SockAddr.
Non-NULL: Site corresponding to the SockAddr.
Entry should be derefenced using NlDerefSiteEntry
--*/
{
PNL_SITE_ENTRY SiteEntry = NULL;
PNL_SUBNET Subnet = NULL;
PNL_SUBNET_TREE_ENTRY SubnetTreeEntry;
ULONG ByteIndex;
ULONG IpAddress;
//
// Convert SockAddr to IP address.
//
if ( ARGUMENT_PRESENT(RetSubnet) ) {
*RetSubnet = NULL;
}
if ( SockAddr->sa_family != AF_INET ) {
return NULL;
}
IpAddress = ((PSOCKADDR_IN)SockAddr)->sin_addr.S_un.S_addr;
//
// If there are no subnet entries and only one site,
// then all clients belong to that site.
// Don't bother mapping.
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( NlGlobalOnlyOneSite ) {
if ( NlGlobalSiteEntry == NULL ) {
LeaveCriticalSection( &NlGlobalSiteCritSect );
return NULL;
}
SiteEntry = NlGlobalSiteEntry;
NlRefSiteEntry( SiteEntry );
//
// If the caller isn't interested in the subnet name,
// we are done
//
if ( RetSubnet == NULL ) {
LeaveCriticalSection( &NlGlobalSiteCritSect );
return SiteEntry;
}
}
//
// Loop for each byte in the Ip address
//
SubnetTreeEntry = &NlGlobalSubnetTree;
for ( ByteIndex=0; ByteIndex<sizeof(IpAddress); ByteIndex++) {
ULONG SubnetIndex;
//
// If there is no subtree,
// we're done.
//
SubnetIndex = ((LPBYTE)(&IpAddress))[ByteIndex];
NlPrint(( NL_SITE_MORE, "%ld: Lookup: Doing byte\n", SubnetIndex ));
if ( SubnetTreeEntry->Subtree == NULL ) {
break;
}
//
// Compute which entry is being referenced
//
SubnetTreeEntry = &SubnetTreeEntry->Subtree->Subtree[SubnetIndex];
//
// If there already is a subnet linked off here,
// use it.
//
// (but continue walking down the tree trying to find a more explicit entry.)
//
if ( SubnetTreeEntry->Subnet != NULL ) {
NlPrint(( NL_SITE_MORE, "%ld: Lookup: saving subnet at this level\n", SubnetIndex ));
Subnet = SubnetTreeEntry->Subnet;
}
}
//
// If we found a subnet,
// return the site associated with the subnet.
if ( Subnet != NULL ) {
//
// If we already know the site name (because there is
// only one site), this subnet must map to it
//
if ( SiteEntry != NULL ) {
NlAssert( SiteEntry == Subnet->SiteEntry );
} else {
SiteEntry = Subnet->SiteEntry;
NlRefSiteEntry( SiteEntry );
}
if ( ARGUMENT_PRESENT(RetSubnet) ) {
NlSitesRefSubnet( Subnet );
*RetSubnet = Subnet;
}
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
return SiteEntry;
}
PNL_SITE_ENTRY
NlFindSiteEntryBySockAddr(
IN PSOCKADDR SockAddr
)
/*++
Routine Description:
This routine look up the specified socket address and translate it to a
site name.
Arguments:
SockAddr - Socket Address to lookup
RetSubnet - If specified, returns a pointer to the subnet object used to do
the mapping.
Might return NULL indicating a subnet object wasn't used.
Entry should be dereferenced using NlSitesDerefSubnet.
Return Value:
NULL: No site can be found for this SockAddr.
Non-NULL: Site corresponding to the SockAddr.
Entry should be derefenced using NlDerefSiteEntry
--*/
{
return NlFindSiteEntryBySockAddrEx( SockAddr, NULL );
}
BOOL
NlCaptureSiteName(
WCHAR CapturedSiteName[NL_MAX_DNS_LABEL_LENGTH+1]
)
/*++
Routine Description:
Capture the current sitename of the site this machine is in.
Arguments:
CapturedSiteName - Returns the name of the site this machine is in.
Return Value:
TRUE - if there is a site name.
FALSE - if there is no site name.
--*/
{
BOOL RetVal;
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( NlGlobalUnicodeSiteName == NULL ) {
CapturedSiteName[0] = L'\0';
RetVal = FALSE;
} else {
wcscpy( CapturedSiteName, NlGlobalUnicodeSiteName );
RetVal = TRUE;
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
return RetVal;
}
NET_API_STATUS
DsrGetSiteName(
IN LPWSTR ComputerName OPTIONAL,
OUT LPWSTR *SiteName
)
/*++
Routine Description:
Same as DsGetSiteNameW except:
* This is the RPC server side implementation.
Arguments:
Same as DsGetSiteNameW except as above.
Return Value:
Same as DsGetSiteNameW except as above.
Note: On a workstation or a member server, this function makes a
reasonable attempt to retrieve a valid name of the site ComputerName
is in. If the locally stored name is too old, the function receives
the name from a DC. If any error occurs during this process, the local
value for the name is returned. It is possible that the name received
from the DC is out of date. In that case the function will return it
anyway.
--*/
{
NET_API_STATUS NetStatus;
NTSTATUS Status;
UNICODE_STRING DomainNameString;
PDOMAIN_INFO DomainInfo = NULL;
PCLIENT_SESSION ClientSession = NULL;
PNL_DC_CACHE_ENTRY NlDcCacheEntry;
BOOL AmWriter = FALSE;
//
// Lookup which domain this call pertains to.
//
DomainInfo = NlFindDomainByServerName( ComputerName );
if ( DomainInfo == NULL ) {
NetStatus = ERROR_INVALID_COMPUTERNAME;
goto Cleanup;
}
EnterCriticalSection( &NlGlobalSiteCritSect );
//
// On a workstation or a member server, update the site name if it's not
// statically configured and is old.
// However, do not update if we are in NT4 domain since there is no site
// concept in NT4.
//
if ( NlGlobalMemberWorkstation &&
!NlGlobalParameters.SiteNameConfigured &&
DomainInfo->DomUnicodeDnsDomainNameString.Length != 0 &&
NetpLogonTimeHasElapsed(
NlGlobalSiteNameSetTime,
NlGlobalParameters.SiteNameTimeout * 1000 ) ) {
NlPrint(( NL_SITE, "DsrGetSiteName: Site name '%ws' is old. Getting a new one from DC.\n",
NlGlobalUnicodeSiteName ));
LeaveCriticalSection( &NlGlobalSiteCritSect );
//
// Fill in the primary domain name.
//
RtlInitUnicodeString( &DomainNameString, DomainInfo->DomUnicodeDomainName );
//
// On the PDC or BDC,
// find the Client session for the domain.
// On workstations,
// find the primary domain client session.
//
ClientSession = NlFindNamedClientSession( DomainInfo,
&DomainNameString,
NL_DIRECT_TRUST_REQUIRED,
NULL );
if ( ClientSession == NULL ) {
NlPrintDom(( NL_CRITICAL, DomainInfo,
"DsrGetSiteName: %wZ: No such trusted domain\n",
&DomainNameString ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
goto Cleanup;
}
//
// Become a writer of the client session.
//
if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
NlPrintCs(( NL_CRITICAL, ClientSession,
"DsrGetSiteName: Can't become writer of client session.\n" ));
NetStatus = ERROR_NO_LOGON_SERVERS;
goto Cleanup;
}
AmWriter = TRUE;
//
// Get the DC info from the server
//
Status = NlGetAnyDCName( ClientSession,
TRUE, // Require IP be used to determine site correctly
FALSE, // Don't do with-account discovery
&NlDcCacheEntry,
NULL ); // don't care if the DC was rediscovered
//
// Do not error out on failure. Rather, use local cache.
// Use the response only if it is from an NT5 DC (that
// knows about the site of the client)
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( NT_SUCCESS(Status) ) {
if ( (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) != 0 ) {
NlSetDynamicSiteName( NlDcCacheEntry->UnicodeClientSiteName );
} else {
NlPrint(( NL_SITE,
"DsrGetSiteName: NlGetAnyDCName returned NT4 DC. Returning site '%ws' from local cache\n",
NlGlobalUnicodeSiteName ));
}
NetpDcDerefCacheEntry( NlDcCacheEntry );
} else {
NlPrint(( NL_CRITICAL,
"DsrGetSiteName: NlGetAnyDCName failed. Returning site '%ws' from local cache.\n",
NlGlobalUnicodeSiteName ));
}
} else {
NlPrint(( NL_SITE, "DsrGetSiteName: Returning site name '%ws' from local cache.\n",
NlGlobalUnicodeSiteName ));
}
if ( NlGlobalUnicodeSiteName == NULL ) {
*SiteName = NULL;
NetStatus = ERROR_NO_SITENAME;
} else {
*SiteName = NetpAllocWStrFromWStr( NlGlobalUnicodeSiteName );
if ( *SiteName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
NetStatus = NO_ERROR;
}
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
Cleanup:
if ( DomainInfo != NULL ) {
NlDereferenceDomain( DomainInfo );
}
if ( ClientSession != NULL ) {
if ( AmWriter ) {
NlResetWriterClientSession( ClientSession );
}
NlUnrefClientSession( ClientSession );
}
return NetStatus;
}
NET_API_STATUS
NlSetSiteName(
IN LPWSTR SiteName OPTIONAL,
OUT PBOOLEAN SiteNameChanged OPTIONAL
)
/*++
Routine Description:
This routine set the current site name in a global.
Any bogus site name is truncated to be a valid site name.
Arguments:
SiteName - Name of the site this machine is in.
NULL: machine is no longer in a site.
SiteNameChanged - If specified, returns TRUE if the site name changed
Return Value:
NO_ERROR: success
ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
--*/
{
NET_API_STATUS NetStatus;
LPWSTR TempUnicodeSiteName = NULL;
LPWSTR LocalUnicodeSiteName = NULL;
LPSTR LocalUtf8SiteName = NULL;
PNL_SITE_ENTRY LocalSiteEntry = NULL;
//
// Initialization
//
if ( ARGUMENT_PRESENT( SiteNameChanged )) {
*SiteNameChanged = FALSE;
}
//
// If the site name hasn't changed,
// early out.
// (Case sensitive compare to allow case changes.)
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( SiteName != NULL &&
NlGlobalUnicodeSiteName != NULL &&
wcscmp( NlGlobalUnicodeSiteName, SiteName ) == 0 ) {
LeaveCriticalSection( &NlGlobalSiteCritSect );
NetStatus = NO_ERROR;
goto Cleanup;
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
//
// Copy the site name into a Locally allocated buffer.
//
NlPrint(( NL_SITE, "Setting site name to '%ws'\n", SiteName ));
if ( SiteName == NULL ) {
LocalUnicodeSiteName = NULL;
LocalUtf8SiteName = NULL;
LocalSiteEntry = NULL;
} else {
BOOLEAN LogMessage = FALSE;
UNICODE_STRING UnicodeStringOfSiteName;
LPWSTR Period;
DNS_STATUS DnsStatus;
//
// Ditch any period in the site name.
//
RtlInitUnicodeString( &UnicodeStringOfSiteName, SiteName );
Period = wcschr( SiteName, L'.' );
if ( Period != NULL ) {
UnicodeStringOfSiteName.Length = (USHORT)(Period-SiteName) * sizeof(WCHAR);
NlPrint(( NL_CRITICAL,
"Site name '%ws' contains a period (truncated to '%wZ').\n",
SiteName,
&UnicodeStringOfSiteName ));
if ( UnicodeStringOfSiteName.Length == 0 ) {
NlPrint(( NL_CRITICAL,
"Site name '%ws' truncated to zero characters (Set to '1').\n",
SiteName ));
RtlInitUnicodeString( &UnicodeStringOfSiteName, L"1" );
}
LogMessage = TRUE;
}
//
// Loop truncating the name until it is short enough.
//
// The length restriction only makes sense in the UTF-8 character set.
// UTF-8 has multibyte characters so only truncate the UNICODE string
// and test the UTF-8 string.
//
for (;;) {
LocalUtf8SiteName = NetpAllocUtf8StrFromUnicodeString( &UnicodeStringOfSiteName );
if ( LocalUtf8SiteName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// If the site name is OK, we're done.
//
if ( strlen(LocalUtf8SiteName) <= NL_MAX_DNS_LABEL_LENGTH ) {
break;
}
//
// Truncate the site name (and press on)
//
UnicodeStringOfSiteName.Length -= sizeof(WCHAR);
NlPrint(( NL_CRITICAL,
"Site name '%ws' is too long (trucated to '%wZ')\n",
SiteName,
&UnicodeStringOfSiteName ));
LogMessage = TRUE;
}
//
// Validate the character set of the site name.
// (If invalid, map the bogus characters)
//
DnsStatus = DnsValidateName_UTF8( LocalUtf8SiteName, DnsNameDomain );
if ( DnsStatus != ERROR_SUCCESS &&
DnsStatus != DNS_ERROR_NON_RFC_NAME ) {
ULONG i;
//
// Grab a copy of the string to map into
//
TempUnicodeSiteName = NetpAllocWStrFromWStr( SiteName );
if ( TempUnicodeSiteName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
UnicodeStringOfSiteName.Buffer = TempUnicodeSiteName;
//
// Map the bogus characters
//
for ( i=0; i<UnicodeStringOfSiteName.Length/sizeof(WCHAR); i++) {
WCHAR JustOneChar[2];
//
// Test one character at a time.
//
JustOneChar[0] = UnicodeStringOfSiteName.Buffer[i];
JustOneChar[1] = '\0';
DnsStatus = DnsValidateName_W( JustOneChar, DnsNameDomain );
if ( DnsStatus != ERROR_SUCCESS &&
DnsStatus != DNS_ERROR_NON_RFC_NAME ) {
UnicodeStringOfSiteName.Buffer[i] = L'-';
}
}
//
// Map back to UTF-8
//
NetpMemoryFree( LocalUtf8SiteName );
LocalUtf8SiteName = NetpAllocUtf8StrFromUnicodeString( &UnicodeStringOfSiteName );
if ( LocalUtf8SiteName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
NlPrint(( NL_CRITICAL,
"Site name '%ws' has invalid character (set to '%wZ')\n",
SiteName,
&UnicodeStringOfSiteName ));
LogMessage = TRUE;
}
//
// If any munging of the name occurred,
// log the failure.
//
if ( LogMessage ) {
LPWSTR MsgStrings[1];
MsgStrings[0] = (LPWSTR) SiteName;
NlpWriteEventlog( NELOG_NetlogonBadSiteName,
EVENTLOG_ERROR_TYPE,
NULL,
0,
MsgStrings,
1 );
}
LocalUnicodeSiteName = NetpAllocWStrFromUtf8Str( LocalUtf8SiteName );
if ( LocalUnicodeSiteName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
NlAssert( wcslen(LocalUnicodeSiteName) <= NL_MAX_DNS_LABEL_LENGTH );
LocalSiteEntry = NlFindSiteEntry( LocalUnicodeSiteName );
if ( LocalSiteEntry == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
//
// If the site name hasn't changed (Using modified site name),
// early out.
// (Case sensitive compare to allow case changes.)
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( LocalUnicodeSiteName != NULL &&
NlGlobalUnicodeSiteName != NULL &&
wcscmp( NlGlobalUnicodeSiteName, LocalUnicodeSiteName ) == 0 ) {
LeaveCriticalSection( &NlGlobalSiteCritSect );
NetStatus = NO_ERROR;
goto Cleanup;
}
//
// Free any previous entry
//
if ( NlGlobalUnicodeSiteName != NULL ) {
NetpMemoryFree( NlGlobalUnicodeSiteName );
}
if ( NlGlobalUtf8SiteName != NULL ) {
NetpMemoryFree( NlGlobalUtf8SiteName );
}
if ( NlGlobalSiteEntry != NULL ) {
NlDerefSiteEntry( NlGlobalSiteEntry );
}
//
// Save the new site name.
//
NlGlobalUnicodeSiteName = LocalUnicodeSiteName;
LocalUnicodeSiteName = NULL;
NlGlobalUtf8SiteName = LocalUtf8SiteName;
LocalUtf8SiteName = NULL;
NlGlobalSiteEntry = LocalSiteEntry;
LocalSiteEntry = NULL;
LeaveCriticalSection( &NlGlobalSiteCritSect );
if ( ARGUMENT_PRESENT( SiteNameChanged )) {
*SiteNameChanged = TRUE;
}
NetStatus = NO_ERROR;
//
// Cleanup local data.
//
Cleanup:
if ( TempUnicodeSiteName != NULL ) {
NetpMemoryFree( TempUnicodeSiteName );
}
if ( LocalUnicodeSiteName != NULL ) {
NetApiBufferFree( LocalUnicodeSiteName );
}
if ( LocalUtf8SiteName != NULL ) {
NetpMemoryFree( LocalUtf8SiteName );
}
if ( LocalSiteEntry != NULL ) {
NlDerefSiteEntry( LocalSiteEntry );
}
//
// Set the time when the site name was updated
//
if ( NetStatus == NO_ERROR ) {
NlQuerySystemTime( &NlGlobalSiteNameSetTime );
}
return NetStatus;
}
VOID
NlSetDynamicSiteName(
IN LPWSTR SiteName OPTIONAL
)
/*++
Routine Description:
This routine set the current site name of this machine in the registry
and in Netlogon globals
Arguments:
SiteName - Name of the site this machine is in.
NULL: machine is no longer in a site.
Return Value:
None.
--*/
{
NET_API_STATUS NetStatus;
HKEY ParmHandle = NULL;
ULONG SiteNameSize;
//
// Avoid changing the site name on DCs.
//
if ( !NlGlobalMemberWorkstation ) {
return;
}
//
// Don't change the sitename back to its current value.
// (Case sensitive compare to allow case changes.)
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( NlGlobalUnicodeSiteName != NULL &&
SiteName != NULL &&
wcscmp(SiteName, NlGlobalUnicodeSiteName) == 0 ) {
NlPrint(( NL_SITE_MORE, "NlSetDynamicSiteName: Old and new site names '%ws' are identical.\n",
SiteName ));
NlQuerySystemTime( &NlGlobalSiteNameSetTime );
goto Cleanup;
}
//
// If the site name was explicitly configured,
// don't set the site name.
//
if ( NlGlobalParameters.SiteNameConfigured ) {
NlPrint(( NL_SITE_MORE,
"Cannot set site name to %ws from %ws since it is statically configured\n",
SiteName,
NlGlobalUnicodeSiteName ));
goto Cleanup;
}
//
// Save the name in globals.
//
NlSetSiteName( SiteName, NULL );
//
// Save the name in the registry to keep it across boots.
//
//
// Open the key for Netlogon\Parameters
//
ParmHandle = NlOpenNetlogonKey( NL_PARAM_KEY );
if (ParmHandle == NULL) {
NlPrint(( NL_CRITICAL,
"Cannot NlOpenNetlogonKey to set site name to %ws from %ws\n",
SiteName,
NlGlobalUnicodeSiteName ));
goto Cleanup;
}
//
// If the we're no longer in a site,
// delete the value.
//
if ( SiteName == NULL ) {
NetStatus = RegDeleteValueW( ParmHandle,
NETLOGON_KEYWORD_DYNAMICSITENAME );
if ( NetStatus != ERROR_SUCCESS ) {
if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
NlPrint(( NL_CRITICAL,
"NlSetDynamicSiteName: Cannot delete '" NL_PARAM_KEY "\\%ws' %ld.\n",
NETLOGON_KEYWORD_DYNAMICSITENAME,
NetStatus ));
}
goto Cleanup;
}
//
// Set the value in the registry.
//
} else {
SiteNameSize = (wcslen(SiteName)+1) * sizeof(WCHAR);
NetStatus = RegSetValueExW( ParmHandle,
NETLOGON_KEYWORD_DYNAMICSITENAME,
0, // Reserved
REG_SZ,
(LPBYTE)SiteName,
SiteNameSize+1 );
if ( NetStatus != ERROR_SUCCESS ) {
NlPrint(( NL_CRITICAL,
"NlSetDynamicSiteName: Cannot Set '" NL_PARAM_KEY "\\%ws' %ld.\n",
NETLOGON_KEYWORD_DYNAMICSITENAME,
NetStatus ));
goto Cleanup;
}
}
Cleanup:
LeaveCriticalSection( &NlGlobalSiteCritSect );
if ( ParmHandle != NULL ) {
RegCloseKey( ParmHandle );
}
return;
}
NET_API_STATUS
NlSitesAddSubnetFromDs(
OUT PBOOLEAN SiteNameChanged OPTIONAL
)
/*++
Routine Description:
This routine reads the subnet\site mapping from the DS and populates
Netlogon's cache with that information
Arguments:
SiteNameChanged - If specified, returns TRUE if the site name changed
Return Value:
NO_ERROR: success
ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
--*/
{
NET_API_STATUS NetStatus;
NTSTATUS Status;
PLSAP_SUBNET_INFO SubnetInfo = NULL;
PLSAP_SITENAME_INFO SiteNameInfo = NULL;
ULONG i;
BOOLEAN MoreThanOneSite = FALSE;
ULONG LocalSubnetCount = 0;
//
// Get the site name of this site.
//
Status = LsaIGetSiteName( &SiteNameInfo );
if ( !NT_SUCCESS(Status) ) {
//
// If the DS simply isn't running,
// skip this.
//
if ( Status == STATUS_INVALID_DOMAIN_STATE ) {
NlPrint(( NL_SITE,
"DS isn't running so site to subnet mapping ignored\n" ));
NetStatus = NO_ERROR;
goto Cleanup;
}
NlPrint(( NL_CRITICAL,
"Cannot LsaIGetSiteName %lx\n", Status ));
NetStatus = NetpNtStatusToApiStatus(Status);
goto Cleanup;
}
NlGlobalDsaGuid = SiteNameInfo->DsaGuid;
NetStatus = NlSetSiteName( SiteNameInfo->SiteName.Buffer, SiteNameChanged );
if ( NetStatus != NO_ERROR ) {
NlPrint(( NL_CRITICAL,
"Cannot NlSetSiteName %ld\n", NetStatus ));
goto Cleanup;
}
//
// If this machine is marked as a GC,
// flag it so.
//
// Really this is only needed if netlogon.dll is unloaded via nltest /unload.
// Otherwise the flag is saved across starts/stops in a global.
//
if ( NlGlobalNetlogonUnloaded &&
(SiteNameInfo->DsaOptions & NTDSDSA_OPT_IS_GC) != 0 ) {
NlPrint((NL_INIT,
"Set GC-running bit after netlogon.dll unload\n" ));
I_NetLogonSetServiceBits( DS_GC_FLAG, DS_GC_FLAG );
}
//
// Get the list of subnet to site mappings from the DS
//
NlPrint(( NL_SITE, "Adding subnet to site mappings from the DS\n" ));
Status = LsaIQuerySubnetInfo( &SubnetInfo );
if ( !NT_SUCCESS(Status) ) {
NlPrint((NL_CRITICAL, "Cannot LsaIQuerySubnetInfo %lx\n", Status ));
NetStatus = NetpNtStatusToApiStatus( Status );
goto Cleanup;
}
//
// Put them in our in-memory cache.
//
for ( i=0; i<SubnetInfo->SubnetCount; i++ ) {
//
// If there is no site associated with the subnet,
// silently ignore it.
//
if ( SubnetInfo->Subnets[i].SiteName.Length == 0 ) {
NlPrint(( NL_SITE, "%wZ: Subnet has no associated site (ignored)\n",
&SubnetInfo->Subnets[i].SubnetName ));
continue;
}
LocalSubnetCount ++;
//
// Determine if there are multiple sites in the enterprise
//
if ( !RtlEqualUnicodeString( &SiteNameInfo->SiteName,
&SubnetInfo->Subnets[i].SiteName,
TRUE )) {
NlPrint(( NL_SITE, "%wZ: Site %wZ is not site this DC is in.\n",
&SubnetInfo->Subnets[i].SubnetName,
&SubnetInfo->Subnets[i].SiteName ));
MoreThanOneSite = TRUE;
}
//
// Add the subnet to out in memory cache.
//
NetStatus = NlSitesAddSubnet(
SubnetInfo->Subnets[i].SiteName.Buffer,
SubnetInfo->Subnets[i].SubnetName.Buffer );
if ( NetStatus != NO_ERROR ) {
NlPrint(( NL_CRITICAL,
"%wZ: %wZ: Cannot add subnet-to-site mapping to cache: %ld\n",
&SubnetInfo->Subnets[i].SubnetName,
&SubnetInfo->Subnets[i].SiteName,
NetStatus ));
if ( NetStatus == ERROR_INVALID_NAME ) {
LPWSTR MsgStrings[1];
MsgStrings[0] = (LPWSTR) SubnetInfo->Subnets[i].SubnetName.Buffer;
NlpWriteEventlog( NELOG_NetlogonBadSubnetName,
EVENTLOG_INFORMATION_TYPE,
NULL,
0,
MsgStrings,
1 );
}
}
}
//
// Indicate that all the subnets have been added.
//
NlSitesEndSubnetEnum();
//
// If there are no subnet entries,
// and there is only one site in the enterprise,
// indicate that all client belong to this site.
//
// If there are subnet entries,
// and all of them indicate the same site as our site,
// indicate that all clients belong to this site.
//
EnterCriticalSection( &NlGlobalSiteCritSect );
if ( LocalSubnetCount == 0) {
NlGlobalOnlyOneSite = (SubnetInfo->SiteCount == 1);
} else {
NlGlobalOnlyOneSite = !MoreThanOneSite;
}
if ( NlGlobalOnlyOneSite ) {
NlPrint(( NL_SITE, "There is only one site. All clients belong to it.\n" ));
}
LeaveCriticalSection( &NlGlobalSiteCritSect );
NetStatus = NO_ERROR;
//
// Free locally used resources
//
Cleanup:
if ( SubnetInfo != NULL ) {
LsaIFree_LSAP_SUBNET_INFO( SubnetInfo );
}
if ( SiteNameInfo != NULL ) {
LsaIFree_LSAP_SITENAME_INFO( SiteNameInfo );
}
return NetStatus;
}
NET_API_STATUS
DsrAddressToSiteNamesW(
IN LPWSTR ComputerName,
IN DWORD EntryCount,
IN PNL_SOCKET_ADDRESS SocketAddresses,
OUT PNL_SITE_NAME_ARRAY *SiteNames
)
/*++
Routine Description:
The DsAddressToSiteNames API returns the site names that correspond to
the specified addresses.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Number of addresses to convert.
SocketAddresses - Specifies an array of addresses to convert. EntryCount
addresses must be specified. Each address must be of type AF_INET.
SiteNames - Returns an array of pointers to site names. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding
address does not map to any site or if the address is malformed.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_ENTRY *SiteEntries = NULL;
ULONG i;
ULONG Size;
PUNICODE_STRING Strings;
LPBYTE Where;
//
// This API is not supported on workstations.
//
*SiteNames = NULL;
if ( NlGlobalMemberWorkstation ) {
return ERROR_NOT_SUPPORTED;
}
//
// Initialization
//
if ( EntryCount == 0 ) {
return ERROR_INVALID_PARAMETER;
}
//
// Allocate an array for intermediate results
//
SiteEntries = LocalAlloc( LMEM_ZEROINIT, EntryCount*sizeof(PNL_SITE_ENTRY) );
if ( SiteEntries == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Loop mapping each entry
//
for ( i=0; i<EntryCount; i++ ) {
PSOCKET_ADDRESS SocketAddress;
PSOCKADDR SockAddr;
//
// Validate the entry
//
SocketAddress = (PSOCKET_ADDRESS)&SocketAddresses[i];
SockAddr = SocketAddress->lpSockaddr;
if ( (SocketAddress->iSockaddrLength < sizeof(SOCKADDR) ) ||
(SockAddr == NULL) ) {
NlPrint((NL_CRITICAL,
"DsrAddressToSiteNamesW: Sockaddr is too small %ld (ignoring it)\n",
SocketAddress->iSockaddrLength ));
SiteEntries[i] = NULL;
} else if ( SockAddr->sa_family != AF_INET ) {
NlPrint((NL_CRITICAL,
"DsrAddressToSiteNamesW: Address familty isn't AF_INET %ld (ignoring it)\n",
SockAddr->sa_family ));
SiteEntries[i] = NULL;
} else {
//
// The SockAddr is valid so map it to a site name.
//
SiteEntries[i] = NlFindSiteEntryBySockAddrEx( SockAddr, NULL );
}
}
//
// Allocate a structure to return to the caller.
//
Size = sizeof(NL_SITE_NAME_ARRAY) + EntryCount * sizeof(UNICODE_STRING);
for ( i=0; i<EntryCount; i++ ) {
if ( SiteEntries[i] != NULL ) {
Size += SiteEntries[i]->SiteNameString.Length + sizeof(WCHAR);
}
}
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
Strings = (PUNICODE_STRING) ((*SiteNames)+1);
(*SiteNames)->EntryCount = EntryCount;
(*SiteNames)->SiteNames = Strings;
Where = (LPBYTE) &Strings[EntryCount];
//
// Loop copying the names into the return buffer.
//
for ( i=0; i<EntryCount; i++ ) {
if ( SiteEntries[i] == NULL ) {
RtlInitUnicodeString( &Strings[i], NULL );
} else {
Strings[i].Length = SiteEntries[i]->SiteNameString.Length;
Strings[i].MaximumLength = Strings[i].Length + sizeof(WCHAR);
Strings[i].Buffer = (LPWSTR)Where;
RtlCopyMemory( Where, SiteEntries[i]->SiteName, Strings[i].MaximumLength );
Where += Strings[i].Length + sizeof(WCHAR);
}
}
NetStatus = NO_ERROR;
Cleanup:
//
// Derference the site entries.
//
if ( SiteEntries != NULL ) {
for ( i=0; i<EntryCount; i++ ) {
if ( SiteEntries[i] != NULL ) {
NlDerefSiteEntry( SiteEntries[i] );
}
}
LocalFree( SiteEntries );
}
if ( NetStatus != NO_ERROR ) {
if ( *SiteNames != NULL ) {
MIDL_user_free( *SiteNames );
*SiteNames = NULL;
}
}
return NetStatus;
UNREFERENCED_PARAMETER( ComputerName );
}
NET_API_STATUS
DsrAddressToSiteNamesExW(
IN LPWSTR ComputerName,
IN DWORD EntryCount,
IN PNL_SOCKET_ADDRESS SocketAddresses,
OUT PNL_SITE_NAME_EX_ARRAY *SiteNames
)
/*++
Routine Description:
The DsAddressToSiteNames API returns the site names and subnet names
that correspond to the specified addresses.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Number of addresses to convert.
SocketAddresses - Specifies an array of addresses to convert. EntryCount
addresses must be specified. Each address must be of type AF_INET.
SiteNames - Returns an array of pointers to site names. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding
address does not map to any site or if the address is malformed.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_ENTRY *SiteEntries = NULL;
PNL_SUBNET *SubnetEntries;
ULONG i;
ULONG Size;
PUNICODE_STRING SiteStrings = NULL;
PUNICODE_STRING SubnetStrings = NULL;
//
// This API is not supported on workstations.
//
*SiteNames = NULL;
if ( NlGlobalMemberWorkstation ) {
return ERROR_NOT_SUPPORTED;
}
//
// Initialization
//
if ( EntryCount == 0 ) {
return ERROR_INVALID_PARAMETER;
}
//
// Allocate an array for intermediate results
//
SiteEntries = LocalAlloc( LMEM_ZEROINIT,
EntryCount*(sizeof(PNL_SITE_ENTRY)+sizeof(PNL_SUBNET)) );
if ( SiteEntries == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
SubnetEntries = (PNL_SUBNET *) (&SiteEntries[EntryCount]);
//
// Loop mapping each entry
//
for ( i=0; i<EntryCount; i++ ) {
PSOCKET_ADDRESS SocketAddress;
PSOCKADDR SockAddr;
//
// Validate the entry
//
SocketAddress = (PSOCKET_ADDRESS)&SocketAddresses[i];
SockAddr = SocketAddress->lpSockaddr;
if ( (SocketAddress->iSockaddrLength < sizeof(SOCKADDR) ) ||
(SockAddr == NULL) ) {
NlPrint((NL_CRITICAL,
"DsrAddressToSiteNamesW: Sockaddr is too small %ld (ignoring it)\n",
SocketAddress->iSockaddrLength ));
SiteEntries[i] = NULL;
} else if ( SockAddr->sa_family != AF_INET ) {
NlPrint((NL_CRITICAL,
"DsrAddressToSiteNamesW: Address familty isn't AF_INET %ld (ignoring it)\n",
SockAddr->sa_family ));
SiteEntries[i] = NULL;
} else {
//
// The SockAddr is valid so map it to a site name.
//
SiteEntries[i] = NlFindSiteEntryBySockAddrEx( SockAddr, &SubnetEntries[i] );
}
}
//
// Allocate a structure to return to the caller.
//
*SiteNames = MIDL_user_allocate( sizeof(NL_SITE_NAME_EX_ARRAY) );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
SubnetStrings = MIDL_user_allocate( EntryCount * sizeof(UNICODE_STRING) );
if ( SubnetStrings == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
RtlZeroMemory( SubnetStrings, EntryCount * sizeof(UNICODE_STRING) );
SiteStrings = MIDL_user_allocate( EntryCount * sizeof(UNICODE_STRING) );
if ( SiteStrings == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
RtlZeroMemory( SiteStrings, EntryCount * sizeof(UNICODE_STRING) );
(*SiteNames)->EntryCount = EntryCount;
(*SiteNames)->SiteNames = SiteStrings;
(*SiteNames)->SubnetNames = SubnetStrings;
//
// Loop copying the names into the return buffer.
//
for ( i=0; i<EntryCount; i++ ) {
if ( SiteEntries[i] != NULL ) {
LPWSTR Name;
Name = NetpAllocWStrFromWStr( SiteEntries[i]->SiteName );
if ( Name == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
RtlInitUnicodeString( &SiteStrings[i], Name );
}
if ( SubnetEntries[i] != NULL ) {
WCHAR SubnetAddressString[NL_IP_ADDRESS_LENGTH+1+2+1];
ULONG Length;
UNICODE_STRING NumberString;
LPWSTR Name;
//
// Compute the IP address part of the subnet name
//
NetpIpAddressToWStr( SubnetEntries[i]->SubnetAddress,
SubnetAddressString );
Length = wcslen(SubnetAddressString);
SubnetAddressString[Length] = '/';
Length ++;
//
// Compute the bit count part of the subnet name
//
NumberString.Buffer = &SubnetAddressString[Length];
NumberString.MaximumLength = 3 * sizeof(WCHAR);
RtlIntegerToUnicodeString( SubnetEntries[i]->SubnetBitCount,
10,
&NumberString );
SubnetAddressString[Length+NumberString.Length/sizeof(WCHAR)] = '\0';
//
// Return it to the caller
//
Name = NetpAllocWStrFromWStr( SubnetAddressString );
if ( Name == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
RtlInitUnicodeString( &SubnetStrings[i], Name );
}
}
NetStatus = NO_ERROR;
Cleanup:
//
// Derference the site entries.
//
if ( SiteEntries != NULL ) {
for ( i=0; i<EntryCount; i++ ) {
if ( SiteEntries[i] != NULL ) {
NlDerefSiteEntry( SiteEntries[i] );
}
if ( SubnetEntries[i] != NULL ) {
NlSitesDerefSubnet( SubnetEntries[i] );
}
}
}
if ( NetStatus != NO_ERROR ) {
if ( *SiteNames != NULL ) {
MIDL_user_free( *SiteNames );
*SiteNames = NULL;
}
if ( SiteStrings != NULL ) {
for ( i=0; i<EntryCount; i++ ) {
if ( SiteStrings[i].Buffer != NULL ) {
MIDL_user_free( SiteStrings[i].Buffer );
}
}
MIDL_user_free( SiteStrings );
}
if ( SubnetStrings != NULL ) {
for ( i=0; i<EntryCount; i++ ) {
if ( SubnetStrings[i].Buffer != NULL ) {
MIDL_user_free( SubnetStrings[i].Buffer );
}
}
MIDL_user_free( SubnetStrings );
}
}
return NetStatus;
UNREFERENCED_PARAMETER( ComputerName );
}
NET_API_STATUS
NlSiteInitialize(
VOID
)
/*++
Routine Description:
Initialize this module.
Calls NlExit upon failure.
Arguments:
None.
Return Value:
Status of the initialization.
--*/
{
NET_API_STATUS NetStatus;
try {
InitializeCriticalSection(&NlGlobalSiteCritSect);
} except( EXCEPTION_EXECUTE_HANDLER ) {
NlPrint((NL_CRITICAL, "Cannot InitializeCriticalSection for SiteCritSect\n" ));
return ERROR_NOT_ENOUGH_MEMORY;
}
NlGlobalUnicodeSiteName = NULL;
NlGlobalSiteEntry = NULL;
InitializeListHead( &NlGlobalSiteList );
InitializeListHead( &NlGlobalSubnetList );
RtlZeroMemory( &NlGlobalSubnetTree, sizeof(NlGlobalSubnetTree) );
RtlZeroMemory( &NlGlobalNewSubnetTree, sizeof(NlGlobalNewSubnetTree) );
NlGlobalSiteInitialized = TRUE;
//
// Initially set the site name and populate the subnet tree.
//
if ( NlGlobalMemberWorkstation ) {
NetStatus = NlSetSiteName( NlGlobalParameters.SiteName, NULL );
} else {
NetStatus = NlSitesAddSubnetFromDs( NULL );
}
return NetStatus;
}
VOID
NlSiteTerminate(
VOID
)
/*++
Routine Description:
De-Initialize this module.
Arguments:
None.
Return Value:
None.
--*/
{
PLIST_ENTRY ListEntry;
//
// If we've not initialized,
// we're done.
//
if ( !NlGlobalSiteInitialized ) {
return;
}
NlPrint(( NL_SITE_MORE, "NlSiteTerminate: Entered\n" ));
//
// Free all entries in NlGlobalSubnetTree and NlGlobalNewSubnetTree
//
EnterCriticalSection( &NlGlobalSiteCritSect );
NlSiteDeleteSubnetTree( &NlGlobalSubnetTree );
NlSiteDeleteSubnetTree( &NlGlobalNewSubnetTree );
//
// Delete the site name.
//
NlSetSiteName( NULL, NULL );
LeaveCriticalSection( &NlGlobalSiteCritSect );
//
// There should be no more sites or subnets since all covered sites
// have been previously dereferenced and all remaining references
// were from the tree above
//
NlAssert( IsListEmpty( &NlGlobalSiteList ) );
NlAssert( IsListEmpty( &NlGlobalSubnetList ) );
DeleteCriticalSection(&NlGlobalSiteCritSect);
NlGlobalSiteInitialized = FALSE;
NlPrint(( NL_SITE_MORE, "NlSiteTerminate: Exitted\n" ));
}
int __cdecl NlpCompareSiteName(
const void *String1,
const void *String2
)
/*++
Routine Description:
String comparison routine for DsrGetDcSiteCoverageW.
Arguments:
String1: First string to compare
String2: Second string to compare
Return Value:
--*/
{
return RtlCompareUnicodeString(
(PUNICODE_STRING) String1,
(PUNICODE_STRING) String2,
TRUE );
}
NET_API_STATUS
DsrGetDcSiteCoverageW(
IN LPWSTR ComputerName OPTIONAL,
OUT PNL_SITE_NAME_ARRAY *SiteNames
)
/*++
Routine Description:
This API returns the site names of all sites covered by DC.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
SiteNames - Returns an array of pointers to site names.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PDOMAIN_INFO DomainInfo = NULL;
if ( NlGlobalMemberWorkstation ) {
NetStatus = ERROR_NOT_SUPPORTED;
goto Cleanup;
}
//
// Lookup which domain this call pertains to.
//
DomainInfo = NlFindDomainByServerName( ComputerName );
if ( DomainInfo == NULL ) {
NetStatus = ERROR_INVALID_COMPUTERNAME;
goto Cleanup;
}
//
// Get the site names
//
NetStatus = NlSitesGetCloseSites( DomainInfo,
DOM_REAL_DOMAIN,
SiteNames );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Sort them into alphabetical order
//
qsort( (*SiteNames)->SiteNames,
(*SiteNames)->EntryCount,
sizeof(UNICODE_STRING),
NlpCompareSiteName );
Cleanup:
if ( DomainInfo != NULL ) {
NlDereferenceDomain( DomainInfo );
}
return NetStatus;
}
NET_API_STATUS
I_NetLogonAddressToSiteName(
IN PSOCKET_ADDRESS SocketAddress,
OUT LPWSTR *SiteName
)
/*++
Routine Description:
This API returns the site name, if any, of the address in SocketAddress.
It is provided for in-process callers. See DsrAddressToSiteNamesW for details.
Arguments:
SocketAddess -- the address to be looked up
SiteName -- the site name of the address; NULL is returned if no site
is found.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
ERROR_NETLOGON_NOT_STARTED - Netlogon is stopped.
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
PNL_SITE_NAME_ARRAY SiteNameArray = NULL;
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return ERROR_NETLOGON_NOT_STARTED;
}
*SiteName = NULL;
NetStatus = DsrAddressToSiteNamesW( NULL,
1,
(PNL_SOCKET_ADDRESS)SocketAddress,
&SiteNameArray );
if ( (NO_ERROR == NetStatus)
&& SiteNameArray->EntryCount > 0
&& SiteNameArray->SiteNames[0].Length > 0 ) {
ULONG Size = SiteNameArray->SiteNames[0].Length + sizeof(WCHAR);
*SiteName = MIDL_user_allocate(Size);
if (*SiteName) {
RtlZeroMemory(*SiteName, Size);
RtlCopyMemory(*SiteName, SiteNameArray->SiteNames[0].Buffer, SiteNameArray->SiteNames[0].Length);
} else {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
}
}
if (SiteNameArray != NULL) {
MIDL_user_free(SiteNameArray);
}
//
// Indicate that the calling thread has left netlogon.dll
//
NlEndNetlogonCall();
return NetStatus;
}