/*++

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
BrGetMasterServerNameForNet(
    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)) {

        //
        //  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.
        //

        (VOID) BrUpdateNetworkAnnouncementBits(Network, (PVOID)BR_PARANOID );

        Status = BrIssueAsyncBrowserIoControl(Network,
                            IOCTL_LMDR_BECOME_MASTER,
                            BecomeMasterCompletion,
                            NULL );

        if ( Status == NERR_Success ) {
            Network->Flags |= NETWORK_BECOME_MASTER_POSTED;
        }
    }

    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;

        }

        Status = NO_ERROR;
        //
        //  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;


    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: 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;


        Status = BrUpdateNetworkAnnouncementBits(Network, 0 );

        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;


            //
            //  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);
            }

            try_return(NOTHING);

        }
try_exit:NOTHING;
    } finally {

        //
        //  Make sure there's a become master oustanding.
        //

        if ( NetReferenced ) {
            PostBecomeMaster(Network);
        }

        if (NetworkLocked) {
            UNLOCK_NETWORK(Network);
        }

        if ( NetReferenced ) {
            BrDereferenceNetwork( Network );
        }

        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;

    PNETWORK Network;

} BROWSER_GETNAMES_CONTEXT, *PBROWSER_GETNAMES_CONTEXT;

VOID
BrGetMasterServerNameAysnc(
    PNETWORK Network
    )
/*++

Routine Description:

    Queue a workitem to asynchronously get the master browser names for a
    transport.

Arguments:

    Network - Identifies the network 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)
    //

    BrReferenceNetwork( Network );
    Context->Network = Network;

    BrInitializeWorkItem( &Context->WorkItem,
                          BrGetMasterServerNameForNet,
                          Context );

    BrQueueWorkItem( &Context->WorkItem );

    return;

}

VOID
BrGetMasterServerNameForNet(
    IN PVOID Context
    )
/*++

Routine Description:

    Routine to get the master browser name for a particular network.

Arguments:

    Context - Context containing the workitem and the description of the
        network to query.

Return Value:

    None

--*/
{
    PNETWORK Network = ((PBROWSER_GETNAMES_CONTEXT)Context)->Network;

    BrPrint(( BR_NETWORK,
              "%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);
    }

    BrDereferenceNetwork( Network );
    (VOID) LocalFree( Context );

    return;

}

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_NETWORK,
              "%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_CRITICAL,
                  "%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_NETWORK, "%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;
    PDOMAIN_CONTROLLER_INFO  pDcInfo=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);
        }



#ifdef ENABLE_PSEUDO_BROWSER
        if (BrInfo.PseudoServerLevel == BROWSER_PSEUDO) {
            BrFreeNetworkTables(Network);
            try_return(NOTHING);
        }
#endif


        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....
            //
            // Skip processing if we're a semi-pseudo server (no dmb
            // communications
            //


#ifdef ENABLE_PSEUDO_BROWSER
            if ( (Network->Flags & NETWORK_PDC) == 0  &&
                 BrInfo.PseudoServerLevel != BROWSER_SEMI_PSEUDO_NO_DMB) {
#else
                if ( (Network->Flags & NETWORK_PDC) == 0 ) {
#endif

                ASSERT (NetLocked);

                UNLOCK_NETWORK(Network);

                NetLocked = FALSE;

                Status = DsGetDcName( NULL, NULL, NULL, NULL,
                                      DS_PDC_REQUIRED    |
                                      DS_BACKGROUND_ONLY |
                                      DS_RETURN_FLAT_NAME,
                                      &pDcInfo );

                //
                // If the PDC can be found,
                //  Exchange server lists with it.
                //

                if (Status == NERR_Success) {

                    PrimaryDomainController = pDcInfo->DomainControllerName;

                    //
                    //  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);

                }   // dsgetdc


            //
            //  If we're on the PDC, we need to get the list of servers from
            //  the WINS server.
            //

#ifdef ENABLE_PSEUDO_BROWSER
            } else if ((Network->Flags & NETWORK_PDC) != 0) {
#else
            } else {
#endif
                //
                //  Ensure a GetMasterAnnouncement request is posted to the bowser.
                //

                (VOID) PostGetMasterAnnouncement ( Network );

                //
                //  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 (pDcInfo != NULL) {
            NetApiBufferFree((LPVOID)pDcInfo);
        }

        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;
    NET_API_STATUS Status;

#ifdef ENABLE_PSEUDO_BROWSER
    if ( BrInfo.PseudoServerLevel == BROWSER_PSEUDO ) {
        // cancel announcements for phase out black hole server
        return;
    }
#endif

    //
    // 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.
    //

    (VOID) BrUpdateNetworkAnnouncementBits( Network, (PVOID) BR_PARANOID );


    //
    // Setup the timer for the next announcement.
    //

    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;

    //
    //  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);

        Status = BrUpdateNetworkAnnouncementBits(Network, 0);

        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);
        }


        //
        //  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;

