/*++ 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 #include // NetUseDel //-------------------------------------------------------------------// // // // Local structure definitions // // // //-------------------------------------------------------------------// //-------------------------------------------------------------------// // // // Local function prototypes // // // //-------------------------------------------------------------------// 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( IN PNETWORK Network ) /*++ 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 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; } // // 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, NULL); 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) { // Might not be since we dropped the lock. // ASSERT (Network->Role & ROLE_BACKUP); // // We're now a backup server, announce ourselves as such. // Status = BrUpdateNetworkAnnouncementBits(Network, 0); 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) { // // 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); // // Make sure that we're going to become a potential browser // (we might not if we're an advanced server). // 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, 0); 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); } Status = NO_ERROR; 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. --*/ { 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|ROLE_POTENTIAL_BACKUP); Status = BrUpdateNetworkAnnouncementBits( Network, 0 ); 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); } 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; #ifdef ENABLE_PSEUDO_BROWSER if ( BrInfo.PseudoServerLevel == BROWSER_PSEUDO ) { // // No-op for Pseudo server // BrFreeNetworkTables(Network); return NERR_Success; } #endif // // 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)) { Status = NERR_InternalError; goto finally_exit; } 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, 0); } 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 ); finally_exit:; } return Status; } NET_API_STATUS BrRetrieveInterimServerList( IN PNETWORK Network, IN ULONG ServerType ) { ULONG EntriesInList; ULONG TotalEntriesInList; ULONG RetryCount = 2; TCHAR ServerName[UNCLEN+1]; WCHAR ShareName[UNCLEN+1+LM20_NNLEN]; LPTSTR TransportName; BOOLEAN NetworkLocked = TRUE; NET_API_STATUS Status; PVOID Buffer = NULL; ULONG ModifiedServerType = ServerType; LPTSTR ModifiedTransportName; ASSERT (Network->LockCount == 1); #ifdef ENABLE_PSEUDO_BROWSER if ( BrInfo.PseudoServerLevel == BROWSER_PSEUDO ) { // // No-op for Pseudo black hole server. // return NERR_Success; } #endif 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 the redir is being possesive of some other transport, // urge it to behave. // if ( Status == ERROR_CONNECTION_ACTIVE ) { BrPrint(( BR_BACKUP, "%ws: %ws: Failed to retrieve %s list from server %ws: Connection is active (Try NetUseDel)\n", Network->DomainInfo->DomUnicodeDomainName, TransportName, (ServerType == SV_TYPE_ALL ? "server" : "domain"), ServerName )); // // Delete the IPC$ share. // Status = NetUseDel( NULL, ShareName, USE_FORCE ); if ( Status != NO_ERROR ) { BrPrint(( BR_BACKUP, "%ws: %ws: Failed to retrieve %s list from server %ws: NetUseDel failed: %ld\n", Network->DomainInfo->DomUnicodeDomainName, TransportName, (ServerType == SV_TYPE_ALL ? "server" : "domain"), ServerName, Status)); Status = ERROR_CONNECTION_ACTIVE; // // That worked so try it again. // } else { 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; if ((EntriesInList != 0) && (Buffer != NULL)) { NetApiBufferFree(Buffer); Buffer = NULL; } 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); } 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); } NET_API_STATUS PostWaitForNewMasterName( PNETWORK Network, LPWSTR MasterName OPTIONAL ) { // // Can't wait for new master on direct host IPC // if (Network->Flags & NETWORK_IPX) { return STATUS_SUCCESS; } 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); } return; } #ifdef ENABLE_PSEUDO_BROWSER // // Pseudo Server // Phase out black hole Helper routines // VOID BrFreeNetworkTables( IN PNETWORK Network ) /*++ Routine Description: Free network tables Arguments: Network to operate upon Return Value: None. Remarks: Acquire & release network locks --*/ { BOOL NetLocked = FALSE; // // Prevent the network from being deleted while we're in this timer routine. // if ( BrReferenceNetwork( Network ) == NULL ) { return; } try{ // lock network if (!LOCK_NETWORK(Network)) { try_return(NOTHING); } NetLocked = TRUE; // // Delete tables // UninitializeInterimServerList(&Network->BrowseTable); UninitializeInterimServerList(&Network->DomainList); if (Network->BackupServerList != NULL) { MIDL_user_free(Network->BackupServerList); Network->BackupServerList = NULL; Network->TotalBackupServerListEntries = 0; } if (Network->BackupDomainList != NULL) { MIDL_user_free(Network->BackupDomainList); Network->BackupDomainList = NULL; Network->TotalBackupDomainListEntries = 0; } BrDestroyResponseCache(Network); try_exit:NOTHING; } finally { // // Release network // if (NetLocked) { UNLOCK_NETWORK(Network); } BrDereferenceNetwork( Network ); } } #endif