/*++ 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 // // // //-------------------------------------------------------------------// ULONG DomainAnnouncementPeriodicity[] = {1*60*1000, 1*60*1000, 5*60*1000, 5*60*1000, 10*60*1000, 10*60*1000, 15*60*1000}; ULONG DomainAnnouncementMax = (sizeof(DomainAnnouncementPeriodicity) / sizeof(ULONG)) - 1; //-------------------------------------------------------------------// // // // Local function prototypes // // // //-------------------------------------------------------------------// VOID BrGetMasterServerNamesOnAllNets( IN PVOID Context ); NET_API_STATUS BrGetMasterServerNameForNet( IN PNETWORK Network, IN PVOID Context ); VOID BecomeMasterCompletion ( IN PVOID Ctx ); NET_API_STATUS StartMasterBrowserTimer( IN PNETWORK Network ); NET_API_STATUS AnnounceMasterToDomainMaster( IN PNETWORK Network, IN LPWSTR ServerName ); //-------------------------------------------------------------------// // // // Global function prototypes // // // //-------------------------------------------------------------------// NET_API_STATUS PostBecomeMaster( PNETWORK Network ) /*++ Routine Description: This function is the worker routine called to actually issue a BecomeMaster FsControl to the bowser driver on all the bound transports. It will complete when the machine becomes a master browser server. Please note that this might never complete. Arguments: None. Return Value: Status - The status of the operation. --*/ { NTSTATUS Status = NERR_Success; if (!LOCK_NETWORK(Network)) { return NERR_InternalError; } if (!(Network->Flags & NETWORK_BECOME_MASTER_POSTED)) { Network->Flags |= NETWORK_BECOME_MASTER_POSTED; // // Make certain that we have the browser election name added // before we allow ourselves to become a master. This is a NOP // if we already have the election name added. // Status = BrUpdateBrowserStatus(Network, BrGetBrowserServiceBits(Network) | SV_TYPE_POTENTIAL_BROWSER); if (Status != NERR_Success) { Network->Flags &= ~NETWORK_BECOME_MASTER_POSTED; BrPrint(( BR_CRITICAL, "%ws: %ws: Unable to update browser status: %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status )); UNLOCK_NETWORK(Network); return Status; } Status = BrIssueAsyncBrowserIoControl(Network, IOCTL_LMDR_BECOME_MASTER, BecomeMasterCompletion, NULL ); } UNLOCK_NETWORK(Network); return Status; } NET_API_STATUS BrRecoverFromFailedPromotion( IN PVOID Ctx ) /*++ Routine Description: When we attempt to promote a machine to master browser and fail, we will effectively shut down the browser for a period of time. When that period of time expires, we will call BrRecoverFromFailedPromotion to recover from the failure. This routine will do one of the following: 1) Force the machine to become a backup browser, or 2) Attempt to discover the name of the master. Arguments: IN PVOID Ctx - The network structure we failed on. Return Value: Status - The status of the operation (usually ignored). --*/ { PNETWORK Network = Ctx; NET_API_STATUS Status; BOOL 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); } BrPrint(( BR_MASTER, "%ws: %ws: BrRecoverFromFailedPromotion.\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer )); NetworkLocked = TRUE; // // We had better not be the master now. // ASSERT (!(Network->Role & ROLE_MASTER)); // // If we're configured to become a backup by default, then become // a backup now. // if (BrInfo.MaintainServerList == 1) { BrPrint(( BR_MASTER, "%ws: %ws: BrRecoverFromFailedPromotion. Become backup.\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer )); Status = BecomeBackup(Network, NULL); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "%ws: %ws: Could not become backup: %lx\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); } } else { BrPrint(( BR_MASTER, "%ws: %ws: BrRecoverFromFailedPromotion. FindMaster.\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer)); UNLOCK_NETWORK(Network); NetworkLocked = FALSE; // // Now try to figure out who is the master. // Status = GetMasterServerNames(Network); // // Ignore the status from this and re-lock the network to // recover cleanly. // if (!LOCK_NETWORK(Network)) { try_return( Status = NERR_InternalError); } NetworkLocked = TRUE; } // // Otherwise, just let sleeping dogs lie. // try_exit:NOTHING; } finally { if (NetworkLocked) { UNLOCK_NETWORK(Network); } BrDereferenceNetwork( Network ); } return Status; } VOID BecomeMasterCompletion ( IN PVOID Ctx ) /*++ Routine Description: This function is called by the I/O system when the request to become a master completes. Please note that it is possible that the request may complete with an error. Arguments: None. Return Value: Status - The status of the operation. --*/ { NET_API_STATUS Status; PBROWSERASYNCCONTEXT Context = Ctx; PNETWORK Network = Context->Network; BOOLEAN NetworkLocked = FALSE; BOOLEAN NetReferenced = FALSE; ULONG ServiceBits; try { // // Ensure the network wasn't deleted from under us. // if ( BrReferenceNetwork( Network ) == NULL ) { try_return(NOTHING); } NetReferenced = TRUE; // // Lock the network structure. // if (!LOCK_NETWORK(Network)) { try_return(NOTHING); } NetworkLocked = TRUE; Network->Flags &= ~NETWORK_BECOME_MASTER_POSTED; if (!NT_SUCCESS(Context->IoStatusBlock.Status)) { BrPrint(( BR_CRITICAL, "%ws: %ws: Failure in BecomeMaster: %X\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Context->IoStatusBlock.Status)); try_return(NOTHING); } BrPrint(( BR_MASTER, "%ws: %ws: BecomeMasterCompletion. Now master on network %ws\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer)); // // If we're already a master, ignore this request. // if (Network->Role & ROLE_MASTER) { try_return(NOTHING); } // // Cancel any outstanding backup timers - we don't download the list // anymore. // Status = BrCancelTimer(&Network->BackupBrowserTimer); if (!NT_SUCCESS(Status)) { BrPrint(( BR_CRITICAL, "%ws: %ws: Could not stop backup timer: %lx\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); } // // Figure out what service bits we should be using when announcing ourselves // Network->Role |= ROLE_MASTER; ServiceBits = BrGetBrowserServiceBits(Network); Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER); if (Status != NERR_Success) { BrPrint(( BR_MASTER, "%ws: %ws: Unable to set master announcement bits in browser: %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); // // When we're in this state, we can't rely on our being a backup // browser - we may not be able to retrieve a valid list of // browsers from the master. // Network->Role &= ~ROLE_BACKUP; Network->NumberOfFailedPromotions += 1; // // Log every 5 failed promotion attempts, and after having logged 5 // promotion events, stop logging them, this means that it's been // 25 times that we've tried to promote, and it's not likely to get // any better. We'll keep on trying, but we won't complain any more. // if ((Network->NumberOfFailedPromotions % 5) == 0) { ULONG AStatStatus; LPWSTR SubString[1]; WCHAR CurrentMasterName[CNLEN+1]; if (Network->NumberOfPromotionEventsLogged < 5) { AStatStatus = GetNetBiosMasterName( Network->NetworkName.Buffer, Network->DomainInfo->DomUnicodeDomainName, CurrentMasterName, BrLmsvcsGlobalData->NetBiosReset ); if (AStatStatus == NERR_Success) { SubString[0] = CurrentMasterName; BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED, Status, 1, SubString); } else { BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_NO_MASTER, Status, 0, NULL); } Network->NumberOfPromotionEventsLogged += 1; if (Network->NumberOfPromotionEventsLogged == 5) { BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_STOPPING, Status, 0, NULL); } } } // // We were unable to promote ourselves to master. // // We want to set our role back to browser, and re-issue the become // master request. // BrStopMaster(Network); BrSetTimer(&Network->MasterBrowserTimer, FAILED_PROMOTION_PERIODICITY*1000, BrRecoverFromFailedPromotion, Network); } else { // // Initialize the number of times the master timer has run. // Network->MasterBrowserTimerCount = 0; Status = StartMasterBrowserTimer(Network); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "%ws: %ws: Could not start browser master timer: %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status )); } Network->NumberOfFailedPromotions = 0; Network->NumberOfPromotionEventsLogged = 0; Network->MasterAnnouncementIndex = 0; Status = I_NetServerSetServiceBitsEx( NULL, Network->DomainInfo->DomUnicodeComputerName, Network->NetworkName.Buffer, BROWSER_SERVICE_BITS_OF_INTEREST, ServiceBits, TRUE); if (Status == NERR_Success) { // // We successfully became the master. // // Now announce ourselves as the new master for this domain. // BrMasterAnnouncement(Network); // // Populate the browse list with the information retrieved // while we were a backup browser. // if (Network->TotalBackupServerListEntries != 0) { MergeServerList(&Network->BrowseTable, 101, Network->BackupServerList, Network->TotalBackupServerListEntries, Network->TotalBackupServerListEntries ); MIDL_user_free(Network->BackupServerList); Network->BackupServerList = NULL; Network->TotalBackupServerListEntries = 0; } if (Network->TotalBackupDomainListEntries != 0) { MergeServerList(&Network->DomainList, 101, Network->BackupDomainList, Network->TotalBackupDomainListEntries, Network->TotalBackupDomainListEntries ); MIDL_user_free(Network->BackupDomainList); Network->BackupDomainList = NULL; Network->TotalBackupDomainListEntries = 0; } // // Unlock the network before calling BrWanMasterInitialize. // UNLOCK_NETWORK(Network); NetworkLocked = FALSE; // // Run the master browser timer routine to get the entire domains // list of servers. // if (Network->Flags & NETWORK_WANNISH) { BrWanMasterInitialize(Network); MasterBrowserTimerRoutine(Network); } } else { BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL); // // Stop being a master browser. // BrStopMaster(Network); BrPrint(( BR_MASTER, "%ws: %ws: Unable to set master announcement bits to server: %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); try_return(NOTHING); } try_return(NOTHING); } try_exit:NOTHING; } finally { // // Make sure there's a become master oustanding. // PostBecomeMaster(Network); if (NetworkLocked) { UNLOCK_NETWORK(Network); } if ( NetReferenced ) { BrDereferenceNetwork( Network ); } MIDL_user_free(Context->RequestPacket); MIDL_user_free(Context); } } NET_API_STATUS ChangeMasterPeriodicityWorker( PNETWORK Network, PVOID Ctx ) /*++ Routine Description: This function changes the master periodicity for a single network. Arguments: None. Return Value: Status - The status of the operation. --*/ { // // Lock the network // if (LOCK_NETWORK(Network)) { // // Ensure we're the master. // if ( Network->Role & ROLE_MASTER ) { NET_API_STATUS NetStatus; // // Cancel the timer to ensure it doesn't go off while we're // processing this change. // NetStatus = BrCancelTimer(&Network->MasterBrowserTimer); ASSERT (NetStatus == NERR_Success); // // Unlock the network while we execute the timer routine. // UNLOCK_NETWORK( Network ); // // Call the timer routine immediately. // MasterBrowserTimerRoutine(Network); } else { UNLOCK_NETWORK( Network ); } } UNREFERENCED_PARAMETER(Ctx); return NERR_Success; } VOID BrChangeMasterPeriodicity ( VOID ) /*++ Routine Description: This function is called when the master periodicity is changed in the registry. Arguments: None. Return Value: None. --*/ { (VOID)BrEnumerateNetworks(ChangeMasterPeriodicityWorker, NULL); } NET_API_STATUS StartMasterBrowserTimer( IN PNETWORK Network ) { NET_API_STATUS Status; Status = BrSetTimer( &Network->MasterBrowserTimer, BrInfo.MasterPeriodicity*1000, MasterBrowserTimerRoutine, Network); return Status; } typedef struct _BROWSER_GETNAMES_CONTEXT { WORKER_ITEM WorkItem; PDOMAIN_INFO DomainInfo; } BROWSER_GETNAMES_CONTEXT, *PBROWSER_GETNAMES_CONTEXT; VOID BrGetMasterServerNamesAysnc( PDOMAIN_INFO DomainInfo ) /*++ Routine Description: Queue a workitem to asynchronously get the master browser names for all transports of a domain. Arguments: DomainInfo - Identifies the domain to query. Return Value: None --*/ { PBROWSER_GETNAMES_CONTEXT Context; // // Allocate context for this async call. // Context = LocalAlloc( 0, sizeof(BROWSER_GETNAMES_CONTEXT) ); if ( Context == NULL ) { return; } // // Just queue this for later execution. // We're doing this for information purposes only. In the case that // the master can't be found, we don't want to wait for completion. // (e.g., on a machine with multiple transports and the net cable is // pulled) // Context->DomainInfo = DomainInfo; BrInitializeWorkItem( &Context->WorkItem, BrGetMasterServerNamesOnAllNets, Context ); BrQueueWorkItem( &Context->WorkItem ); return; } VOID BrGetMasterServerNamesOnAllNets( IN PVOID Context ) /*++ Routine Description: Worker routine to asynchronously get the master browser names for all transports of a domain. Arguments: Context - Context containing the workitem and the description of the domain to query. Return Value: None --*/ { (VOID) BrEnumerateNetworksForDomain( ((PBROWSER_GETNAMES_CONTEXT)Context)->DomainInfo, BrGetMasterServerNameForNet, NULL ); (VOID) LocalFree( Context ); return; } NET_API_STATUS BrGetMasterServerNameForNet( IN PNETWORK Network, IN PVOID Context ) /*++ Routine Description: Routine to get the master browser name for a particular network. Arguments: Context - Not used. Return Value: Status of the operation. --*/ { BrPrint(( BR_INIT, "%ws: %ws: FindMaster during startup\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer)); // // We only call this on startup, so on IPX networks, don't bother to // find out the master. // if (!(Network->Flags & NETWORK_IPX)) { GetMasterServerNames(Network); } return NERR_Success; } NET_API_STATUS GetMasterServerNames( IN PNETWORK Network ) /*++ Routine Description: This function is the worker routine called to determine the name of the master browser server for a particular network. Arguments: None. Return Value: Status - The status of the operation. --*/ { NET_API_STATUS Status; PLMDR_REQUEST_PACKET RequestPacket = NULL; BrPrint(( BR_INIT, "%ws: %ws: FindMaster started\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer)); // // This request could cause an election. Make sure that if we win // the election that we can handle it. // PostBecomeMaster( Network); RequestPacket = MIDL_user_allocate( (UINT) sizeof(LMDR_REQUEST_PACKET)+ MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR) ); if (RequestPacket == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM; // // Set level to TRUE to indicate that find master should initiate // a findmaster request. // RequestPacket->Level = 1; RequestPacket->TransportName = Network->NetworkName; RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString; // // Reference the network while the I/O is pending. // Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_GET_MASTER_NAME, RequestPacket, sizeof(LMDR_REQUEST_PACKET)+Network->NetworkName.Length, RequestPacket, sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR), NULL); if (Status != NERR_Success) { BrPrint(( BR_INIT, "%ws: %ws: FindMaster failed: %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); MIDL_user_free(RequestPacket); return(Status); } if (!LOCK_NETWORK(Network)) { MIDL_user_free(RequestPacket); return NERR_InternalError; } // // Copy the master browser name into the network structure // wcsncpy( Network->UncMasterBrowserName, RequestPacket->Parameters.GetMasterName.Name, UNCLEN+1 ); Network->UncMasterBrowserName[UNCLEN] = L'\0'; ASSERT ( NetpIsUncComputerNameValid( Network->UncMasterBrowserName ) ); BrPrint(( BR_INIT, "%ws: %ws: FindMaster succeeded. Master: %ws\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Network->UncMasterBrowserName)); UNLOCK_NETWORK(Network); MIDL_user_free(RequestPacket); return Status; } VOID MasterBrowserTimerRoutine ( IN PVOID TimerContext ) { IN PNETWORK Network = TimerContext; NET_API_STATUS Status; PVOID ServerList = NULL; PVOID WinsServerList = NULL; ULONG EntriesInList; ULONG TotalEntriesInList; LPWSTR TransportName; BOOLEAN NetLocked = FALSE; LPWSTR PrimaryDomainController = NULL; LPWSTR PrimaryWinsServerAddress = NULL; LPWSTR SecondaryWinsServerAddress = NULL; // // Prevent the network from being deleted while we're in this timer routine. // if ( BrReferenceNetwork( Network ) == NULL ) { return; } try { // // If we're not a master any more, blow away this request. // if (!(Network->Role & ROLE_MASTER)) { try_return(NOTHING); } if (!LOCK_NETWORK(Network)) { try_return(NOTHING); } NetLocked = TRUE; TransportName = Network->NetworkName.Buffer; // // Now that we have the network locked, re-test to see if we are // still the master. // if (!(Network->Role & ROLE_MASTER)) { try_return(NOTHING); } Network->MasterBrowserTimerCount += 1; // // If this is a wannish network, we always want to run the master // timer because we might have information about other subnets // in our list. // if (Network->Flags & NETWORK_WANNISH) { // // Age out servers and domains from the server list. // AgeInterimServerList(&Network->BrowseTable); AgeInterimServerList(&Network->DomainList); // // If we're not the PDC, then we need to retrieve the list // from the PDC.... // if (!Network->DomainInfo->IsPrimaryDomainController) { ASSERT (NetLocked); UNLOCK_NETWORK(Network); NetLocked = FALSE; Status = NetGetDCName(NULL, NULL, (LPBYTE *)&PrimaryDomainController); // // If the PDC can be found, // Exchange server lists with it. // if (Status == NERR_Success) { // // Tell the Domain Master (PDC) that we're a master browser. // (VOID) AnnounceMasterToDomainMaster (Network, &PrimaryDomainController[2]); // // Retrieve the list of all the servers from the PDC. // Status = RxNetServerEnum(PrimaryDomainController, TransportName, 101, (LPBYTE *)&ServerList, 0xffffffff, &EntriesInList, &TotalEntriesInList, SV_TYPE_ALL, NULL, NULL ); if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) { ASSERT (!NetLocked); if (LOCK_NETWORK(Network)) { NetLocked = TRUE; if (Network->Role & ROLE_MASTER) { (VOID) MergeServerList(&Network->BrowseTable, 101, ServerList, EntriesInList, TotalEntriesInList ); } } } if (ServerList != NULL) { MIDL_user_free(ServerList); ServerList = NULL; } if (NetLocked) { UNLOCK_NETWORK(Network); NetLocked = FALSE; } // // Retrieve the list of all the domains from the PDC. // Status = RxNetServerEnum(PrimaryDomainController, TransportName, 101, (LPBYTE *)&ServerList, 0xffffffff, &EntriesInList, &TotalEntriesInList, SV_TYPE_DOMAIN_ENUM, NULL, NULL ); if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) { ASSERT (!NetLocked); if (LOCK_NETWORK(Network)) { NetLocked = TRUE; if (Network->Role & ROLE_MASTER) { (VOID) MergeServerList(&Network->DomainList, 101, ServerList, EntriesInList, TotalEntriesInList ); } } } if (ServerList != NULL) { MIDL_user_free(ServerList); ServerList = NULL; } // // Unlock the network before calling BrWanMasterInitialize. // if (NetLocked) { UNLOCK_NETWORK(Network); NetLocked = FALSE; } BrWanMasterInitialize(Network); } // // If we're on the PDC, we need to get the list of servers from // the WINS server. // } else { // // Ensure a GetMasterAnnouncement request is posted to the bowser. // (VOID) PostGetMasterAnnouncement ( Network, NULL ); // // We want to contact the WINS server now, so we figure out the // IP address of our primary WINS server // Status = BrGetWinsServerName(&Network->NetworkName, &PrimaryWinsServerAddress, &SecondaryWinsServerAddress); if (Status == NERR_Success) { // // Don't keep the network locked during the WINS query // if (NetLocked) { UNLOCK_NETWORK(Network); NetLocked = FALSE; } // // This transport supports WINS queries, so query the WINS // server to retrieve the list of domains on this adapter. // Status = BrQueryWinsServer(PrimaryWinsServerAddress, SecondaryWinsServerAddress, &WinsServerList, &EntriesInList, &TotalEntriesInList ); if (Status == NERR_Success) { // // Lock the network to merge the server list // ASSERT (!NetLocked); if (LOCK_NETWORK(Network)) { NetLocked = TRUE; if (Network->Role & ROLE_MASTER) { // // Merge the list of domains from WINS into the one collected elsewhere // (VOID) MergeServerList( &Network->DomainList, 1010, // Special level to not overide current values WinsServerList, EntriesInList, TotalEntriesInList ); } } } } } // // Restart the timer for this domain. // // Wait to restart it until we're almost done with this iteration. // Otherwise, we could end up with two copies of this routine // running. // Status = StartMasterBrowserTimer(Network); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "%ws: %ws: Unable to restart browser backup timer: %lx\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); try_return(NOTHING); } } else { // // If it is a lan-ish transport, and we have run the master // timer for enough times (ie. we've been a master // for "long enough", we can toss the interim server list in the // master, because the bowser driver will have enough data in its // list by now. // if (Network->MasterBrowserTimerCount >= MASTER_BROWSER_LAN_TIMER_LIMIT) { ASSERT (NetLocked); // // Make all the servers and domains in the interim server list // go away - they aren't needed any more for a LAN-ish transport. // UninitializeInterimServerList(&Network->BrowseTable); ASSERT (Network->BrowseTable.EntriesRead == 0); ASSERT (Network->BrowseTable.TotalEntries == 0); UninitializeInterimServerList(&Network->DomainList); ASSERT (Network->DomainList.EntriesRead == 0); ASSERT (Network->DomainList.TotalEntries == 0); } else { // // Age out servers and domains from the server list. // AgeInterimServerList(&Network->BrowseTable); AgeInterimServerList(&Network->DomainList); // // Restart the timer for this domain. // Status = StartMasterBrowserTimer(Network); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "%ws: %ws: Unable to restart browser backup timer: %lx\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); try_return(NOTHING); } } } try_exit:NOTHING; } finally { if (PrimaryDomainController != NULL) { NetApiBufferFree(PrimaryDomainController); } if (PrimaryWinsServerAddress) { MIDL_user_free(PrimaryWinsServerAddress); } if (SecondaryWinsServerAddress) { MIDL_user_free(SecondaryWinsServerAddress); } if (WinsServerList) { MIDL_user_free(WinsServerList); } if (NetLocked) { UNLOCK_NETWORK(Network); } BrDereferenceNetwork( Network ); } } VOID BrMasterAnnouncement( IN PVOID TimerContext ) /*++ Routine Description: This routine is called to announce the domain on the local sub-net. Arguments: None. Return Value: None --*/ { PNETWORK Network = TimerContext; ULONG Periodicity; DWORD ServiceBits; NET_API_STATUS Status; // // Prevent the network from being deleted while we're in this timer routine. // if ( BrReferenceNetwork( Network ) == NULL ) { return; } if (!LOCK_NETWORK(Network)) { BrDereferenceNetwork( Network ); return; } // // Make absolutely certain that the server thinks that the browser service // bits for this transport are up to date. We do NOT have to force an // announcement, since theoretically, the status didn't change. // ServiceBits = BrGetBrowserServiceBits(Network); Status = I_NetServerSetServiceBitsEx( NULL, Network->DomainInfo->DomUnicodeComputerName, Network->NetworkName.Buffer, BROWSER_SERVICE_BITS_OF_INTEREST, ServiceBits, FALSE); if (Status != NERR_Success) { BrPrint(( BR_CRITICAL, "%ws: %ws: BrMasterAnnouncement: Cannot I_NetServerSetServiceBitsEx %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status )); BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL); } Periodicity = DomainAnnouncementPeriodicity[Network->MasterAnnouncementIndex]; BrSetTimer(&Network->MasterBrowserAnnouncementTimer, Periodicity, BrMasterAnnouncement, Network); if (Network->MasterAnnouncementIndex != DomainAnnouncementMax) { Network->MasterAnnouncementIndex += 1; } // // Announce this domain to the world using the current periodicity. // BrAnnounceDomain(Network, Periodicity); UNLOCK_NETWORK(Network); BrDereferenceNetwork( Network ); } NET_API_STATUS BrStopMaster( IN PNETWORK Network ) { NET_API_STATUS Status; ULONG ServiceBits; // // This guy is shutting down - set his role to 0 and announce. // if (!LOCK_NETWORK(Network)) { return NERR_InternalError; } try { BrPrint(( BR_MASTER, "%ws: %ws: Stopping being master.\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer)); // // When we stop being a master, we can no longer be considered a // backup either, since backups maintain their server list // differently than the master. // Network->Role &= ~(ROLE_MASTER | ROLE_BACKUP); ServiceBits = BrGetBrowserServiceBits(Network); ASSERT ((ServiceBits & SV_TYPE_MASTER_BROWSER) == 0); Status = BrUpdateBrowserStatus(Network, ServiceBits | SV_TYPE_POTENTIAL_BROWSER); if (Status != NERR_Success) { BrPrint(( BR_MASTER, "%ws: %ws: Unable to clear master announcement bits in browser: %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); try_return(Status); } Status = I_NetServerSetServiceBitsEx( NULL, Network->DomainInfo->DomUnicodeComputerName, Network->NetworkName.Buffer, BROWSER_SERVICE_BITS_OF_INTEREST, ServiceBits, TRUE ); if (Status != NERR_Success) { BrLogEvent(EVENT_BROWSER_STATUS_BITS_UPDATE_FAILED, Status, 0, NULL); BrPrint(( BR_MASTER, "%ws: %ws: Unable to clear master announcement bits to server: %ld\n", Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer, Status)); try_return(Status); } // // Stop our master related timers. // Status = BrCancelTimer(&Network->MasterBrowserAnnouncementTimer); ASSERT (Status == NERR_Success); Status = BrCancelTimer(&Network->MasterBrowserTimer); ASSERT (Status == NERR_Success); try_exit:NOTHING; } finally { UNLOCK_NETWORK(Network); } return Status; } NET_API_STATUS AnnounceMasterToDomainMaster( IN PNETWORK Network, IN LPWSTR ServerName ) { NET_API_STATUS Status; UNICODE_STRING EmulatedDomainName; CHAR Buffer[sizeof(MASTER_ANNOUNCEMENT)+CNLEN+1]; PMASTER_ANNOUNCEMENT MasterAnnouncementp = (PMASTER_ANNOUNCEMENT)Buffer; lstrcpyA( MasterAnnouncementp->MasterAnnouncement.MasterName, Network->DomainInfo->DomOemComputerName ); MasterAnnouncementp->Type = MasterAnnouncement; Status = SendDatagram( BrDgReceiverDeviceHandle, &Network->NetworkName, &Network->DomainInfo->DomUnicodeDomainNameString, ServerName, ComputerName, MasterAnnouncementp, FIELD_OFFSET(MASTER_ANNOUNCEMENT, MasterAnnouncement.MasterName) + Network->DomainInfo->DomOemComputerNameLength+sizeof(CHAR) ); return Status; } NET_API_STATUS NET_API_FUNCTION I_BrowserrResetNetlogonState( IN BROWSER_IDENTIFY_HANDLE ServerName ) /*++ Routine Description: This routine will reset the bowser's concept of the state of the netlogon service. It is called by the UI when it promotes or demotes a DC. Arguments: IN BROWSER_IDENTIFY_HANDLE ServerName - Ignored. Return Value: NET_API_STATUS - The status of this request. --*/ { // // This routine has been superceeded by I_BrowserrSetNetlogonState // return ERROR_NOT_SUPPORTED; UNREFERENCED_PARAMETER( ServerName ); } NET_API_STATUS NET_API_FUNCTION I_BrowserrSetNetlogonState( IN BROWSER_IDENTIFY_HANDLE ServerName, IN LPWSTR DomainName, IN LPWSTR EmulatedComputerName, IN DWORD Role ) /*++ Routine Description: This routine will reset the bowser's concept of the state of the netlogon service. It is called by the Netlogon service when it promotes or demotes a DC. Arguments: ServerName - Ignored. DomainName - Name of the domain whose role has changed. If the domain name specified isn't the primary domain or an emulated domain, an emulated domain is added. EmulatedComputerName - Name of the server within DomainName that's being emulated. Role - New role of the machine. Zero implies emulated domain is to be deleted. Return Value: NET_API_STATUS - The status of this request. --*/ { NET_API_STATUS NetStatus = NERR_Success; PDOMAIN_INFO DomainInfo = NULL; BOOLEAN ConfigCritSectLocked = FALSE; BOOLEAN RoleChanged = FALSE; if (!BrInfo.IsLanmanNt) { NetStatus = NERR_NotPrimary; goto Cleanup; } // // See if we're handling the specified domain. // DomainInfo = BrFindDomain( DomainName, FALSE ); if ( DomainInfo == NULL ) { // // Try to create the domain. // if ( EmulatedComputerName == NULL || Role == 0 ) { NetStatus = ERROR_NO_SUCH_DOMAIN; goto Cleanup; } NetStatus = BrCreateDomainInWorker( DomainName, EmulatedComputerName, TRUE, (BOOLEAN)((Role & BROWSER_ROLE_PDC) != 0)); if ( NetStatus != NERR_Success ) { goto Cleanup; } // // Find the newly created domain // DomainInfo = BrFindDomain( DomainName, FALSE ); if ( DomainInfo == NULL ) { NetStatus = ERROR_NO_SUCH_DOMAIN; goto Cleanup; } } // // Set the role to PDC. // EnterCriticalSection(&BrInfo.ConfigCritSect); ConfigCritSectLocked = TRUE; if ( Role & BROWSER_ROLE_PDC ) { // // We don't think we're the PDC. Update our information. // if ( !DomainInfo->IsPrimaryDomainController) { DomainInfo->IsPrimaryDomainController = TRUE; RoleChanged = TRUE; DomainInfo->IsDomainMasterBrowser = TRUE; // // Make sure a GetMasterAnnouncement request is pending. // (VOID) BrPostGetMasterAnnouncementInWorker( DomainInfo ); } // // Set the role to BDC. // } else if ( Role & BROWSER_ROLE_BDC ) { // // We think we're the PDC. Update our information. // if ( DomainInfo->IsPrimaryDomainController) { DomainInfo->IsPrimaryDomainController = FALSE; RoleChanged = TRUE; // // We're not a domain master any more, since we're not the PDC. // DomainInfo->IsDomainMasterBrowser = FALSE; } // // Delete the Emulated domain. // } else if ( Role == 0 ) { // // Don't allow the primary domain to be deleted. // if ( !DomainInfo->IsEmulatedDomain ) { NetStatus = ERROR_NO_SUCH_DOMAIN; goto Cleanup; } BrDeleteDomain( DomainInfo ); // // All other combinations are invalid // } else { NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } LeaveCriticalSection(&BrInfo.ConfigCritSect); ConfigCritSectLocked = FALSE; // // Handle changing the role. // if ( RoleChanged ) { // // Update this information for all transports now. This will also update // the status for the driver. // NetStatus = BrUpdateAnnouncementBits( DomainInfo, BrGlobalData.StatusHandle); if (NetStatus == NERR_Success) { // // The update worked. Now force an election and let the best server // win. // BrForceElectionOnAllNetworks(DomainInfo, EVENT_BROWSER_ELECTION_SENT_ROLE_CHANGED); } } // // Free locally used resources // Cleanup: if ( ConfigCritSectLocked ) { LeaveCriticalSection(&BrInfo.ConfigCritSect); } if ( DomainInfo != NULL ) { BrDereferenceDomain( DomainInfo ); } return NetStatus; } NET_API_STATUS NET_API_FUNCTION I_BrowserrQueryEmulatedDomains ( IN LPTSTR ServerName OPTIONAL, IN OUT PBROWSER_EMULATED_DOMAIN_CONTAINER EmulatedDomains ) /*++ Routine Description: Enumerate the emulated domain list. Arguments: ServerName - Supplies the name of server to execute this function EmulatedDomains - Returns a pointer to a an allocated array of emulated domain information. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS NetStatus; PBROWSER_EMULATED_DOMAIN Domains = NULL; PLIST_ENTRY DomainEntry; PDOMAIN_INFO DomainInfo; DWORD BufferSize; DWORD Index; LPBYTE Where; DWORD EntryCount; // // Initialization // EnterCriticalSection(&NetworkCritSect); EmulatedDomains->EntriesRead = 0; EmulatedDomains->Buffer = NULL; // // Loop through the list of emulated domains determining the size of the // return buffer. // BufferSize = 0; EntryCount = 0; for (DomainEntry = ServicedDomains.Flink ; DomainEntry != &ServicedDomains; DomainEntry = DomainEntry->Flink ) { DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next); if ( DomainInfo->IsEmulatedDomain ) { BufferSize += sizeof(BROWSER_EMULATED_DOMAIN) + DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR) + DomainInfo->DomUnicodeComputerNameLength * sizeof(WCHAR) + sizeof(WCHAR); EntryCount ++; } } // // Allocate the return buffer. // Domains = MIDL_user_allocate( BufferSize ); if ( Domains == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // Copy the information into the buffer // Index = 0; Where = (LPBYTE)(Domains+EntryCount); for (DomainEntry = ServicedDomains.Flink ; DomainEntry != &ServicedDomains; DomainEntry = DomainEntry->Flink ) { DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next); if ( DomainInfo->IsEmulatedDomain ) { Domains[Index].DomainName = (LPWSTR)Where; wcscpy( (LPWSTR) Where, DomainInfo->DomUnicodeDomainNameString.Buffer ); Where += DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR); Domains[Index].EmulatedServerName = (LPWSTR)Where; wcscpy( (LPWSTR) Where, DomainInfo->DomUnicodeComputerName ); Where += DomainInfo->DomUnicodeComputerNameLength * sizeof(WCHAR) + sizeof(WCHAR); Domains[Index].Role = DomainInfo->IsPrimaryDomainController ? BROWSER_ROLE_PDC : BROWSER_ROLE_BDC; Index ++; } } // // Success // EmulatedDomains->Buffer = (PVOID) Domains; EmulatedDomains->EntriesRead = EntryCount; NetStatus = NERR_Success; Cleanup: LeaveCriticalSection(&NetworkCritSect); return NetStatus; }