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.
1083 lines
34 KiB
1083 lines
34 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
bowelect.c
|
|
|
|
Abstract:
|
|
|
|
This module implements all of the election 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
|
|
BowserStartElection(
|
|
IN PTRANSPORT Transport
|
|
);
|
|
|
|
LONG
|
|
BowserSetElectionCriteria(
|
|
IN PPAGED_TRANSPORT Transport
|
|
);
|
|
|
|
NTSTATUS
|
|
BowserElectMaster(
|
|
IN PTRANSPORT Transport
|
|
);
|
|
|
|
VOID
|
|
HandleElectionWorker(
|
|
IN PVOID Ctx
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, GetMasterName)
|
|
#pragma alloc_text(PAGE, HandleElectionWorker)
|
|
#pragma alloc_text(PAGE, BowserSetElectionCriteria)
|
|
#pragma alloc_text(PAGE, BowserStartElection)
|
|
#pragma alloc_text(PAGE, BowserElectMaster)
|
|
#pragma alloc_text(PAGE, BowserLoseElection)
|
|
#pragma alloc_text(PAGE, BowserFindMaster)
|
|
#pragma alloc_text(PAGE, BowserSendElection)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
GetMasterName (
|
|
IN PIRP Irp,
|
|
IN BOOLEAN Wait,
|
|
IN BOOLEAN InFsd,
|
|
IN PLMDR_REQUEST_PACKET InputBuffer,
|
|
IN ULONG InputBufferLength
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PTRANSPORT Transport = NULL;
|
|
PPAGED_TRANSPORT PagedTransport;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
PAGED_CODE();
|
|
|
|
try {
|
|
WCHAR TransportNameBuffer[MAX_PATH+1];
|
|
WCHAR DomainNameBuffer[DNLEN+1];
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
(ULONG)FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.GetMasterName.Name)+3*sizeof(WCHAR)) {
|
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (InputBufferLength < sizeof(LMDR_REQUEST_PACKET)) {
|
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
|
|
CAPTURE_UNICODE_STRING( &InputBuffer->TransportName, TransportNameBuffer );
|
|
CAPTURE_UNICODE_STRING( &InputBuffer->EmulatedDomainName, DomainNameBuffer );
|
|
Transport = BowserFindTransport(&InputBuffer->TransportName, &InputBuffer->EmulatedDomainName );
|
|
dprintf(DPRT_REF, ("Called Find transport %lx from GetMasterName.\n", Transport));
|
|
|
|
if (Transport == NULL) {
|
|
try_return (Status = STATUS_OBJECT_NAME_NOT_FOUND);
|
|
}
|
|
|
|
PagedTransport = Transport->PagedTransport;
|
|
|
|
dlog(DPRT_FSCTL,
|
|
("%s: %ws: NtDeviceIoControlFile: GetMasterName\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
|
|
PagedTransport->ElectionCount = ELECTION_COUNT;
|
|
|
|
Status = BowserQueueNonBufferRequest(Irp,
|
|
&Transport->FindMasterQueue,
|
|
BowserCancelQueuedRequest
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
try_return(Status);
|
|
}
|
|
|
|
Status = BowserFindMaster(Transport);
|
|
|
|
//
|
|
// If we couldn't initiate the find master process, complete all the
|
|
// queued find master requests.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BowserCompleteFindMasterRequests(Transport, &PagedTransport->MasterName, Status);
|
|
}
|
|
|
|
//
|
|
// Since we marked the IRP as pending, we need to return pending
|
|
// now.
|
|
//
|
|
|
|
try_return(Status = STATUS_PENDING);
|
|
|
|
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
if ( Transport != NULL ) {
|
|
BowserDereferenceTransport(Transport);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
|
|
UNREFERENCED_PARAMETER(Wait);
|
|
UNREFERENCED_PARAMETER(InFsd);
|
|
|
|
}
|
|
|
|
|
|
DATAGRAM_HANDLER(BowserHandleElection)
|
|
{
|
|
// PTA_NETBIOS_ADDRESS Address = SourceAddress;
|
|
|
|
return BowserPostDatagramToWorkerThread(
|
|
TransportName,
|
|
Buffer,
|
|
BytesAvailable,
|
|
BytesTaken,
|
|
SourceAddress,
|
|
SourceAddressLength,
|
|
SourceName,
|
|
SourceNameLength,
|
|
HandleElectionWorker,
|
|
NonPagedPool,
|
|
CriticalWorkQueue,
|
|
ReceiveFlags,
|
|
FALSE // Response will be sent, but...
|
|
);
|
|
|
|
}
|
|
|
|
VOID
|
|
HandleElectionWorker(
|
|
IN PVOID Ctx
|
|
)
|
|
{
|
|
PPOST_DATAGRAM_CONTEXT Context = Ctx;
|
|
PTRANSPORT_NAME TransportName = Context->TransportName;
|
|
PREQUEST_ELECTION_1 ElectionResponse = Context->Buffer;
|
|
ULONG BytesAvailable = Context->BytesAvailable;
|
|
ULONG TimeUp;
|
|
BOOLEAN Winner;
|
|
PTRANSPORT Transport = TransportName->Transport;
|
|
NTSTATUS Status;
|
|
LONG ElectionDelay, NextElection;
|
|
OEM_STRING ClientNameO;
|
|
UNICODE_STRING ClientName;
|
|
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
|
|
|
|
PAGED_CODE();
|
|
|
|
LOCK_TRANSPORT(Transport);
|
|
|
|
ClientName.Buffer = NULL;
|
|
|
|
try {
|
|
|
|
//
|
|
// If this packet was smaller than a minimal packet,
|
|
// ignore the packet.
|
|
//
|
|
|
|
if (BytesAvailable <= FIELD_OFFSET(REQUEST_ELECTION_1, ServerName)) {
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
//
|
|
// If the packet doesn't have a zero terminated ServerName,
|
|
// ignore the packet.
|
|
//
|
|
|
|
if ( !IsZeroTerminated(
|
|
ElectionResponse->ServerName,
|
|
BytesAvailable - FIELD_OFFSET(REQUEST_ELECTION_1, ServerName) ) ) {
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
BowserStatistics.NumberOfElectionPackets += 1;
|
|
|
|
//
|
|
// Remember the last time we heard an election packet.
|
|
//
|
|
|
|
PagedTransport->LastElectionSeen = BowserTimeUp();
|
|
|
|
if (Transport->ElectionState == DeafToElections) {
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
//
|
|
// If we've disable the transport for any reason,
|
|
// then we disregard all elections.
|
|
//
|
|
|
|
if (PagedTransport->DisabledTransport) {
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
//
|
|
// Convert the client name in the election packet to unicode so we can
|
|
// log it.
|
|
//
|
|
|
|
RtlInitString(&ClientNameO, ElectionResponse->ServerName);
|
|
|
|
Status = RtlOemStringToUnicodeString(&ClientName, &ClientNameO, TRUE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BowserLogIllegalName( Status, ClientNameO.Buffer, ClientNameO.Length );
|
|
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
if (BowserLogElectionPackets) {
|
|
BowserWriteErrorLogEntry(EVENT_BOWSER_ELECTION_RECEIVED, STATUS_SUCCESS, ElectionResponse, (USHORT)BytesAvailable, 2, ClientName.Buffer, PagedTransport->TransportName.Buffer);
|
|
}
|
|
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Received election packet from machine %s. Version: %lx; Criteria: %lx; TimeUp: %lx\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer,
|
|
ElectionResponse->ServerName,
|
|
ElectionResponse->Version,
|
|
SmbGetUlong(&ElectionResponse->Criteria),
|
|
SmbGetUlong(&ElectionResponse->TimeUp)));
|
|
|
|
|
|
|
|
//
|
|
// Figure out our time up for the election compare.
|
|
//
|
|
// If we're running an election, we'll use our advertised time, else
|
|
// we'll use our actual uptime. Also, if we're running an election
|
|
// we'll check to see if we sent this. If we're not running an election
|
|
// and we receive this, it's because the redirector didn't find a
|
|
// master, so we want to continue the election and become master.
|
|
//
|
|
|
|
if (Transport->ElectionState == RunningElection) {
|
|
if (!strcmp(Transport->DomainInfo->DomOemComputerNameBuffer, ElectionResponse->ServerName)) {
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
//
|
|
// If this request was initiated from a client, ignore it.
|
|
//
|
|
if ((SmbGetUlong(&ElectionResponse->Criteria) == 0) &&
|
|
(ElectionResponse->ServerName[0] == '\0')) {
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Dummy election request ignored during election.\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
if (PagedTransport->Role == Master) {
|
|
ElectionDelay = BowserRandom(MASTER_ELECTION_DELAY);
|
|
} else {
|
|
ElectionDelay = ELECTION_RESPONSE_MIN + BowserRandom(ELECTION_RESPONSE_MAX-ELECTION_RESPONSE_MIN);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Starting a new election - set various election criteria
|
|
// including Uptime.
|
|
//
|
|
|
|
ElectionDelay = BowserSetElectionCriteria(PagedTransport);
|
|
|
|
}
|
|
|
|
TimeUp = PagedTransport->Uptime;
|
|
|
|
if (ElectionResponse->Version != BROWSER_ELECTION_VERSION) {
|
|
Winner = (ElectionResponse->Version < BROWSER_ELECTION_VERSION);
|
|
} else if (SmbGetUlong(&ElectionResponse->Criteria) != PagedTransport->ElectionCriteria) {
|
|
Winner = (SmbGetUlong(&ElectionResponse->Criteria) < PagedTransport->ElectionCriteria);
|
|
} else if (TimeUp != SmbGetUlong(&ElectionResponse->TimeUp)) {
|
|
Winner = TimeUp > SmbGetUlong(&ElectionResponse->TimeUp);
|
|
} else {
|
|
Winner = (strcmp(Transport->DomainInfo->DomOemDomainName, ElectionResponse->ServerName) <= 0);
|
|
}
|
|
|
|
//
|
|
// If we lost, we stop our timer and turn off our election flag, just
|
|
// in case we had an election or find master going. If we're a backup,
|
|
// we want to find out who the new master is, either from this election
|
|
// frame or waiting awhile and querying.
|
|
//
|
|
|
|
if (!Winner) {
|
|
|
|
//
|
|
// Remember if we legitimately lost the last election, and if
|
|
// so, don't force an election if we see server announcements
|
|
// from a non DC, just give up.
|
|
//
|
|
|
|
PagedTransport->Flags |= ELECT_LOST_LAST_ELECTION;
|
|
}
|
|
|
|
if (!Winner || (PagedTransport->ElectionsSent > ELECTION_MAX)) {
|
|
|
|
if (PagedTransport->IsPrimaryDomainController) {
|
|
|
|
DWORD ElectionInformation[6];
|
|
|
|
ElectionInformation[0] = ElectionResponse->Version;
|
|
ElectionInformation[1] = SmbGetUlong(&ElectionResponse->Criteria);
|
|
ElectionInformation[2] = SmbGetUlong(&ElectionResponse->TimeUp);
|
|
ElectionInformation[3] = BROWSER_ELECTION_VERSION;
|
|
ElectionInformation[4] = PagedTransport->ElectionCriteria;
|
|
ElectionInformation[5] = TimeUp;
|
|
|
|
//
|
|
// Write this information into the event log.
|
|
//
|
|
|
|
BowserWriteErrorLogEntry(EVENT_BOWSER_PDC_LOST_ELECTION,
|
|
STATUS_SUCCESS,
|
|
ElectionInformation,
|
|
sizeof(ElectionInformation),
|
|
2,
|
|
ClientName.Buffer,
|
|
PagedTransport->TransportName.Buffer);
|
|
|
|
KdPrint(("HandleElectionWorker: Lose election, but we're the PDC. Winner: Version: %lx; Criteria: %lx; Time Up: %lx; Name: %s\n",
|
|
ElectionResponse->Version,
|
|
SmbGetUlong(&ElectionResponse->Criteria),
|
|
SmbGetUlong(&ElectionResponse->TimeUp),
|
|
ElectionResponse->ServerName));
|
|
|
|
}
|
|
|
|
BowserLoseElection(Transport);
|
|
|
|
} else {
|
|
//
|
|
// We won this election, make sure that we don't think that we
|
|
// lost it.
|
|
//
|
|
|
|
PagedTransport->Flags &= ~ELECT_LOST_LAST_ELECTION;
|
|
|
|
//
|
|
// If we won and we're not running an election, we'll start one.
|
|
// If we are running, we don't do anything because our timer will
|
|
// take care of it. If the NET_ELECTION flag is clear, we know
|
|
// timeup is approx. equal to time_up() because we set it above,
|
|
// so we'll use that. This algorithm includes a damping constant
|
|
// (we won't start an election if we've just lost one in the
|
|
// last 1.5 seconds) to avoid election storms.
|
|
//
|
|
|
|
|
|
if (Transport->ElectionState != RunningElection) {
|
|
|
|
//
|
|
// If we recently lost an election, then ignore the fact
|
|
// that we won, and pretend we lost this one.
|
|
//
|
|
|
|
if ((PagedTransport->TimeLastLost != 0) &&
|
|
((BowserTimeUp() - PagedTransport->TimeLastLost) < ELECTION_EXEMPT_TIME)) {
|
|
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Browser is exempt from election\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Better criteria, calling elect_master in %ld milliseconds.\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer,
|
|
ElectionDelay));
|
|
|
|
//
|
|
// Ensure the timer is running.
|
|
// We don't actually win the election until the timer expires.
|
|
//
|
|
|
|
Transport->ElectionState = RunningElection;
|
|
|
|
PagedTransport->NextElection = 0;
|
|
}
|
|
|
|
PagedTransport->ElectionCount = ELECTION_COUNT;
|
|
|
|
//
|
|
// Note: the next elect time must be computed into a signed
|
|
// integer in case the expiration time has already passed so
|
|
// don't try to optimize this code too much.
|
|
//
|
|
|
|
NextElection = PagedTransport->NextElection - (TimeUp - BowserTimeUp());
|
|
|
|
if ((PagedTransport->NextElection == 0) || NextElection > ElectionDelay) {
|
|
BowserStopTimer(&Transport->ElectionTimer);
|
|
|
|
PagedTransport->NextElection = TimeUp + ElectionDelay;
|
|
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Calling ElectMaster in %ld milliseconds\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer,
|
|
ElectionDelay));
|
|
|
|
BowserStartTimer(&Transport->ElectionTimer, ElectionDelay, BowserElectMaster, Transport);
|
|
}
|
|
}
|
|
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
|
|
UNLOCK_TRANSPORT(Transport);
|
|
|
|
InterlockedDecrement( &BowserPostedCriticalDatagramCount );
|
|
FREE_POOL(Context);
|
|
|
|
if (ClientName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&ClientName);
|
|
}
|
|
|
|
BowserDereferenceTransportName(TransportName);
|
|
BowserDereferenceTransport(Transport);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LONG
|
|
BowserSetElectionCriteria(
|
|
IN PPAGED_TRANSPORT PagedTransport
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Set election criteria for a network.
|
|
|
|
Prepare for an election by setting Transport->ElectionCriteria based upon the local
|
|
browser state. Set Transport->Uptime to the current local up time.
|
|
|
|
Arguments:
|
|
Transport - The transport for the net we're on.
|
|
|
|
Return Value
|
|
Number of milliseconds to delay before sending the election packet.
|
|
|
|
--*/
|
|
{
|
|
LONG Delay;
|
|
|
|
PAGED_CODE();
|
|
|
|
PagedTransport->ElectionsSent = 0; // clear bid counter
|
|
PagedTransport->Uptime = BowserTimeUp();
|
|
|
|
if (BowserData.IsLanmanNt) {
|
|
PagedTransport->ElectionCriteria = ELECTION_CR_LM_NT;
|
|
} else {
|
|
PagedTransport->ElectionCriteria = ELECTION_CR_WIN_NT;
|
|
}
|
|
|
|
PagedTransport->ElectionCriteria |=
|
|
ELECTION_MAKE_REV(BROWSER_VERSION_MAJOR, BROWSER_VERSION_MINOR);
|
|
|
|
if (BowserData.MaintainServerList &&
|
|
((PagedTransport->NumberOfServersInTable +
|
|
RtlNumberGenericTableElements(&PagedTransport->AnnouncementTable)+
|
|
RtlNumberGenericTableElements(&PagedTransport->DomainTable)) != 0)) {
|
|
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_CFG_BKP;
|
|
}
|
|
|
|
if (PagedTransport->IsPrimaryDomainController) {
|
|
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_PDC;
|
|
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_DOMMSTR;
|
|
}
|
|
|
|
#ifdef ENABLE_PSEUDO_BROWSER
|
|
if (BowserData.PseudoServerLevel == BROWSER_PSEUDO ||
|
|
BowserData.PseudoServerLevel == BROWSER_SEMI_PSEUDO_NO_DMB ) {
|
|
// Pseudo or Semi-Pseudo will win elections over peers
|
|
// & in case of semi-pseudo except no DMB communications
|
|
// all other functionality will remain on.
|
|
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_PSEUDO;
|
|
}
|
|
#endif
|
|
|
|
if (PagedTransport->Role == Master) {
|
|
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_MASTER;
|
|
|
|
Delay = MASTER_ELECTION_DELAY;
|
|
|
|
} else if (PagedTransport->IsPrimaryDomainController) {
|
|
|
|
//
|
|
// If we are the PDC, we want to set our timeouts
|
|
// as if we were already the master.
|
|
//
|
|
// This prevents us from getting into a situation where it takes
|
|
// more than ELECTION_DELAY_MAX to actually send out our response
|
|
// to an election.
|
|
//
|
|
|
|
Delay = MASTER_ELECTION_DELAY;
|
|
|
|
} else if ((PagedTransport->Role == Backup) ||
|
|
BowserData.IsLanmanNt) {
|
|
//
|
|
// Likewise, if we are NTAS machines, we want to set out delay
|
|
// to match that of backup browsers (even if we're not a backup
|
|
// quite yet).
|
|
//
|
|
|
|
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_BACKUP;
|
|
Delay = BACKUP_ELECTION_DELAY_MIN + BowserRandom(BACKUP_ELECTION_DELAY_MAX-BACKUP_ELECTION_DELAY_MIN);
|
|
|
|
} else {
|
|
Delay = ELECTION_DELAY_MIN + BowserRandom(ELECTION_DELAY_MAX-ELECTION_DELAY_MIN);
|
|
}
|
|
|
|
//
|
|
// Assume for now that all wannish transports are running the WINS client.
|
|
//
|
|
if ( PagedTransport->Wannish ) {
|
|
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_WINS_CLIENT;
|
|
}
|
|
|
|
return Delay;
|
|
}
|
|
|
|
NTSTATUS
|
|
BowserStartElection(
|
|
IN PTRANSPORT Transport
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Initiate a browser election
|
|
|
|
This routine is called when we are unable to find a master, and we want to
|
|
elect one.
|
|
|
|
Arguments:
|
|
Transport - The transport for the net we're on.
|
|
|
|
Return Value
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
|
|
PAGED_CODE();
|
|
|
|
LOCK_TRANSPORT(Transport);
|
|
|
|
try {
|
|
|
|
//
|
|
// If we've disable the transport for any reason,
|
|
// then we disregard all elections.
|
|
//
|
|
|
|
if (PagedTransport->DisabledTransport) {
|
|
try_return(Status = STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
//
|
|
// If we're deaf to elections, or aren't any kind of
|
|
// browser then we can't start elections either.
|
|
//
|
|
|
|
if (Transport->ElectionState == DeafToElections ||
|
|
PagedTransport->Role == None) {
|
|
try_return(Status = STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
PagedTransport->ElectionCount = ELECTION_COUNT;
|
|
|
|
Transport->ElectionState = RunningElection;
|
|
|
|
BowserSetElectionCriteria(PagedTransport);
|
|
|
|
Status = BowserElectMaster(Transport);
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
UNLOCK_TRANSPORT(Transport);
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BowserElectMaster(
|
|
IN PTRANSPORT Transport
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Elect a master browser server.
|
|
|
|
This routine is called when we think there is no master and we need to
|
|
elect one. We check our retry count, and if it's non-zero we send an
|
|
elect datagram to the group name. Otherwise we become the master ourselves.
|
|
|
|
Arguments:
|
|
Transport - The transport for the net we're on.
|
|
|
|
Return Value
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
|
|
PAGED_CODE();
|
|
|
|
LOCK_TRANSPORT(Transport);
|
|
|
|
try {
|
|
|
|
//
|
|
// If we're not running the election at this time, it means that
|
|
// between the time that we decided we were going to win the election
|
|
// and now, someone else announced with better criteria. It is
|
|
// possible that this could happen if the announcement came in just
|
|
// before we ran (ie. if the announcement occured between when the
|
|
// timer DPC was queued and when the DPC actually fired).
|
|
//
|
|
|
|
if (Transport->ElectionState != RunningElection) {
|
|
|
|
KdPrint(("BowserElectMaster: Lose election because we are no longer running the election\n"));
|
|
|
|
BowserLoseElection(Transport);
|
|
|
|
} else if (PagedTransport->ElectionCount != 0) {
|
|
|
|
BowserStopTimer(&Transport->ElectionTimer);
|
|
|
|
PagedTransport->ElectionCount -= 1;
|
|
|
|
PagedTransport->ElectionsSent += 1;
|
|
|
|
PagedTransport->NextElection = BowserTimeUp() + ELECTION_RESEND_DELAY;
|
|
|
|
Status = BowserSendElection(&Transport->DomainInfo->DomUnicodeDomainName, BrowserElection, Transport, TRUE);
|
|
|
|
// Lose the election if we can't send a datagram.
|
|
if (!NT_SUCCESS(Status)) {
|
|
BowserLoseElection(Transport);
|
|
try_return(Status);
|
|
}
|
|
|
|
//
|
|
// If we were able to send the election,
|
|
// start the timer running.
|
|
//
|
|
|
|
BowserStartTimer(&Transport->ElectionTimer,
|
|
ELECTION_RESEND_DELAY,
|
|
BowserElectMaster,
|
|
Transport);
|
|
|
|
|
|
} else {
|
|
Transport->ElectionState = Idle;
|
|
|
|
//
|
|
// If we're already the master we just return. This can happen if
|
|
// somebody starts an election (which we win) while we're already
|
|
// the master.
|
|
//
|
|
|
|
if (PagedTransport->Role != Master) {
|
|
|
|
//
|
|
// We're the new master - we won!
|
|
//
|
|
|
|
BowserNewMaster(Transport, Transport->DomainInfo->DomOemComputerNameBuffer );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Were already the master. Make sure that all the backups
|
|
// know this by sending an announcent
|
|
//
|
|
|
|
//
|
|
// This one's easy - simply set the servers announcement event to the
|
|
// signalled state. If the server is running, this will force an
|
|
// announcement
|
|
//
|
|
|
|
KeSetEvent(BowserServerAnnouncementEvent, IO_NETWORK_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
UNLOCK_TRANSPORT(Transport);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
BowserLoseElection(
|
|
IN PTRANSPORT Transport
|
|
)
|
|
{
|
|
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
|
|
PAGED_CODE();
|
|
|
|
|
|
LOCK_TRANSPORT(Transport);
|
|
|
|
BowserStopTimer(&Transport->ElectionTimer);
|
|
|
|
dlog(DPRT_ELECT,
|
|
("We lost the election\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
|
|
PagedTransport->TimeLastLost = BowserTimeUp();
|
|
|
|
//
|
|
// We lost the election - we re-enter the idle state.
|
|
//
|
|
|
|
Transport->ElectionState = Idle;
|
|
|
|
if (PagedTransport->Role == Master) {
|
|
|
|
//
|
|
// If we lost, and we are currently a master, then tickle
|
|
// the browser service and stop being a master.
|
|
//
|
|
|
|
BowserResetStateForTransport(Transport, RESET_STATE_STOP_MASTER);
|
|
|
|
//
|
|
// Remove all the entries on the server list for this
|
|
// transport.
|
|
//
|
|
|
|
LOCK_ANNOUNCE_DATABASE(Transport);
|
|
|
|
//
|
|
// Flag that there should be no more announcements received on
|
|
// this name.
|
|
//
|
|
|
|
BowserForEachTransportName(Transport, BowserStopProcessingAnnouncements, NULL);
|
|
|
|
// KdPrint(("Deleting entire table on transport %wZ because we lost the election\n", &Transport->TransportName));
|
|
|
|
BowserDeleteGenericTable(&PagedTransport->AnnouncementTable);
|
|
|
|
BowserDeleteGenericTable(&PagedTransport->DomainTable);
|
|
|
|
UNLOCK_ANNOUNCE_DATABASE(Transport);
|
|
|
|
#if 0
|
|
} else if (Transport->Role == Backup) { // If we're a backup, find master
|
|
dlog(DPRT_ELECT, ("We're a backup - Find the new master\n"));
|
|
|
|
//
|
|
// If this guy is not the master, then we want to
|
|
// find a master at some later time.
|
|
//
|
|
|
|
Transport->ElectionCount = FIND_MASTER_COUNT;
|
|
Transport->Uptime = Transport->TimeLastLost;
|
|
BowserStopTimer(&Transport->FindMasterTimer);
|
|
BowserStartTimer(&Transport->FindMasterTimer,
|
|
FIND_MASTER_WAIT-(FIND_MASTER_WAIT/8)+ BowserRandom(FIND_MASTER_WAIT/4),
|
|
BowserFindMaster,
|
|
Transport);
|
|
#endif
|
|
|
|
}
|
|
|
|
UNLOCK_TRANSPORT(Transport);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
BowserFindMaster(
|
|
IN PTRANSPORT Transport
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Find the master browser server.
|
|
|
|
This routine attempts to find the master browser server by
|
|
sending a request announcement message to the master. If no response is
|
|
heard after a while, we assume the master isn't present and run and
|
|
election.
|
|
|
|
Arguments:
|
|
Transport - The transport for the net we're on.
|
|
|
|
Return Value
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
|
|
PAGED_CODE();
|
|
|
|
LOCK_TRANSPORT(Transport);
|
|
|
|
try {
|
|
|
|
//
|
|
// If our count hasn't gone to 0 yet, we'll send a find master PDU.
|
|
//
|
|
|
|
if (PagedTransport->ElectionCount != 0) {
|
|
|
|
PagedTransport->ElectionCount -= 1; // Update count, and set timer
|
|
|
|
BowserStopTimer(&Transport->FindMasterTimer);
|
|
|
|
Status = BowserSendRequestAnnouncement(
|
|
&Transport->DomainInfo->DomUnicodeDomainName,
|
|
MasterBrowser,
|
|
Transport);
|
|
|
|
if (NT_SUCCESS(Status) ||
|
|
Status == STATUS_BAD_NETWORK_PATH) {
|
|
//
|
|
// We will retry on the following cases:
|
|
// - netbt returns success. Meaning I'll be looking for it.
|
|
// - nwlnknb returns STATUS_BAD_NETWORK_PATH meaning I can't find it.
|
|
// In either case, we would try for ElectionCount times & then move
|
|
// fwd to initiate elections. Otherwise we may end up in a state where because
|
|
// we didn't find a master browser, we won't attempt to elect one & we'll fail
|
|
// to become one. This will result w/ a domain w/ no master browser.
|
|
//
|
|
BowserStartTimer(&Transport->FindMasterTimer,
|
|
FIND_MASTER_DELAY,
|
|
BowserFindMaster,
|
|
Transport);
|
|
} else {
|
|
try_return(Status);
|
|
}
|
|
|
|
} else {
|
|
ULONG CurrentTime;
|
|
LONG TimeTilNextElection;
|
|
|
|
//
|
|
// Count has expired, so we'll try to elect a new master.
|
|
//
|
|
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Find_Master: Master not found, forcing election.\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
|
|
if (BowserLogElectionPackets) {
|
|
BowserWriteErrorLogEntry(EVENT_BOWSER_ELECTION_SENT_FIND_MASTER_FAILED, STATUS_SUCCESS, NULL, 0, 1, PagedTransport->TransportName.Buffer);
|
|
}
|
|
|
|
//
|
|
// If it's been more than a reasonable of time since the last
|
|
// election, force a new election, otherwise set a timer to
|
|
// start an election after a reasonable amount of time.
|
|
//
|
|
//
|
|
// Calculate the time until the next election only once
|
|
// since it is possible that we might cross over the ELECTION_TIME
|
|
// threshold while performing these checks.
|
|
//
|
|
|
|
CurrentTime = BowserTimeUp();
|
|
if ( CurrentTime >= PagedTransport->LastElectionSeen) {
|
|
TimeTilNextElection = (ELECTION_TIME - (CurrentTime - PagedTransport->LastElectionSeen));
|
|
} else {
|
|
TimeTilNextElection = ELECTION_TIME;
|
|
}
|
|
|
|
if ( TimeTilNextElection <= 0 ) {
|
|
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Last election long enough ago, forcing election\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
|
|
Status = BowserStartElection(Transport);
|
|
|
|
//
|
|
// If we couldn't start the election, complete the find
|
|
// master requests with the appropriate error.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Complete the requests with the current master name - it's
|
|
// as good as anyone.
|
|
//
|
|
|
|
BowserCompleteFindMasterRequests(Transport, &PagedTransport->MasterName, Status);
|
|
}
|
|
|
|
} else {
|
|
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Last election too recent, delay %ld before forcing election\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer,
|
|
TimeTilNextElection ));
|
|
|
|
BowserStartTimer(&Transport->FindMasterTimer,
|
|
TimeTilNextElection,
|
|
BowserStartElection,
|
|
Transport);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
UNLOCK_TRANSPORT(Transport);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BowserSendElection(
|
|
IN PUNICODE_STRING NameToSend OPTIONAL,
|
|
IN DGRECEIVER_NAME_TYPE NameType,
|
|
IN PTRANSPORT Transport,
|
|
IN BOOLEAN SendActualBrowserInfo
|
|
)
|
|
{
|
|
UCHAR Buffer[sizeof(REQUEST_ELECTION)+LM20_CNLEN+1];
|
|
PREQUEST_ELECTION ElectionRequest = (PREQUEST_ELECTION) Buffer;
|
|
ULONG ComputerNameSize;
|
|
NTSTATUS Status;
|
|
PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
|
|
|
|
PAGED_CODE();
|
|
|
|
ElectionRequest->Type = Election;
|
|
|
|
//
|
|
// If this transport is disabled,
|
|
// don't send any election packets.
|
|
//
|
|
|
|
if ( PagedTransport->DisabledTransport ) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// If we are supposed to send the actual browser info, and we are
|
|
// running the browser send a real election packet, otherwise we
|
|
// just want to send a dummy packet.
|
|
//
|
|
|
|
if (SendActualBrowserInfo &&
|
|
(PagedTransport->ServiceStatus & (SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_BACKUP_BROWSER | SV_TYPE_MASTER_BROWSER))) {
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Send true election.\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
|
|
//
|
|
// If this request comes as a part of an election, we want to send
|
|
// the accurate browser information.
|
|
//
|
|
|
|
ElectionRequest->ElectionRequest.Version = BROWSER_ELECTION_VERSION;
|
|
|
|
ElectionRequest->ElectionRequest.TimeUp = PagedTransport->Uptime;
|
|
|
|
ElectionRequest->ElectionRequest.Criteria = PagedTransport->ElectionCriteria;
|
|
|
|
ElectionRequest->ElectionRequest.MustBeZero = 0;
|
|
|
|
ComputerNameSize = Transport->DomainInfo->DomOemComputerName.Length;
|
|
strcpy( ElectionRequest->ElectionRequest.ServerName,
|
|
Transport->DomainInfo->DomOemComputerName.Buffer );
|
|
|
|
} else {
|
|
dlog(DPRT_ELECT,
|
|
("%s: %ws: Send dummy election.\n",
|
|
Transport->DomainInfo->DomOemDomainName,
|
|
PagedTransport->TransportName.Buffer ));
|
|
|
|
//
|
|
// If we are forcing the election because we can't get a backup list,
|
|
// send only dummy information.
|
|
//
|
|
|
|
ElectionRequest->ElectionRequest.Version = 0;
|
|
ElectionRequest->ElectionRequest.Criteria = 0;
|
|
ElectionRequest->ElectionRequest.TimeUp = 0;
|
|
ElectionRequest->ElectionRequest.ServerName[0] = '\0';
|
|
ElectionRequest->ElectionRequest.MustBeZero = 0;
|
|
ComputerNameSize = 0;
|
|
}
|
|
|
|
return BowserSendSecondClassMailslot(Transport,
|
|
NameToSend,
|
|
NameType,
|
|
ElectionRequest,
|
|
FIELD_OFFSET(REQUEST_ELECTION, ElectionRequest.ServerName)+ComputerNameSize+sizeof(UCHAR),
|
|
TRUE,
|
|
MAILSLOT_BROWSER_NAME,
|
|
NULL
|
|
);
|
|
}
|