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
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;
|
|
}
|