NT4/private/ntos/bowser/bowelect.c
2020-09-30 17:12:29 +02:00

1100 lines
33 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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
)
{
NTSTATUS Status;
PTRANSPORT Transport;
PPAGED_TRANSPORT PagedTransport;
PAGED_CODE();
dprintf(DPRT_FSCTL, ("NtDeviceIoControlFile: GetMasterName "));
ExAcquireResourceExclusive(&BowserDataResource, TRUE);
if (BowserData.Initialized != TRUE) {
dprintf(DPRT_FSCTL, ("Bowser already started\n"));
ExReleaseResource(&BowserDataResource);
return STATUS_REDIRECTOR_NOT_STARTED;
}
ExReleaseResource(&BowserDataResource);
Transport = BowserFindTransport(&InputBuffer->TransportName);
if (Transport == NULL) {
return STATUS_OBJECT_NAME_NOT_FOUND;
}
PagedTransport = Transport->PagedTransport;
try {
//
// Ensure the primary name has been added.
//
if ( Transport->PrimaryDomain == NULL ) {
try_return( STATUS_OBJECT_NAME_NOT_FOUND );
}
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 {
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;
UCHAR ComputerName[LM20_CNLEN+1];
PTRANSPORT Transport = TransportName->Transport;
OEM_STRING AnsiName;
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 {
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)) {
BowserWriteErrorLogEntry(EVENT_BOWSER_NAME_CONVERSION_FAILED, Status, ClientNameO.Buffer, ClientNameO.Length, 0);
try_return(NOTHING);
}
if (BowserLogElectionPackets) {
BowserWriteErrorLogEntry(EVENT_BOWSER_ELECTION_RECEIVED, STATUS_SUCCESS, ElectionResponse, (USHORT)BytesAvailable, 2, ClientName.Buffer, PagedTransport->TransportName.Buffer);
}
dlog(DPRT_ELECT, ("Received election packet on net %wZ from machine %s. Version: %lx; Criteria: %lx; TimeUp: %lx\n", &PagedTransport->TransportName,
ElectionResponse->ServerName,
ElectionResponse->Version,
SmbGetUlong(&ElectionResponse->Criteria),
SmbGetUlong(&ElectionResponse->TimeUp)));
AnsiName.Buffer = ComputerName;
AnsiName.MaximumLength = LM20_CNLEN+1;
Status = RtlUnicodeStringToOemString(&AnsiName, &Transport->ComputerName->PagedTransportName->Name->Name, FALSE);
if (!NT_SUCCESS(Status)) {
BowserWriteErrorLogEntry( EVENT_BOWSER_NAME_CONVERSION_FAILED,
Status,
Transport->ComputerName->PagedTransportName->Name->Name.Buffer,
Transport->ComputerName->PagedTransportName->Name->Name.Length,
0);
try_return(NOTHING);
}
//
// 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(ComputerName, 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, ("Dummy election request ignored during election.\n"));
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(ComputerName, 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 (BowserData.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 0
//
// If we've won and we're not a DC and this is't a wanish
// transport, and we're a member of a domain, log an event. This
// should never happen.
//
if (ElectionResponse->ServerName[0] != '\0' &&
SmbGetUlong(&ElectionResponse->Criteria) != 0 &&
Transport->ElectionState == RunningElection &&
BowserData.IsDomainMember &&
!BowserData.IsLanmanNt &&
!Transport->Wannish) {
DWORD ElectionInformation[6];
ElectionInformation[0] = ElectionResponse->Version;
ElectionInformation[1] = SmbGetUlong(&ElectionResponse->Criteria);
ElectionInformation[2] = SmbGetUlong(&ElectionResponse->TimeUp);
ElectionInformation[3] = BROWSER_ELECTION_VERSION;
ElectionInformation[4] = Transport->ElectionCriteria;
ElectionInformation[5] = TimeUp;
//
// Write this information into the event log.
//
BowserWriteErrorLogEntry(EVENT_BOWSER_NON_PDC_WON_ELECTION,
STATUS_SUCCESS,
ElectionInformation,
sizeof(ElectionInformation),
1,
Transport->TransportName.Buffer);
dlog(DPRT_ELECT, ("HandleElectionWorker: Won election, but we're not the PDC. Winner: Version: %lx; Criteria: %lx; Time Up: %lx; Name: %s\n",
ElectionResponse->Version,
SmbGetUlong(&ElectionResponse->Criteria),
SmbGetUlong(&ElectionResponse->TimeUp),
ElectionResponse->ServerName));
}
#endif
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, ("Bowser: Browser is exempt from election\n"));
try_return(NOTHING);
}
dlog(DPRT_ELECT, ("Bowser:elect_rcv: Better criteria, calling elect_master in %ld milliseconds.\n", 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, ("Calling ElectMaster in %ld milliseconds\n", 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 (BowserData.IsPrimaryDomainController) {
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_PDC;
}
if (BowserData.IsDomainMaster) {
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_DOMMSTR;
}
if (PagedTransport->Role == Master) {
PagedTransport->ElectionCriteria |= ELECTION_DESIRE_AM_MASTER;
Delay = MASTER_ELECTION_DELAY;
} else if (BowserData.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->PrimaryDomain->PagedTransportName->Name->Name, 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) {
OEM_STRING OemString;
NTSTATUS Status;
//
// Convert the unicode computer name into ANSI for BowserNewMaster
//
Status = RtlUnicodeStringToOemString(&OemString, &Transport->ComputerName->PagedTransportName->Name->Name, TRUE);
if (!NT_SUCCESS(Status)) {
BowserWriteErrorLogEntry(EVENT_BOWSER_NAME_CONVERSION_FAILED,
Status,
Transport->ComputerName->PagedTransportName->Name->Name.Buffer,
Transport->ComputerName->PagedTransportName->Name->Name.Length,
0);
try_return(Status);
}
//
// We're the new master - we won!
//
BowserNewMaster(Transport, OemString.Buffer);
RtlFreeAnsiString(&OemString);
} 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"));
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->PrimaryDomain->PagedTransportName->Name->Name,
MasterBrowser,
Transport);
if (NT_SUCCESS(Status)) {
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, ("Bowser::Find_Master: Master not found, forcing election.\n"));
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, ("Bowser: Last election long enough ago, forcing election on %wZ\n",
&PagedTransport->TransportName));
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, ("Bowser: Last election too recent, delay %ld before forcing election on %wZ\n",
TimeTilNextElection,
&PagedTransport->TransportName));
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;
OEM_STRING OemString;
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, ("Send true election.\n"));
//
// 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;
OemString.Buffer = ElectionRequest->ElectionRequest.ServerName;
OemString.MaximumLength = LM20_CNLEN+1;
Status = RtlUnicodeStringToOemString(&OemString, &Transport->ComputerName->PagedTransportName->Name->Name, FALSE);
if (!NT_SUCCESS(Status)) {
BowserWriteErrorLogEntry(EVENT_BOWSER_NAME_CONVERSION_FAILED,
Status,
Transport->ComputerName->PagedTransportName->Name->Name.Buffer,
Transport->ComputerName->PagedTransportName->Name->Name.Length,
0);
return Status;
}
} else {
dlog(DPRT_ELECT, ("Send dummy election.\n"));
//
// 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;
OemString.Length = 0;
}
return BowserSendSecondClassMailslot(Transport,
NameToSend,
NameType,
ElectionRequest,
FIELD_OFFSET(REQUEST_ELECTION, ElectionRequest.ServerName)+OemString.Length+sizeof(UCHAR),
TRUE,
MAILSLOT_BROWSER_NAME,
NULL
);
}