/*++ 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); }