Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1785 lines
48 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. browser.c
  5. Abstract:
  6. This module contains the worker routines for the NetWksta APIs
  7. implemented in the Workstation service.
  8. Author:
  9. Rita Wong (ritaw) 20-Feb-1991
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. //-------------------------------------------------------------------//
  15. // //
  16. // Local structure definitions //
  17. // //
  18. //-------------------------------------------------------------------//
  19. ULONG
  20. DomainAnnouncementPeriodicity[] = {1*60*1000, 1*60*1000, 5*60*1000, 5*60*1000, 10*60*1000, 10*60*1000, 15*60*1000};
  21. ULONG
  22. DomainAnnouncementMax = (sizeof(DomainAnnouncementPeriodicity) / sizeof(ULONG)) - 1;
  23. //-------------------------------------------------------------------//
  24. // //
  25. // Local function prototypes //
  26. // //
  27. //-------------------------------------------------------------------//
  28. VOID
  29. BrGetMasterServerNameForNet(
  30. IN PVOID Context
  31. );
  32. VOID
  33. BecomeMasterCompletion (
  34. IN PVOID Ctx
  35. );
  36. NET_API_STATUS
  37. StartMasterBrowserTimer(
  38. IN PNETWORK Network
  39. );
  40. NET_API_STATUS
  41. AnnounceMasterToDomainMaster(
  42. IN PNETWORK Network,
  43. IN LPWSTR ServerName
  44. );
  45. //-------------------------------------------------------------------//
  46. // //
  47. // Global function prototypes //
  48. // //
  49. //-------------------------------------------------------------------//
  50. NET_API_STATUS
  51. PostBecomeMaster(
  52. PNETWORK Network
  53. )
  54. /*++
  55. Routine Description:
  56. This function is the worker routine called to actually issue a BecomeMaster
  57. FsControl to the bowser driver on all the bound transports. It will
  58. complete when the machine becomes a master browser server.
  59. Please note that this might never complete.
  60. Arguments:
  61. None.
  62. Return Value:
  63. Status - The status of the operation.
  64. --*/
  65. {
  66. NTSTATUS Status = NERR_Success;
  67. if (!LOCK_NETWORK(Network)) {
  68. return NERR_InternalError;
  69. }
  70. if (!(Network->Flags & NETWORK_BECOME_MASTER_POSTED)) {
  71. //
  72. // Make certain that we have the browser election name added
  73. // before we allow ourselves to become a master. This is a NOP
  74. // if we already have the election name added.
  75. //
  76. (VOID) BrUpdateNetworkAnnouncementBits(Network, (PVOID)BR_PARANOID );
  77. Status = BrIssueAsyncBrowserIoControl(Network,
  78. IOCTL_LMDR_BECOME_MASTER,
  79. BecomeMasterCompletion,
  80. NULL );
  81. if ( Status == NERR_Success ) {
  82. Network->Flags |= NETWORK_BECOME_MASTER_POSTED;
  83. }
  84. }
  85. UNLOCK_NETWORK(Network);
  86. return Status;
  87. }
  88. NET_API_STATUS
  89. BrRecoverFromFailedPromotion(
  90. IN PVOID Ctx
  91. )
  92. /*++
  93. Routine Description:
  94. When we attempt to promote a machine to master browser and fail, we will
  95. effectively shut down the browser for a period of time. When that period
  96. of time expires, we will call BrRecoverFromFailedPromotion to recover
  97. from the failure.
  98. This routine will do one of the following:
  99. 1) Force the machine to become a backup browser,
  100. or 2) Attempt to discover the name of the master.
  101. Arguments:
  102. IN PVOID Ctx - The network structure we failed on.
  103. Return Value:
  104. Status - The status of the operation (usually ignored).
  105. --*/
  106. {
  107. PNETWORK Network = Ctx;
  108. NET_API_STATUS Status;
  109. BOOL NetworkLocked = FALSE;
  110. //
  111. // Prevent the network from being deleted while we're in this timer routine.
  112. //
  113. if ( BrReferenceNetwork( Network ) == NULL ) {
  114. return NERR_InternalError;
  115. }
  116. try {
  117. if (!LOCK_NETWORK(Network)) {
  118. try_return( Status = NERR_InternalError);
  119. }
  120. BrPrint(( BR_MASTER,
  121. "%ws: %ws: BrRecoverFromFailedPromotion.\n",
  122. Network->DomainInfo->DomUnicodeDomainName,
  123. Network->NetworkName.Buffer ));
  124. NetworkLocked = TRUE;
  125. //
  126. // We had better not be the master now.
  127. //
  128. // ASSERT (!(Network->Role & ROLE_MASTER));
  129. //
  130. // If we're configured to become a backup by default, then become
  131. // a backup now.
  132. //
  133. if (BrInfo.MaintainServerList == 1) {
  134. BrPrint(( BR_MASTER,
  135. "%ws: %ws: BrRecoverFromFailedPromotion. Become backup.\n",
  136. Network->DomainInfo->DomUnicodeDomainName,
  137. Network->NetworkName.Buffer ));
  138. Status = BecomeBackup(Network, NULL);
  139. if (Status != NERR_Success) {
  140. BrPrint(( BR_CRITICAL,
  141. "%ws: %ws: Could not become backup: %lx\n",
  142. Network->DomainInfo->DomUnicodeDomainName,
  143. Network->NetworkName.Buffer,
  144. Status));
  145. }
  146. } else {
  147. BrPrint(( BR_MASTER,
  148. "%ws: %ws: BrRecoverFromFailedPromotion. FindMaster.\n",
  149. Network->DomainInfo->DomUnicodeDomainName,
  150. Network->NetworkName.Buffer));
  151. UNLOCK_NETWORK(Network);
  152. NetworkLocked = FALSE;
  153. //
  154. // Now try to figure out who is the master.
  155. //
  156. Status = GetMasterServerNames(Network);
  157. //
  158. // Ignore the status from this and re-lock the network to
  159. // recover cleanly.
  160. //
  161. if (!LOCK_NETWORK(Network)) {
  162. try_return( Status = NERR_InternalError);
  163. }
  164. NetworkLocked = TRUE;
  165. }
  166. Status = NO_ERROR;
  167. //
  168. // Otherwise, just let sleeping dogs lie.
  169. //
  170. try_exit:NOTHING;
  171. } finally {
  172. if (NetworkLocked) {
  173. UNLOCK_NETWORK(Network);
  174. }
  175. BrDereferenceNetwork( Network );
  176. }
  177. return Status;
  178. }
  179. VOID
  180. BecomeMasterCompletion (
  181. IN PVOID Ctx
  182. )
  183. /*++
  184. Routine Description:
  185. This function is called by the I/O system when the request to become a
  186. master completes.
  187. Please note that it is possible that the request may complete with an
  188. error.
  189. Arguments:
  190. None.
  191. Return Value:
  192. Status - The status of the operation.
  193. --*/
  194. {
  195. NET_API_STATUS Status;
  196. PBROWSERASYNCCONTEXT Context = Ctx;
  197. PNETWORK Network = Context->Network;
  198. BOOLEAN NetworkLocked = FALSE;
  199. BOOLEAN NetReferenced = FALSE;
  200. try {
  201. //
  202. // Ensure the network wasn't deleted from under us.
  203. //
  204. if ( BrReferenceNetwork( Network ) == NULL ) {
  205. try_return(NOTHING);
  206. }
  207. NetReferenced = TRUE;
  208. //
  209. // Lock the network structure.
  210. //
  211. if (!LOCK_NETWORK(Network)) {
  212. try_return(NOTHING);
  213. }
  214. NetworkLocked = TRUE;
  215. Network->Flags &= ~NETWORK_BECOME_MASTER_POSTED;
  216. if (!NT_SUCCESS(Context->IoStatusBlock.Status)) {
  217. BrPrint(( BR_CRITICAL,
  218. "%ws: %ws: Failure in BecomeMaster: %X\n",
  219. Network->DomainInfo->DomUnicodeDomainName,
  220. Network->NetworkName.Buffer,
  221. Context->IoStatusBlock.Status));
  222. try_return(NOTHING);
  223. }
  224. BrPrint(( BR_MASTER,
  225. "%ws: BecomeMasterCompletion. Now master on network %ws\n",
  226. Network->DomainInfo->DomUnicodeDomainName,
  227. Network->NetworkName.Buffer));
  228. //
  229. // If we're already a master, ignore this request.
  230. //
  231. if (Network->Role & ROLE_MASTER) {
  232. try_return(NOTHING);
  233. }
  234. //
  235. // Cancel any outstanding backup timers - we don't download the list
  236. // anymore.
  237. //
  238. Status = BrCancelTimer(&Network->BackupBrowserTimer);
  239. if (!NT_SUCCESS(Status)) {
  240. BrPrint(( BR_CRITICAL,
  241. "%ws: %ws: Could not stop backup timer: %lx\n",
  242. Network->DomainInfo->DomUnicodeDomainName,
  243. Network->NetworkName.Buffer,
  244. Status));
  245. }
  246. //
  247. // Figure out what service bits we should be using when announcing ourselves
  248. //
  249. Network->Role |= ROLE_MASTER;
  250. Status = BrUpdateNetworkAnnouncementBits(Network, 0 );
  251. if (Status != NERR_Success) {
  252. BrPrint(( BR_MASTER,
  253. "%ws: %ws: Unable to set master announcement bits in browser: %ld\n",
  254. Network->DomainInfo->DomUnicodeDomainName,
  255. Network->NetworkName.Buffer,
  256. Status));
  257. //
  258. // When we're in this state, we can't rely on our being a backup
  259. // browser - we may not be able to retrieve a valid list of
  260. // browsers from the master.
  261. //
  262. Network->Role &= ~ROLE_BACKUP;
  263. Network->NumberOfFailedPromotions += 1;
  264. //
  265. // Log every 5 failed promotion attempts, and after having logged 5
  266. // promotion events, stop logging them, this means that it's been
  267. // 25 times that we've tried to promote, and it's not likely to get
  268. // any better. We'll keep on trying, but we won't complain any more.
  269. //
  270. if ((Network->NumberOfFailedPromotions % 5) == 0) {
  271. ULONG AStatStatus;
  272. LPWSTR SubString[1];
  273. WCHAR CurrentMasterName[CNLEN+1];
  274. if (Network->NumberOfPromotionEventsLogged < 5) {
  275. AStatStatus = GetNetBiosMasterName(
  276. Network->NetworkName.Buffer,
  277. Network->DomainInfo->DomUnicodeDomainName,
  278. CurrentMasterName,
  279. BrLmsvcsGlobalData->NetBiosReset
  280. );
  281. if (AStatStatus == NERR_Success) {
  282. SubString[0] = CurrentMasterName;
  283. BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED, Status, 1, SubString);
  284. } else {
  285. BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_NO_MASTER, Status, 0, NULL);
  286. }
  287. Network->NumberOfPromotionEventsLogged += 1;
  288. if (Network->NumberOfPromotionEventsLogged == 5) {
  289. BrLogEvent(EVENT_BROWSER_MASTER_PROMOTION_FAILED_STOPPING, Status, 0, NULL);
  290. }
  291. }
  292. }
  293. //
  294. // We were unable to promote ourselves to master.
  295. //
  296. // We want to set our role back to browser, and re-issue the become
  297. // master request.
  298. //
  299. BrStopMaster(Network);
  300. BrSetTimer(&Network->MasterBrowserTimer, FAILED_PROMOTION_PERIODICITY*1000, BrRecoverFromFailedPromotion, Network);
  301. } else {
  302. //
  303. // Initialize the number of times the master timer has run.
  304. //
  305. Network->MasterBrowserTimerCount = 0;
  306. Status = StartMasterBrowserTimer(Network);
  307. if (Status != NERR_Success) {
  308. BrPrint(( BR_CRITICAL,
  309. "%ws: %ws: Could not start browser master timer: %ld\n",
  310. Network->DomainInfo->DomUnicodeDomainName,
  311. Network->NetworkName.Buffer,
  312. Status ));
  313. }
  314. Network->NumberOfFailedPromotions = 0;
  315. Network->NumberOfPromotionEventsLogged = 0;
  316. Network->MasterAnnouncementIndex = 0;
  317. //
  318. // We successfully became the master.
  319. //
  320. // Now announce ourselves as the new master for this domain.
  321. //
  322. BrMasterAnnouncement(Network);
  323. //
  324. // Populate the browse list with the information retrieved
  325. // while we were a backup browser.
  326. //
  327. if (Network->TotalBackupServerListEntries != 0) {
  328. MergeServerList(&Network->BrowseTable,
  329. 101,
  330. Network->BackupServerList,
  331. Network->TotalBackupServerListEntries,
  332. Network->TotalBackupServerListEntries
  333. );
  334. MIDL_user_free(Network->BackupServerList);
  335. Network->BackupServerList = NULL;
  336. Network->TotalBackupServerListEntries = 0;
  337. }
  338. if (Network->TotalBackupDomainListEntries != 0) {
  339. MergeServerList(&Network->DomainList,
  340. 101,
  341. Network->BackupDomainList,
  342. Network->TotalBackupDomainListEntries,
  343. Network->TotalBackupDomainListEntries
  344. );
  345. MIDL_user_free(Network->BackupDomainList);
  346. Network->BackupDomainList = NULL;
  347. Network->TotalBackupDomainListEntries = 0;
  348. }
  349. //
  350. // Unlock the network before calling BrWanMasterInitialize.
  351. //
  352. UNLOCK_NETWORK(Network);
  353. NetworkLocked = FALSE;
  354. //
  355. // Run the master browser timer routine to get the entire domains
  356. // list of servers.
  357. //
  358. if (Network->Flags & NETWORK_WANNISH) {
  359. BrWanMasterInitialize(Network);
  360. MasterBrowserTimerRoutine(Network);
  361. }
  362. try_return(NOTHING);
  363. }
  364. try_exit:NOTHING;
  365. } finally {
  366. //
  367. // Make sure there's a become master oustanding.
  368. //
  369. if ( NetReferenced ) {
  370. PostBecomeMaster(Network);
  371. }
  372. if (NetworkLocked) {
  373. UNLOCK_NETWORK(Network);
  374. }
  375. if ( NetReferenced ) {
  376. BrDereferenceNetwork( Network );
  377. }
  378. MIDL_user_free(Context);
  379. }
  380. }
  381. NET_API_STATUS
  382. ChangeMasterPeriodicityWorker(
  383. PNETWORK Network,
  384. PVOID Ctx
  385. )
  386. /*++
  387. Routine Description:
  388. This function changes the master periodicity for a single network.
  389. Arguments:
  390. None.
  391. Return Value:
  392. Status - The status of the operation.
  393. --*/
  394. {
  395. //
  396. // Lock the network
  397. //
  398. if (LOCK_NETWORK(Network)) {
  399. //
  400. // Ensure we're the master.
  401. //
  402. if ( Network->Role & ROLE_MASTER ) {
  403. NET_API_STATUS NetStatus;
  404. //
  405. // Cancel the timer to ensure it doesn't go off while we're
  406. // processing this change.
  407. //
  408. NetStatus = BrCancelTimer(&Network->MasterBrowserTimer);
  409. ASSERT (NetStatus == NERR_Success);
  410. //
  411. // Unlock the network while we execute the timer routine.
  412. //
  413. UNLOCK_NETWORK( Network );
  414. //
  415. // Call the timer routine immediately.
  416. //
  417. MasterBrowserTimerRoutine(Network);
  418. } else {
  419. UNLOCK_NETWORK( Network );
  420. }
  421. }
  422. UNREFERENCED_PARAMETER(Ctx);
  423. return NERR_Success;
  424. }
  425. VOID
  426. BrChangeMasterPeriodicity (
  427. VOID
  428. )
  429. /*++
  430. Routine Description:
  431. This function is called when the master periodicity is changed in the
  432. registry.
  433. Arguments:
  434. None.
  435. Return Value:
  436. None.
  437. --*/
  438. {
  439. (VOID)BrEnumerateNetworks(ChangeMasterPeriodicityWorker, NULL);
  440. }
  441. NET_API_STATUS
  442. StartMasterBrowserTimer(
  443. IN PNETWORK Network
  444. )
  445. {
  446. NET_API_STATUS Status;
  447. Status = BrSetTimer( &Network->MasterBrowserTimer,
  448. BrInfo.MasterPeriodicity*1000,
  449. MasterBrowserTimerRoutine,
  450. Network);
  451. return Status;
  452. }
  453. typedef struct _BROWSER_GETNAMES_CONTEXT {
  454. WORKER_ITEM WorkItem;
  455. PNETWORK Network;
  456. } BROWSER_GETNAMES_CONTEXT, *PBROWSER_GETNAMES_CONTEXT;
  457. VOID
  458. BrGetMasterServerNameAysnc(
  459. PNETWORK Network
  460. )
  461. /*++
  462. Routine Description:
  463. Queue a workitem to asynchronously get the master browser names for a
  464. transport.
  465. Arguments:
  466. Network - Identifies the network to query.
  467. Return Value:
  468. None
  469. --*/
  470. {
  471. PBROWSER_GETNAMES_CONTEXT Context;
  472. //
  473. // Allocate context for this async call.
  474. //
  475. Context = LocalAlloc( 0, sizeof(BROWSER_GETNAMES_CONTEXT) );
  476. if ( Context == NULL ) {
  477. return;
  478. }
  479. //
  480. // Just queue this for later execution.
  481. // We're doing this for information purposes only. In the case that
  482. // the master can't be found, we don't want to wait for completion.
  483. // (e.g., on a machine with multiple transports and the net cable is
  484. // pulled)
  485. //
  486. BrReferenceNetwork( Network );
  487. Context->Network = Network;
  488. BrInitializeWorkItem( &Context->WorkItem,
  489. BrGetMasterServerNameForNet,
  490. Context );
  491. BrQueueWorkItem( &Context->WorkItem );
  492. return;
  493. }
  494. VOID
  495. BrGetMasterServerNameForNet(
  496. IN PVOID Context
  497. )
  498. /*++
  499. Routine Description:
  500. Routine to get the master browser name for a particular network.
  501. Arguments:
  502. Context - Context containing the workitem and the description of the
  503. network to query.
  504. Return Value:
  505. None
  506. --*/
  507. {
  508. PNETWORK Network = ((PBROWSER_GETNAMES_CONTEXT)Context)->Network;
  509. BrPrint(( BR_NETWORK,
  510. "%ws: %ws: FindMaster during startup\n",
  511. Network->DomainInfo->DomUnicodeDomainName,
  512. Network->NetworkName.Buffer));
  513. //
  514. // We only call this on startup, so on IPX networks, don't bother to
  515. // find out the master.
  516. //
  517. if (!(Network->Flags & NETWORK_IPX)) {
  518. GetMasterServerNames(Network);
  519. }
  520. BrDereferenceNetwork( Network );
  521. (VOID) LocalFree( Context );
  522. return;
  523. }
  524. NET_API_STATUS
  525. GetMasterServerNames(
  526. IN PNETWORK Network
  527. )
  528. /*++
  529. Routine Description:
  530. This function is the worker routine called to determine the name of the
  531. master browser server for a particular network.
  532. Arguments:
  533. None.
  534. Return Value:
  535. Status - The status of the operation.
  536. --*/
  537. {
  538. NET_API_STATUS Status;
  539. PLMDR_REQUEST_PACKET RequestPacket = NULL;
  540. BrPrint(( BR_NETWORK,
  541. "%ws: %ws: FindMaster started\n",
  542. Network->DomainInfo->DomUnicodeDomainName,
  543. Network->NetworkName.Buffer));
  544. //
  545. // This request could cause an election. Make sure that if we win
  546. // the election that we can handle it.
  547. //
  548. PostBecomeMaster( Network);
  549. RequestPacket = MIDL_user_allocate(
  550. (UINT) sizeof(LMDR_REQUEST_PACKET)+
  551. MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)
  552. );
  553. if (RequestPacket == NULL) {
  554. return(ERROR_NOT_ENOUGH_MEMORY);
  555. }
  556. RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
  557. //
  558. // Set level to TRUE to indicate that find master should initiate
  559. // a findmaster request.
  560. //
  561. RequestPacket->Level = 1;
  562. RequestPacket->TransportName = Network->NetworkName;
  563. RequestPacket->EmulatedDomainName = Network->DomainInfo->DomUnicodeDomainNameString;
  564. //
  565. // Reference the network while the I/O is pending.
  566. //
  567. Status = BrDgReceiverIoControl(BrDgReceiverDeviceHandle,
  568. IOCTL_LMDR_GET_MASTER_NAME,
  569. RequestPacket,
  570. sizeof(LMDR_REQUEST_PACKET)+Network->NetworkName.Length,
  571. RequestPacket,
  572. sizeof(LMDR_REQUEST_PACKET)+MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR),
  573. NULL);
  574. if (Status != NERR_Success) {
  575. BrPrint(( BR_CRITICAL,
  576. "%ws: %ws: FindMaster failed: %ld\n",
  577. Network->DomainInfo->DomUnicodeDomainName,
  578. Network->NetworkName.Buffer,
  579. Status));
  580. MIDL_user_free(RequestPacket);
  581. return(Status);
  582. }
  583. if (!LOCK_NETWORK(Network)) {
  584. MIDL_user_free(RequestPacket);
  585. return NERR_InternalError;
  586. }
  587. //
  588. // Copy the master browser name into the network structure
  589. //
  590. wcsncpy( Network->UncMasterBrowserName,
  591. RequestPacket->Parameters.GetMasterName.Name,
  592. UNCLEN+1 );
  593. Network->UncMasterBrowserName[UNCLEN] = L'\0';
  594. ASSERT ( NetpIsUncComputerNameValid( Network->UncMasterBrowserName ) );
  595. BrPrint(( BR_NETWORK, "%ws: %ws: FindMaster succeeded. Master: %ws\n",
  596. Network->DomainInfo->DomUnicodeDomainName,
  597. Network->NetworkName.Buffer,
  598. Network->UncMasterBrowserName));
  599. UNLOCK_NETWORK(Network);
  600. MIDL_user_free(RequestPacket);
  601. return Status;
  602. }
  603. VOID
  604. MasterBrowserTimerRoutine (
  605. IN PVOID TimerContext
  606. )
  607. {
  608. IN PNETWORK Network = TimerContext;
  609. NET_API_STATUS Status;
  610. PVOID ServerList = NULL;
  611. PVOID WinsServerList = NULL;
  612. ULONG EntriesInList;
  613. ULONG TotalEntriesInList;
  614. LPWSTR TransportName;
  615. BOOLEAN NetLocked = FALSE;
  616. LPWSTR PrimaryDomainController = NULL;
  617. LPWSTR PrimaryWinsServerAddress = NULL;
  618. LPWSTR SecondaryWinsServerAddress = NULL;
  619. PDOMAIN_CONTROLLER_INFO pDcInfo=NULL;
  620. //
  621. // Prevent the network from being deleted while we're in this timer routine.
  622. //
  623. if ( BrReferenceNetwork( Network ) == NULL ) {
  624. return;
  625. }
  626. try {
  627. //
  628. // If we're not a master any more, blow away this request.
  629. //
  630. if (!(Network->Role & ROLE_MASTER)) {
  631. try_return(NOTHING);
  632. }
  633. #ifdef ENABLE_PSEUDO_BROWSER
  634. if (BrInfo.PseudoServerLevel == BROWSER_PSEUDO) {
  635. BrFreeNetworkTables(Network);
  636. try_return(NOTHING);
  637. }
  638. #endif
  639. if (!LOCK_NETWORK(Network)) {
  640. try_return(NOTHING);
  641. }
  642. NetLocked = TRUE;
  643. TransportName = Network->NetworkName.Buffer;
  644. //
  645. // Now that we have the network locked, re-test to see if we are
  646. // still the master.
  647. //
  648. if (!(Network->Role & ROLE_MASTER)) {
  649. try_return(NOTHING);
  650. }
  651. Network->MasterBrowserTimerCount += 1;
  652. //
  653. // If this is a wannish network, we always want to run the master
  654. // timer because we might have information about other subnets
  655. // in our list.
  656. //
  657. if (Network->Flags & NETWORK_WANNISH) {
  658. //
  659. // Age out servers and domains from the server list.
  660. //
  661. AgeInterimServerList(&Network->BrowseTable);
  662. AgeInterimServerList(&Network->DomainList);
  663. //
  664. // If we're not the PDC, then we need to retrieve the list
  665. // from the PDC....
  666. //
  667. // Skip processing if we're a semi-pseudo server (no dmb
  668. // communications
  669. //
  670. #ifdef ENABLE_PSEUDO_BROWSER
  671. if ( (Network->Flags & NETWORK_PDC) == 0 &&
  672. BrInfo.PseudoServerLevel != BROWSER_SEMI_PSEUDO_NO_DMB) {
  673. #else
  674. if ( (Network->Flags & NETWORK_PDC) == 0 ) {
  675. #endif
  676. ASSERT (NetLocked);
  677. UNLOCK_NETWORK(Network);
  678. NetLocked = FALSE;
  679. Status = DsGetDcName( NULL, NULL, NULL, NULL,
  680. DS_PDC_REQUIRED |
  681. DS_BACKGROUND_ONLY |
  682. DS_RETURN_FLAT_NAME,
  683. &pDcInfo );
  684. //
  685. // If the PDC can be found,
  686. // Exchange server lists with it.
  687. //
  688. if (Status == NERR_Success) {
  689. PrimaryDomainController = pDcInfo->DomainControllerName;
  690. //
  691. // Tell the Domain Master (PDC) that we're a master browser.
  692. //
  693. (VOID) AnnounceMasterToDomainMaster (Network, &PrimaryDomainController[2]);
  694. //
  695. // Retrieve the list of all the servers from the PDC.
  696. //
  697. Status = RxNetServerEnum(PrimaryDomainController,
  698. TransportName,
  699. 101,
  700. (LPBYTE *)&ServerList,
  701. 0xffffffff,
  702. &EntriesInList,
  703. &TotalEntriesInList,
  704. SV_TYPE_ALL,
  705. NULL,
  706. NULL
  707. );
  708. if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
  709. ASSERT (!NetLocked);
  710. if (LOCK_NETWORK(Network)) {
  711. NetLocked = TRUE;
  712. if (Network->Role & ROLE_MASTER) {
  713. (VOID) MergeServerList(&Network->BrowseTable,
  714. 101,
  715. ServerList,
  716. EntriesInList,
  717. TotalEntriesInList );
  718. }
  719. }
  720. }
  721. if (ServerList != NULL) {
  722. MIDL_user_free(ServerList);
  723. ServerList = NULL;
  724. }
  725. if (NetLocked) {
  726. UNLOCK_NETWORK(Network);
  727. NetLocked = FALSE;
  728. }
  729. //
  730. // Retrieve the list of all the domains from the PDC.
  731. //
  732. Status = RxNetServerEnum(PrimaryDomainController,
  733. TransportName,
  734. 101,
  735. (LPBYTE *)&ServerList,
  736. 0xffffffff,
  737. &EntriesInList,
  738. &TotalEntriesInList,
  739. SV_TYPE_DOMAIN_ENUM,
  740. NULL,
  741. NULL
  742. );
  743. if ((Status == NERR_Success) || (Status == ERROR_MORE_DATA)) {
  744. ASSERT (!NetLocked);
  745. if (LOCK_NETWORK(Network)) {
  746. NetLocked = TRUE;
  747. if (Network->Role & ROLE_MASTER) {
  748. (VOID) MergeServerList(&Network->DomainList,
  749. 101,
  750. ServerList,
  751. EntriesInList,
  752. TotalEntriesInList );
  753. }
  754. }
  755. }
  756. if (ServerList != NULL) {
  757. MIDL_user_free(ServerList);
  758. ServerList = NULL;
  759. }
  760. //
  761. // Unlock the network before calling BrWanMasterInitialize.
  762. //
  763. if (NetLocked) {
  764. UNLOCK_NETWORK(Network);
  765. NetLocked = FALSE;
  766. }
  767. BrWanMasterInitialize(Network);
  768. } // dsgetdc
  769. //
  770. // If we're on the PDC, we need to get the list of servers from
  771. // the WINS server.
  772. //
  773. #ifdef ENABLE_PSEUDO_BROWSER
  774. } else if ((Network->Flags & NETWORK_PDC) != 0) {
  775. #else
  776. } else {
  777. #endif
  778. //
  779. // Ensure a GetMasterAnnouncement request is posted to the bowser.
  780. //
  781. (VOID) PostGetMasterAnnouncement ( Network );
  782. //
  783. // We want to contact the WINS server now, so we figure out the
  784. // IP address of our primary WINS server
  785. //
  786. Status = BrGetWinsServerName(&Network->NetworkName,
  787. &PrimaryWinsServerAddress,
  788. &SecondaryWinsServerAddress);
  789. if (Status == NERR_Success) {
  790. //
  791. // Don't keep the network locked during the WINS query
  792. //
  793. if (NetLocked) {
  794. UNLOCK_NETWORK(Network);
  795. NetLocked = FALSE;
  796. }
  797. //
  798. // This transport supports WINS queries, so query the WINS
  799. // server to retrieve the list of domains on this adapter.
  800. //
  801. Status = BrQueryWinsServer(PrimaryWinsServerAddress,
  802. SecondaryWinsServerAddress,
  803. &WinsServerList,
  804. &EntriesInList,
  805. &TotalEntriesInList
  806. );
  807. if (Status == NERR_Success) {
  808. //
  809. // Lock the network to merge the server list
  810. //
  811. ASSERT (!NetLocked);
  812. if (LOCK_NETWORK(Network)) {
  813. NetLocked = TRUE;
  814. if (Network->Role & ROLE_MASTER) {
  815. //
  816. // Merge the list of domains from WINS into the one collected elsewhere
  817. //
  818. (VOID) MergeServerList(
  819. &Network->DomainList,
  820. 1010, // Special level to not overide current values
  821. WinsServerList,
  822. EntriesInList,
  823. TotalEntriesInList );
  824. }
  825. }
  826. }
  827. }
  828. }
  829. //
  830. // Restart the timer for this domain.
  831. //
  832. // Wait to restart it until we're almost done with this iteration.
  833. // Otherwise, we could end up with two copies of this routine
  834. // running.
  835. //
  836. Status = StartMasterBrowserTimer(Network);
  837. if (Status != NERR_Success) {
  838. BrPrint(( BR_CRITICAL,
  839. "%ws: %ws: Unable to restart browser backup timer: %lx\n",
  840. Network->DomainInfo->DomUnicodeDomainName,
  841. Network->NetworkName.Buffer,
  842. Status));
  843. try_return(NOTHING);
  844. }
  845. } else {
  846. //
  847. // If it is a lan-ish transport, and we have run the master
  848. // timer for enough times (ie. we've been a master
  849. // for "long enough", we can toss the interim server list in the
  850. // master, because the bowser driver will have enough data in its
  851. // list by now.
  852. //
  853. if (Network->MasterBrowserTimerCount >= MASTER_BROWSER_LAN_TIMER_LIMIT) {
  854. ASSERT (NetLocked);
  855. //
  856. // Make all the servers and domains in the interim server list
  857. // go away - they aren't needed any more for a LAN-ish transport.
  858. //
  859. UninitializeInterimServerList(&Network->BrowseTable);
  860. ASSERT (Network->BrowseTable.EntriesRead == 0);
  861. ASSERT (Network->BrowseTable.TotalEntries == 0);
  862. UninitializeInterimServerList(&Network->DomainList);
  863. ASSERT (Network->DomainList.EntriesRead == 0);
  864. ASSERT (Network->DomainList.TotalEntries == 0);
  865. } else {
  866. //
  867. // Age out servers and domains from the server list.
  868. //
  869. AgeInterimServerList(&Network->BrowseTable);
  870. AgeInterimServerList(&Network->DomainList);
  871. //
  872. // Restart the timer for this domain.
  873. //
  874. Status = StartMasterBrowserTimer(Network);
  875. if (Status != NERR_Success) {
  876. BrPrint(( BR_CRITICAL,
  877. "%ws: %ws: Unable to restart browser backup timer: %lx\n",
  878. Network->DomainInfo->DomUnicodeDomainName,
  879. Network->NetworkName.Buffer,
  880. Status));
  881. try_return(NOTHING);
  882. }
  883. }
  884. }
  885. try_exit:NOTHING;
  886. } finally {
  887. if (pDcInfo != NULL) {
  888. NetApiBufferFree((LPVOID)pDcInfo);
  889. }
  890. if (PrimaryWinsServerAddress) {
  891. MIDL_user_free(PrimaryWinsServerAddress);
  892. }
  893. if (SecondaryWinsServerAddress) {
  894. MIDL_user_free(SecondaryWinsServerAddress);
  895. }
  896. if (WinsServerList) {
  897. MIDL_user_free(WinsServerList);
  898. }
  899. if (NetLocked) {
  900. UNLOCK_NETWORK(Network);
  901. }
  902. BrDereferenceNetwork( Network );
  903. }
  904. }
  905. VOID
  906. BrMasterAnnouncement(
  907. IN PVOID TimerContext
  908. )
  909. /*++
  910. Routine Description:
  911. This routine is called to announce the domain on the local sub-net.
  912. Arguments:
  913. None.
  914. Return Value:
  915. None
  916. --*/
  917. {
  918. PNETWORK Network = TimerContext;
  919. ULONG Periodicity;
  920. NET_API_STATUS Status;
  921. #ifdef ENABLE_PSEUDO_BROWSER
  922. if ( BrInfo.PseudoServerLevel == BROWSER_PSEUDO ) {
  923. // cancel announcements for phase out black hole server
  924. return;
  925. }
  926. #endif
  927. //
  928. // Prevent the network from being deleted while we're in this timer routine.
  929. //
  930. if ( BrReferenceNetwork( Network ) == NULL ) {
  931. return;
  932. }
  933. if (!LOCK_NETWORK(Network)) {
  934. BrDereferenceNetwork( Network );
  935. return;
  936. }
  937. //
  938. // Make absolutely certain that the server thinks that the browser service
  939. // bits for this transport are up to date. We do NOT have to force an
  940. // announcement, since theoretically, the status didn't change.
  941. //
  942. (VOID) BrUpdateNetworkAnnouncementBits( Network, (PVOID) BR_PARANOID );
  943. //
  944. // Setup the timer for the next announcement.
  945. //
  946. Periodicity = DomainAnnouncementPeriodicity[Network->MasterAnnouncementIndex];
  947. BrSetTimer(&Network->MasterBrowserAnnouncementTimer, Periodicity, BrMasterAnnouncement, Network);
  948. if (Network->MasterAnnouncementIndex != DomainAnnouncementMax) {
  949. Network->MasterAnnouncementIndex += 1;
  950. }
  951. //
  952. // Announce this domain to the world using the current periodicity.
  953. //
  954. BrAnnounceDomain(Network, Periodicity);
  955. UNLOCK_NETWORK(Network);
  956. BrDereferenceNetwork( Network );
  957. }
  958. NET_API_STATUS
  959. BrStopMaster(
  960. IN PNETWORK Network
  961. )
  962. {
  963. NET_API_STATUS Status;
  964. //
  965. // This guy is shutting down - set his role to 0 and announce.
  966. //
  967. if (!LOCK_NETWORK(Network)) {
  968. return NERR_InternalError;
  969. }
  970. try {
  971. BrPrint(( BR_MASTER,
  972. "%ws: %ws: Stopping being master.\n",
  973. Network->DomainInfo->DomUnicodeDomainName,
  974. Network->NetworkName.Buffer));
  975. //
  976. // When we stop being a master, we can no longer be considered a
  977. // backup either, since backups maintain their server list
  978. // differently than the master.
  979. //
  980. Network->Role &= ~(ROLE_MASTER | ROLE_BACKUP);
  981. Status = BrUpdateNetworkAnnouncementBits(Network, 0);
  982. if (Status != NERR_Success) {
  983. BrPrint(( BR_MASTER,
  984. "%ws: %ws: Unable to clear master announcement bits in browser: %ld\n",
  985. Network->DomainInfo->DomUnicodeDomainName,
  986. Network->NetworkName.Buffer,
  987. Status));
  988. try_return(Status);
  989. }
  990. //
  991. // Stop our master related timers.
  992. //
  993. Status = BrCancelTimer(&Network->MasterBrowserAnnouncementTimer);
  994. ASSERT (Status == NERR_Success);
  995. Status = BrCancelTimer(&Network->MasterBrowserTimer);
  996. ASSERT (Status == NERR_Success);
  997. try_exit:NOTHING;
  998. } finally {
  999. UNLOCK_NETWORK(Network);
  1000. }
  1001. return Status;
  1002. }
  1003. NET_API_STATUS
  1004. AnnounceMasterToDomainMaster(
  1005. IN PNETWORK Network,
  1006. IN LPWSTR ServerName
  1007. )
  1008. {
  1009. NET_API_STATUS Status;
  1010. UNICODE_STRING EmulatedDomainName;
  1011. CHAR Buffer[sizeof(MASTER_ANNOUNCEMENT)+CNLEN+1];
  1012. PMASTER_ANNOUNCEMENT MasterAnnouncementp = (PMASTER_ANNOUNCEMENT)Buffer;
  1013. lstrcpyA( MasterAnnouncementp->MasterAnnouncement.MasterName,
  1014. Network->DomainInfo->DomOemComputerName );
  1015. MasterAnnouncementp->Type = MasterAnnouncement;
  1016. Status = SendDatagram( BrDgReceiverDeviceHandle,
  1017. &Network->NetworkName,
  1018. &Network->DomainInfo->DomUnicodeDomainNameString,
  1019. ServerName,
  1020. ComputerName,
  1021. MasterAnnouncementp,
  1022. FIELD_OFFSET(MASTER_ANNOUNCEMENT, MasterAnnouncement.MasterName) + Network->DomainInfo->DomOemComputerNameLength+sizeof(CHAR)
  1023. );
  1024. return Status;
  1025. }
  1026. NET_API_STATUS NET_API_FUNCTION
  1027. I_BrowserrResetNetlogonState(
  1028. IN BROWSER_IDENTIFY_HANDLE ServerName
  1029. )
  1030. /*++
  1031. Routine Description:
  1032. This routine will reset the bowser's concept of the state of the netlogon
  1033. service. It is called by the UI when it promotes or demotes a DC.
  1034. Arguments:
  1035. IN BROWSER_IDENTIFY_HANDLE ServerName - Ignored.
  1036. Return Value:
  1037. NET_API_STATUS - The status of this request.
  1038. --*/
  1039. {
  1040. //
  1041. // This routine has been superceeded by I_BrowserrSetNetlogonState
  1042. //
  1043. return ERROR_NOT_SUPPORTED;
  1044. UNREFERENCED_PARAMETER( ServerName );
  1045. }
  1046. NET_API_STATUS NET_API_FUNCTION
  1047. I_BrowserrSetNetlogonState(
  1048. IN BROWSER_IDENTIFY_HANDLE ServerName,
  1049. IN LPWSTR DomainName,
  1050. IN LPWSTR EmulatedComputerName,
  1051. IN DWORD Role
  1052. )
  1053. /*++
  1054. Routine Description:
  1055. This routine will reset the bowser's concept of the state of the netlogon
  1056. service. It is called by the Netlogon service when it promotes or demotes a DC.
  1057. Arguments:
  1058. ServerName - Ignored.
  1059. DomainName - Name of the domain whose role has changed. If the domain name specified
  1060. isn't the primary domain or an emulated domain, an emulated domain is added.
  1061. EmulatedComputerName - Name of the server within DomainName that's being emulated.
  1062. Role - New role of the machine.
  1063. Zero implies emulated domain is to be deleted.
  1064. Return Value:
  1065. NET_API_STATUS - The status of this request.
  1066. --*/
  1067. {
  1068. NET_API_STATUS NetStatus = NERR_Success;
  1069. PDOMAIN_INFO DomainInfo = NULL;
  1070. BOOLEAN ConfigCritSectLocked = FALSE;
  1071. #ifdef notdef
  1072. // This routine no longer sets the role.
  1073. //
  1074. // This routine is currently disabled since it doesn't work well with
  1075. // the PNP logic. Specifically,
  1076. //
  1077. // When a hosted domain is created, this routine calls BrCreateNetwork.
  1078. // That creates a network in the bowser. The bowser PNPs that up.
  1079. // HandlePnpMessage tries to create the transport on all hosted domains.
  1080. // Of course, that fails since all the transports exist.
  1081. // This is just wasted effort.
  1082. //
  1083. // However,
  1084. // When a hosted domain is deleted, we delete the transport. The bowser
  1085. // PNPs that up to us. HandlePnpMessage then deletes the transport for
  1086. // all hosted domains.
  1087. //
  1088. // I think the best solution to this would be for the browser to flag
  1089. // the IOCTL_LMDR_BIND_TO_TRANSPORT_DOM calls it makes to the bowser. The
  1090. // bowser would NOT PNP such creations up to the browser or netlogon.
  1091. // (Be careful. Netlogon depends on getting the notification that NwLnkIpx
  1092. // was created by the browser. Perhaps we can let that one through.)
  1093. //
  1094. //
  1095. // Perform access validation on the caller.
  1096. //
  1097. NetStatus = NetpAccessCheck(
  1098. BrGlobalBrowserSecurityDescriptor, // Security descriptor
  1099. BROWSER_CONTROL_ACCESS, // Desired access
  1100. &BrGlobalBrowserInfoMapping ); // Generic mapping
  1101. if ( NetStatus != NERR_Success) {
  1102. BrPrint((BR_CRITICAL,
  1103. "I_BrowserrSetNetlogonState failed NetpAccessCheck\n" ));
  1104. return ERROR_ACCESS_DENIED;
  1105. }
  1106. if (!BrInfo.IsLanmanNt) {
  1107. NetStatus = NERR_NotPrimary;
  1108. goto Cleanup;
  1109. }
  1110. //
  1111. // See if we're handling the specified domain.
  1112. //
  1113. DomainInfo = BrFindDomain( DomainName, FALSE );
  1114. if ( DomainInfo == NULL ) {
  1115. //
  1116. // Try to create the domain.
  1117. //
  1118. if ( EmulatedComputerName == NULL ||
  1119. Role == 0 ||
  1120. (Role & BROWSER_ROLE_AVOID_CREATING_DOMAIN) != 0 ) {
  1121. NetStatus = ERROR_NO_SUCH_DOMAIN;
  1122. goto Cleanup;
  1123. }
  1124. NetStatus = BrCreateDomainInWorker(
  1125. DomainName,
  1126. EmulatedComputerName,
  1127. TRUE );
  1128. if ( NetStatus != NERR_Success ) {
  1129. goto Cleanup;
  1130. }
  1131. //
  1132. // Find the newly created domain
  1133. //
  1134. DomainInfo = BrFindDomain( DomainName, FALSE );
  1135. if ( DomainInfo == NULL ) {
  1136. NetStatus = ERROR_NO_SUCH_DOMAIN;
  1137. goto Cleanup;
  1138. }
  1139. }
  1140. //
  1141. // Delete the Emulated domain.
  1142. //
  1143. EnterCriticalSection(&BrInfo.ConfigCritSect);
  1144. ConfigCritSectLocked = TRUE;
  1145. if ( Role == 0 ) {
  1146. //
  1147. // Don't allow the primary domain to be deleted.
  1148. //
  1149. if ( !DomainInfo->IsEmulatedDomain ) {
  1150. NetStatus = ERROR_NO_SUCH_DOMAIN;
  1151. goto Cleanup;
  1152. }
  1153. BrDeleteDomain( DomainInfo );
  1154. }
  1155. LeaveCriticalSection(&BrInfo.ConfigCritSect);
  1156. ConfigCritSectLocked = FALSE;
  1157. //
  1158. // Free locally used resources
  1159. //
  1160. Cleanup:
  1161. if ( ConfigCritSectLocked ) {
  1162. LeaveCriticalSection(&BrInfo.ConfigCritSect);
  1163. }
  1164. if ( DomainInfo != NULL ) {
  1165. BrDereferenceDomain( DomainInfo );
  1166. }
  1167. return NetStatus;
  1168. #endif // notdef
  1169. return ERROR_NOT_SUPPORTED;
  1170. }
  1171. NET_API_STATUS NET_API_FUNCTION
  1172. I_BrowserrQueryEmulatedDomains (
  1173. IN LPTSTR ServerName OPTIONAL,
  1174. IN OUT PBROWSER_EMULATED_DOMAIN_CONTAINER EmulatedDomains
  1175. )
  1176. /*++
  1177. Routine Description:
  1178. Enumerate the emulated domain list.
  1179. Arguments:
  1180. ServerName - Supplies the name of server to execute this function
  1181. EmulatedDomains - Returns a pointer to a an allocated array of emulated domain
  1182. information.
  1183. Return Value:
  1184. NET_API_STATUS - NERR_Success or reason for failure.
  1185. --*/
  1186. {
  1187. NET_API_STATUS NetStatus;
  1188. PBROWSER_EMULATED_DOMAIN Domains = NULL;
  1189. PLIST_ENTRY DomainEntry;
  1190. PDOMAIN_INFO DomainInfo;
  1191. DWORD BufferSize;
  1192. DWORD Index;
  1193. LPBYTE Where;
  1194. DWORD EntryCount;
  1195. //
  1196. // Perform access validation on the caller.
  1197. //
  1198. NetStatus = NetpAccessCheck(
  1199. BrGlobalBrowserSecurityDescriptor, // Security descriptor
  1200. BROWSER_QUERY_ACCESS, // Desired access
  1201. &BrGlobalBrowserInfoMapping ); // Generic mapping
  1202. if ( NetStatus != NERR_Success) {
  1203. BrPrint((BR_CRITICAL,
  1204. "I_BrowserrQueryEmulatedDomains failed NetpAccessCheck\n" ));
  1205. return ERROR_ACCESS_DENIED;
  1206. }
  1207. // Do not accept pre-allocated IN param, since
  1208. // we overwrite the pointer & this can lead to a mem leak.
  1209. // (security attack defence).
  1210. if ( EmulatedDomains->EntriesRead != 0 ||
  1211. EmulatedDomains->Buffer ) {
  1212. return ERROR_INVALID_PARAMETER;
  1213. }
  1214. ASSERT ( EmulatedDomains->EntriesRead == 0 );
  1215. ASSERT ( EmulatedDomains->Buffer == NULL );
  1216. //
  1217. // Initialization
  1218. //
  1219. EnterCriticalSection(&NetworkCritSect);
  1220. //
  1221. // Loop through the list of emulated domains determining the size of the
  1222. // return buffer.
  1223. //
  1224. BufferSize = 0;
  1225. EntryCount = 0;
  1226. for (DomainEntry = ServicedDomains.Flink ;
  1227. DomainEntry != &ServicedDomains;
  1228. DomainEntry = DomainEntry->Flink ) {
  1229. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next);
  1230. if ( DomainInfo->IsEmulatedDomain ) {
  1231. BufferSize += sizeof(BROWSER_EMULATED_DOMAIN) +
  1232. DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR) +
  1233. DomainInfo->DomUnicodeComputerNameLength * sizeof(WCHAR) + sizeof(WCHAR);
  1234. EntryCount ++;
  1235. }
  1236. }
  1237. //
  1238. // Allocate the return buffer.
  1239. //
  1240. Domains = MIDL_user_allocate( BufferSize );
  1241. if ( Domains == NULL ) {
  1242. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1243. goto Cleanup;
  1244. }
  1245. //
  1246. // Copy the information into the buffer
  1247. //
  1248. Index = 0;
  1249. Where = (LPBYTE)(Domains+EntryCount);
  1250. for (DomainEntry = ServicedDomains.Flink ;
  1251. DomainEntry != &ServicedDomains;
  1252. DomainEntry = DomainEntry->Flink ) {
  1253. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, Next);
  1254. if ( DomainInfo->IsEmulatedDomain ) {
  1255. Domains[Index].DomainName = (LPWSTR)Where;
  1256. wcscpy( (LPWSTR) Where, DomainInfo->DomUnicodeDomainNameString.Buffer );
  1257. Where += DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR);
  1258. Domains[Index].EmulatedServerName = (LPWSTR)Where;
  1259. wcscpy( (LPWSTR) Where, DomainInfo->DomUnicodeComputerName );
  1260. Where += DomainInfo->DomUnicodeComputerNameLength * sizeof(WCHAR) + sizeof(WCHAR);
  1261. Index ++;
  1262. }
  1263. }
  1264. //
  1265. // Success
  1266. //
  1267. EmulatedDomains->Buffer = (PVOID) Domains;
  1268. EmulatedDomains->EntriesRead = EntryCount;
  1269. NetStatus = NERR_Success;
  1270. Cleanup:
  1271. LeaveCriticalSection(&NetworkCritSect);
  1272. return NetStatus;
  1273. }