#ifdef notdef

    // This routine no longer sets the role.

    //
    // This routine is currently disabled since it doesn't work well with
    //  the PNP logic.  Specifically,
    //
    //      When a hosted domain is created, this routine calls BrCreateNetwork.
    //      That creates a network in the bowser.  The bowser PNPs that up.
    //      HandlePnpMessage tries to create the transport on all hosted domains.
    //      Of course, that fails since all the transports exist.
    //      This is just wasted effort.
    //
    //  However,
    //      When a hosted domain is deleted, we delete the transport. The bowser
    //      PNPs that up to us.  HandlePnpMessage then deletes the transport for
    //      all hosted domains.
    //
    // I think the best solution to this would be for the browser to flag
    // the IOCTL_LMDR_BIND_TO_TRANSPORT_DOM calls it makes to the bowser.  The
    // bowser would NOT PNP such creations up to the browser or netlogon.
    // (Be careful.  Netlogon depends on getting the notification that NwLnkIpx
    // was created by the browser.  Perhaps we can let that one through.)
    //



    //
    // Perform access validation on the caller.
    //

    NetStatus = NetpAccessCheck(
            BrGlobalBrowserSecurityDescriptor,     // Security descriptor
            BROWSER_CONTROL_ACCESS,                // Desired access
            &BrGlobalBrowserInfoMapping );         // Generic mapping

    if ( NetStatus != NERR_Success) {

        BrPrint((BR_CRITICAL,
                "I_BrowserrSetNetlogonState failed NetpAccessCheck\n" ));
        return ERROR_ACCESS_DENIED;
    }


    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 ||
             (Role & BROWSER_ROLE_AVOID_CREATING_DOMAIN) != 0 ) {
            NetStatus = ERROR_NO_SUCH_DOMAIN;
            goto Cleanup;
        }

        NetStatus = BrCreateDomainInWorker(
                        DomainName,
                        EmulatedComputerName,
                        TRUE );

        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;
        }
    }

    //
    // Delete the Emulated domain.
    //

    EnterCriticalSection(&BrInfo.ConfigCritSect);
    ConfigCritSectLocked = TRUE;

    if ( Role == 0 ) {

        //
        // Don't allow the primary domain to be deleted.
        //

        if ( !DomainInfo->IsEmulatedDomain ) {
            NetStatus = ERROR_NO_SUCH_DOMAIN;
            goto Cleanup;
        }

        BrDeleteDomain( DomainInfo );

    }

    LeaveCriticalSection(&BrInfo.ConfigCritSect);
    ConfigCritSectLocked = FALSE;


    //
    // Free locally used resources
    //
Cleanup:

    if ( ConfigCritSectLocked ) {
        LeaveCriticalSection(&BrInfo.ConfigCritSect);
    }

    if ( DomainInfo != NULL ) {
        BrDereferenceDomain( DomainInfo );
    }
    return NetStatus;
#endif // notdef
    return ERROR_NOT_SUPPORTED;

}


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;

    //
    // Perform access validation on the caller.
    //

    NetStatus = NetpAccessCheck(
            BrGlobalBrowserSecurityDescriptor,     // Security descriptor
            BROWSER_QUERY_ACCESS,                  // Desired access
            &BrGlobalBrowserInfoMapping );         // Generic mapping

    if ( NetStatus != NERR_Success) {

        BrPrint((BR_CRITICAL,
                "I_BrowserrQueryEmulatedDomains failed NetpAccessCheck\n" ));
        return ERROR_ACCESS_DENIED;
    }

    // Do not accept pre-allocated IN param, since
    // we overwrite the pointer & this can lead to a mem leak.
    // (security attack defence).
    if ( EmulatedDomains->EntriesRead != 0 ||
         EmulatedDomains->Buffer ) {
        return ERROR_INVALID_PARAMETER;
    }
    ASSERT ( EmulatedDomains->EntriesRead == 0 );
    ASSERT ( EmulatedDomains->Buffer == NULL );

    //
    // Initialization
    //

    EnterCriticalSection(&NetworkCritSect);

    //
    // 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);

            Index ++;
        }

    }

    //
    // Success
    //

    EmulatedDomains->Buffer = (PVOID) Domains;
    EmulatedDomains->EntriesRead = EntryCount;
    NetStatus = NERR_Success;


Cleanup:
    LeaveCriticalSection(&NetworkCritSect);

    return NetStatus;
}