1084 lines
33 KiB
C
1084 lines
33 KiB
C
/*++
|
||
|
||
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
|
||
);
|
||
}
|