windows-nt/Source/XPSP1/NT/base/fs/rdr2/bowser/bowmastr.c

745 lines
21 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
bowmastr.c
Abstract:
This module implements all of the master browser related routines for the
NT browser
Author:
Larry Osterman (LarryO) 21-Jun-1990
Revision History:
21-Jun-1990 LarryO
Created
--*/
#include "precomp.h"
#pragma hdrstop
#define INCLUDE_SMB_TRANSACTION
NTSTATUS
StartProcessingAnnouncements(
IN PTRANSPORT_NAME TransportName,
IN PVOID Context
);
VOID
BowserMasterAnnouncementWorker(
IN PVOID Ctx
);
NTSTATUS
TimeoutFindMasterRequests(
IN PTRANSPORT Transport,
IN PVOID Context
);
NTSTATUS
BowserPrimeDomainTableWithOtherDomains(
IN PTRANSPORT_NAME TransportName,
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, BowserBecomeMaster)
#pragma alloc_text(PAGE, StartProcessingAnnouncements)
#pragma alloc_text(PAGE, BowserPrimeDomainTableWithOtherDomains)
#pragma alloc_text(PAGE, BowserNewMaster)
#pragma alloc_text(PAGE, BowserCompleteFindMasterRequests)
#pragma alloc_text(PAGE, BowserTimeoutFindMasterRequests)
#pragma alloc_text(PAGE, TimeoutFindMasterRequests)
#pragma alloc_text(PAGE, BowserMasterAnnouncementWorker)
#endif
NTSTATUS
BowserBecomeMaster(
IN PTRANSPORT Transport
)
/*++
Routine Description:
Make this machine a master browser.
This routine is called when we are changing the state of a machine from
backup to master browser.
Arguments:
Transport - The transport on which to become a master.
Return Value
NTSTATUS - The status of the upgrade operation.
--*/
{
NTSTATUS Status;
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
PAGED_CODE();
try {
LOCK_TRANSPORT(Transport);
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
//
// Post the addname on this transport for the master name..
//
Status = BowserAllocateName(
&Transport->DomainInfo->DomUnicodeDomainName,
MasterBrowser,
Transport,
Transport->DomainInfo );
if (NT_SUCCESS(Status)) {
//
// Post the addname on this transport for the domain announcement.
//
Status = BowserAllocateName(&Transport->DomainInfo->DomUnicodeDomainName,
DomainAnnouncement,
Transport,
Transport->DomainInfo );
}
//
// The addition of the name failed - we can't be a master any
// more.
//
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
PagedTransport->Role = Master;
//
// Start processing host announcements on each of
// the names associated with the server.
//
BowserForEachTransportName(Transport, StartProcessingAnnouncements, NULL);
//
// If we don't have any elements in our announcement table,
// send a request announcement packet to all the servers to
// allow ourselves to populate the table as quickly as possible.
//
#ifdef ENABLE_PSEUDO_BROWSER
if ((RtlNumberGenericTableElements(&PagedTransport->AnnouncementTable) == 0) &&
PagedTransport->NumberOfServersInTable == 0 &&
BowserData.PseudoServerLevel != BROWSER_PSEUDO) {
#else
if ((RtlNumberGenericTableElements(&PagedTransport->AnnouncementTable) == 0) &&
PagedTransport->NumberOfServersInTable == 0) {
#endif
BowserSendRequestAnnouncement(&Transport->DomainInfo->DomUnicodeDomainName,
PrimaryDomain,
Transport);
}
//
// If we don't have any elements in our domain table,
// send a request announcement packet to all the servers to
// allow ourselves to populate the table as quickly as possible.
//
#ifdef ENABLE_PSEUDO_BROWSER
if ((RtlNumberGenericTableElements(&PagedTransport->DomainTable) == 0) &&
PagedTransport->NumberOfServersInTable == 0 &&
BowserData.PseudoServerLevel != BROWSER_PSEUDO) {
#else
if ((RtlNumberGenericTableElements(&PagedTransport->DomainTable) == 0) &&
PagedTransport->NumberOfServersInTable == 0) {
#endif
BowserSendRequestAnnouncement(&Transport->DomainInfo->DomUnicodeDomainName,
DomainAnnouncement,
Transport);
}
PagedTransport->TimeMaster = BowserTimeUp();
//
// Now walk the transport names associated with this transport and
// seed all the "otherdomains" into the browse list.
//
BowserForEachTransportName(
Transport,
BowserPrimeDomainTableWithOtherDomains,
NULL);
//
// Now complete any and all find master requests outstanding on this
// transport.
//
BowserCompleteFindMasterRequests(Transport, &Transport->DomainInfo->DomUnicodeComputerName, STATUS_REQUEST_NOT_ACCEPTED);
try_return(Status = STATUS_SUCCESS);
try_exit:NOTHING;
} finally {
if (!NT_SUCCESS(Status)) {
dlog(DPRT_ELECT|DPRT_MASTER,
("%s: %ws: There's already a master on this net - we need to find who it is",
Transport->DomainInfo->DomOemDomainName,
PagedTransport->TransportName.Buffer ));
//
// We couldn't become a master. Reset our state and fail the
// promotion request.
//
PagedTransport->Role = PotentialBackup;
PagedTransport->ElectionCount = ELECTION_COUNT;
PagedTransport->Uptime = BowserTimeUp();
Transport->ElectionState = Idle;
//
// Stop processing host announcements on each of
// the names associated with the server.
//
BowserForEachTransportName(Transport, BowserStopProcessingAnnouncements, NULL);
//
// Stop any timers that are running (ie. if there's an election
// in progress)
//
BowserStopTimer(&Transport->ElectionTimer);
//
// Delete the names we added above.
//
BowserDeleteTransportNameByName(Transport,
NULL,
MasterBrowser);
BowserDeleteTransportNameByName(Transport,
NULL,
DomainAnnouncement);
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
}
UNLOCK_TRANSPORT(Transport);
}
return Status;
}
NTSTATUS
StartProcessingAnnouncements(
IN PTRANSPORT_NAME TransportName,
IN PVOID Context
)
{
PAGED_CODE();
ASSERT (TransportName->Signature == STRUCTURE_SIGNATURE_TRANSPORTNAME);
ASSERT (TransportName->NameType == TransportName->PagedTransportName->Name->NameType);
if ((TransportName->NameType == OtherDomain) ||
(TransportName->NameType == MasterBrowser) ||
(TransportName->NameType == PrimaryDomain) ||
(TransportName->NameType == BrowserElection) ||
(TransportName->NameType == DomainAnnouncement)) {
if (!TransportName->ProcessHostAnnouncements) {
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
DISCARDABLE_CODE( BowserDiscardableCodeSection );
TransportName->ProcessHostAnnouncements = TRUE;
}
}
return(STATUS_SUCCESS);
UNREFERENCED_PARAMETER(Context);
}
NTSTATUS
BowserPrimeDomainTableWithOtherDomains(
IN PTRANSPORT_NAME TransportName,
IN PVOID Context
)
{
PAGED_CODE();
if (TransportName->NameType == OtherDomain) {
PPAGED_TRANSPORT PagedTransport = TransportName->Transport->PagedTransport;
PTRANSPORT Transport = TransportName->Transport;
ANNOUNCE_ENTRY OtherDomainPrototype;
PANNOUNCE_ENTRY Announcement;
BOOLEAN NewElement;
RtlZeroMemory( &OtherDomainPrototype, sizeof(OtherDomainPrototype) );
OtherDomainPrototype.Signature = STRUCTURE_SIGNATURE_ANNOUNCE_ENTRY;
OtherDomainPrototype.Size = sizeof(OtherDomainPrototype) -
sizeof(OtherDomainPrototype.ServerComment) +
Transport->DomainInfo->DomUnicodeComputerName.Length + sizeof(WCHAR);
RtlCopyMemory(OtherDomainPrototype.ServerName, TransportName->PagedTransportName->Name->Name.Buffer, TransportName->PagedTransportName->Name->Name.Length);
OtherDomainPrototype.ServerName[TransportName->PagedTransportName->Name->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
RtlCopyMemory(OtherDomainPrototype.ServerComment, Transport->DomainInfo->DomUnicodeComputerName.Buffer, Transport->DomainInfo->DomUnicodeComputerName.Length);
OtherDomainPrototype.ServerComment[Transport->DomainInfo->DomUnicodeComputerName.Length / sizeof(WCHAR)] = UNICODE_NULL;
OtherDomainPrototype.ServerType = SV_TYPE_DOMAIN_ENUM;
OtherDomainPrototype.ServerVersionMajor = 2;
OtherDomainPrototype.ServerVersionMinor = 0;
OtherDomainPrototype.ServerPeriodicity = 0xffff;
OtherDomainPrototype.ExpirationTime = 0xffffffff;
OtherDomainPrototype.SerialId = 0;
OtherDomainPrototype.Name = TransportName->PagedTransportName->Name;
//
// Make sure that no-one else is messing with the domain list.
//
LOCK_ANNOUNCE_DATABASE(Transport);
Announcement = RtlInsertElementGenericTable(&PagedTransport->DomainTable,
&OtherDomainPrototype, OtherDomainPrototype.Size, &NewElement);
if (Announcement != NULL && NewElement ) {
// Indicate the name is referenced by the announce entry we just inserted.
BowserReferenceName( OtherDomainPrototype.Name );
}
UNLOCK_ANNOUNCE_DATABASE(Transport);
}
return(STATUS_SUCCESS);
}
VOID
BowserNewMaster(
IN PTRANSPORT Transport,
IN PUCHAR MasterName
)
/*++
Routine Description:
Flag that a machine is the new master browser server.
This routine is called to register a new master browser server.
Arguments:
IN PTRANSPORT Transport - The transport for the net we're on.
IN PUCHAR MasterName - The name of the new master browser server.
Return Value
None.
--*/
{
PIRP Irp = NULL;
WCHAR MasterNameBuffer[LM20_CNLEN+1];
UNICODE_STRING UMasterName;
OEM_STRING OMasterName;
NTSTATUS Status;
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
PAGED_CODE();
UMasterName.Buffer = MasterNameBuffer;
UMasterName.MaximumLength = (LM20_CNLEN+1)*sizeof(WCHAR);
RtlInitAnsiString(&OMasterName, MasterName);
Status = RtlOemStringToUnicodeString(&UMasterName, &OMasterName, FALSE);
if (!NT_SUCCESS(Status)) {
BowserLogIllegalName( Status, OMasterName.Buffer, OMasterName.Length );
return;
}
LOCK_TRANSPORT(Transport);
try {
//
// There's a new master, we can stop our election timers.
//
PagedTransport->ElectionCount = 0;
Transport->ElectionState = Idle;
BowserStopTimer(&Transport->ElectionTimer);
//
// Check to see if we are the winner of the election. If we are
// we want to complete any BecomeMaster requests that are outstanding.
//
if (RtlEqualUnicodeString(&UMasterName, &Transport->DomainInfo->DomUnicodeComputerName, TRUE)) {
//
// We're the new master for this domain. Complete any BecomeMaster
// requests.
//
Irp = BowserDequeueQueuedIrp(&Transport->BecomeMasterQueue);
if (Irp != NULL) {
//
// Don't copy anything into the users buffer.
//
Irp->IoStatus.Information = 0;
BowserCompleteRequest(Irp, STATUS_SUCCESS);
} else {
//
// Go deaf to elections until we can become a master.
//
Transport->ElectionState = DeafToElections;
//
// If we're the master browser, stop being a master browser.
//
//
if (PagedTransport->Role == MasterBrowser) {
//
// Delete the names that make us a master.
//
BowserDeleteTransportNameByName(Transport,
NULL,
MasterBrowser);
BowserDeleteTransportNameByName(Transport,
NULL,
DomainAnnouncement);
}
dlog(DPRT_MASTER,
("%s: %ws: Unable to find a BecomeMasterIrp\n",
Transport->DomainInfo->DomOemDomainName,
PagedTransport->TransportName.Buffer ));
}
//
// Complete any outstanding find master requests with the special error MORE_PROCESSING_REQUIRED.
//
// This will cause the browser service to promote itself.
//
BowserCompleteFindMasterRequests(Transport, &UMasterName, STATUS_MORE_PROCESSING_REQUIRED);
} else {
BowserCompleteFindMasterRequests(Transport, &UMasterName, STATUS_SUCCESS);
}
} finally {
UNLOCK_TRANSPORT(Transport);
}
}
VOID
BowserCompleteFindMasterRequests(
IN PTRANSPORT Transport,
IN PUNICODE_STRING MasterName,
IN NTSTATUS Status
)
{
PIO_STACK_LOCATION IrpSp;
PIRP Irp = NULL;
BOOLEAN MasterNameChanged;
WCHAR MasterNameBuffer[CNLEN+1];
UNICODE_STRING MasterNameCopy;
NTSTATUS UcaseStatus;
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
PAGED_CODE();
MasterNameCopy.Buffer = MasterNameBuffer;
MasterNameCopy.MaximumLength = sizeof(MasterNameBuffer);
UcaseStatus = RtlUpcaseUnicodeString(&MasterNameCopy, MasterName, FALSE);
if (!NT_SUCCESS(UcaseStatus)) {
BowserLogIllegalName( UcaseStatus, MasterName->Buffer, MasterName->Length );
return;
}
LOCK_TRANSPORT(Transport);
MasterNameChanged = !RtlEqualUnicodeString(&MasterNameCopy, &PagedTransport->MasterName, FALSE);
if (MasterNameChanged) {
//
// If the master name changed, update the masters name in
// the transport structure.
//
RtlCopyUnicodeString(&PagedTransport->MasterName, &MasterNameCopy);
}
UNLOCK_TRANSPORT(Transport);
do {
//
// Complete any the find master requests outstanding against this
// workstation.
//
Irp = BowserDequeueQueuedIrp(&Transport->FindMasterQueue);
if (MasterNameChanged &&
(Irp == NULL)) {
Irp = BowserDequeueQueuedIrp(&Transport->WaitForNewMasterNameQueue);
}
if (Irp != NULL) {
PLMDR_REQUEST_PACKET RequestPacket = Irp->AssociatedIrp.SystemBuffer;
if (NT_SUCCESS(Status)) {
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (MasterName->Length > (USHORT)(IrpSp->Parameters.DeviceIoControl.OutputBufferLength-
(FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.GetMasterName.Name))+3*sizeof(WCHAR)) ) {
Status = STATUS_BUFFER_TOO_SMALL;
} else {
RequestPacket->Parameters.GetMasterName.Name[0] = L'\\';
RequestPacket->Parameters.GetMasterName.Name[1] = L'\\';
RtlCopyMemory(&RequestPacket->Parameters.GetMasterName.Name[2], MasterName->Buffer, MasterName->Length);
RequestPacket->Parameters.GetMasterName.Name[2+(MasterName->Length/sizeof(WCHAR))] = UNICODE_NULL;
}
dlog(DPRT_MASTER,
("%s: %ws: Completing a find master request with new master %ws\n",
Transport->DomainInfo->DomOemDomainName,
PagedTransport->TransportName.Buffer,
RequestPacket->Parameters.GetMasterName.Name));
RequestPacket->Parameters.GetMasterName.MasterNameLength = MasterName->Length+2*sizeof(WCHAR);
Irp->IoStatus.Information = FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.GetMasterName.Name)+MasterName->Length+3*sizeof(WCHAR);
}
BowserCompleteRequest(Irp, Status);
}
} while ( Irp != NULL );
}
DATAGRAM_HANDLER(BowserMasterAnnouncement)
{
PUCHAR MasterName = ((PMASTER_ANNOUNCEMENT_1)Buffer)->MasterName;
ULONG i;
//
// We need to make sure that the incoming packet contains a properly
// terminated ASCII string.
//
for (i = 0; i < BytesAvailable; i++) {
if (MasterName[i] == '\0') {
break;
}
}
if (i == BytesAvailable) {
return(STATUS_REQUEST_NOT_ACCEPTED);
}
return BowserPostDatagramToWorkerThread(
TransportName,
Buffer,
BytesAvailable,
BytesTaken,
SourceAddress,
SourceAddressLength,
SourceName,
SourceNameLength,
BowserMasterAnnouncementWorker,
NonPagedPool,
DelayedWorkQueue,
ReceiveFlags,
FALSE // No response will be sent.
);
}
VOID
BowserMasterAnnouncementWorker(
IN PVOID Ctx
)
{
PPOST_DATAGRAM_CONTEXT Context = Ctx;
PTRANSPORT Transport = Context->TransportName->Transport;
PCHAR LocalMasterName = (PCHAR)((PMASTER_ANNOUNCEMENT_1)Context->Buffer)->MasterName;
size_t cbLocalMasterName;
PIRP Irp;
NTSTATUS Status;
PAGED_CODE();
Irp = BowserDequeueQueuedIrp(&Transport->WaitForMasterAnnounceQueue);
if (Irp != NULL) {
PIO_STACK_LOCATION IrpSp;
PLMDR_REQUEST_PACKET RequestPacket = Irp->AssociatedIrp.SystemBuffer;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
cbLocalMasterName = strlen(LocalMasterName);
if (0 == cbLocalMasterName) {
// ensure we didn't get an invalid NULL announcement
// see bug 440813
// The request completed successfully, but the data is trash.
// - we won't fail the IRP (another one is posted immediately
// upon completion anyway), but not process further this one.
Irp->IoStatus.Information = 0;
Status = STATUS_SUCCESS;
}
else if ((cbLocalMasterName + 1) * sizeof(WCHAR) >
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength -
FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.WaitForMasterAnnouncement.Name))) {
//
// ensure there's enough buffer space to return name. If not,
// return error.
//
Irp->IoStatus.Information = 0;
Status = STATUS_BUFFER_TOO_SMALL;
} else {
//
// All is well. Fill info.
//
OEM_STRING MasterName;
UNICODE_STRING MasterNameU;
RtlInitString(&MasterName, LocalMasterName);
Status = RtlOemStringToUnicodeString(&MasterNameU, &MasterName, TRUE);
if ( NT_SUCCESS(Status) ) {
RequestPacket->Parameters.WaitForMasterAnnouncement.MasterNameLength = MasterNameU.Length;
RtlCopyMemory(RequestPacket->Parameters.WaitForMasterAnnouncement.Name, MasterNameU.Buffer, MasterNameU.Length);
RequestPacket->Parameters.WaitForMasterAnnouncement.Name[MasterNameU.Length/sizeof(WCHAR)] = UNICODE_NULL;
Irp->IoStatus.Information = FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.WaitForMasterAnnouncement.Name)+MasterNameU.Length + sizeof(UNICODE_NULL);
RtlFreeUnicodeString(&MasterNameU);
Status = STATUS_SUCCESS;
}
}
BowserCompleteRequest(Irp, Status);
}
BowserDereferenceTransportName(Context->TransportName);
BowserDereferenceTransport(Transport);
InterlockedDecrement( &BowserPostedDatagramCount );
FREE_POOL(Context);
}
NTSTATUS
TimeoutFindMasterRequests(
IN PTRANSPORT Transport,
IN PVOID Context
)
{
PAGED_CODE();
//
// Perform an unprotected early out to prevent our calling into
// discardable code section during the scavenger. Since the discardable
// code section is <4K, touching the code would have the effect of
// bringing the entire page into memory, which is a waste - since the
// scavenger runs every 30 seconds, this would cause the discardable
// code section to be a part of the browsers working set.
//
if (BowserIsIrpQueueEmpty(&Transport->FindMasterQueue)) {
return STATUS_SUCCESS;
}
BowserTimeoutQueuedIrp(&Transport->FindMasterQueue, BowserFindMasterTimeout);
return STATUS_SUCCESS;
}
VOID
BowserTimeoutFindMasterRequests(
VOID
)
{
PAGED_CODE();
BowserForEachTransport(TimeoutFindMasterRequests, NULL);
}