summaryrefslogtreecommitdiffstats
path: root/private/net/svcdlls/browser2/server/browser.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/net/svcdlls/browser2/server/browser.c')
-rw-r--r--private/net/svcdlls/browser2/server/browser.c1633
1 files changed, 1633 insertions, 0 deletions
diff --git a/private/net/svcdlls/browser2/server/browser.c b/private/net/svcdlls/browser2/server/browser.c
new file mode 100644
index 000000000..18ae245e1
--- /dev/null
+++ b/private/net/svcdlls/browser2/server/browser.c
@@ -0,0 +1,1633 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ browser.c
+
+Abstract:
+
+ This module contains the worker routines for the NetWksta APIs
+ implemented in the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+//-------------------------------------------------------------------//
+// //
+// Local structure definitions //
+// //
+//-------------------------------------------------------------------//
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+NET_API_STATUS
+BecomeFullBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ );
+
+VOID
+CompleteAsyncBrowserIoControl(
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ );
+
+VOID
+BecomeBackupCompletion (
+ IN PVOID Ctx
+ );
+
+
+VOID
+ChangeBrowserRole (
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+PostWaitForNewMasterName(
+ PNETWORK Network,
+ LPWSTR MasterName OPTIONAL
+ );
+
+VOID
+NewMasterCompletionRoutine(
+ IN PVOID Ctx
+ );
+
+NET_API_STATUS
+BrRetrieveInterimServerList(
+ IN PNETWORK Network,
+ IN ULONG ServerType
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+ NET_API_STATUS
+BrBecomeBackup(
+ PDOMAIN_INFO DomainInfo
+ )
+/*++
+
+Routine Description:
+
+ Force this machine to become a backup browser on all networks for the
+ specified domain.
+
+Arguments:
+
+ DomainInfo - Specifies the domain to become the backup browser for.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ return BrEnumerateNetworksForDomain( DomainInfo, BecomeFullBackup, NULL);
+}
+
+ NET_API_STATUS
+BecomeFullBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This function performs all the operations needed to make a browser server
+ a backup browser server when starting the browser from scratch.
+
+Arguments:
+
+ Network - Network to become backup browser for
+
+ Context - Not used.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ //
+ // Checkpoint the service controller - this gives us 30 seconds/transport
+ // before the service controller gets unhappy.
+ //
+
+ (void) BrGiveInstallHints( SERVICE_START_PENDING );
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // Were starting from "net start browser". We need to push in potential
+ // browser before calling BecomeBackup to make the browser add the election
+ // name.
+ //
+
+ Status = BrUpdateBrowserStatus(Network, SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to update browser status: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status ));
+ UNLOCK_NETWORK(Network);
+ return Status;
+ }
+
+ //
+ // We want to ignore any failures from becoming a backup browser.
+ //
+ // We do this because we will fail to become a backup on a disconnected
+ // (or connected) RAS link, and if we failed this routine, we would
+ // fail to start at all.
+ //
+ // We will handle failure to become a backup in a "reasonable manner"
+ // inside BecomeBackup.
+ //
+
+ BecomeBackup(Network, Context);
+
+ UNLOCK_NETWORK(Network);
+
+ return NERR_Success;
+
+}
+
+ NET_API_STATUS
+BecomeBackup(
+ IN PNETWORK Network,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This function performs all the operations needed to make a browser server
+ a backup browser server
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+
+NOTE:::: THIS ROUTINE IS CALLED WITH THE NETWORK STRUCTURE LOCKED!!!!!
+
+
+--*/
+{
+ NET_API_STATUS Status = NERR_Success;
+ PUNICODE_STRING MasterName = Context;
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BecomeBackup called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ if (Network->TimeStoppedBackup != 0 &&
+ (BrCurrentSystemTime() - Network->TimeStoppedBackup) <= (BrInfo.BackupBrowserRecoveryTime / 1000)) {
+
+ //
+ // We stopped being a backup too recently for us to restart being
+ // a backup again, so just return a generic error.
+ //
+
+ //
+ // Before we return, make sure we're not a backup in the eyes of
+ // the browser.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: can't BecomeBackup since we were backup recently.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+ BrStopBackup(Network);
+
+ return ERROR_ACCESS_DENIED;
+
+ }
+
+ //
+ // If we know the name of the master, then we must have become a backup
+ // after being a potential, in which case we already have a
+ // becomemaster request outstanding.
+ //
+
+ if (MasterName == NULL) {
+
+ //
+ // Post a BecomeMaster request for each server. This will complete
+ // when the machine becomes the master browser server (ie. it wins an
+ // election).
+ //
+
+ //
+ // Please note that we only post it if the machine is a backup -
+ // if it's a potential master, then the become master will have
+ // already been posted.
+ //
+
+ Status = PostBecomeMaster(Network);
+
+ if (Status != NERR_Success) {
+
+ return(Status);
+ }
+
+ //
+ // Find out the name of the master on each network. This will force an
+ // election if necessary. Please note that we must post the BecomeMaster
+ // IoControl first to allow us to handle an election.
+ //
+
+ //
+ // We unlock the network, because this may cause us to become promoted
+ // to a master.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: FindMaster called from BecomeBackup\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ UNLOCK_NETWORK(Network);
+
+ Status = GetMasterServerNames(Network);
+
+ if (Status != NERR_Success) {
+
+ //
+ // Re-lock the network structure so we will return with the
+ // network locked.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // We couldn't find who the master is. Stop being a backup now.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: can't BecomeBackup since we can't find master.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ BrStopBackup(Network);
+
+ //
+ // If we're a master now, we should return success. We've not
+ // become a backup, but it wasn't an error.
+ //
+ // ERROR_MORE_DATA is the mapping for
+ // STATUS_MORE_PROCESSING_REQUIRED which is returned when this
+ // situation happens.
+ //
+
+ if ((Status == ERROR_MORE_DATA) || (Network->Role & ROLE_MASTER)) {
+ Status = NERR_Success;
+ }
+
+ return(Status);
+ }
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ //
+ // We managed to become a master. We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+
+ return NERR_Success;
+ }
+
+ }
+
+#ifdef notdef
+ //
+ // ?? For now, we'll always PostForRoleChange on all transports regardless
+ // of role.
+ // We not only need to do it here. But we need to do it when we become
+ // the master so we can find out when we loose an election.
+ //
+
+
+ //
+ // We're now a backup, we need to issue an API that will complete if the
+ // browse master doesn't like us (and thus forces us to shutdown).
+ //
+ //
+
+ PostWaitForRoleChange ( Network );
+#endif // notdef
+
+ PostWaitForNewMasterName(Network, Network->UncMasterBrowserName );
+
+ //
+ // Unlock the network structure before calling BackupBrowserTimerRoutine.
+ //
+
+ UNLOCK_NETWORK(Network);
+
+ //
+ // Run the timer that causes the browser to download a new browse list
+ // from the master. This will seed our server and domain lists to
+ // guarantee that any clients have a reasonable list. It will also
+ // restart the timer to announce later on.
+ //
+
+ Status = BackupBrowserTimerRoutine(Network);
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ if (Status == NERR_Success) {
+
+ ASSERT (Network->Role & ROLE_BACKUP);
+
+ //
+ // We're now a backup server, announce ourselves as such.
+ //
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ if (Status != NERR_Success) {
+
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to become backup: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+
+ if (Network->Role & ROLE_BACKUP) {
+
+ //
+ // Make sure that we're going to become a potential browser
+ // (we might not if we're an advanced server).
+ //
+
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ //
+ // We were unable to become a backup.
+ //
+ // We need to back out and become a potential browser now.
+ //
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: can't BecomeBackup since we can't update announce bits.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+ BrStopBackup(Network);
+
+ PostBecomeBackup(Network);
+
+ }
+ }
+
+ return Status;
+
+ }
+
+ return Status;
+}
+
+
+ NET_API_STATUS
+BrBecomePotentialBrowser (
+ IN PVOID TimerContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a machine has stopped being a backup browser.
+
+ It runs after a reasonable timeout period has elapsed, and marks the
+ machine as a potential browser.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+
+--*/
+
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+
+ //
+ // Prevent the network from being deleted while we're in this timer routine.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ return NERR_InternalError;
+ }
+
+
+ //
+ // Mark this guy as a potential browser.
+ //
+
+ try {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError );
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BrBecomePotentialBrowser called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // Reset that we've stopped being a backup, since it's been long
+ // enough.
+ //
+
+ Network->TimeStoppedBackup = 0;
+
+ if (BrInfo.MaintainServerList == 0) {
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Unable to reset backup announcement bits: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+ } else {
+
+ //
+ // If we're configured to be a backup browser, then we want to
+ // become a backup once again.
+ //
+
+ BecomeBackup(Network, NULL);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ UNLOCK_NETWORK(Network);
+ BrDereferenceNetwork( Network );
+ }
+
+ return Status;
+}
+
+ NET_API_STATUS
+BrStopBackup (
+ IN PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to stop a machine from being a backup browser.
+
+ It is typically called after some form of error has occurred while
+ running as a browser to make sure that we aren't telling anyone that
+ we're a backup browser.
+
+ We are also called when we receive a "reset state" tickle packet.
+
+Arguments:
+
+ Network - The network being shut down.
+
+Return Value:
+
+ Status - The status of the operation.
+
+Note:
+ This routine must be careful about the ROLE_POTENTIAL bit. If the
+ net does not have the potential bit set, it is possible that we might
+ delete the election name in BrUpdateNetworkAnnouncementBits, so we
+ do the work "by hand".
+
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ //
+ // This guy is shutting down - set his role to 0 and announce.
+ //
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BrStopBackup called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ Network->Role &= ~ROLE_BACKUP;
+
+ Status = BrUpdateBrowserStatus(Network, SV_TYPE_POTENTIAL_BROWSER);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to clear backup announcement bits: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+ //
+ // Clear ALL the browser bits in the server - we aren't even a
+ // potential browser to the server anymore.
+ //
+
+ Status = I_NetServerSetServiceBitsEx(
+ NULL,
+ Network->DomainInfo->DomUnicodeComputerName,
+ Network->NetworkName.Buffer,
+ BROWSER_SERVICE_BITS_OF_INTEREST,
+ 0,
+ TRUE );
+
+ if (Status != NERR_Success) {
+ BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL);
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to update server status: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+ Status = BrCancelTimer(&Network->BackupBrowserTimer);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to clear backup browser timer: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+ if (Network->BackupDomainList != NULL) {
+
+ NetApiBufferFree(Network->BackupDomainList);
+
+ Network->BackupDomainList = NULL;
+
+ Network->TotalBackupDomainListEntries = 0;
+ }
+
+ if (Network->BackupServerList != NULL) {
+ NetApiBufferFree(Network->BackupServerList);
+
+ Network->BackupServerList = NULL;
+
+ Network->TotalBackupServerListEntries = 0;
+ }
+
+ BrDestroyResponseCache(Network);
+
+ //
+ // After our recovery time, we can become a potential browser again.
+ //
+
+ Status = BrSetTimer(&Network->BackupBrowserTimer, BrInfo.BackupBrowserRecoveryTime, BrBecomePotentialBrowser, Network);
+
+ if (Status != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to clear backup browser timer: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ try_return(Status);
+ }
+
+
+try_exit:NOTHING;
+ } finally {
+ //
+ // Remember when we were asked to stop being a backup browser.
+ //
+
+ Network->TimeStoppedBackup = BrCurrentSystemTime();
+
+ UNLOCK_NETWORK(Network);
+ }
+
+ return Status;
+
+}
+
+
+ NET_API_STATUS
+BackupBrowserTimerRoutine (
+ IN PVOID TimerContext
+ )
+{
+ IN PNETWORK Network = TimerContext;
+ NET_API_STATUS Status;
+ PVOID ServerList = NULL;
+ BOOLEAN NetworkLocked = FALSE;
+
+ //
+ // Prevent the network from being deleted while we're in this timer routine.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ return NERR_InternalError;
+ }
+
+ try {
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError );
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+ ASSERT ( NetpIsUncComputerNameValid( Network->UncMasterBrowserName ) );
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BackupBrowserTimerRoutine called\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network);
+
+ //
+ // We managed to become a master by the time we locked the structure.
+ // We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(Status = NERR_Success);
+ }
+
+ Status = BrRetrieveInterimServerList(Network, SV_TYPE_ALL);
+
+ //
+ // Bail out if we didn't get any new servers.
+ //
+
+ if (Status != NERR_Success && Status != ERROR_MORE_DATA) {
+
+ //
+ // Try again after an appropriate error delay.
+ //
+
+ try_return(Status);
+ }
+
+ //
+ // Now do everything that we did above for the server list for the
+ // list of domains.
+ //
+
+ Status = BrRetrieveInterimServerList(Network, SV_TYPE_DOMAIN_ENUM);
+
+ //
+ // We successfully updated the server and domain lists for this
+ // server. Now age all the cached domain entries out of the cache.
+ //
+
+ if (Status == NERR_Success || Status == ERROR_MORE_DATA) {
+ BrAgeResponseCache(Network);
+ }
+
+ try_return(Status);
+
+try_exit:NOTHING;
+ } finally {
+ NET_API_STATUS NetStatus;
+
+ if (!NetworkLocked) {
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ NetworkLocked = TRUE;
+ }
+
+ //
+ // If the API succeeded, Mark that we're a backup and
+ // reset the timer.
+ //
+
+ if (Status == NERR_Success || Status == ERROR_MORE_DATA ) {
+
+ if ((Network->Role & ROLE_BACKUP) == 0) {
+
+ //
+ // If we weren't a backup, we are one now.
+ //
+
+ Network->Role |= ROLE_BACKUP;
+
+ Status = BrUpdateNetworkAnnouncementBits(Network, NULL);
+
+ }
+
+ Network->NumberOfFailedBackupTimers = 0;
+
+ Network->TimeStoppedBackup = 0;
+
+ //
+ // Restart the timer for this domain.
+ //
+
+ NetStatus = BrSetTimer(&Network->BackupBrowserTimer, BrInfo.BackupPeriodicity*1000, BackupBrowserTimerRoutine, Network);
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to restart browser backup timer: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ }
+
+ } else {
+
+ //
+ // We failed to retrieve a backup list, remember the failure and
+ // decide if it's been too many failures. If not, just log
+ // the error, if it has, stop being a backup browser.
+ //
+
+ Network->NumberOfFailedBackupTimers += 1;
+
+ if (Network->NumberOfFailedBackupTimers >= BACKUP_ERROR_FAILURE) {
+ LPWSTR SubStrings[1];
+
+ SubStrings[0] = Network->NetworkName.Buffer;
+
+ //
+ // This guy can't be a backup any more, bail out now.
+ //
+
+ BrLogEvent(EVENT_BROWSER_BACKUP_STOPPED, Status, 1, SubStrings);
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BackupBrowserTimerRoutine retrieve backup list so stop being backup.\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+ BrStopBackup(Network);
+ } else {
+ //
+ // Restart the timer for this domain.
+ //
+
+ NetStatus = BrSetTimer(&Network->BackupBrowserTimer, BACKUP_ERROR_PERIODICITY*1000, BackupBrowserTimerRoutine, Network);
+
+ if (NetStatus != NERR_Success) {
+ BrPrint(( BR_CRITICAL,
+ "%ws: %ws: Unable to restart browser backup timer: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Status));
+ }
+
+ }
+
+ }
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+
+ return Status;
+
+}
+
+ NET_API_STATUS
+BrRetrieveInterimServerList(
+ IN PNETWORK Network,
+ IN ULONG ServerType
+ )
+{
+ ULONG EntriesInList;
+ ULONG TotalEntriesInList;
+ ULONG RetryCount = 2;
+ TCHAR ServerName[UNCLEN+1];
+ LPTSTR TransportName;
+ BOOLEAN NetworkLocked = TRUE;
+ NET_API_STATUS Status;
+ PVOID Buffer = NULL;
+ ULONG ModifiedServerType = ServerType;
+ LPTSTR ModifiedTransportName;
+
+ ASSERT (Network->LockCount == 1);
+
+ wcscpy(ServerName, Network->UncMasterBrowserName );
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BrRetrieveInterimServerList: UNC servername is %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ServerName));
+
+ try {
+
+ TransportName = Network->NetworkName.Buffer;
+ ModifiedTransportName = TransportName;
+ //
+ // If this is direct host IPX,
+ // we remote the API over the Netbios IPX transport since
+ // the NT redir doesn't support direct host IPX.
+ //
+
+ if ( (Network->Flags & NETWORK_IPX) &&
+ Network->AlternateNetwork != NULL) {
+
+ //
+ // Use the alternate transport
+ //
+
+ ModifiedTransportName = Network->AlternateNetwork->NetworkName.Buffer;
+
+ //
+ // Tell the server to use it's alternate transport.
+ //
+
+ if ( ServerType == SV_TYPE_ALL ) {
+ ModifiedServerType = SV_TYPE_ALTERNATE_XPORT;
+ } else {
+ ModifiedServerType |= SV_TYPE_ALTERNATE_XPORT;
+ }
+
+ }
+
+ while (RetryCount--) {
+
+ //
+ // If we are promoted to master and fail to become the master,
+ // we will still be marked as being the master in our network
+ // structure, thus we should bail out of the loop in order
+ // to prevent us from looping back on ourselves.
+ //
+
+ if (STRICMP(&ServerName[2], Network->DomainInfo->DomUnicodeComputerName) == 0) {
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ }
+
+ //
+ // We were unable to find the master. Attempt to find out who
+ // the master is. If there is none, this will force an
+ // election.
+ //
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: FindMaster called from BrRetrieveInterimServerList\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ Status = GetMasterServerNames(Network);
+
+ if (Status != NERR_Success) {
+ try_return(Status);
+ }
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ break;
+ }
+
+ //
+ // If we somehow became the master, we don't want to try to
+ // retrieve the list from ourselves either.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+ try_return(Status = NERR_Success);
+ }
+
+ ASSERT (Network->LockCount == 1);
+
+ if (NetworkLocked) {
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+
+ }
+
+ EntriesInList = 0;
+
+ Status = RxNetServerEnum(ServerName, // Server name
+ ModifiedTransportName, // Transport name
+ 101, // Level
+ (LPBYTE *)&Buffer, // Buffer
+ 0xffffffff, // Prefered Max Length
+ &EntriesInList, // EntriesRead
+ &TotalEntriesInList, // TotalEntries
+ ModifiedServerType, // Server type
+ NULL, // Domain (use default)
+ NULL // Resume key
+ );
+
+ if (Status != NERR_Success && Status != ERROR_MORE_DATA) {
+ LPWSTR SubStrings[2];
+
+ SubStrings[0] = ServerName;
+ SubStrings[1] = TransportName;
+
+ BrLogEvent((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ EVENT_BROWSER_DOMAIN_LIST_FAILED :
+ EVENT_BROWSER_SERVER_LIST_FAILED),
+ Status,
+ 2,
+ SubStrings);
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Failed to retrieve %s list from server %ws: %ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ TransportName,
+ (ServerType == SV_TYPE_ALL ? "server" : "domain"),
+ ServerName,
+ Status));
+ } else {
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Retrieved %s list from server %ws: E:%ld, T:%ld\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ TransportName,
+ (ServerType == SV_TYPE_ALL ? "server" : "domain"),
+ ServerName,
+ EntriesInList,
+ TotalEntriesInList));
+ }
+
+ //
+ // If we succeeded in retrieving the list, but we only got
+ // a really small number of either servers or domains,
+ // we want to turn this into a failure.
+ //
+
+ if (Status == NERR_Success) {
+ if (((ServerType == SV_TYPE_DOMAIN_ENUM) &&
+ (EntriesInList < BROWSER_MINIMUM_DOMAIN_NUMBER)) ||
+ ((ServerType == SV_TYPE_ALL) &&
+ (EntriesInList < BROWSER_MINIMUM_SERVER_NUMBER))) {
+
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ }
+ }
+
+ if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ Status = NERR_InternalError;
+ break;
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+#if DBG
+ BrUpdateDebugInformation((ServerType == SV_TYPE_DOMAIN_ENUM ?
+ L"LastDomainListRead" :
+ L"LastServerListRead"),
+ L"BrowserServerName",
+ TransportName,
+ ServerName,
+ 0);
+#endif
+
+ //
+ // We've retrieved a new list from the browse master, save
+ // the new list away in the "appropriate" spot.
+ //
+
+ //
+ // Of course, we free up the old buffer before we do this..
+ //
+
+ if (ServerType == SV_TYPE_DOMAIN_ENUM) {
+ if (Network->BackupDomainList != NULL) {
+ NetApiBufferFree(Network->BackupDomainList);
+ }
+
+ Network->BackupDomainList = Buffer;
+
+ Network->TotalBackupDomainListEntries = EntriesInList;
+ } else {
+ if (Network->BackupServerList != NULL) {
+ NetApiBufferFree(Network->BackupServerList);
+ }
+
+ Network->BackupServerList = Buffer;
+
+ Network->TotalBackupServerListEntries = EntriesInList;
+ }
+
+ break;
+ } else {
+ NET_API_STATUS GetMasterNameStatus;
+
+ if ((EntriesInList != 0) && (Buffer != NULL)) {
+ NetApiBufferFree(Buffer);
+ Buffer = NULL;
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: Unable to contact browser server %ws: %lx\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ TransportName,
+ ServerName,
+ Status));
+
+ if (NetworkLocked) {
+
+ //
+ // We were unable to find the master. Attempt to find out who
+ // the master is. If there is none, this will force an
+ // election.
+ //
+
+ ASSERT (Network->LockCount == 1);
+
+ UNLOCK_NETWORK(Network);
+
+ NetworkLocked = FALSE;
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: FindMaster called from BrRetrieveInterimServerList for failure\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer));
+
+ GetMasterNameStatus = GetMasterServerNames(Network);
+
+ //
+ // We were able to find out who the master is.
+ //
+ // Retry and retrieve the server/domain list.
+ //
+
+ if (GetMasterNameStatus == NERR_Success) {
+
+ ASSERT (!NetworkLocked);
+
+ if (!LOCK_NETWORK(Network)) {
+ try_return(Status = NERR_InternalError);
+ }
+
+ NetworkLocked = TRUE;
+
+ ASSERT (Network->LockCount == 1);
+
+ //
+ // We managed to become a master. We want to return right away.
+ //
+
+ if (Network->Role & ROLE_MASTER) {
+
+ try_return(Status = NERR_InternalError);
+ }
+
+ wcscpy(ServerName, Network->UncMasterBrowserName );
+
+ ASSERT ( NetpIsUncComputerNameValid( ServerName ) );
+
+ ASSERT (STRICMP(&ServerName[2], Network->DomainInfo->DomUnicodeComputerName) != 0);
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: New master name is %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ ServerName));
+
+ } else {
+ try_return(Status);
+ }
+ }
+ }
+try_exit:NOTHING;
+ } finally {
+ if (!NetworkLocked) {
+ if (!LOCK_NETWORK(Network)) {
+ Status = NERR_InternalError;
+ }
+
+ ASSERT (Network->LockCount == 1);
+
+ }
+ }
+
+ return Status;
+}
+
+
+ NET_API_STATUS
+PostBecomeBackup(
+ PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a BecomeBackup
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a backup browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ Network->Role |= ROLE_POTENTIAL_BACKUP;
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_BECOME_BACKUP,
+ BecomeBackupCompletion,
+ NULL );
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+}
+
+VOID
+BecomeBackupCompletion (
+ IN PVOID Ctx
+ )
+{
+ NET_API_STATUS Status;
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+
+ if (NT_SUCCESS(Context->IoStatusBlock.Status)) {
+
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) != NULL ) {
+
+ if (LOCK_NETWORK(Network)) {
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: BecomeBackupCompletion. We are now a backup server\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ Status = BecomeBackup(Context->Network, NULL);
+
+ UNLOCK_NETWORK(Network);
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+}
+
+ VOID
+BrBrowseTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ //
+ // We need to miss 3 retrievals of the browse list for us to toss the
+ // server.
+ //
+
+ InterimElement->Periodicity = BrInfo.BackupPeriodicity * 3;
+
+ if (InterimElement->TimeLastSeen != 0xffffffff) {
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+ }
+}
+
+ VOID
+BrBrowseTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+// BrPrint(( BR_CRITICAL, "Deleting element for server %ws\n", InterimElement->Name));
+}
+
+ VOID
+BrBrowseTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ if (InterimElement->TimeLastSeen != 0xffffffff) {
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+ }
+}
+
+ BOOLEAN
+BrBrowseTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when we are scanning an interim server list trying
+ to age the elements in the list. It returns TRUE if the entry is too
+ old.
+
+Arguments:
+
+ PINTERIM_SERVER_LIST InterimTable - A pointer to the interim server list.
+ PINTERIM_ELEMENT InterimElement - A pointer to the element to check.
+
+Return Value:
+
+ TRUE if the element should be deleted.
+
+--*/
+
+{
+ if (InterimElement->TimeLastSeen == 0xffffffff) {
+ return FALSE;
+ }
+
+ if ((InterimElement->TimeLastSeen + InterimElement->Periodicity) < BrCurrentSystemTime()) {
+// BrPrint(( BR_CRITICAL, "Aging out element for server %ws\n", InterimElement->Name));
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+ VOID
+BrDomainTableInsertRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ InterimElement->Periodicity = BrInfo.BackupPeriodicity * 3;
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+
+}
+
+ VOID
+BrDomainTableDeleteRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+// BrPrint(( BR_CRITICAL, "Deleting element for domain %ws\n", InterimElement->Name));
+}
+
+ VOID
+BrDomainTableUpdateRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+{
+ InterimElement->TimeLastSeen = BrCurrentSystemTime();
+}
+
+ BOOLEAN
+BrDomainTableAgeRoutine(
+ IN PINTERIM_SERVER_LIST InterimTable,
+ IN PINTERIM_ELEMENT InterimElement
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when we are scanning an interim server list trying
+ to age the elements in the list. It returns TRUE if the entry is too
+ old.
+
+Arguments:
+
+ PINTERIM_SERVER_LIST InterimTable - A pointer to the interim server list.
+ PINTERIM_ELEMENT InterimElement - A pointer to the element to check.
+
+Return Value:
+
+ TRUE if the element should be deleted.
+
+--*/
+
+{
+ if ((InterimElement->TimeLastSeen + InterimElement->Periodicity) < BrCurrentSystemTime()) {
+// BrPrint(( BR_CRITICAL, "Aging out element for domain %ws\n", InterimElement->Name));
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+ NET_API_STATUS
+PostWaitForRoleChange (
+ PNETWORK Network
+ )
+/*++
+
+Routine Description:
+
+ This function is the worker routine called to actually issue a WaitForRoleChange
+ FsControl to the bowser driver on all the bound transports. It will
+ complete when the machine becomes a backup browser server.
+
+ Please note that this might never complete.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Status - The status of the operation.
+
+--*/
+{
+ NET_API_STATUS Status;
+
+ if (!LOCK_NETWORK(Network)) {
+ return NERR_InternalError;
+ }
+
+ Status = BrIssueAsyncBrowserIoControl(Network,
+ IOCTL_LMDR_CHANGE_ROLE,
+ ChangeBrowserRole,
+ NULL );
+ UNLOCK_NETWORK(Network);
+
+ return Status;
+}
+
+VOID
+ChangeBrowserRole (
+ IN PVOID Ctx
+ )
+{
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+
+ if (NT_SUCCESS(Context->IoStatusBlock.Status)) {
+ PWSTR MasterName = NULL;
+ PLMDR_REQUEST_PACKET Packet = Context->RequestPacket;
+
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) != NULL ) {
+
+ if (LOCK_NETWORK(Network)) {
+
+ PostWaitForRoleChange(Network);
+
+ if (Packet->Parameters.ChangeRole.RoleModification & RESET_STATE_CLEAR_ALL) {
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Reset state request to clear all\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ if (Network->Role & ROLE_MASTER) {
+ BrStopMaster(Network);
+ }
+
+ //
+ // Stop being a backup as well.
+ //
+
+ BrStopBackup(Network);
+
+ }
+
+ if ((Network->Role & ROLE_MASTER) &&
+ (Packet->Parameters.ChangeRole.RoleModification & RESET_STATE_STOP_MASTER)) {
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: Reset state request to stop master\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ BrStopMaster(Network);
+
+ //
+ // If we are configured to be a backup, then become a backup
+ // again.
+ //
+
+ if (BrInfo.MaintainServerList == 1) {
+ BecomeBackup(Network, NULL);
+ }
+ }
+
+ //
+ // Make sure there's a become master oustanding.
+ //
+
+ PostBecomeMaster(Network);
+
+ UNLOCK_NETWORK(Network);
+
+ }
+
+ BrDereferenceNetwork( Network );
+ }
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+}
+
+
+NET_API_STATUS
+PostWaitForNewMasterName(
+ PNETWORK Network,
+ LPWSTR MasterName OPTIONAL
+ )
+{
+
+ return BrIssueAsyncBrowserIoControl(
+ Network,
+ IOCTL_LMDR_NEW_MASTER_NAME,
+ NewMasterCompletionRoutine,
+ MasterName );
+
+
+}
+
+VOID
+NewMasterCompletionRoutine(
+ IN PVOID Ctx
+ )
+{
+ PBROWSERASYNCCONTEXT Context = Ctx;
+ PNETWORK Network = Context->Network;
+ BOOLEAN NetLocked = FALSE;
+ BOOLEAN NetReferenced = FALSE;
+
+
+ try {
+ UNICODE_STRING NewMasterName;
+
+ //
+ // Ensure the network wasn't deleted from under us.
+ //
+ if ( BrReferenceNetwork( Network ) == NULL ) {
+ try_return(NOTHING);
+ }
+ NetReferenced = TRUE;
+
+ BrPrint(( BR_MASTER,
+ "%ws: %ws: NewMasterCompletionRoutine: Got master changed\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer ));
+
+ if (!LOCK_NETWORK(Network)){
+ try_return(NOTHING);
+ }
+ NetLocked = TRUE;
+
+ //
+ // The request failed for some other reason - just return immediately.
+ //
+
+ if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
+
+ try_return(NOTHING);
+
+ }
+
+ // Remove new master name & put in transport
+
+ if ( Network->Role & ROLE_MASTER ) {
+
+ try_return(NOTHING);
+
+ }
+
+ BrPrint(( BR_BACKUP,
+ "%ws: %ws: NewMasterCompletionRoutin: New:%ws Old %ws\n",
+ Network->DomainInfo->DomUnicodeDomainName,
+ Network->NetworkName.Buffer,
+ Context->RequestPacket->Parameters.GetMasterName.Name,
+ Network->UncMasterBrowserName ));
+
+ //
+ // Copy the master browser name into the network structure
+ //
+
+ wcsncpy( Network->UncMasterBrowserName,
+ Context->RequestPacket->Parameters.GetMasterName.Name,
+ UNCLEN+1 );
+
+ Network->UncMasterBrowserName[UNCLEN] = L'\0';
+
+ ASSERT ( NetpIsUncComputerNameValid ( Network->UncMasterBrowserName ) );
+
+ PostWaitForNewMasterName( Network, Network->UncMasterBrowserName );
+
+try_exit:NOTHING;
+ } finally {
+
+ if (NetLocked) {
+ UNLOCK_NETWORK(Network);
+ }
+
+ if ( NetReferenced ) {
+ BrDereferenceNetwork( Network );
+ }
+
+ MIDL_user_free(Context->RequestPacket);
+
+ MIDL_user_free(Context);
+
+ }
+
+ return;
+}