windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/browser/server/brdomain.c

1071 lines
25 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
brdomain.c
Abstract:
Code to manage primary and emulated networks.
Author:
Cliff Van Dyke (CliffV) 11-Jan-1995
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// Module specific globals
//
// Serialized by NetworkCritSect
LIST_ENTRY ServicedDomains = {0};
//
// Local procedure forwards.
//
NET_API_STATUS
BrCreateDomain(
LPWSTR DomainName,
LPWSTR ComputerName,
BOOLEAN IsEmulatedDomain
);
VOID
BrCreateDomainWorker(
IN PVOID Ctx
);
NET_API_STATUS
BrInitializeDomains(
VOID
)
/*++
Routine Description:
Initialize brdomain.c and create the primary domain.
Arguments:
None
Return Value:
Status of operation.
--*/
{
NET_API_STATUS NetStatus;
LPWSTR ComputerName = NULL;
LPWSTR DomainName = NULL;
//
// Initialize globals
//
InitializeListHead(&ServicedDomains);
//
// Initialize actual domain of this machine.
//
// Get the configured computer name. NetpGetComputerName allocates
// the memory to hold the computername string using LocalAlloc.
//
NetStatus = NetpGetComputerName( &ComputerName );
if ( NetStatus != NERR_Success ) {
goto Cleanup;
}
NetStatus = NetpGetDomainName( &DomainName );
if ( NetStatus != NERR_Success ) {
goto Cleanup;
}
NetStatus = BrCreateDomain( DomainName,
ComputerName,
FALSE );
if ( NetStatus != NERR_Success ) {
goto Cleanup;
}
NetStatus = NERR_Success;
//
// Free locally used resources
//
Cleanup:
if ( ComputerName != NULL ) {
(VOID)LocalFree( ComputerName );
}
if ( DomainName != NULL ) {
(VOID)LocalFree( DomainName );
}
return NetStatus;
}
NET_API_STATUS
BrCreateDomain(
LPWSTR DomainName,
LPWSTR ComputerName,
BOOLEAN IsEmulatedDomain
)
/*++
Routine Description:
Create a new domain to browse on.
Arguments:
DomainName - Name of the domain to browse on
ComputerName - Name of this computer in the specified domain.
IsEmulatedDomain - TRUE iff this domain is an emulated domain of this machine.
Return Value:
Status of operation.
--*/
{
NTSTATUS Status;
NET_API_STATUS NetStatus;
BOOLEAN CanCallBrDeleteDomain = FALSE;
PDOMAIN_INFO DomainInfo = NULL;
ULONG AComputerNameLength;
BrPrint(( BR_DOMAIN, "%ws: Added new domain and computer: %ws\n",
DomainName,
ComputerName ));
//
// Allocate a structure describing the new domain.
//
DomainInfo = LocalAlloc( LMEM_ZEROINIT, sizeof(DOMAIN_INFO) );
if ( DomainInfo == NULL ) {
NetStatus = GetLastError();
goto Cleanup;
}
//
// Create an interim reference count for this domain.
//
DomainInfo->ReferenceCount = 1;
DomainInfo->IsEmulatedDomain = IsEmulatedDomain;
//
// Copy the computer name into the structure.
//
NetStatus = I_NetNameCanonicalize(
NULL,
ComputerName,
DomainInfo->DomUnicodeComputerName,
sizeof(DomainInfo->DomUnicodeComputerName),
NAMETYPE_COMPUTER,
0 );
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL,
"ComputerName " FORMAT_LPWSTR " is invalid\n",
ComputerName ));
goto Cleanup;
}
DomainInfo->DomUnicodeComputerNameLength = wcslen(DomainInfo->DomUnicodeComputerName);
Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemComputerName,
sizeof(DomainInfo->DomOemComputerName),
&DomainInfo->DomOemComputerNameLength,
DomainInfo->DomUnicodeComputerName,
DomainInfo->DomUnicodeComputerNameLength*sizeof(WCHAR));
if (!NT_SUCCESS(Status)) {
BrPrint(( BR_CRITICAL, "Unable to convert computer name to OEM %ws %lx\n", ComputerName, Status ));
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
DomainInfo->DomOemComputerName[DomainInfo->DomOemComputerNameLength] = '\0';
//
// Copy the domain name into the structure
// Note: Use workgroup type rather then domain since
// we have no notion of domain/workgroup in the browser (all are "groups")
// an workgroup is less restrictive (see bug 348606)
//
NetStatus = I_NetNameCanonicalize(
NULL,
DomainName,
DomainInfo->DomUnicodeDomainName,
sizeof(DomainInfo->DomUnicodeDomainName),
NAMETYPE_WORKGROUP,
0 );
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL, "%ws: DomainName is invalid\n", DomainName ));
goto Cleanup;
}
RtlInitUnicodeString( &DomainInfo->DomUnicodeDomainNameString,
DomainInfo->DomUnicodeDomainName );
Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemDomainName,
sizeof(DomainInfo->DomOemDomainName),
&DomainInfo->DomOemDomainNameLength,
DomainInfo->DomUnicodeDomainNameString.Buffer,
DomainInfo->DomUnicodeDomainNameString.Length);
if (!NT_SUCCESS(Status)) {
BrPrint(( BR_CRITICAL, "%ws: Unable to convert Domain name to OEM\n", DomainName ));
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
DomainInfo->DomOemDomainName[DomainInfo->DomOemDomainNameLength] = '\0';
//
// Create the domain rename timer
//
NetStatus = BrCreateTimer( &DomainInfo->DomainRenameTimer );
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL, "%ws: Cannot create domain rename timer %ld\n", DomainName, NetStatus ));
goto Cleanup;
}
//
// Link the domain into the list of domains
// (And mark that any future cleanup can be done by calling BrDeleteDomain)
EnterCriticalSection(&NetworkCritSect);
InsertTailList(&ServicedDomains, &DomainInfo->Next);
LeaveCriticalSection(&NetworkCritSect);
CanCallBrDeleteDomain = TRUE;
//
// Create the various networks for this domain.
//
NetStatus = BrCreateNetworks( DomainInfo );
if ( NetStatus != NERR_Success ) {
goto Cleanup;
}
//
// Free Locally used resources
//
Cleanup:
if (NetStatus != NERR_Success) {
if (DomainInfo != NULL) {
//
// If we've initialized to the point where we can call
// we can call BrDeleteDomain, do so.
//
if ( CanCallBrDeleteDomain ) {
(VOID) BrDeleteDomain( DomainInfo );
//
// Otherwise, just delete what we've created.
//
} else {
(VOID) LocalFree(DomainInfo);
}
}
}
return NetStatus;
}
typedef struct _BROWSER_CREATE_DOMAIN_CONTEXT {
LPWSTR DomainName;
LPWSTR ComputerName;
BOOLEAN IsEmulatedDomain;
HANDLE EventHandle;
NET_API_STATUS NetStatus;
} BROWSER_CREATE_DOMAIN_CONTEXT, *PBROWSER_CREATE_DOMAIN_CONTEXT;
NET_API_STATUS
BrCreateDomainInWorker(
LPWSTR DomainName,
LPWSTR ComputerName,
BOOLEAN IsEmulatedDomain
)
/*++
Routine Description:
Wrapper for BrCreateDomain. Since BrCreateDomain starts several pending
IO's to the browser driver, the thread that calls BrCreateDomain must
remain around forever. This wrapper can be called by any transient thread
(e.g., an RPC thread). It simply causes BrCreateDomain to be called in a
worker thread.
Arguments:
DomainName - Name of the domain to browse on
ComputerName - Name of this computer in the specified domain.
IsEmulatedDomain - TRUE iff this domain is an emulated domain of this machine.
Return Value:
Status of operation.
--*/
{
NET_API_STATUS NetStatus;
DWORD WaitStatus;
WORKER_ITEM WorkItem;
BROWSER_CREATE_DOMAIN_CONTEXT Context;
//
// Copy our arguments into a context block for the worker thread
//
Context.DomainName = DomainName;
Context.ComputerName = ComputerName;
Context.IsEmulatedDomain = IsEmulatedDomain;
//
// Create an event which we use to wait on the worker thread.
//
Context.EventHandle = CreateEvent(
NULL, // Event attributes
TRUE, // Event must be manually reset
FALSE, // Initial state not signalled
NULL ); // Event name
if ( Context.EventHandle == NULL ) {
NetStatus = GetLastError();
return NetStatus;
}
//
// Queue the request to the worker thread.
//
BrInitializeWorkItem( &WorkItem,
BrCreateDomainWorker,
&Context );
BrQueueWorkItem( &WorkItem );
//
// Wait for the worker thread to finish
//
WaitStatus = WaitForSingleObject( Context.EventHandle, INFINITE );
if ( WaitStatus == WAIT_OBJECT_0 ) {
NetStatus = Context.NetStatus;
} else {
NetStatus = GetLastError();
}
CloseHandle( Context.EventHandle );
return NetStatus;
}
VOID
BrCreateDomainWorker(
IN PVOID Ctx
)
/*++
Routine Description:
Worker routine for BrCreateDomainInWorker.
This routine executes in the context of a worker thread.
Arguments:
Context - Context containing the workitem and the description of the
domain to create.
Return Value:
None
--*/
{
PBROWSER_CREATE_DOMAIN_CONTEXT Context = (PBROWSER_CREATE_DOMAIN_CONTEXT) Ctx;
//
// Create the domain.
//
Context->NetStatus = BrCreateDomain(
Context->DomainName,
Context->ComputerName,
Context->IsEmulatedDomain );
//
// Let the caller know we're done.
//
SetEvent( Context->EventHandle );
}
PDOMAIN_INFO
BrFindDomain(
LPWSTR DomainName,
BOOLEAN DefaultToPrimary
)
/*++
Routine Description:
This routine will look up a domain given a name.
Arguments:
DomainName - The name of the domain to look up.
DefaultToPrimary - Return the primary domain if DomainName is NULL or
can't be found.
Return Value:
NULL - No such domain exists
A pointer to the domain found. The found domain should be dereferenced
using BrDereferenceDomain.
--*/
{
NTSTATUS Status;
PLIST_ENTRY DomainEntry;
PDOMAIN_INFO DomainInfo = NULL;
CHAR OemDomainName[DNLEN+1];
DWORD OemDomainNameLength;
EnterCriticalSection(&NetworkCritSect);
//
// If domain was specified,
// try to return primary domain.
//
if ( DomainName != NULL ) {
//
// Convert the domain name to OEM for faster comparison
//
Status = RtlUpcaseUnicodeToOemN( OemDomainName,
sizeof(OemDomainName),
&OemDomainNameLength,
DomainName,
wcslen(DomainName)*sizeof(WCHAR));
if (!NT_SUCCESS(Status)) {
BrPrint(( BR_CRITICAL, "%ws: Unable to convert Domain name to OEM\n", DomainName ));
DomainInfo = NULL;
goto Cleanup;
}
//
// Loop trying to find this domain name.
//
for (DomainEntry = ServicedDomains.Flink ;
DomainEntry != &ServicedDomains;
DomainEntry = DomainEntry->Flink ) {
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next);
if ( DomainInfo->DomOemDomainNameLength == OemDomainNameLength &&
RtlCompareMemory( DomainInfo->DomOemDomainName,
OemDomainName,
OemDomainNameLength ) == OemDomainNameLength ) {
break;
}
DomainInfo = NULL;
}
}
//
// If we're to default to the primary domain,
// do so.
//
if ( DefaultToPrimary && DomainInfo == NULL ) {
if ( !IsListEmpty( &ServicedDomains ) ) {
DomainInfo = CONTAINING_RECORD(ServicedDomains.Flink, DOMAIN_INFO, Next);
}
}
//
// Reference the domain.
//
if ( DomainInfo != NULL ) {
DomainInfo->ReferenceCount ++;
}
Cleanup:
LeaveCriticalSection(&NetworkCritSect);
return DomainInfo;
}
PDOMAIN_INFO
BrReferenceDomain(
PDOMAIN_INFO PotentialDomainInfo
)
/*++
Routine Description:
This routine will look up a domain given a potential pointer to the domain
This routine is useful if a caller has a pointer to a domain but
hasn't incremented the reference count. For instance,
a timer completion routine has such a pointer.
Arguments:
PotentialDomainInfo - Pointer to the DomainInfo to be verified.
Return Value:
NULL - No such domain exists
A pointer to the domain found. The found domain should be dereferenced
using BrDereferenceDomain.
--*/
{
NTSTATUS Status;
PLIST_ENTRY ListEntry;
EnterCriticalSection(&NetworkCritSect);
for (ListEntry = ServicedDomains.Flink ;
ListEntry != &ServicedDomains;
ListEntry = ListEntry->Flink ) {
PDOMAIN_INFO DomainInfo = CONTAINING_RECORD(ListEntry, DOMAIN_INFO, Next);
if ( PotentialDomainInfo == DomainInfo ) {
DomainInfo->ReferenceCount ++;
BrPrint(( BR_LOCKS,
"%ws: reference domain: %ld\n",
DomainInfo->DomUnicodeDomainName,
DomainInfo->ReferenceCount ));
LeaveCriticalSection(&NetworkCritSect);
return DomainInfo;
}
}
LeaveCriticalSection(&NetworkCritSect);
return NULL;
}
VOID
BrDereferenceDomain(
IN PDOMAIN_INFO DomainInfo
)
/*++
Routine Description:
Decrement the reference count on a domain.
If the reference count goes to 0, remove the domain.
On entry, the global NetworkCritSect may not be locked
Arguments:
DomainInfo - The domain to dereference
Return Value:
None
--*/
{
NTSTATUS Status;
ULONG ReferenceCount;
//
// Decrement the reference count
//
EnterCriticalSection(&NetworkCritSect);
ReferenceCount = -- DomainInfo->ReferenceCount;
LeaveCriticalSection(&NetworkCritSect);
if ( ReferenceCount != 0 ) {
return;
}
//
// Ditch the rename timer
//
BrDestroyTimer( &DomainInfo->DomainRenameTimer );
//
// Free the Domain Info structure.
//
(VOID) LocalFree( DomainInfo );
}
NET_API_STATUS
BrRenameDomainForNetwork(
PNETWORK Network,
PVOID Context
)
/*++
Routine Description:
Handle domain rename for a particular network.
Reset the network indicating this machine plays no special role.
Then, re-enable any role we're currently playing.
Arguments:
Network - Network to reset (Referenced)
Return Value:
Status - The status of the operation.
--*/
{
NET_API_STATUS NetStatus;
//
// Lock the network
//
if (LOCK_NETWORK(Network)) {
//
// Stop it from being a master.
//
if (Network->Role & ROLE_MASTER) {
NetStatus = BrStopMaster(Network);
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL,
"%ws: %ws: BrRenameDomainForNetwork: Cannot BrStopMaster %ld\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer,
NetStatus ));
}
}
//
// Stop being a backup as well.
//
NetStatus = BrStopBackup(Network);
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL,
"%ws: %ws: BrRenameDomainForNetwork: Cannot BrStopBackup %ld\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer,
NetStatus ));
}
//
// Stop even being a potential browser.
// Close the <DomainName>[1E] name
//
NetStatus = BrUpdateNetworkAnnouncementBits(Network, (PVOID)(BR_SHUTDOWN|BR_PARANOID) );
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL,
"%ws: %ws: BrRenameDomainForNetwork: Cannot BrUpdateNetworkAnnouncementBits %ld\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer,
NetStatus ));
}
//
// Register the new <DomainName>[1E] name
//
NetStatus = BrUpdateNetworkAnnouncementBits(Network, (PVOID)BR_PARANOID );
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL,
"%ws: %ws: BrRenameDomainForNetwork: Cannot BrUpdateNetworkAnnouncementBits %ld\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer,
NetStatus ));
}
//
// If we are on either a domain master, or on a lanman/NT machine,
// force an election on all our transports to make sure that we're
// the master
//
if ( (Network->Flags & NETWORK_PDC) != 0 || BrInfo.IsLanmanNt) {
NetStatus = BrElectMasterOnNet( Network, (PVOID)EVENT_BROWSER_ELECTION_SENT_LANMAN_NT_STARTED );
if (NetStatus != NERR_Success) {
BrPrint(( BR_CRITICAL,
"%ws: %ws: BrRenameDomainForNetwork: Can't Elect Master.\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer,
NetStatus ));
// This isn't fatal.
} else {
BrPrint(( BR_NETWORK, "%ws: %ws: Election forced on domain rename.\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer ));
}
}
//
// If forced to MaintainServerList, become a backup again
//
EnterCriticalSection(&BrInfo.ConfigCritSect);
if (BrInfo.MaintainServerList == 1){
//
// Become a backup server now.
//
NetStatus = BrBecomeBackup( Network );
if (NetStatus != NERR_Success) {
BrPrint(( BR_CRITICAL,
"%ws: %ws: BrRenameDomainForNetwork: Can't BecomeBackup.\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer,
NetStatus ));
// This isn't fatal.
} else {
BrPrint(( BR_NETWORK, "%ws: %ws: Became Backup.\n",
Network->DomainInfo->DomUnicodeDomainName,
Network->NetworkName.Buffer ));
}
}
LeaveCriticalSection(&BrInfo.ConfigCritSect);
UNLOCK_NETWORK(Network);
}
//
// Always return success so allow the caller to continue to the next network.
//
return NERR_Success;
}
VOID
BrRenameDomain(
IN PVOID Context
)
/*++
Routine Description:
Rename the domain from the specified name to the currently register named
for the domain.
Arguments:
OldDomainName - Name that the domain is currently known by.
Return Value:
None
--*/
{
NET_API_STATUS NetStatus;
NTSTATUS Status;
PDOMAIN_INFO DomainInfo = NULL;
LPWSTR NewDomainName = NULL;
//
// Reference the domain.
// This routine can be called as a timer routine. In that case, the
// domain might not exist any more.
//
DomainInfo = BrReferenceDomain( Context );
if ( DomainInfo == NULL ) {
BrPrint(( BR_CRITICAL, "%lx: Renamed domain no longer exists\n", Context ));
NetStatus = ERROR_INTERNAL_ERROR;
goto Cleanup;
}
BrPrint(( BR_DOMAIN, "%ws: BrRenameDomain called\n",
DomainInfo->DomUnicodeDomainName ));
//
// Determine the new domain name
//
NetStatus = NetpGetDomainName( &NewDomainName );
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL, "%ws: Cannot determine the new domain name %ld\n",
DomainInfo->DomUnicodeDomainName,
NetStatus ));
goto Cleanup;
}
//
// Put the new domain name into the domain structure
// Note: Use workgroup type rather then domain since
// we have no notion of domain/workgroup in the browser (all are "groups")
// an workgroup is less restrictive (see bug 348606)
//
EnterCriticalSection(&NetworkCritSect);
NetStatus = I_NetNameCanonicalize(
NULL,
NewDomainName,
DomainInfo->DomUnicodeDomainName,
sizeof(DomainInfo->DomUnicodeDomainName),
NAMETYPE_WORKGROUP,
0 );
if ( NetStatus != NERR_Success ) {
LeaveCriticalSection(&NetworkCritSect);
BrPrint(( BR_CRITICAL, "%ws: DomainName is invalid %ld\n",
NewDomainName,
NetStatus ));
goto Cleanup;
}
RtlInitUnicodeString( &DomainInfo->DomUnicodeDomainNameString,
DomainInfo->DomUnicodeDomainName );
Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemDomainName,
sizeof(DomainInfo->DomOemDomainName),
&DomainInfo->DomOemDomainNameLength,
DomainInfo->DomUnicodeDomainNameString.Buffer,
DomainInfo->DomUnicodeDomainNameString.Length);
if (!NT_SUCCESS(Status)) {
LeaveCriticalSection(&NetworkCritSect);
BrPrint(( BR_CRITICAL, "%ws: Unable to convert Domain name to OEM\n", DomainName ));
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
DomainInfo->DomOemDomainName[DomainInfo->DomOemDomainNameLength] = '\0';
LeaveCriticalSection(&NetworkCritSect);
//
// Reset all of the networks telling them of the new domain name
//
NetStatus = BrEnumerateNetworksForDomain(DomainInfo, BrRenameDomainForNetwork, NULL);
if ( NetStatus != NERR_Success ) {
BrPrint(( BR_CRITICAL, "%ws: Cannot do rename on all networks %ld\n",
NewDomainName,
NetStatus ));
goto Cleanup;
}
NetStatus = NERR_Success;
//
// Free locally used resources
//
Cleanup:
if ( NewDomainName != NULL ) {
(VOID)LocalFree( NewDomainName );
}
if ( DomainInfo != NULL ) {
//
// If the domain rename failed,
// try it again in 15 minutes.
//
if ( NetStatus != NERR_Success ) {
BrSetTimer(&DomainInfo->DomainRenameTimer, 15 * 1000 * 60, BrRenameDomain, DomainInfo);
}
BrDereferenceDomain( DomainInfo );
}
return;
}
VOID
BrDeleteDomain(
IN PDOMAIN_INFO DomainInfo
)
/*++
Routine Description:
Force a domain to be deleted.
Arguments:
DomainInfo - The domain to delete
Return Value:
None
--*/
{
//
// Delete each of the networks for this domain.
//
BrEnumerateNetworksForDomain(DomainInfo, BrDeleteNetwork, NULL );
//
// Delink the domain from the global list and remove the final reference.
//
EnterCriticalSection(&NetworkCritSect);
RemoveEntryList(&DomainInfo->Next);
LeaveCriticalSection(&NetworkCritSect);
BrDereferenceDomain( DomainInfo );
}
VOID
BrUninitializeDomains(
VOID
)
/*++
Routine Description:
Delete all of the domains.
Arguments:
None.
Return Value:
None
--*/
{
//
// Loop through the domains deleting each of them
//
EnterCriticalSection(&NetworkCritSect);
while (!IsListEmpty(&ServicedDomains)) {
PDOMAIN_INFO DomainInfo = CONTAINING_RECORD(ServicedDomains.Flink, DOMAIN_INFO, Next);
DomainInfo->ReferenceCount ++;
LeaveCriticalSection(&NetworkCritSect);
//
// Clean up the domain.
//
BrDeleteDomain( DomainInfo );
//
// Actually delete the delinked structure by removing the last reference
//
ASSERT( DomainInfo->ReferenceCount == 1 );
BrDereferenceDomain( DomainInfo );
EnterCriticalSection(&NetworkCritSect);
}
LeaveCriticalSection(&NetworkCritSect);
}