Source code of Windows XP (NT5)
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.

4226 lines
112 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. nlsite.c
  5. Abstract:
  6. Routines to handle sites and subnets.
  7. Author:
  8. Cliff Van Dyke (CliffV) 1-May-1997
  9. Revision History:
  10. --*/
  11. //
  12. // Common include files.
  13. //
  14. #include "logonsrv.h" // Include files common to entire service
  15. #pragma hdrstop
  16. //
  17. // Include files specific to this .c file
  18. //
  19. #include "ismapi.h"
  20. //
  21. // Structure describing several subnets.
  22. //
  23. // NlGlobalSubnetTree is the head of a tree of pointer to subnet.
  24. // The most significant byte of an IP address is used to index into an array
  25. // of SubTrees. Each Subtree entry has either a pointer to the next level of
  26. // the tree (to be indexed into with the next byte of the IP address) or a
  27. // pointer to an NL_SUBNET leaf identifying the subnet this IP address is on.
  28. //
  29. // Both pointers can be NULL indicating that the subnet isn't registered.
  30. //
  31. // Both pointers can be non-NULL indicating that both a non-specific and specific
  32. // subnet may be available. The most specific subnet available for a particular
  33. // IP address should be used.
  34. //
  35. //
  36. // Multiple entries can point to the same NL_SUBNET leaf. If the subnet mask is
  37. // not an even number of bytes long, all of the entries represent IP addresses
  38. // that correspond to the subnet mask will point to the subnet mask.
  39. //
  40. typedef struct _NL_SUBNET_TREE_ENTRY {
  41. //
  42. // Link to the next level of the tree
  43. //
  44. struct _NL_SUBNET_TREE *Subtree;
  45. //
  46. // Pointer to the subnet itself.
  47. //
  48. struct _NL_SUBNET *Subnet;
  49. } NL_SUBNET_TREE_ENTRY, *PNL_SUBNET_TREE_ENTRY;
  50. typedef struct _NL_SUBNET_TREE {
  51. NL_SUBNET_TREE_ENTRY Subtree[256];
  52. } NL_SUBNET_TREE, *PNL_SUBNET_TREE;
  53. //
  54. // Structure describing a single subnet.
  55. //
  56. typedef struct _NL_SUBNET {
  57. //
  58. // Link for NlGlobalSubnetList
  59. //
  60. LIST_ENTRY Next;
  61. //
  62. // Subnet address. (Network bytes order)
  63. //
  64. ULONG SubnetAddress;
  65. //
  66. // Subnet mask. (Network byte order).
  67. //
  68. ULONG SubnetMask;
  69. //
  70. // Pointer to Site this subnet is in.
  71. //
  72. PNL_SITE_ENTRY SiteEntry;
  73. //
  74. // Reference Count.
  75. //
  76. ULONG ReferenceCount;
  77. //
  78. // Number of bits in the subnet mask
  79. //
  80. BYTE SubnetBitCount;
  81. } NL_SUBNET, *PNL_SUBNET;
  82. //
  83. // Globals specific to this .c file.
  84. //
  85. BOOLEAN NlGlobalSiteInitialized = 0;
  86. // List of all NL_SITE_ENTRY entries.
  87. LIST_ENTRY NlGlobalSiteList;
  88. // List of all NL_SUBNET entries
  89. LIST_ENTRY NlGlobalSubnetList;
  90. // Tree of subnets.
  91. NL_SUBNET_TREE_ENTRY NlGlobalSubnetTree;
  92. NL_SUBNET_TREE_ENTRY NlGlobalNewSubnetTree;
  93. //
  94. // Site Entry for the site this domain is a member of
  95. //
  96. PNL_SITE_ENTRY NlGlobalSiteEntry;
  97. BOOLEAN NlGlobalOnlyOneSite;
  98. VOID
  99. NlRefSiteEntry(
  100. IN PNL_SITE_ENTRY SiteEntry
  101. )
  102. /*++
  103. Routine Description:
  104. Reference a site entry.
  105. NlGlobalSiteCritSect must be locked.
  106. Arguments:
  107. SiteEntry - Entry to be referenced.
  108. Return Value:
  109. None.
  110. --*/
  111. {
  112. SiteEntry->ReferenceCount ++;
  113. }
  114. VOID
  115. NlDerefSiteEntry(
  116. IN PNL_SITE_ENTRY SiteEntry
  117. )
  118. /*++
  119. Routine Description:
  120. Dereference a site entry.
  121. If the reference count goes to zero,
  122. the site entry will be deleted.
  123. Arguments:
  124. SiteEntry - Entry to be referenced.
  125. Return Value:
  126. None.
  127. --*/
  128. {
  129. EnterCriticalSection( &NlGlobalSiteCritSect );
  130. if ( (--(SiteEntry->ReferenceCount)) == 0 ) {
  131. RemoveEntryList( &SiteEntry->Next );
  132. LocalFree( SiteEntry );
  133. }
  134. LeaveCriticalSection( &NlGlobalSiteCritSect );
  135. }
  136. BOOL
  137. NetpEqualTStrArrays(
  138. LPTSTR_ARRAY TStrArray1 OPTIONAL,
  139. LPTSTR_ARRAY TStrArray2 OPTIONAL
  140. )
  141. /*++
  142. Routine Description:
  143. Compares to see if two TStrArray's are identical.
  144. Arguments:
  145. TStrArray1 - First array to compare
  146. TStrArray2 - Second array to compare
  147. Return Value:
  148. TRUE - Arrays are identical
  149. FALSE - Arrays are different
  150. --*/
  151. {
  152. //
  153. // Handle the NULL pointer cases.
  154. //
  155. if ( TStrArray1 == NULL && TStrArray2 == NULL ) {
  156. return TRUE;
  157. }
  158. if ( TStrArray1 != NULL && TStrArray2 == NULL ) {
  159. return FALSE;
  160. }
  161. if ( TStrArray1 == NULL && TStrArray2 != NULL ) {
  162. return FALSE;
  163. }
  164. //
  165. // Handle the case where both arrays exist
  166. //
  167. if ( NetpTStrArrayEntryCount ( TStrArray1 ) !=
  168. NetpTStrArrayEntryCount ( TStrArray2 ) ) {
  169. return FALSE;
  170. }
  171. while (!NetpIsTStrArrayEmpty(TStrArray1)) {
  172. //
  173. // Check if the entry is different.
  174. //
  175. // Do a case sensitive comparison to allow case changes to be detected.
  176. //
  177. if ( wcscmp( TStrArray1, TStrArray2 ) != 0 ) {
  178. return FALSE;
  179. }
  180. // Move to the next element
  181. TStrArray1 = NetpNextTStrArrayEntry(TStrArray1);
  182. TStrArray2 = NetpNextTStrArrayEntry(TStrArray2);
  183. }
  184. return TRUE;
  185. }
  186. NET_API_STATUS
  187. NlSitesGetCloseSites(
  188. IN PDOMAIN_INFO DomainInfo,
  189. IN ULONG ServerRole,
  190. OUT PNL_SITE_NAME_ARRAY *SiteNames
  191. )
  192. /*++
  193. Routine Description:
  194. This routine returns the site names of all the sites this DC covers.
  195. Arguments:
  196. DomainInfo - Domain/Forest/NDNC info for which to return close sites
  197. ServerRole - The role this server plays in the domain/forest/NDNC.
  198. ??: When we go multihosted, this parameter will not be needed
  199. because the role will be uniquely identified in the DomainInfo.
  200. SiteNames - Returns an array of pointers to site names.
  201. The returned buffer must be deallocated using NetApiBufferFree.
  202. Return Value:
  203. NO_ERROR - Operation completed successfully;
  204. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  205. operation.
  206. --*/
  207. {
  208. NET_API_STATUS NetStatus;
  209. PNL_COVERED_SITE CoveredSiteArray;
  210. ULONG CoveredSiteCount = 0;
  211. ULONG EntryCount;
  212. ULONG i;
  213. ULONG Size;
  214. PUNICODE_STRING Strings;
  215. LPBYTE Where;
  216. ULONG Index;
  217. EnterCriticalSection( &NlGlobalSiteCritSect );
  218. if ( (ServerRole & DOM_FOREST) != 0 ) {
  219. CoveredSiteArray = DomainInfo->GcCoveredSites;
  220. CoveredSiteCount = DomainInfo->GcCoveredSitesCount;
  221. } else if ( (ServerRole & DOM_REAL_DOMAIN) != 0 ||
  222. (ServerRole & DOM_NON_DOMAIN_NC) != 0 ) {
  223. CoveredSiteArray = DomainInfo->CoveredSites;
  224. CoveredSiteCount = DomainInfo->CoveredSitesCount;
  225. }
  226. //
  227. // Determine the length of the returned information
  228. //
  229. Size = sizeof(NL_SITE_NAME_ARRAY);
  230. EntryCount = 0;
  231. for ( Index = 0; Index < CoveredSiteCount; Index++ ) {
  232. Size += sizeof(UNICODE_STRING) +
  233. CoveredSiteArray[Index].CoveredSite->SiteNameString.Length + sizeof(WCHAR);
  234. EntryCount++;
  235. }
  236. //
  237. // Allocate the return buffer.
  238. //
  239. *SiteNames = MIDL_user_allocate( Size );
  240. if ( *SiteNames == NULL ) {
  241. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  242. goto Cleanup;
  243. }
  244. Strings = (PUNICODE_STRING) ((*SiteNames)+1);
  245. (*SiteNames)->EntryCount = EntryCount;
  246. (*SiteNames)->SiteNames = Strings;
  247. Where = (LPBYTE) &Strings[EntryCount];
  248. //
  249. // Loop copying the names into the return buffer.
  250. //
  251. i = 0;
  252. for ( Index = 0; Index < CoveredSiteCount; Index++ ) {
  253. RtlCopyMemory( Where,
  254. CoveredSiteArray[Index].CoveredSite->SiteName,
  255. CoveredSiteArray[Index].CoveredSite->SiteNameString.Length + sizeof(WCHAR) );
  256. Strings[i].Buffer = (LPWSTR)Where;
  257. Strings[i].Length = CoveredSiteArray[Index].CoveredSite->SiteNameString.Length;
  258. Strings[i].MaximumLength = Strings[i].Length + sizeof(WCHAR);
  259. Where += Strings[i].Length + sizeof(WCHAR);
  260. i++;
  261. }
  262. NetStatus = NO_ERROR;
  263. NlPrint(( NL_MISC, "NlSitesGetCloseSites returns successfully\n" ));
  264. Cleanup:
  265. LeaveCriticalSection( &NlGlobalSiteCritSect );
  266. if ( NetStatus != NO_ERROR ) {
  267. if ( *SiteNames != NULL ) {
  268. MIDL_user_free( *SiteNames );
  269. *SiteNames = NULL;
  270. }
  271. NlPrint((NL_MISC, "NlSitesGetCloseSites returns unsuccessfully with status %ld.\n", NetStatus ));
  272. }
  273. return NetStatus;
  274. }
  275. BOOL
  276. NlSitesSetSiteCoverageParam(
  277. IN ULONG ServerRole,
  278. IN LPTSTR_ARRAY NewSiteCoverage OPTIONAL
  279. )
  280. /*++
  281. Routine Description:
  282. This routine set the site names of all the sites this DC covers.
  283. Arguments:
  284. ServerRole - Specifies the role of the server (DC/GC/NDNC) for which
  285. the registry site coverage is being set.
  286. NewSiteCoverage - Specifies the new covered sites
  287. Return Value:
  288. TRUE: iff site coverage changed
  289. --*/
  290. {
  291. LPTSTR_ARRAY *OldSiteCoverage = NULL;
  292. BOOL SiteCoverageChanged;
  293. PLIST_ENTRY ListEntry;
  294. //
  295. // If asking about the GC,
  296. // use GC specific globals.
  297. //
  298. EnterCriticalSection( &NlGlobalSiteCritSect );
  299. if ( ServerRole & DOM_FOREST ) {
  300. OldSiteCoverage = &NlGlobalParameters.GcSiteCoverage;
  301. } else if ( ServerRole & DOM_REAL_DOMAIN ) {
  302. OldSiteCoverage = &NlGlobalParameters.SiteCoverage;
  303. } else if ( ServerRole & DOM_NON_DOMAIN_NC ) {
  304. OldSiteCoverage = &NlGlobalParameters.NdncSiteCoverage;
  305. }
  306. NlAssert( OldSiteCoverage != NULL );
  307. //
  308. // Handle SiteCoverage changing
  309. //
  310. SiteCoverageChanged = !NetpEqualTStrArrays(
  311. *OldSiteCoverage,
  312. NewSiteCoverage );
  313. if ( SiteCoverageChanged ) {
  314. //
  315. // Swap in the new value.
  316. (VOID) NetApiBufferFree( *OldSiteCoverage );
  317. *OldSiteCoverage = NewSiteCoverage;
  318. }
  319. LeaveCriticalSection( &NlGlobalSiteCritSect );
  320. return SiteCoverageChanged;
  321. }
  322. //
  323. // Procedure forwards of procedures in ntdsapi.dll
  324. //
  325. typedef
  326. DWORD
  327. (*PDsGetDomainControllerInfoW)(
  328. HANDLE hDs, // in
  329. LPCWSTR DomainName, // in
  330. DWORD InfoLevel, // in
  331. DWORD *pcOut, // out
  332. VOID **ppInfo); // out
  333. PDsGetDomainControllerInfoW NlGlobalpDsGetDomainControllerInfoW;
  334. typedef
  335. VOID
  336. (*PDsFreeDomainControllerInfoW)(
  337. DWORD InfoLevel, // in
  338. DWORD cInfo, // in
  339. VOID *pInfo); // in
  340. PDsFreeDomainControllerInfoW NlGlobalpDsFreeDomainControllerInfoW;
  341. NTSTATUS
  342. NlLoadNtDsApiDll(
  343. VOID
  344. )
  345. /*++
  346. Routine Description:
  347. This function loads the ntdsaapi.dll module if it is not loaded
  348. already.
  349. Arguments:
  350. None
  351. Return Value:
  352. NT Status code.
  353. --*/
  354. {
  355. static NTSTATUS DllLoadStatus = STATUS_SUCCESS;
  356. HANDLE DllHandle = NULL;
  357. //
  358. // If the DLL is already loaded,
  359. // we're done.
  360. //
  361. EnterCriticalSection( &NlGlobalSiteCritSect );
  362. if ( NlGlobalDsApiDllHandle != NULL ) {
  363. LeaveCriticalSection( &NlGlobalSiteCritSect );
  364. return STATUS_SUCCESS;
  365. }
  366. //
  367. // If we've tried to load the DLL before and it failed,
  368. // return the same error code again.
  369. //
  370. if( DllLoadStatus != STATUS_SUCCESS ) {
  371. goto Cleanup;
  372. }
  373. //
  374. // Load the dlls
  375. //
  376. DllHandle = LoadLibraryA( "NtDsApi" );
  377. if ( DllHandle == NULL ) {
  378. DllLoadStatus = STATUS_DLL_NOT_FOUND;
  379. goto Cleanup;
  380. }
  381. //
  382. // Macro to grab the address of the named procedure from ntdsa.dll
  383. //
  384. #define GRAB_ADDRESS( _X ) \
  385. NlGlobalp##_X = (P##_X) GetProcAddress( DllHandle, #_X ); \
  386. \
  387. if ( NlGlobalp##_X == NULL ) { \
  388. DllLoadStatus = STATUS_PROCEDURE_NOT_FOUND;\
  389. goto Cleanup; \
  390. }
  391. //
  392. // Get the addresses of the required procedures.
  393. //
  394. GRAB_ADDRESS( DsBindW )
  395. GRAB_ADDRESS( DsGetDomainControllerInfoW )
  396. GRAB_ADDRESS( DsFreeDomainControllerInfoW )
  397. GRAB_ADDRESS( DsUnBindW )
  398. DllLoadStatus = STATUS_SUCCESS;
  399. Cleanup:
  400. if (DllLoadStatus == STATUS_SUCCESS) {
  401. NlGlobalDsApiDllHandle = DllHandle;
  402. } else {
  403. if ( DllHandle != NULL ) {
  404. FreeLibrary( DllHandle );
  405. }
  406. }
  407. LeaveCriticalSection( &NlGlobalSiteCritSect );
  408. return( DllLoadStatus );
  409. }
  410. VOID
  411. NlSitesAddCloseSite(
  412. IN LPWSTR SiteName,
  413. IN OUT PNL_COVERED_SITE CoveredSites,
  414. IN OUT PULONG CoveredSitesCount,
  415. IN BOOLEAN CoveredAuto
  416. )
  417. /*++
  418. Routine Description:
  419. Add a site entry to the list passed. If the site entry already
  420. exists on the list, this is no-op. Otherwise, the site entry
  421. is added to the list (and to the global list of sites if this
  422. entry wasn't on the global list) and a reference on the site
  423. entry in the global list in incremented.
  424. Arguments:
  425. SiteName - Name of site entry to add
  426. CoveredSites - Array of covered site entries. The array
  427. must be big enough to accomodate a new potential entry.
  428. CoveredSiteCount - The current number of entries in CoveredSites.
  429. May be incremented if a new entry is added.
  430. CoveredAuto - If TRUE, this site is covered automatically.
  431. Return Value:
  432. None
  433. --*/
  434. {
  435. PNL_SITE_ENTRY SiteEntry = NULL;
  436. ULONG CoveredSiteIndex;
  437. //
  438. // Sanity check
  439. //
  440. if ( SiteName == NULL || *SiteName == UNICODE_NULL ) {
  441. return;
  442. }
  443. //
  444. // Find/Add the site to the global list of sites
  445. //
  446. SiteEntry = NlFindSiteEntry( SiteName );
  447. if ( SiteEntry != NULL ) {
  448. //
  449. // If we already have this site on the current list of covered
  450. // sites, just update the auto coverage boolean and dereference
  451. // this site entry.
  452. //
  453. for ( CoveredSiteIndex = 0; CoveredSiteIndex < *CoveredSitesCount; CoveredSiteIndex++ ) {
  454. if ( CoveredSites[CoveredSiteIndex].CoveredSite == SiteEntry ) {
  455. CoveredSites[CoveredSiteIndex].CoveredAuto = CoveredAuto;
  456. NlDerefSiteEntry( SiteEntry );
  457. return;
  458. }
  459. }
  460. //
  461. // We don't have this site on the current list of covered
  462. // sites. So add this site to the list, set the auto coverage
  463. // boolean, and keep just added reference in the global list
  464. // of sites.
  465. //
  466. CoveredSites[*CoveredSitesCount].CoveredSite = SiteEntry;
  467. CoveredSites[*CoveredSitesCount].CoveredAuto = CoveredAuto;
  468. (*CoveredSitesCount) ++;
  469. }
  470. }
  471. BOOL
  472. NlSitesGetIsmConnect(
  473. IN LPWSTR SiteName,
  474. OUT PISM_CONNECTIVITY *SiteConnect,
  475. OUT PULONG ThisSite
  476. )
  477. /*++
  478. Routine Description:
  479. Get the site connection matrix from ISM.
  480. Arguments:
  481. SiteName - Site name of the site this DC is in
  482. SiteConnect - Returns a pointer to the site connection matrix
  483. Use I_ISMFree to free this data.
  484. ThisSite - Return an index to SiteName within SiteConnect
  485. 0xFFFFFFFF - means this site is not in SiteConnect
  486. Return Value:
  487. TRUE on success
  488. None.
  489. --*/
  490. {
  491. NET_API_STATUS NetStatus;
  492. NTSTATUS Status;
  493. DWORD Length;
  494. PDSNAME DsRoot = NULL;
  495. LPWSTR IpName = NULL;
  496. DWORD SiteIndex1;
  497. DWORD SiteIndex2;
  498. BOOLEAN RetVal = FALSE;
  499. //
  500. // Initialization
  501. //
  502. *SiteConnect = NULL;
  503. *ThisSite = 0xFFFFFFFF;
  504. //
  505. // If netlogon isn't running,
  506. // simply return since we don't want to wait for the ISM service to start
  507. // while we're starting.
  508. //
  509. if ( NlGlobalChangeLogNetlogonState != NetlogonStarted ) {
  510. NlPrint(( NL_SITE_MORE,
  511. "NlSitesGetIsmConnect: Avoided during startup.\n" ));
  512. goto Cleanup;
  513. }
  514. //
  515. // Wait up to 45 seconds for the ISM service to start
  516. //
  517. Status = NlWaitForService( SERVICE_ISMSERV, 45, TRUE );
  518. if ( Status != STATUS_SUCCESS ) {
  519. NlPrint(( NL_SITE_MORE,
  520. "NlSitesGetIsmConnect: ISM service not started.\n" ));
  521. goto Cleanup;
  522. }
  523. //
  524. // Build the name of the IP transport
  525. //
  526. #define ISM_IP_TRANSPORT L"CN=IP,CN=Inter-Site Transports,CN=Sites,"
  527. Length = 0;
  528. Status = NlGetConfigurationName( DSCONFIGNAME_CONFIGURATION, &Length, NULL );
  529. NlAssert( Status == STATUS_BUFFER_TOO_SMALL );
  530. if ( Status != STATUS_BUFFER_TOO_SMALL ) {
  531. NlPrint(( NL_CRITICAL,
  532. "NlSitesGetIsmConnect: Cannot GetConfigurationName 0x%lx.\n",
  533. Status ));
  534. goto Cleanup;
  535. }
  536. DsRoot = LocalAlloc( 0, Length );
  537. if ( DsRoot == NULL ) {
  538. goto Cleanup;
  539. }
  540. Status = NlGetConfigurationName( DSCONFIGNAME_CONFIGURATION, &Length, DsRoot );
  541. if ( !NT_SUCCESS( Status ) ) {
  542. NlPrint(( NL_CRITICAL,
  543. "NlSitesGetIsmConnect: Cannot GetConfigurationName 0x%lx.\n",
  544. Status ));
  545. goto Cleanup;
  546. }
  547. IpName = LocalAlloc( 0, DsRoot->NameLen * sizeof(WCHAR) +
  548. sizeof( ISM_IP_TRANSPORT ) );
  549. if ( IpName == NULL ) {
  550. goto Cleanup;
  551. }
  552. wcscpy( IpName, ISM_IP_TRANSPORT );
  553. wcscat( IpName, DsRoot->StringName );
  554. //
  555. // Get the site link costs
  556. //
  557. NetStatus = I_ISMGetConnectivity( IpName, SiteConnect);
  558. if ( NetStatus != NO_ERROR ) {
  559. NlPrint(( NL_CRITICAL,
  560. "NlSitesGetIsmConnect: Cannot I_ISMGetConnectivity %ld.\n",
  561. NetStatus ));
  562. goto Cleanup;
  563. }
  564. if ( *SiteConnect == NULL ) {
  565. NlPrint(( NL_CRITICAL,
  566. "NlSitesGetIsmConnect: I_ISMGetConnectivity returned NULL.\n" ));
  567. goto Cleanup;
  568. }
  569. //
  570. // Convert the returned site name to a canonical form
  571. //
  572. for (SiteIndex1 = 0; SiteIndex1 < (*SiteConnect)->cNumSites; SiteIndex1++) {
  573. if ( _wcsnicmp( (*SiteConnect)->ppSiteDNs[SiteIndex1], L"CN=", 3 ) == 0 ) {
  574. WCHAR *Comma;
  575. (*SiteConnect)->ppSiteDNs[SiteIndex1] += 3;
  576. Comma = wcschr( (*SiteConnect)->ppSiteDNs[SiteIndex1], L',' );
  577. if ( Comma != NULL ) {
  578. *Comma = L'\0';
  579. }
  580. }
  581. //
  582. // Remember which site this site is:
  583. //
  584. if ( _wcsicmp( SiteName,
  585. (*SiteConnect)->ppSiteDNs[SiteIndex1] ) == 0 ) {
  586. *ThisSite = SiteIndex1;
  587. }
  588. }
  589. //
  590. // Be verbose
  591. //
  592. #if NETLOGONDBG
  593. EnterCriticalSection( &NlGlobalLogFileCritSect );
  594. NlPrint(( NL_SITE_MORE,
  595. "NlSitesGetIsmConnect: Site link costs for %ld sites:\n",
  596. (*SiteConnect)->cNumSites ));
  597. for (SiteIndex2 = 0; SiteIndex2 < (*SiteConnect)->cNumSites; SiteIndex2++) {
  598. NlPrint(( NL_SITE_MORE,
  599. "%s%5d",
  600. SiteIndex2 ? "," : " ",
  601. SiteIndex2 ));
  602. }
  603. NlPrint(( NL_SITE_MORE, "\n"));
  604. for (SiteIndex1 = 0; SiteIndex1 < (*SiteConnect)->cNumSites; SiteIndex1++) {
  605. if ( *ThisSite == SiteIndex1 ) {
  606. NlPrint(( NL_SITE_MORE, "*" ));
  607. } else {
  608. NlPrint(( NL_SITE_MORE, " " ));
  609. }
  610. NlPrint(( NL_SITE_MORE,
  611. "(%2d) %ws\n",
  612. SiteIndex1,
  613. (*SiteConnect)->ppSiteDNs[SiteIndex1]));
  614. for (SiteIndex2 = 0; SiteIndex2 < (*SiteConnect)->cNumSites; SiteIndex2++) {
  615. PISM_LINK pLink = &((*SiteConnect)->pLinkValues[SiteIndex1 * (*SiteConnect)->cNumSites + SiteIndex2]);
  616. NlPrint(( NL_SITE_MORE,
  617. "%s%5d",
  618. SiteIndex2 ? "," : " ",
  619. pLink->ulCost ));
  620. }
  621. NlPrint(( NL_SITE_MORE, "\n"));
  622. }
  623. LeaveCriticalSection( &NlGlobalLogFileCritSect );
  624. #endif // NETLOGONDBG
  625. RetVal = TRUE;
  626. Cleanup:
  627. if ( DsRoot != NULL ) {
  628. LocalFree( DsRoot );
  629. }
  630. if ( IpName != NULL ) {
  631. LocalFree( IpName );
  632. }
  633. return RetVal;
  634. }
  635. NET_API_STATUS
  636. NlSitesUpdateSiteCoverageForRole(
  637. IN PDOMAIN_INFO DomainInfo,
  638. IN ULONG DomFlags,
  639. IN HANDLE DsHandle,
  640. IN PISM_CONNECTIVITY SiteConnect,
  641. IN LPWSTR ThisSiteName,
  642. IN ULONG ThisSiteIndex,
  643. OUT PBOOLEAN SiteCoverageChanged OPTIONAL
  644. )
  645. /*++
  646. Routine Description:
  647. This routine recomputes the site coverage for this DC based on the costs
  648. associated with the site links.
  649. Basically, for each site that has no DCs for this domain, the site this DC
  650. is in might be chosen to "cover" the site. The following criteria is used:
  651. * Site link cost.
  652. * For sites where the above is equal, the site having the most DCs is chosen.
  653. * For sites where the above is equal, the site having the alphabetically least
  654. name is chosen.
  655. Arguments:
  656. DomainInfo - Hosted domain whose site coverage is to be updated
  657. DomFlags - The role for which we need to update the site coverage
  658. DsHandle - Handle to the DS
  659. SiteConnect - If specified, the site link costs info returned by NlSitesGetIsmConnect
  660. ThisSiteName - The name of the site of this server
  661. ThisSiteIndex - The index of the site of this server in the SiteConnect info
  662. SiteCoverageChanged - If specified and the site covereage changes, returns TRUE.
  663. Otherwise, left intact.
  664. Return Value:
  665. NO_ERROR
  666. --*/
  667. {
  668. NET_API_STATUS NetStatus = NO_ERROR;
  669. WCHAR DnsDomainName[NL_MAX_DNS_LENGTH+1];
  670. WCHAR CapturedDnsForestName[NL_MAX_DNS_LENGTH+1];
  671. ULONG SiteCount = 0;
  672. ULONG TmpSiteCount = 0;
  673. DWORD SiteIndex1;
  674. DWORD SiteIndex2;
  675. PULONG DcsInSite = NULL;
  676. BOOLEAN LocalSiteCoverageChanged = FALSE;
  677. PDS_DOMAIN_CONTROLLER_INFO_1W DcInfo = NULL;
  678. ULONG DcInfoCount;
  679. PDS_NAME_RESULT GcInfo = NULL;
  680. SERVERSITEPAIR *ServerSitePairs = NULL;
  681. PNL_SITE_ENTRY SiteEntry;
  682. PLIST_ENTRY ListEntry;
  683. LPSTR GcOrDcOrNdnc = NULL;
  684. BOOLEAN AtleastOneDc = FALSE;
  685. LPTSTR_ARRAY SiteCoverageParameter = NULL;
  686. PNL_COVERED_SITE CoveredSites = NULL;
  687. ULONG CoveredSitesCount = 0;
  688. ULONG CoveredSitesIndex;
  689. PNL_COVERED_SITE OldCoveredSites = NULL;
  690. ULONG OldCoveredSitesCount = 0;
  691. ULONG OldCoveredSitesIndex;
  692. //
  693. // Local variable initialization
  694. //
  695. if ( DomFlags & DOM_FOREST ) {
  696. GcOrDcOrNdnc = "GC";
  697. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  698. GcOrDcOrNdnc = "DC";
  699. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  700. GcOrDcOrNdnc = "NDNC";
  701. }
  702. //
  703. // Allocate a temporary storage for all possible site entries
  704. //
  705. // Count for the site link cost entries
  706. //
  707. if ( SiteConnect != NULL ) {
  708. TmpSiteCount += SiteConnect->cNumSites;
  709. }
  710. //
  711. // Count for the registry coverage parameter
  712. //
  713. if ( DomFlags & DOM_FOREST ) {
  714. SiteCoverageParameter = NlGlobalParameters.GcSiteCoverage;
  715. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  716. SiteCoverageParameter = NlGlobalParameters.SiteCoverage;
  717. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  718. SiteCoverageParameter = NlGlobalParameters.NdncSiteCoverage;
  719. }
  720. if ( SiteCoverageParameter != NULL ) {
  721. TmpSiteCount += NetpTStrArrayEntryCount( (LPTSTR_ARRAY) SiteCoverageParameter );
  722. }
  723. //
  724. // Count for the site of this machine
  725. //
  726. TmpSiteCount ++;
  727. //
  728. // Allocate the storage
  729. //
  730. CoveredSites = LocalAlloc( LMEM_ZEROINIT, TmpSiteCount * sizeof(NL_COVERED_SITE) );
  731. if ( CoveredSites == NULL ) {
  732. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  733. goto Cleanup;
  734. }
  735. //
  736. // Capture the dns domain name of the hosted
  737. //
  738. NlCaptureDomainInfo ( DomainInfo, DnsDomainName, NULL );
  739. //
  740. // Capture the forest name
  741. //
  742. NlCaptureDnsForestName( CapturedDnsForestName );
  743. //
  744. // If we are to automatically determine site coverage and
  745. // we have the site link costs, build the coverage list
  746. // corresponding to the specified role.
  747. //
  748. if ( NlGlobalParameters.AutoSiteCoverage &&
  749. SiteConnect != NULL &&
  750. SiteConnect->cNumSites != 0 ) {
  751. SiteCount = SiteConnect->cNumSites;
  752. //
  753. // Allocate a buffer for temporary storage.
  754. //
  755. DcsInSite = LocalAlloc( LMEM_ZEROINIT, SiteCount * sizeof(ULONG) );
  756. if ( DcsInSite == NULL ) {
  757. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  758. goto Cleanup;
  759. }
  760. //
  761. // Depending on the role, get the list of GCs/DCs/NDNCs and their sites
  762. //
  763. if ( DsHandle != NULL ) {
  764. //
  765. // Handle building the GC coverage list
  766. //
  767. if ( DomFlags & DOM_FOREST ) {
  768. LPWSTR DummyName = L".";
  769. //
  770. // Get the list of GCs in the forest and their sites.
  771. // Avoid this operation if we are not currently a GC in
  772. // which case update the GC covered site info based
  773. // on the registry setting below.
  774. //
  775. // ??: For multihosting, we will indicate whether we
  776. // are a GC in the forest DOMAIN_INFO struct instead of
  777. // the global as we currently use.
  778. //
  779. if ( (NlGetDomainFlags(DomainInfo) & DS_GC_FLAG) == 0 ) {
  780. NlPrint(( NL_SITE_MORE,
  781. "GC: This DC isn't a GC. So it doesn't auto cover any sites as a GC.\n" ));
  782. } else {
  783. NetStatus = DsCrackNamesW(
  784. DsHandle,
  785. 0,
  786. DS_LIST_GLOBAL_CATALOG_SERVERS,
  787. DS_LIST_GLOBAL_CATALOG_SERVERS,
  788. 1,
  789. &DummyName,
  790. &GcInfo );
  791. if ( NetStatus != NO_ERROR ) {
  792. NlPrint(( NL_CRITICAL,
  793. "NlSitesUpdateSiteCoverage: CrackNames failed: %ld\n",
  794. NetStatus ));
  795. } else {
  796. ULONG GcIndex;
  797. //
  798. // Determine which sites are already covered by GCs.
  799. //
  800. for ( GcIndex=0; GcIndex < GcInfo->cItems; GcIndex++ ) {
  801. LPWSTR GcSiteName = GcInfo->rItems[GcIndex].pName;
  802. LPWSTR GcDnsHostName = GcInfo->rItems[GcIndex].pDomain;
  803. NlPrint(( NL_SITE,
  804. "GC list: %ws %ws\n",
  805. GcSiteName,
  806. GcDnsHostName ));
  807. if ( GcInfo->rItems[GcIndex].status != DS_NAME_NO_ERROR ) {
  808. NlPrint(( NL_CRITICAL,
  809. "NlSitesUpdateSiteCoverage: CrackNameStatus bad: %ws %ws: %ld\n",
  810. GcSiteName,
  811. GcDnsHostName,
  812. GcInfo->rItems[GcIndex].status ));
  813. continue;
  814. }
  815. //
  816. // Count the number of GCs in each site.
  817. //
  818. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  819. if ( GcSiteName != NULL &&
  820. _wcsicmp( GcSiteName,
  821. SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
  822. DcsInSite[SiteIndex1] ++;
  823. AtleastOneDc = TRUE;
  824. break;
  825. }
  826. }
  827. //
  828. // If this DC isn't in any known site,
  829. // simply ignore it.
  830. //
  831. if ( SiteIndex1 >= SiteCount ) {
  832. NlPrint(( NL_CRITICAL,
  833. "GC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
  834. GcSiteName,
  835. GcDnsHostName ));
  836. }
  837. }
  838. }
  839. }
  840. //
  841. // Handle building the DC coverage list
  842. //
  843. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  844. ULONG DcIndex;
  845. //
  846. // Get the list of DCs in the domain and their sites
  847. //
  848. NetStatus = (*NlGlobalpDsGetDomainControllerInfoW)(
  849. DsHandle,
  850. DnsDomainName,
  851. 1,
  852. &DcInfoCount,
  853. &DcInfo );
  854. if ( NetStatus != NO_ERROR ) {
  855. NlPrint(( NL_CRITICAL,
  856. "NlSitesUpdateSiteCoverage: Cannot DsGetDomainControllerInfoW %ld.\n",
  857. NetStatus ));
  858. DcInfoCount = 0;
  859. }
  860. //
  861. // Determine which sites are already covered by DCs.
  862. //
  863. for ( DcIndex=0; DcIndex<DcInfoCount; DcIndex++ ) {
  864. NlPrint(( NL_SITE,
  865. "DC list: %ws %ws\n",
  866. DcInfo[DcIndex].SiteName,
  867. DcInfo[DcIndex].DnsHostName ));
  868. //
  869. // Count the number of DCs in each site.
  870. //
  871. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  872. if ( DcInfo[DcIndex].SiteName != NULL &&
  873. _wcsicmp( DcInfo[DcIndex].SiteName,
  874. SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
  875. DcsInSite[SiteIndex1] ++;
  876. AtleastOneDc = TRUE;
  877. break;
  878. }
  879. }
  880. //
  881. // If this DC isn't in any known site,
  882. // simply ignore it.
  883. //
  884. if ( SiteIndex1 >= SiteCount ) {
  885. NlPrint(( NL_CRITICAL,
  886. "DC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
  887. DcInfo[DcIndex].SiteName,
  888. DcInfo[DcIndex].DnsHostName ));
  889. }
  890. }
  891. //
  892. // Handle building the NDNC coverage list
  893. //
  894. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  895. SERVERSITEPAIR *ServerSitePairEntry;
  896. NetStatus = NlDsGetServersAndSitesForNetLogon( DnsDomainName,
  897. &ServerSitePairs );
  898. //
  899. // Determine which sites are already covered by LDAP servers
  900. //
  901. if ( NetStatus == NO_ERROR ) {
  902. ServerSitePairEntry = ServerSitePairs;
  903. while ( ServerSitePairEntry != NULL &&
  904. ServerSitePairEntry->wszDnsServer != NULL ) {
  905. NlPrint(( NL_SITE,
  906. "NDNC list: %ws %ws\n",
  907. ServerSitePairEntry->wszSite,
  908. ServerSitePairEntry->wszDnsServer ));
  909. //
  910. // Count the number of LDAP servers in each site.
  911. //
  912. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  913. if ( ServerSitePairEntry->wszSite != NULL &&
  914. _wcsicmp( ServerSitePairEntry->wszSite,
  915. SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
  916. DcsInSite[SiteIndex1] ++;
  917. AtleastOneDc = TRUE;
  918. break;
  919. }
  920. }
  921. //
  922. // If this LDAP server isn't in any known site,
  923. // simply ignore it.
  924. //
  925. if ( SiteIndex1 >= SiteCount ) {
  926. NlPrint(( NL_CRITICAL,
  927. "NDNC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
  928. ServerSitePairEntry->wszSite,
  929. ServerSitePairEntry->wszDnsServer ));
  930. }
  931. ServerSitePairEntry ++;
  932. }
  933. }
  934. }
  935. }
  936. }
  937. //
  938. // Now that all of the information has been collected.
  939. // Update the in memory information with the CritSect locked.
  940. //
  941. EnterCriticalSection( &NlGlobalSiteCritSect );
  942. //
  943. // If we were able to build the DcsInSite array,
  944. // Compute the auto site coverage.
  945. //
  946. if ( AtleastOneDc ) {
  947. //
  948. // For each site that has no DCs ...
  949. //
  950. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  951. DWORD BestSite;
  952. if ( DcsInSite[SiteIndex1] != 0 ) {
  953. continue;
  954. }
  955. NlPrint(( NL_SITE_MORE,
  956. "%s: %ws: Site has no %ss\n",
  957. GcOrDcOrNdnc,
  958. SiteConnect->ppSiteDNs[SiteIndex1],
  959. GcOrDcOrNdnc ));
  960. //
  961. // Pick the site that should cover that site
  962. //
  963. BestSite = 0xFFFFFFFF;
  964. for (SiteIndex2 = 0; SiteIndex2 < SiteCount; SiteIndex2++) {
  965. PISM_LINK Link2 = &(SiteConnect->pLinkValues[SiteIndex1 * SiteCount + SiteIndex2]);
  966. //
  967. // A site cannot auto cover itself.
  968. //
  969. if ( SiteIndex1 == SiteIndex2 ) {
  970. #ifdef notdef
  971. NlPrint(( NL_SITE_MORE,
  972. "%s: %ws: Site ignoring itself.\n",
  973. GcOrDcOrNdnc,
  974. SiteConnect->ppSiteDNs[SiteIndex1] ));
  975. #endif // notdef
  976. //
  977. // A site with an infinite cost cannot be reached.
  978. // so don't consider it.
  979. //
  980. } else if ( Link2->ulCost == 0xFFFFFFFF ) {
  981. NlPrint(( NL_SITE_MORE,
  982. "%s: %ws: Site '%ws' has infinite cost.\n",
  983. GcOrDcOrNdnc,
  984. SiteConnect->ppSiteDNs[SiteIndex1],
  985. SiteConnect->ppSiteDNs[SiteIndex2] ));
  986. //
  987. // A site with no DCs cannot cover any site
  988. // so don't consider it.
  989. //
  990. } else if ( DcsInSite[SiteIndex2] == 0 ) {
  991. NlPrint(( NL_SITE_MORE,
  992. "%s: %ws: Site '%ws' has no Dcs.\n",
  993. GcOrDcOrNdnc,
  994. SiteConnect->ppSiteDNs[SiteIndex1],
  995. SiteConnect->ppSiteDNs[SiteIndex2] ));
  996. //
  997. // If no best site has yet been picked,
  998. // use this one.
  999. //
  1000. } else if ( BestSite == 0xFFFFFFFF ) {
  1001. NlPrint(( NL_SITE_MORE,
  1002. "%s: %ws: Site '%ws' is the first valid site.\n",
  1003. GcOrDcOrNdnc,
  1004. SiteConnect->ppSiteDNs[SiteIndex1],
  1005. SiteConnect->ppSiteDNs[SiteIndex2] ));
  1006. BestSite = SiteIndex2;
  1007. } else {
  1008. PISM_LINK LinkBest = &(SiteConnect->pLinkValues[SiteIndex1 * SiteCount + BestSite]);
  1009. //
  1010. // If the SiteLink cost is less than the current best,
  1011. // use it.
  1012. //
  1013. if ( Link2->ulCost < LinkBest->ulCost ) {
  1014. NlPrint(( NL_SITE_MORE,
  1015. "%s: %ws: '%ws' has cheaper link costs than '%ws'\n",
  1016. GcOrDcOrNdnc,
  1017. SiteConnect->ppSiteDNs[SiteIndex1],
  1018. SiteConnect->ppSiteDNs[SiteIndex2],
  1019. SiteConnect->ppSiteDNs[BestSite] ));
  1020. BestSite = SiteIndex2;
  1021. //
  1022. // If the SiteLink cose is equal ...
  1023. //
  1024. } else if ( Link2->ulCost == LinkBest->ulCost ) {
  1025. //
  1026. // ... then pick the site with the greater number of DCs
  1027. //
  1028. if ( DcsInSite[SiteIndex2] > DcsInSite[BestSite] ) {
  1029. NlPrint(( NL_SITE_MORE,
  1030. "%s: %ws: '%ws' has more DCs than '%ws'\n",
  1031. GcOrDcOrNdnc,
  1032. SiteConnect->ppSiteDNs[SiteIndex1],
  1033. SiteConnect->ppSiteDNs[SiteIndex2],
  1034. SiteConnect->ppSiteDNs[BestSite] ));
  1035. BestSite = SiteIndex2;
  1036. //
  1037. // If the number of DCs is equal ...
  1038. //
  1039. } else if ( DcsInSite[SiteIndex2] == DcsInSite[BestSite] ) {
  1040. //
  1041. // Break the tie by using the site with the alphabetically
  1042. // least name.
  1043. //
  1044. if ( _wcsicmp( SiteConnect->ppSiteDNs[SiteIndex2],
  1045. SiteConnect->ppSiteDNs[BestSite] ) < 0 ) {
  1046. NlPrint(( NL_SITE_MORE,
  1047. "%s: %ws: '%ws' is alphabetically before '%ws'\n",
  1048. GcOrDcOrNdnc,
  1049. SiteConnect->ppSiteDNs[SiteIndex1],
  1050. SiteConnect->ppSiteDNs[SiteIndex2],
  1051. SiteConnect->ppSiteDNs[BestSite] ));
  1052. BestSite = SiteIndex2;
  1053. }
  1054. }
  1055. }
  1056. }
  1057. }
  1058. //
  1059. // If the best site is the site this DC is in,
  1060. // then mark this site as covered.
  1061. //
  1062. if ( BestSite == ThisSiteIndex ) {
  1063. NlPrint(( NL_SITE,
  1064. "%s: %ws: Site is auto covered by our site.\n",
  1065. GcOrDcOrNdnc,
  1066. SiteConnect->ppSiteDNs[SiteIndex1] ));
  1067. NlSitesAddCloseSite( SiteConnect->ppSiteDNs[SiteIndex1],
  1068. CoveredSites,
  1069. &CoveredSitesCount,
  1070. TRUE ); // auto covered
  1071. } else {
  1072. //
  1073. // Note if no site was found
  1074. //
  1075. if ( BestSite == 0xFFFFFFFF ) {
  1076. NlPrint(( NL_SITE,
  1077. "%s: %ws: Site is not auto covered by any site.\n",
  1078. GcOrDcOrNdnc,
  1079. SiteConnect->ppSiteDNs[SiteIndex1] ));
  1080. } else {
  1081. NlPrint(( NL_SITE,
  1082. "%s: %ws: Site is auto covered by site '%ws'.\n",
  1083. GcOrDcOrNdnc,
  1084. SiteConnect->ppSiteDNs[SiteIndex1],
  1085. SiteConnect->ppSiteDNs[BestSite] ));
  1086. }
  1087. }
  1088. }
  1089. }
  1090. //
  1091. // Merge in the list of covered sites from the registry
  1092. //
  1093. if ( SiteCoverageParameter != NULL ) {
  1094. LPTSTR_ARRAY TStrArray;
  1095. LPWSTR BackSlash = NULL;
  1096. BOOLEAN SkipThisEntry;
  1097. TStrArray = SiteCoverageParameter;
  1098. while (!NetpIsTStrArrayEmpty(TStrArray)) {
  1099. SkipThisEntry = FALSE;
  1100. BackSlash = wcsstr(TStrArray, L"\\");
  1101. //
  1102. // If backslash is present, then the covered site is specified
  1103. // for a given domain/forest/NDNC. The domain/forest/NDNC name
  1104. // precedes the backslash while the site name follows after the
  1105. // backslash. If backslash is absent, the site is covered for
  1106. // all domains/forests/NDNCs.
  1107. //
  1108. if ( BackSlash != NULL ) {
  1109. *BackSlash = UNICODE_NULL;
  1110. //
  1111. // Check the appropriate name depending on whether this is
  1112. // a forest or a domain or an NDNC
  1113. //
  1114. if ( DomFlags & DOM_FOREST ) {
  1115. if ( !NlEqualDnsName(TStrArray, CapturedDnsForestName) ) {
  1116. SkipThisEntry = TRUE;
  1117. }
  1118. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1119. if ( !NlEqualDnsName(TStrArray, DnsDomainName) &&
  1120. NlNameCompare(TStrArray, DomainInfo->DomUnicodeDomainName, NAMETYPE_DOMAIN) != 0 ) {
  1121. SkipThisEntry = TRUE;
  1122. }
  1123. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1124. if ( !NlEqualDnsName(TStrArray, DnsDomainName) ) {
  1125. SkipThisEntry = TRUE;
  1126. }
  1127. }
  1128. }
  1129. //
  1130. // Add this site to the current list of covered sites
  1131. //
  1132. if ( !SkipThisEntry ) {
  1133. if ( BackSlash != NULL ) {
  1134. if ( *(BackSlash+1) != UNICODE_NULL ) {
  1135. NlPrint(( NL_SITE,
  1136. "%s: %ws: Site is covered by our site (regkey).\n",
  1137. GcOrDcOrNdnc,
  1138. BackSlash+1 ));
  1139. NlSitesAddCloseSite( BackSlash+1, CoveredSites, &CoveredSitesCount, FALSE );
  1140. }
  1141. } else {
  1142. NlPrint(( NL_SITE,
  1143. "%s: %ws: Site is covered by our site (regkey).\n",
  1144. GcOrDcOrNdnc,
  1145. TStrArray ));
  1146. NlSitesAddCloseSite( TStrArray, CoveredSites, &CoveredSitesCount, FALSE );
  1147. }
  1148. }
  1149. if ( BackSlash != NULL ) {
  1150. *BackSlash = L'\\';
  1151. }
  1152. TStrArray = NetpNextTStrArrayEntry(TStrArray);
  1153. }
  1154. }
  1155. //
  1156. // The site this DC is in is covered by definition
  1157. //
  1158. NlSitesAddCloseSite( ThisSiteName, CoveredSites, &CoveredSitesCount, FALSE );
  1159. //
  1160. // Determine if the site coverages changes.
  1161. // Log info if auto site coverage changes.
  1162. //
  1163. if ( (DomFlags & DOM_FOREST) != 0 ) {
  1164. OldCoveredSites = DomainInfo->GcCoveredSites;
  1165. OldCoveredSitesCount = DomainInfo->GcCoveredSitesCount;
  1166. } else if ( (DomFlags & DOM_REAL_DOMAIN) != 0 ||
  1167. (DomFlags & DOM_NON_DOMAIN_NC) != 0 ) {
  1168. OldCoveredSites = DomainInfo->CoveredSites;
  1169. OldCoveredSitesCount = DomainInfo->CoveredSitesCount;
  1170. }
  1171. //
  1172. // Determine if new sites get covered and log events for newly auto
  1173. // covered sites
  1174. //
  1175. for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
  1176. DWORD EventId = 0;
  1177. LPWSTR MsgStrings[3];
  1178. MsgStrings[0] = ThisSiteName;
  1179. MsgStrings[1] = CoveredSites[CoveredSitesIndex].CoveredSite->SiteName;
  1180. //
  1181. // Determine if we didn't cover this site in the past
  1182. //
  1183. for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
  1184. if ( RtlEqualUnicodeString( &CoveredSites[CoveredSitesIndex].CoveredSite->SiteNameString,
  1185. &OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteNameString,
  1186. TRUE ) ) {
  1187. break;
  1188. }
  1189. }
  1190. //
  1191. // Indicate if this is a new covered site
  1192. //
  1193. if ( OldCoveredSitesIndex == OldCoveredSitesCount ) {
  1194. LocalSiteCoverageChanged = TRUE;
  1195. //
  1196. // If the new site is auto covered, log the event
  1197. //
  1198. if ( CoveredSites[CoveredSitesIndex].CoveredAuto ) {
  1199. //
  1200. // Log for GC that this is a newly auto covered site
  1201. //
  1202. if ( DomFlags & DOM_FOREST ) {
  1203. EventId = NELOG_NetlogonGcSiteCovered;
  1204. MsgStrings[2] = CapturedDnsForestName;
  1205. //
  1206. // Log for DC that this is a newly auto covered site
  1207. //
  1208. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1209. EventId = NELOG_NetlogonDcSiteCovered;
  1210. MsgStrings[2] = DomainInfo->DomUnicodeDomainName;
  1211. //
  1212. // Log for NDNC that this is a newly auto covered site
  1213. //
  1214. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1215. EventId = NELOG_NetlogonNdncSiteCovered;
  1216. MsgStrings[2] = DnsDomainName;
  1217. }
  1218. }
  1219. //
  1220. // If we had this site not auto covered in the past and it is
  1221. // auto covered now, log the event
  1222. //
  1223. } else if ( CoveredSites[CoveredSitesIndex].CoveredAuto &&
  1224. !OldCoveredSites[OldCoveredSitesIndex].CoveredAuto ) {
  1225. //
  1226. // Log for GC that this old manually covered site is now auto covered
  1227. //
  1228. if ( DomFlags & DOM_FOREST ) {
  1229. EventId = NELOG_NetlogonGcOldSiteCovered;
  1230. MsgStrings[2] = CapturedDnsForestName;
  1231. //
  1232. // Log for DC that this old manually covered site is now auto covered
  1233. //
  1234. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1235. EventId = NELOG_NetlogonDcOldSiteCovered;
  1236. MsgStrings[2] = DomainInfo->DomUnicodeDomainName;
  1237. //
  1238. // Log for NDNC that this old manually covered site is now auto covered
  1239. //
  1240. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1241. EventId = NELOG_NetlogonNdncOldSiteCovered;
  1242. MsgStrings[2] = DnsDomainName;
  1243. }
  1244. }
  1245. //
  1246. // Log the event if needed
  1247. //
  1248. if ( EventId != 0 ) {
  1249. NlpWriteEventlog ( EventId,
  1250. EVENTLOG_INFORMATION_TYPE,
  1251. NULL,
  1252. 0,
  1253. MsgStrings,
  1254. 3 | NETP_ALLOW_DUPLICATE_EVENTS );
  1255. }
  1256. }
  1257. //
  1258. // Determine if old sites are nolonger covered and log events for old auto
  1259. // covered sites which are nolonger auto covered
  1260. //
  1261. for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
  1262. DWORD EventId = 0;
  1263. LPWSTR MsgStrings[2];
  1264. MsgStrings[0] = OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteName;
  1265. //
  1266. // Determine if we no longer cover this site
  1267. //
  1268. for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
  1269. if ( RtlEqualUnicodeString( &OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteNameString,
  1270. &CoveredSites[CoveredSitesIndex].CoveredSite->SiteNameString,
  1271. TRUE ) ) {
  1272. break;
  1273. }
  1274. }
  1275. //
  1276. // Indicate if this site is no longer covered
  1277. //
  1278. if ( CoveredSitesIndex == CoveredSitesCount ) {
  1279. LocalSiteCoverageChanged = TRUE;
  1280. //
  1281. // If the old site was auto covered, log the event
  1282. //
  1283. if ( OldCoveredSites[OldCoveredSitesIndex].CoveredAuto ) {
  1284. //
  1285. // Log for GC that this old auto covered site is no longer auto covered
  1286. //
  1287. if ( DomFlags & DOM_FOREST ) {
  1288. EventId = NELOG_NetlogonGcSiteNotCovered;
  1289. MsgStrings[1] = CapturedDnsForestName;
  1290. //
  1291. // Log for DC that this old auto covered site is no longer auto covered
  1292. //
  1293. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1294. EventId = NELOG_NetlogonDcSiteNotCovered;
  1295. MsgStrings[1] = DomainInfo->DomUnicodeDomainName;
  1296. //
  1297. // Log for NDNC that this old auto covered site is no longer auto covered
  1298. //
  1299. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1300. EventId = NELOG_NetlogonNdncSiteNotCovered;
  1301. MsgStrings[1] = DnsDomainName;
  1302. }
  1303. }
  1304. //
  1305. // If we had this site auto covered in the past and it is
  1306. // no longer auto covered, log the event
  1307. //
  1308. } else if ( OldCoveredSites[OldCoveredSitesIndex].CoveredAuto &&
  1309. !CoveredSites[CoveredSitesIndex].CoveredAuto ) {
  1310. //
  1311. // Log for GC that this old auto covered site is now manually covered
  1312. //
  1313. if ( DomFlags & DOM_FOREST ) {
  1314. EventId = NELOG_NetlogonGcSiteNotCoveredAuto;
  1315. MsgStrings[1] = CapturedDnsForestName;
  1316. //
  1317. // Log for DC that this old auto covered site is now manually covered
  1318. //
  1319. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1320. EventId = NELOG_NetlogonDcSiteNotCoveredAuto;
  1321. MsgStrings[1] = DomainInfo->DomUnicodeDomainName;
  1322. //
  1323. // Log for NDNC that this old auto covered site is now manually covered
  1324. //
  1325. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1326. EventId = NELOG_NetlogonNdncSiteNotCoveredAuto;
  1327. MsgStrings[1] = DnsDomainName;
  1328. }
  1329. }
  1330. //
  1331. // Log the event if needed
  1332. //
  1333. if ( EventId != 0 ) {
  1334. NlPrint(( NL_SITE,
  1335. "%s: %ws: Site is no longer auto covered by our site.\n",
  1336. GcOrDcOrNdnc,
  1337. OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteName ));
  1338. NlpWriteEventlog ( EventId,
  1339. EVENTLOG_INFORMATION_TYPE,
  1340. NULL,
  1341. 0,
  1342. MsgStrings,
  1343. 2 | NETP_ALLOW_DUPLICATE_EVENTS );
  1344. }
  1345. }
  1346. //
  1347. // Dereference the old entries.
  1348. //
  1349. for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
  1350. NlDerefSiteEntry( OldCoveredSites[OldCoveredSitesIndex].CoveredSite );
  1351. }
  1352. //
  1353. // Finally update the site coverage list.
  1354. // Free the old list and allocate the new one.
  1355. //
  1356. if ( DomFlags & DOM_FOREST ) {
  1357. if ( DomainInfo->GcCoveredSites != NULL ) {
  1358. LocalFree( DomainInfo->GcCoveredSites );
  1359. DomainInfo->GcCoveredSites = NULL;
  1360. DomainInfo->GcCoveredSitesCount = 0;
  1361. }
  1362. if ( CoveredSitesCount != 0 ) {
  1363. DomainInfo->GcCoveredSites = LocalAlloc( 0, CoveredSitesCount * sizeof(NL_COVERED_SITE ) );
  1364. if ( DomainInfo->GcCoveredSites != NULL ) {
  1365. RtlCopyMemory( DomainInfo->GcCoveredSites,
  1366. CoveredSites,
  1367. CoveredSitesCount * sizeof(NL_COVERED_SITE) );
  1368. DomainInfo->GcCoveredSitesCount = CoveredSitesCount;
  1369. } else {
  1370. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1371. }
  1372. }
  1373. //
  1374. // Reference the newly added entries, if any
  1375. //
  1376. for ( CoveredSitesIndex = 0; CoveredSitesIndex < DomainInfo->GcCoveredSitesCount; CoveredSitesIndex++ ) {
  1377. NlRefSiteEntry( (DomainInfo->GcCoveredSites)[CoveredSitesIndex].CoveredSite );
  1378. }
  1379. } else if ( (DomFlags & DOM_REAL_DOMAIN) != 0 ||
  1380. (DomFlags & DOM_NON_DOMAIN_NC) != 0 ) {
  1381. if ( DomainInfo->CoveredSites != NULL ) {
  1382. LocalFree( DomainInfo->CoveredSites );
  1383. DomainInfo->CoveredSites = NULL;
  1384. DomainInfo->CoveredSitesCount = 0;
  1385. }
  1386. if ( CoveredSitesCount != 0 ) {
  1387. DomainInfo->CoveredSites = LocalAlloc( 0, CoveredSitesCount * sizeof(NL_COVERED_SITE) );
  1388. if ( DomainInfo->CoveredSites != NULL ) {
  1389. RtlCopyMemory( DomainInfo->CoveredSites,
  1390. CoveredSites,
  1391. CoveredSitesCount * sizeof(NL_COVERED_SITE) );
  1392. DomainInfo->CoveredSitesCount = CoveredSitesCount;
  1393. } else {
  1394. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1395. }
  1396. }
  1397. //
  1398. // Reference the newly added entries, if any
  1399. //
  1400. for ( CoveredSitesIndex = 0; CoveredSitesIndex < DomainInfo->CoveredSitesCount; CoveredSitesIndex++ ) {
  1401. NlRefSiteEntry( (DomainInfo->CoveredSites)[CoveredSitesIndex].CoveredSite );
  1402. }
  1403. }
  1404. //
  1405. // Now that the datebase is consistent, drop the lock for the next pass
  1406. //
  1407. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1408. Cleanup:
  1409. if ( DcsInSite != NULL ) {
  1410. LocalFree( DcsInSite );
  1411. }
  1412. if ( DcInfo != NULL ) {
  1413. (*NlGlobalpDsFreeDomainControllerInfoW)( 1, DcInfoCount, DcInfo );
  1414. }
  1415. if ( GcInfo != NULL ) {
  1416. DsFreeNameResultW( GcInfo );
  1417. }
  1418. if ( ServerSitePairs != NULL ) {
  1419. NlDsFreeServersAndSitesForNetLogon( ServerSitePairs );
  1420. }
  1421. //
  1422. // Free the temprory list of covered sites.
  1423. // Deref each temp entry.
  1424. //
  1425. if ( CoveredSites != NULL ) {
  1426. for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
  1427. NlDerefSiteEntry( CoveredSites[CoveredSitesIndex].CoveredSite );
  1428. }
  1429. LocalFree( CoveredSites );
  1430. }
  1431. //
  1432. // Update the site coverage change info only if it indeed changed
  1433. //
  1434. if ( NetStatus == NO_ERROR && SiteCoverageChanged != NULL && LocalSiteCoverageChanged ) {
  1435. *SiteCoverageChanged = TRUE;
  1436. }
  1437. return NO_ERROR;
  1438. }
  1439. PNL_SITE_ENTRY
  1440. NlFindSiteEntry(
  1441. IN LPWSTR SiteName
  1442. )
  1443. /*++
  1444. Routine Description:
  1445. This routine finds a site entry for a particular site name. If one
  1446. does not exist, one is created.
  1447. Arguments:
  1448. SiteName - Name of the site.
  1449. Return Value:
  1450. Pointer to the Site entry for the site.
  1451. NULL: Memory could not be allocated.
  1452. --*/
  1453. {
  1454. PLIST_ENTRY ListEntry;
  1455. ULONG SiteNameSize;
  1456. PNL_SITE_ENTRY SiteEntry;
  1457. UNICODE_STRING SiteNameString;
  1458. //
  1459. // If the site entry already exists,
  1460. // return a pointer to it.
  1461. //
  1462. RtlInitUnicodeString( &SiteNameString, SiteName );
  1463. EnterCriticalSection( &NlGlobalSiteCritSect );
  1464. for ( ListEntry = NlGlobalSiteList.Flink ;
  1465. ListEntry != &NlGlobalSiteList;
  1466. ListEntry = ListEntry->Flink) {
  1467. SiteEntry =
  1468. CONTAINING_RECORD( ListEntry, NL_SITE_ENTRY, Next );
  1469. if ( RtlEqualUnicodeString( &SiteEntry->SiteNameString,
  1470. &SiteNameString,
  1471. TRUE ) ) {
  1472. NlRefSiteEntry( SiteEntry );
  1473. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1474. return SiteEntry;
  1475. }
  1476. }
  1477. //
  1478. // If not,
  1479. // allocate one.
  1480. //
  1481. SiteNameSize = SiteNameString.Length + sizeof(WCHAR);
  1482. SiteEntry = LocalAlloc( 0, sizeof(NL_SITE_ENTRY) + SiteNameSize );
  1483. if ( SiteEntry == NULL ) {
  1484. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1485. return NULL;
  1486. }
  1487. //
  1488. // Fill it in.
  1489. //
  1490. // Being in global list is not a reference.
  1491. SiteEntry->ReferenceCount = 1;
  1492. SiteEntry->SiteNameString.Length = SiteNameString.Length;
  1493. SiteEntry->SiteNameString.MaximumLength = SiteNameString.Length + sizeof(WCHAR);
  1494. SiteEntry->SiteNameString.Buffer = SiteEntry->SiteName;
  1495. RtlCopyMemory( &SiteEntry->SiteName, SiteName, SiteNameSize );
  1496. InsertHeadList( &NlGlobalSiteList, &SiteEntry->Next );
  1497. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1498. return SiteEntry;
  1499. }
  1500. VOID
  1501. NlSitesRefSubnet(
  1502. IN PNL_SUBNET Subnet
  1503. )
  1504. /*++
  1505. Routine Description:
  1506. Reference a subnet
  1507. NlGlobalSiteCritSect must be locked.
  1508. Arguments:
  1509. Subnet - Entry to be Referenced.
  1510. Return Value:
  1511. None.
  1512. --*/
  1513. {
  1514. Subnet->ReferenceCount++;
  1515. }
  1516. PNL_SUBNET
  1517. NlFindSubnetEntry(
  1518. IN LPWSTR SiteName,
  1519. IN ULONG SubnetAddress,
  1520. IN ULONG SubnetMask,
  1521. IN BYTE SubnetBitCount
  1522. )
  1523. /*++
  1524. Routine Description:
  1525. This routine finds a subnet entry for a particular subnet name. If one
  1526. does not exist, one is created.
  1527. Arguments:
  1528. SiteName - Name of the site the subnet covers.
  1529. SubnetAddress - Subnet Address for the subnet to find.
  1530. SubnetMask - Subnet mask for the subnet to find.
  1531. SubnetBitCount - Subnet bit count for the subnet to find.
  1532. Return Value:
  1533. Pointer to the Subnet entry for the site.
  1534. Entry should be dereferenced using NlSitesDerefSubnet
  1535. NULL: Memory could not be allocated.
  1536. --*/
  1537. {
  1538. PLIST_ENTRY ListEntry;
  1539. ULONG SiteNameSize;
  1540. PNL_SUBNET Subnet;
  1541. //
  1542. // If the subnet entry already exists,
  1543. // return a pointer to it.
  1544. //
  1545. EnterCriticalSection( &NlGlobalSiteCritSect );
  1546. for ( ListEntry = NlGlobalSubnetList.Flink ;
  1547. ListEntry != &NlGlobalSubnetList;
  1548. ListEntry = ListEntry->Flink) {
  1549. Subnet =
  1550. CONTAINING_RECORD( ListEntry, NL_SUBNET, Next );
  1551. if ( Subnet->SubnetAddress == SubnetAddress &&
  1552. Subnet->SubnetBitCount == SubnetBitCount &&
  1553. Subnet->SubnetMask == SubnetMask &&
  1554. _wcsicmp( Subnet->SiteEntry->SiteName, SiteName ) == 0 ) {
  1555. #if NETLOGONDBG
  1556. {
  1557. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1558. NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
  1559. NlPrint(( NL_SITE, "%s/%ld: Re-adding Subnet for site '%ws'\n", IpAddress, Subnet->SubnetBitCount, SiteName ));
  1560. }
  1561. #endif // NETLOGONDBG
  1562. NlSitesRefSubnet( Subnet );
  1563. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1564. return Subnet;
  1565. }
  1566. }
  1567. //
  1568. // If not,
  1569. // allocate one.
  1570. //
  1571. Subnet = LocalAlloc( 0, sizeof(NL_SUBNET) );
  1572. if ( Subnet == NULL ) {
  1573. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1574. return NULL;
  1575. }
  1576. //
  1577. // Fill it in.
  1578. //
  1579. // Being in global list is not a reference.
  1580. Subnet->ReferenceCount = 1;
  1581. Subnet->SubnetAddress = SubnetAddress;
  1582. Subnet->SubnetMask = SubnetMask;
  1583. Subnet->SubnetBitCount = SubnetBitCount;
  1584. Subnet->SiteEntry = NlFindSiteEntry( SiteName );
  1585. if ( Subnet->SiteEntry == NULL ) {
  1586. LocalFree( Subnet );
  1587. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1588. return NULL;
  1589. }
  1590. #if NETLOGONDBG
  1591. {
  1592. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1593. NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
  1594. NlPrint(( NL_SITE, "%s/%ld: Adding Subnet for site '%ws'\n",
  1595. IpAddress,
  1596. Subnet->SubnetBitCount,
  1597. SiteName ));
  1598. }
  1599. #endif // NETLOGONDBG
  1600. InsertHeadList( &NlGlobalSubnetList, &Subnet->Next );
  1601. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1602. return Subnet;
  1603. }
  1604. VOID
  1605. NlSitesDerefSubnet(
  1606. IN PNL_SUBNET Subnet
  1607. )
  1608. /*++
  1609. Routine Description:
  1610. Dereference a subnet
  1611. If the reference count goes to zero,
  1612. the subnet entry will be deleted.
  1613. Arguments:
  1614. Subnet - Entry to be dereferenced.
  1615. Return Value:
  1616. None.
  1617. --*/
  1618. {
  1619. EnterCriticalSection( &NlGlobalSiteCritSect );
  1620. if ( (--(Subnet->ReferenceCount)) == 0 ) {
  1621. #if NETLOGONDBG
  1622. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1623. NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
  1624. NlPrint(( NL_SITE, "%s/%ld: Subnet deleted\n", IpAddress, Subnet->SubnetBitCount ));
  1625. #endif // NETLOGONDBG
  1626. //
  1627. // If there is a site associated with this subnet,
  1628. // dereference it.
  1629. //
  1630. if ( Subnet->SiteEntry != NULL ) {
  1631. NlDerefSiteEntry( Subnet->SiteEntry );
  1632. }
  1633. //
  1634. // Remove the subnet from the global list
  1635. //
  1636. RemoveEntryList( &Subnet->Next );
  1637. //
  1638. // Free the Subnet entry itself.
  1639. //
  1640. LocalFree( Subnet );
  1641. }
  1642. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1643. }
  1644. VOID
  1645. NlSiteDeleteSubnetTree(
  1646. IN PNL_SUBNET_TREE_ENTRY SubnetTreeEntry
  1647. )
  1648. /*++
  1649. Routine Description:
  1650. Delete everything pointed to by this SubnetTreeEntry
  1651. Enter with NlGlobalSiteCritSect locked.
  1652. Arguments:
  1653. SubnetTreeEntry - SubnetTreeEntry to de-initialize
  1654. Return Value:
  1655. TRUE: SubnetTreeEntry is now empty
  1656. FALSE: SubnetTreeEntry still has entries.
  1657. --*/
  1658. {
  1659. //
  1660. // If there are children,
  1661. // delete them.
  1662. //
  1663. if ( SubnetTreeEntry->Subtree != NULL ) {
  1664. ULONG i;
  1665. for ( i=0; i<256; i++ ) {
  1666. NlSiteDeleteSubnetTree( &SubnetTreeEntry->Subtree->Subtree[i] );
  1667. }
  1668. NlPrint(( NL_SITE_MORE, "Deleting subtree\n" ));
  1669. LocalFree( SubnetTreeEntry->Subtree );
  1670. SubnetTreeEntry->Subtree = NULL;
  1671. }
  1672. //
  1673. // If there is a subnet,
  1674. // dereference it.
  1675. //
  1676. if ( SubnetTreeEntry->Subnet != NULL ) {
  1677. // NlPrint(( NL_SITE_MORE, "Derefing subnet upon tree deletion\n" ));
  1678. NlSitesDerefSubnet( SubnetTreeEntry->Subnet );
  1679. SubnetTreeEntry->Subnet = NULL;
  1680. }
  1681. return;
  1682. }
  1683. VOID
  1684. NlSitesEndSubnetEnum(
  1685. VOID
  1686. )
  1687. /*++
  1688. Routine Description:
  1689. This routine is called at the end of a set of NlSitesAddSubnet calls.
  1690. The sequence is:
  1691. loop for each subnet
  1692. NlSitesAddSubnet
  1693. NlSitesEndSubnetEnum
  1694. NlSiteAddSubnet adds the entries to a temporary tree. This routine
  1695. swaps the temporary tree into the permanent location. This mechanism
  1696. does the following:
  1697. a) Allows the old subnet tree to be used while the new tree is being built.
  1698. b) Allows me to not permanently grab the SiteCritSect for the entire
  1699. enumeration of subnet/site objects from the DS.
  1700. c) Reuse the in-memory subnet/site structures in the old and new tree. This
  1701. avoids re-allocation of these structures (or worse temporarily doubling
  1702. of memory usage).
  1703. Arguments:
  1704. SiteName - Name of the site the subnet is in.
  1705. SubnetName - subnet to be added
  1706. Return Value:
  1707. NO_ERROR: success
  1708. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  1709. --*/
  1710. {
  1711. //
  1712. // Free all old entries in NlGlobalSubnetTree.
  1713. //
  1714. NlPrint(( NL_SITE_MORE, "NlSitesEndSubnetEnum: Entered\n" ));
  1715. EnterCriticalSection( &NlGlobalSiteCritSect );
  1716. NlSiteDeleteSubnetTree( &NlGlobalSubnetTree );
  1717. //
  1718. // Make the "new" subnet tree the real subnet tree.
  1719. //
  1720. NlGlobalSubnetTree = NlGlobalNewSubnetTree;
  1721. RtlZeroMemory( &NlGlobalNewSubnetTree, sizeof(NlGlobalNewSubnetTree) );
  1722. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1723. NlPrint(( NL_SITE_MORE, "NlSitesEndSubnetEnum: Exitted\n" ));
  1724. }
  1725. NET_API_STATUS
  1726. NlSitesAddSubnet(
  1727. IN LPWSTR SiteName,
  1728. IN LPWSTR SubnetName
  1729. )
  1730. /*++
  1731. Routine Description:
  1732. This routine adds a subnet to the tree of subnets.
  1733. Arguments:
  1734. SiteName - Name of the site the subnet is in.
  1735. SubnetName - subnet to be added
  1736. Return Value:
  1737. NO_ERROR: success
  1738. ERROR_INVALID_NAME: Subnet Name is not valid
  1739. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  1740. --*/
  1741. {
  1742. NET_API_STATUS NetStatus;
  1743. PNL_SUBNET Subnet = NULL;
  1744. PNL_SUBNET_TREE_ENTRY SubnetTreeEntry;
  1745. LPBYTE SubnetBytePointer;
  1746. ULONG i;
  1747. ULONG SubnetAddress;
  1748. ULONG SubnetMask;
  1749. BYTE SubnetBitCount;
  1750. //
  1751. // Parse the subnet name
  1752. //
  1753. EnterCriticalSection( &NlGlobalSiteCritSect );
  1754. NetStatus = NlParseSubnetString( SubnetName,
  1755. &SubnetAddress,
  1756. &SubnetMask,
  1757. &SubnetBitCount );
  1758. if ( NetStatus != NO_ERROR ) {
  1759. goto Cleanup;
  1760. }
  1761. //
  1762. // Find or allocate an entry for the subnet
  1763. //
  1764. Subnet = NlFindSubnetEntry( SiteName,
  1765. SubnetAddress,
  1766. SubnetMask,
  1767. SubnetBitCount );
  1768. if ( Subnet == NULL ) {
  1769. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1770. goto Cleanup;
  1771. }
  1772. //
  1773. // Loop for each byte in the subnet address
  1774. //
  1775. SubnetTreeEntry = &NlGlobalNewSubnetTree;
  1776. SubnetBytePointer = (LPBYTE) (&Subnet->SubnetAddress);
  1777. while ( SubnetBitCount != 0 ) {
  1778. NlPrint(( NL_SITE_MORE, "%ld: Doing byte\n", *SubnetBytePointer ));
  1779. //
  1780. // If there isn't a tree branch for the current node,
  1781. // create one.
  1782. //
  1783. if ( SubnetTreeEntry->Subtree == NULL ) {
  1784. NlPrint(( NL_SITE_MORE, "%ld: Creating subtree\n", *SubnetBytePointer ));
  1785. SubnetTreeEntry->Subtree = LocalAlloc( LMEM_ZEROINIT, sizeof(NL_SUBNET_TREE) );
  1786. if ( SubnetTreeEntry->Subtree == NULL ) {
  1787. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1788. goto Cleanup;
  1789. }
  1790. }
  1791. //
  1792. // If this is the last byte of the subnet address,
  1793. // link the subnet onto the tree here.
  1794. //
  1795. if ( SubnetBitCount <= 8 ) {
  1796. ULONG LoopCount;
  1797. //
  1798. // The caller indexes into this array with an IP address.
  1799. // Create a link to our subnet for each possible IP addresses
  1800. // that map onto this subnet.
  1801. //
  1802. // Between 1 and 128 IP addresses map onto this subnet address.
  1803. //
  1804. LoopCount = 1 << (8-SubnetBitCount);
  1805. for ( i=0; i<LoopCount; i++ ) {
  1806. PNL_SUBNET_TREE_ENTRY Subtree;
  1807. ULONG SubnetIndex;
  1808. //
  1809. // Compute which entry is to be updated.
  1810. //
  1811. SubnetIndex = (*SubnetBytePointer) + i;
  1812. NlPrint(( NL_SITE_MORE, "%ld: Doing sub-byte\n", SubnetIndex ));
  1813. NlAssert( SubnetIndex <= 255 );
  1814. Subtree = &SubnetTreeEntry->Subtree->Subtree[SubnetIndex];
  1815. //
  1816. // If there already is a subnet linked off the tree here,
  1817. // handle it.
  1818. //
  1819. if ( Subtree->Subnet != NULL ) {
  1820. NlPrint(( NL_SITE_MORE, "%ld: Subnet already exists %ld\n",
  1821. SubnetIndex,
  1822. Subtree->Subnet->SubnetBitCount ));
  1823. //
  1824. // If the entry is for a less specific subnet
  1825. // delete the current entry.
  1826. //
  1827. if ( Subtree->Subnet->SubnetBitCount < Subnet->SubnetBitCount ) {
  1828. NlPrint(( NL_SITE_MORE, "%ld: Deref previous subnet\n",
  1829. SubnetIndex ));
  1830. NlSitesDerefSubnet( Subtree->Subnet );
  1831. Subtree->Subnet = NULL;
  1832. //
  1833. // Otherwise,
  1834. // use the current entry since it is better than this one.
  1835. //
  1836. } else {
  1837. NlPrint(( NL_SITE_MORE, "%ld: Use previous subnet\n",
  1838. SubnetIndex ));
  1839. continue;
  1840. }
  1841. }
  1842. //
  1843. // Link the subnet into the tree.
  1844. // Increment the reference count.
  1845. //
  1846. NlSitesRefSubnet( Subnet );
  1847. Subtree->Subnet = Subnet;
  1848. }
  1849. break;
  1850. }
  1851. //
  1852. // Move on to the next byte of the subnet address
  1853. //
  1854. SubnetTreeEntry = &SubnetTreeEntry->Subtree->Subtree[*SubnetBytePointer];
  1855. SubnetBitCount -= 8;
  1856. SubnetBytePointer ++;
  1857. }
  1858. NetStatus = NO_ERROR;
  1859. //
  1860. // Free locally used resources.
  1861. //
  1862. Cleanup:
  1863. if ( Subnet != NULL ) {
  1864. NlSitesDerefSubnet( Subnet );
  1865. }
  1866. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1867. return NetStatus;
  1868. }
  1869. PNL_SITE_ENTRY
  1870. NlFindSiteEntryBySockAddrEx(
  1871. IN PSOCKADDR SockAddr,
  1872. OUT PNL_SUBNET *RetSubnet OPTIONAL
  1873. )
  1874. /*++
  1875. Routine Description:
  1876. This routine look up the specified socket address and translate it to a
  1877. site name.
  1878. Arguments:
  1879. SockAddr - Socket Address to lookup
  1880. RetSubnet - If specified, returns a pointer to the subnet object used to do
  1881. the mapping.
  1882. Might return NULL indicating a subnet object wasn't used.
  1883. Entry should be dereferenced using NlSitesDerefSubnet.
  1884. Return Value:
  1885. NULL: No site can be found for this SockAddr.
  1886. Non-NULL: Site corresponding to the SockAddr.
  1887. Entry should be derefenced using NlDerefSiteEntry
  1888. --*/
  1889. {
  1890. PNL_SITE_ENTRY SiteEntry = NULL;
  1891. PNL_SUBNET Subnet = NULL;
  1892. PNL_SUBNET_TREE_ENTRY SubnetTreeEntry;
  1893. ULONG ByteIndex;
  1894. ULONG IpAddress;
  1895. //
  1896. // Convert SockAddr to IP address.
  1897. //
  1898. if ( ARGUMENT_PRESENT(RetSubnet) ) {
  1899. *RetSubnet = NULL;
  1900. }
  1901. if ( SockAddr->sa_family != AF_INET ) {
  1902. return NULL;
  1903. }
  1904. IpAddress = ((PSOCKADDR_IN)SockAddr)->sin_addr.S_un.S_addr;
  1905. //
  1906. // If there are no subnet entries and only one site,
  1907. // then all clients belong to that site.
  1908. // Don't bother mapping.
  1909. //
  1910. EnterCriticalSection( &NlGlobalSiteCritSect );
  1911. if ( NlGlobalOnlyOneSite ) {
  1912. if ( NlGlobalSiteEntry == NULL ) {
  1913. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1914. return NULL;
  1915. }
  1916. SiteEntry = NlGlobalSiteEntry;
  1917. NlRefSiteEntry( SiteEntry );
  1918. //
  1919. // If the caller isn't interested in the subnet name,
  1920. // we are done
  1921. //
  1922. if ( RetSubnet == NULL ) {
  1923. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1924. return SiteEntry;
  1925. }
  1926. }
  1927. //
  1928. // Loop for each byte in the Ip address
  1929. //
  1930. SubnetTreeEntry = &NlGlobalSubnetTree;
  1931. for ( ByteIndex=0; ByteIndex<sizeof(IpAddress); ByteIndex++) {
  1932. ULONG SubnetIndex;
  1933. //
  1934. // If there is no subtree,
  1935. // we're done.
  1936. //
  1937. SubnetIndex = ((LPBYTE)(&IpAddress))[ByteIndex];
  1938. NlPrint(( NL_SITE_MORE, "%ld: Lookup: Doing byte\n", SubnetIndex ));
  1939. if ( SubnetTreeEntry->Subtree == NULL ) {
  1940. break;
  1941. }
  1942. //
  1943. // Compute which entry is being referenced
  1944. //
  1945. SubnetTreeEntry = &SubnetTreeEntry->Subtree->Subtree[SubnetIndex];
  1946. //
  1947. // If there already is a subnet linked off here,
  1948. // use it.
  1949. //
  1950. // (but continue walking down the tree trying to find a more explicit entry.)
  1951. //
  1952. if ( SubnetTreeEntry->Subnet != NULL ) {
  1953. NlPrint(( NL_SITE_MORE, "%ld: Lookup: saving subnet at this level\n", SubnetIndex ));
  1954. Subnet = SubnetTreeEntry->Subnet;
  1955. }
  1956. }
  1957. //
  1958. // If we found a subnet,
  1959. // return the site associated with the subnet.
  1960. if ( Subnet != NULL ) {
  1961. //
  1962. // If we already know the site name (because there is
  1963. // only one site), this subnet must map to it
  1964. //
  1965. if ( SiteEntry != NULL ) {
  1966. NlAssert( SiteEntry == Subnet->SiteEntry );
  1967. } else {
  1968. SiteEntry = Subnet->SiteEntry;
  1969. NlRefSiteEntry( SiteEntry );
  1970. }
  1971. if ( ARGUMENT_PRESENT(RetSubnet) ) {
  1972. NlSitesRefSubnet( Subnet );
  1973. *RetSubnet = Subnet;
  1974. }
  1975. }
  1976. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1977. return SiteEntry;
  1978. }
  1979. PNL_SITE_ENTRY
  1980. NlFindSiteEntryBySockAddr(
  1981. IN PSOCKADDR SockAddr
  1982. )
  1983. /*++
  1984. Routine Description:
  1985. This routine look up the specified socket address and translate it to a
  1986. site name.
  1987. Arguments:
  1988. SockAddr - Socket Address to lookup
  1989. RetSubnet - If specified, returns a pointer to the subnet object used to do
  1990. the mapping.
  1991. Might return NULL indicating a subnet object wasn't used.
  1992. Entry should be dereferenced using NlSitesDerefSubnet.
  1993. Return Value:
  1994. NULL: No site can be found for this SockAddr.
  1995. Non-NULL: Site corresponding to the SockAddr.
  1996. Entry should be derefenced using NlDerefSiteEntry
  1997. --*/
  1998. {
  1999. return NlFindSiteEntryBySockAddrEx( SockAddr, NULL );
  2000. }
  2001. BOOL
  2002. NlCaptureSiteName(
  2003. WCHAR CapturedSiteName[NL_MAX_DNS_LABEL_LENGTH+1]
  2004. )
  2005. /*++
  2006. Routine Description:
  2007. Capture the current sitename of the site this machine is in.
  2008. Arguments:
  2009. CapturedSiteName - Returns the name of the site this machine is in.
  2010. Return Value:
  2011. TRUE - if there is a site name.
  2012. FALSE - if there is no site name.
  2013. --*/
  2014. {
  2015. BOOL RetVal;
  2016. EnterCriticalSection( &NlGlobalSiteCritSect );
  2017. if ( NlGlobalUnicodeSiteName == NULL ) {
  2018. CapturedSiteName[0] = L'\0';
  2019. RetVal = FALSE;
  2020. } else {
  2021. wcscpy( CapturedSiteName, NlGlobalUnicodeSiteName );
  2022. RetVal = TRUE;
  2023. }
  2024. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2025. return RetVal;
  2026. }
  2027. NET_API_STATUS
  2028. DsrGetSiteName(
  2029. IN LPWSTR ComputerName OPTIONAL,
  2030. OUT LPWSTR *SiteName
  2031. )
  2032. /*++
  2033. Routine Description:
  2034. Same as DsGetSiteNameW except:
  2035. * This is the RPC server side implementation.
  2036. Arguments:
  2037. Same as DsGetSiteNameW except as above.
  2038. Return Value:
  2039. Same as DsGetSiteNameW except as above.
  2040. Note: On a workstation or a member server, this function makes a
  2041. reasonable attempt to retrieve a valid name of the site ComputerName
  2042. is in. If the locally stored name is too old, the function receives
  2043. the name from a DC. If any error occurs during this process, the local
  2044. value for the name is returned. It is possible that the name received
  2045. from the DC is out of date. In that case the function will return it
  2046. anyway.
  2047. --*/
  2048. {
  2049. NET_API_STATUS NetStatus;
  2050. NTSTATUS Status;
  2051. UNICODE_STRING DomainNameString;
  2052. PDOMAIN_INFO DomainInfo = NULL;
  2053. PCLIENT_SESSION ClientSession = NULL;
  2054. PNL_DC_CACHE_ENTRY NlDcCacheEntry;
  2055. BOOL AmWriter = FALSE;
  2056. //
  2057. // Lookup which domain this call pertains to.
  2058. //
  2059. DomainInfo = NlFindDomainByServerName( ComputerName );
  2060. if ( DomainInfo == NULL ) {
  2061. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2062. goto Cleanup;
  2063. }
  2064. EnterCriticalSection( &NlGlobalSiteCritSect );
  2065. //
  2066. // On a workstation or a member server, update the site name if it's not
  2067. // statically configured and is old.
  2068. // However, do not update if we are in NT4 domain since there is no site
  2069. // concept in NT4.
  2070. //
  2071. if ( NlGlobalMemberWorkstation &&
  2072. !NlGlobalParameters.SiteNameConfigured &&
  2073. DomainInfo->DomUnicodeDnsDomainNameString.Length != 0 &&
  2074. NetpLogonTimeHasElapsed(
  2075. NlGlobalSiteNameSetTime,
  2076. NlGlobalParameters.SiteNameTimeout * 1000 ) ) {
  2077. NlPrint(( NL_SITE, "DsrGetSiteName: Site name '%ws' is old. Getting a new one from DC.\n",
  2078. NlGlobalUnicodeSiteName ));
  2079. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2080. //
  2081. // Fill in the primary domain name.
  2082. //
  2083. RtlInitUnicodeString( &DomainNameString, DomainInfo->DomUnicodeDomainName );
  2084. //
  2085. // On the PDC or BDC,
  2086. // find the Client session for the domain.
  2087. // On workstations,
  2088. // find the primary domain client session.
  2089. //
  2090. ClientSession = NlFindNamedClientSession( DomainInfo,
  2091. &DomainNameString,
  2092. NL_DIRECT_TRUST_REQUIRED,
  2093. NULL );
  2094. if ( ClientSession == NULL ) {
  2095. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2096. "DsrGetSiteName: %wZ: No such trusted domain\n",
  2097. &DomainNameString ));
  2098. NetStatus = ERROR_NO_SUCH_DOMAIN;
  2099. goto Cleanup;
  2100. }
  2101. //
  2102. // Become a writer of the client session.
  2103. //
  2104. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  2105. NlPrintCs(( NL_CRITICAL, ClientSession,
  2106. "DsrGetSiteName: Can't become writer of client session.\n" ));
  2107. NetStatus = ERROR_NO_LOGON_SERVERS;
  2108. goto Cleanup;
  2109. }
  2110. AmWriter = TRUE;
  2111. //
  2112. // Get the DC info from the server
  2113. //
  2114. Status = NlGetAnyDCName( ClientSession,
  2115. TRUE, // Require IP be used to determine site correctly
  2116. FALSE, // Don't do with-account discovery
  2117. &NlDcCacheEntry,
  2118. NULL ); // don't care if the DC was rediscovered
  2119. //
  2120. // Do not error out on failure. Rather, use local cache.
  2121. // Use the response only if it is from an NT5 DC (that
  2122. // knows about the site of the client)
  2123. //
  2124. EnterCriticalSection( &NlGlobalSiteCritSect );
  2125. if ( NT_SUCCESS(Status) ) {
  2126. if ( (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) != 0 ) {
  2127. NlSetDynamicSiteName( NlDcCacheEntry->UnicodeClientSiteName );
  2128. } else {
  2129. NlPrint(( NL_SITE,
  2130. "DsrGetSiteName: NlGetAnyDCName returned NT4 DC. Returning site '%ws' from local cache\n",
  2131. NlGlobalUnicodeSiteName ));
  2132. }
  2133. NetpDcDerefCacheEntry( NlDcCacheEntry );
  2134. } else {
  2135. NlPrint(( NL_CRITICAL,
  2136. "DsrGetSiteName: NlGetAnyDCName failed. Returning site '%ws' from local cache.\n",
  2137. NlGlobalUnicodeSiteName ));
  2138. }
  2139. } else {
  2140. NlPrint(( NL_SITE, "DsrGetSiteName: Returning site name '%ws' from local cache.\n",
  2141. NlGlobalUnicodeSiteName ));
  2142. }
  2143. if ( NlGlobalUnicodeSiteName == NULL ) {
  2144. *SiteName = NULL;
  2145. NetStatus = ERROR_NO_SITENAME;
  2146. } else {
  2147. *SiteName = NetpAllocWStrFromWStr( NlGlobalUnicodeSiteName );
  2148. if ( *SiteName == NULL ) {
  2149. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2150. } else {
  2151. NetStatus = NO_ERROR;
  2152. }
  2153. }
  2154. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2155. Cleanup:
  2156. if ( DomainInfo != NULL ) {
  2157. NlDereferenceDomain( DomainInfo );
  2158. }
  2159. if ( ClientSession != NULL ) {
  2160. if ( AmWriter ) {
  2161. NlResetWriterClientSession( ClientSession );
  2162. }
  2163. NlUnrefClientSession( ClientSession );
  2164. }
  2165. return NetStatus;
  2166. }
  2167. NET_API_STATUS
  2168. NlSetSiteName(
  2169. IN LPWSTR SiteName OPTIONAL,
  2170. OUT PBOOLEAN SiteNameChanged OPTIONAL
  2171. )
  2172. /*++
  2173. Routine Description:
  2174. This routine set the current site name in a global.
  2175. Any bogus site name is truncated to be a valid site name.
  2176. Arguments:
  2177. SiteName - Name of the site this machine is in.
  2178. NULL: machine is no longer in a site.
  2179. SiteNameChanged - If specified, returns TRUE if the site name changed
  2180. Return Value:
  2181. NO_ERROR: success
  2182. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  2183. --*/
  2184. {
  2185. NET_API_STATUS NetStatus;
  2186. LPWSTR TempUnicodeSiteName = NULL;
  2187. LPWSTR LocalUnicodeSiteName = NULL;
  2188. LPSTR LocalUtf8SiteName = NULL;
  2189. PNL_SITE_ENTRY LocalSiteEntry = NULL;
  2190. //
  2191. // Initialization
  2192. //
  2193. if ( ARGUMENT_PRESENT( SiteNameChanged )) {
  2194. *SiteNameChanged = FALSE;
  2195. }
  2196. //
  2197. // If the site name hasn't changed,
  2198. // early out.
  2199. // (Case sensitive compare to allow case changes.)
  2200. //
  2201. EnterCriticalSection( &NlGlobalSiteCritSect );
  2202. if ( SiteName != NULL &&
  2203. NlGlobalUnicodeSiteName != NULL &&
  2204. wcscmp( NlGlobalUnicodeSiteName, SiteName ) == 0 ) {
  2205. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2206. NetStatus = NO_ERROR;
  2207. goto Cleanup;
  2208. }
  2209. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2210. //
  2211. // Copy the site name into a Locally allocated buffer.
  2212. //
  2213. NlPrint(( NL_SITE, "Setting site name to '%ws'\n", SiteName ));
  2214. if ( SiteName == NULL ) {
  2215. LocalUnicodeSiteName = NULL;
  2216. LocalUtf8SiteName = NULL;
  2217. LocalSiteEntry = NULL;
  2218. } else {
  2219. BOOLEAN LogMessage = FALSE;
  2220. UNICODE_STRING UnicodeStringOfSiteName;
  2221. LPWSTR Period;
  2222. DNS_STATUS DnsStatus;
  2223. //
  2224. // Ditch any period in the site name.
  2225. //
  2226. RtlInitUnicodeString( &UnicodeStringOfSiteName, SiteName );
  2227. Period = wcschr( SiteName, L'.' );
  2228. if ( Period != NULL ) {
  2229. UnicodeStringOfSiteName.Length = (USHORT)(Period-SiteName) * sizeof(WCHAR);
  2230. NlPrint(( NL_CRITICAL,
  2231. "Site name '%ws' contains a period (truncated to '%wZ').\n",
  2232. SiteName,
  2233. &UnicodeStringOfSiteName ));
  2234. if ( UnicodeStringOfSiteName.Length == 0 ) {
  2235. NlPrint(( NL_CRITICAL,
  2236. "Site name '%ws' truncated to zero characters (Set to '1').\n",
  2237. SiteName ));
  2238. RtlInitUnicodeString( &UnicodeStringOfSiteName, L"1" );
  2239. }
  2240. LogMessage = TRUE;
  2241. }
  2242. //
  2243. // Loop truncating the name until it is short enough.
  2244. //
  2245. // The length restriction only makes sense in the UTF-8 character set.
  2246. // UTF-8 has multibyte characters so only truncate the UNICODE string
  2247. // and test the UTF-8 string.
  2248. //
  2249. for (;;) {
  2250. LocalUtf8SiteName = NetpAllocUtf8StrFromUnicodeString( &UnicodeStringOfSiteName );
  2251. if ( LocalUtf8SiteName == NULL ) {
  2252. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2253. goto Cleanup;
  2254. }
  2255. //
  2256. // If the site name is OK, we're done.
  2257. //
  2258. if ( strlen(LocalUtf8SiteName) <= NL_MAX_DNS_LABEL_LENGTH ) {
  2259. break;
  2260. }
  2261. //
  2262. // Truncate the site name (and press on)
  2263. //
  2264. UnicodeStringOfSiteName.Length -= sizeof(WCHAR);
  2265. NlPrint(( NL_CRITICAL,
  2266. "Site name '%ws' is too long (trucated to '%wZ')\n",
  2267. SiteName,
  2268. &UnicodeStringOfSiteName ));
  2269. LogMessage = TRUE;
  2270. }
  2271. //
  2272. // Validate the character set of the site name.
  2273. // (If invalid, map the bogus characters)
  2274. //
  2275. DnsStatus = DnsValidateName_UTF8( LocalUtf8SiteName, DnsNameDomain );
  2276. if ( DnsStatus != ERROR_SUCCESS &&
  2277. DnsStatus != DNS_ERROR_NON_RFC_NAME ) {
  2278. ULONG i;
  2279. //
  2280. // Grab a copy of the string to map into
  2281. //
  2282. TempUnicodeSiteName = NetpAllocWStrFromWStr( SiteName );
  2283. if ( TempUnicodeSiteName == NULL ) {
  2284. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2285. goto Cleanup;
  2286. }
  2287. UnicodeStringOfSiteName.Buffer = TempUnicodeSiteName;
  2288. //
  2289. // Map the bogus characters
  2290. //
  2291. for ( i=0; i<UnicodeStringOfSiteName.Length/sizeof(WCHAR); i++) {
  2292. WCHAR JustOneChar[2];
  2293. //
  2294. // Test one character at a time.
  2295. //
  2296. JustOneChar[0] = UnicodeStringOfSiteName.Buffer[i];
  2297. JustOneChar[1] = '\0';
  2298. DnsStatus = DnsValidateName_W( JustOneChar, DnsNameDomain );
  2299. if ( DnsStatus != ERROR_SUCCESS &&
  2300. DnsStatus != DNS_ERROR_NON_RFC_NAME ) {
  2301. UnicodeStringOfSiteName.Buffer[i] = L'-';
  2302. }
  2303. }
  2304. //
  2305. // Map back to UTF-8
  2306. //
  2307. NetpMemoryFree( LocalUtf8SiteName );
  2308. LocalUtf8SiteName = NetpAllocUtf8StrFromUnicodeString( &UnicodeStringOfSiteName );
  2309. if ( LocalUtf8SiteName == NULL ) {
  2310. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2311. goto Cleanup;
  2312. }
  2313. NlPrint(( NL_CRITICAL,
  2314. "Site name '%ws' has invalid character (set to '%wZ')\n",
  2315. SiteName,
  2316. &UnicodeStringOfSiteName ));
  2317. LogMessage = TRUE;
  2318. }
  2319. //
  2320. // If any munging of the name occurred,
  2321. // log the failure.
  2322. //
  2323. if ( LogMessage ) {
  2324. LPWSTR MsgStrings[1];
  2325. MsgStrings[0] = (LPWSTR) SiteName;
  2326. NlpWriteEventlog( NELOG_NetlogonBadSiteName,
  2327. EVENTLOG_ERROR_TYPE,
  2328. NULL,
  2329. 0,
  2330. MsgStrings,
  2331. 1 );
  2332. }
  2333. LocalUnicodeSiteName = NetpAllocWStrFromUtf8Str( LocalUtf8SiteName );
  2334. if ( LocalUnicodeSiteName == NULL ) {
  2335. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2336. goto Cleanup;
  2337. }
  2338. LocalSiteEntry = NlFindSiteEntry( LocalUnicodeSiteName );
  2339. if ( LocalSiteEntry == NULL ) {
  2340. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2341. goto Cleanup;
  2342. }
  2343. }
  2344. //
  2345. // If the site name hasn't changed (Using modified site name),
  2346. // early out.
  2347. // (Case sensitive compare to allow case changes.)
  2348. //
  2349. EnterCriticalSection( &NlGlobalSiteCritSect );
  2350. if ( LocalUnicodeSiteName != NULL &&
  2351. NlGlobalUnicodeSiteName != NULL &&
  2352. wcscmp( NlGlobalUnicodeSiteName, LocalUnicodeSiteName ) == 0 ) {
  2353. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2354. NetStatus = NO_ERROR;
  2355. goto Cleanup;
  2356. }
  2357. //
  2358. // Free any previous entry
  2359. //
  2360. if ( NlGlobalUnicodeSiteName != NULL ) {
  2361. NetpMemoryFree( NlGlobalUnicodeSiteName );
  2362. }
  2363. if ( NlGlobalUtf8SiteName != NULL ) {
  2364. NetpMemoryFree( NlGlobalUtf8SiteName );
  2365. }
  2366. if ( NlGlobalSiteEntry != NULL ) {
  2367. NlDerefSiteEntry( NlGlobalSiteEntry );
  2368. }
  2369. //
  2370. // Save the new site name.
  2371. //
  2372. NlGlobalUnicodeSiteName = LocalUnicodeSiteName;
  2373. LocalUnicodeSiteName = NULL;
  2374. NlGlobalUtf8SiteName = LocalUtf8SiteName;
  2375. LocalUtf8SiteName = NULL;
  2376. NlGlobalSiteEntry = LocalSiteEntry;
  2377. LocalSiteEntry = NULL;
  2378. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2379. if ( ARGUMENT_PRESENT( SiteNameChanged )) {
  2380. *SiteNameChanged = TRUE;
  2381. }
  2382. NetStatus = NO_ERROR;
  2383. //
  2384. // Cleanup local data.
  2385. //
  2386. Cleanup:
  2387. if ( TempUnicodeSiteName != NULL ) {
  2388. NetpMemoryFree( TempUnicodeSiteName );
  2389. }
  2390. if ( LocalUnicodeSiteName != NULL ) {
  2391. NetApiBufferFree( LocalUnicodeSiteName );
  2392. }
  2393. if ( LocalUtf8SiteName != NULL ) {
  2394. NetpMemoryFree( LocalUtf8SiteName );
  2395. }
  2396. if ( LocalSiteEntry != NULL ) {
  2397. NlDerefSiteEntry( LocalSiteEntry );
  2398. }
  2399. //
  2400. // Set the time when the site name was updated
  2401. //
  2402. if ( NetStatus == NO_ERROR ) {
  2403. NlQuerySystemTime( &NlGlobalSiteNameSetTime );
  2404. }
  2405. return NetStatus;
  2406. }
  2407. VOID
  2408. NlSetDynamicSiteName(
  2409. IN LPWSTR SiteName OPTIONAL
  2410. )
  2411. /*++
  2412. Routine Description:
  2413. This routine set the current site name of this machine in the registry
  2414. and in Netlogon globals
  2415. Arguments:
  2416. SiteName - Name of the site this machine is in.
  2417. NULL: machine is no longer in a site.
  2418. Return Value:
  2419. None.
  2420. --*/
  2421. {
  2422. NET_API_STATUS NetStatus;
  2423. HKEY ParmHandle = NULL;
  2424. ULONG SiteNameSize;
  2425. //
  2426. // Avoid changing the site name on DCs.
  2427. //
  2428. if ( !NlGlobalMemberWorkstation ) {
  2429. return;
  2430. }
  2431. //
  2432. // Don't change the sitename back to its current value.
  2433. // (Case sensitive compare to allow case changes.)
  2434. //
  2435. EnterCriticalSection( &NlGlobalSiteCritSect );
  2436. if ( NlGlobalUnicodeSiteName != NULL &&
  2437. SiteName != NULL &&
  2438. wcscmp(SiteName, NlGlobalUnicodeSiteName) == 0 ) {
  2439. NlPrint(( NL_SITE_MORE, "NlSetDynamicSiteName: Old and new site names '%ws' are identical.\n",
  2440. SiteName ));
  2441. NlQuerySystemTime( &NlGlobalSiteNameSetTime );
  2442. goto Cleanup;
  2443. }
  2444. //
  2445. // If the site name was explicitly configured,
  2446. // don't set the site name.
  2447. //
  2448. if ( NlGlobalParameters.SiteNameConfigured ) {
  2449. NlPrint(( NL_SITE_MORE,
  2450. "Cannot set site name to %ws from %ws since it is statically configured\n",
  2451. SiteName,
  2452. NlGlobalUnicodeSiteName ));
  2453. goto Cleanup;
  2454. }
  2455. //
  2456. // Save the name in globals.
  2457. //
  2458. NlSetSiteName( SiteName, NULL );
  2459. //
  2460. // Save the name in the registry to keep it across boots.
  2461. //
  2462. //
  2463. // Open the key for Netlogon\Parameters
  2464. //
  2465. ParmHandle = NlOpenNetlogonKey( NL_PARAM_KEY );
  2466. if (ParmHandle == NULL) {
  2467. NlPrint(( NL_CRITICAL,
  2468. "Cannot NlOpenNetlogonKey to set site name to %ws from %ws\n",
  2469. SiteName,
  2470. NlGlobalUnicodeSiteName ));
  2471. goto Cleanup;
  2472. }
  2473. //
  2474. // If the we're no longer in a site,
  2475. // delete the value.
  2476. //
  2477. if ( SiteName == NULL ) {
  2478. NetStatus = RegDeleteValueW( ParmHandle,
  2479. NETLOGON_KEYWORD_DYNAMICSITENAME );
  2480. if ( NetStatus != ERROR_SUCCESS ) {
  2481. if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
  2482. NlPrint(( NL_CRITICAL,
  2483. "NlSetDynamicSiteName: Cannot delete '" NL_PARAM_KEY "\\%ws' %ld.\n",
  2484. NETLOGON_KEYWORD_DYNAMICSITENAME,
  2485. NetStatus ));
  2486. }
  2487. goto Cleanup;
  2488. }
  2489. //
  2490. // Set the value in the registry.
  2491. //
  2492. } else {
  2493. SiteNameSize = (wcslen(SiteName)+1) * sizeof(WCHAR);
  2494. NetStatus = RegSetValueExW( ParmHandle,
  2495. NETLOGON_KEYWORD_DYNAMICSITENAME,
  2496. 0, // Reserved
  2497. REG_SZ,
  2498. (LPBYTE)SiteName,
  2499. SiteNameSize+1 );
  2500. if ( NetStatus != ERROR_SUCCESS ) {
  2501. NlPrint(( NL_CRITICAL,
  2502. "NlSetDynamicSiteName: Cannot Set '" NL_PARAM_KEY "\\%ws' %ld.\n",
  2503. NETLOGON_KEYWORD_DYNAMICSITENAME,
  2504. NetStatus ));
  2505. goto Cleanup;
  2506. }
  2507. }
  2508. Cleanup:
  2509. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2510. if ( ParmHandle != NULL ) {
  2511. RegCloseKey( ParmHandle );
  2512. }
  2513. return;
  2514. }
  2515. NET_API_STATUS
  2516. NlSitesAddSubnetFromDs(
  2517. OUT PBOOLEAN SiteNameChanged OPTIONAL
  2518. )
  2519. /*++
  2520. Routine Description:
  2521. This routine reads the subnet\site mapping from the DS and populates
  2522. Netlogon's cache with that information
  2523. Arguments:
  2524. SiteNameChanged - If specified, returns TRUE if the site name changed
  2525. Return Value:
  2526. NO_ERROR: success
  2527. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  2528. --*/
  2529. {
  2530. NET_API_STATUS NetStatus;
  2531. NTSTATUS Status;
  2532. PLSAP_SUBNET_INFO SubnetInfo = NULL;
  2533. PLSAP_SITENAME_INFO SiteNameInfo = NULL;
  2534. ULONG i;
  2535. BOOLEAN MoreThanOneSite = FALSE;
  2536. ULONG LocalSubnetCount = 0;
  2537. //
  2538. // Get the site name of this site.
  2539. //
  2540. Status = LsaIGetSiteName( &SiteNameInfo );
  2541. if ( !NT_SUCCESS(Status) ) {
  2542. //
  2543. // If the DS simply isn't running,
  2544. // skip this.
  2545. //
  2546. if ( Status == STATUS_INVALID_DOMAIN_STATE ) {
  2547. NlPrint(( NL_SITE,
  2548. "DS isn't running so site to subnet mapping ignored\n" ));
  2549. NetStatus = NO_ERROR;
  2550. goto Cleanup;
  2551. }
  2552. NlPrint(( NL_CRITICAL,
  2553. "Cannot LsaIGetSiteName %lx\n", Status ));
  2554. NetStatus = NetpNtStatusToApiStatus(Status);
  2555. goto Cleanup;
  2556. }
  2557. NlGlobalDsaGuid = SiteNameInfo->DsaGuid;
  2558. NetStatus = NlSetSiteName( SiteNameInfo->SiteName.Buffer, SiteNameChanged );
  2559. if ( NetStatus != NO_ERROR ) {
  2560. NlPrint(( NL_CRITICAL,
  2561. "Cannot NlSetSiteName %ld\n", NetStatus ));
  2562. goto Cleanup;
  2563. }
  2564. //
  2565. // If this machine is marked as a GC,
  2566. // flag it so.
  2567. //
  2568. // Really this is only needed if netlogon.dll is unloaded via nltest /unload.
  2569. // Otherwise the flag is saved across starts/stops in a global.
  2570. //
  2571. if ( NlGlobalNetlogonUnloaded &&
  2572. (SiteNameInfo->DsaOptions & NTDSDSA_OPT_IS_GC) != 0 ) {
  2573. NlPrint((NL_INIT,
  2574. "Set GC-running bit after netlogon.dll unload\n" ));
  2575. I_NetLogonSetServiceBits( DS_GC_FLAG, DS_GC_FLAG );
  2576. }
  2577. //
  2578. // Get the list of subnet to site mappings from the DS
  2579. //
  2580. NlPrint(( NL_SITE, "Adding subnet to site mappings from the DS\n" ));
  2581. Status = LsaIQuerySubnetInfo( &SubnetInfo );
  2582. if ( !NT_SUCCESS(Status) ) {
  2583. NlPrint((NL_CRITICAL, "Cannot LsaIQuerySubnetInfo %lx\n", Status ));
  2584. NetStatus = NetpNtStatusToApiStatus( Status );
  2585. goto Cleanup;
  2586. }
  2587. //
  2588. // Put them in our in-memory cache.
  2589. //
  2590. for ( i=0; i<SubnetInfo->SubnetCount; i++ ) {
  2591. //
  2592. // If there is no site associated with the subnet,
  2593. // silently ignore it.
  2594. //
  2595. if ( SubnetInfo->Subnets[i].SiteName.Length == 0 ) {
  2596. NlPrint(( NL_SITE, "%wZ: Subnet has no associated site (ignored)\n",
  2597. &SubnetInfo->Subnets[i].SubnetName ));
  2598. continue;
  2599. }
  2600. LocalSubnetCount ++;
  2601. //
  2602. // Determine if there are multiple sites in the enterprise
  2603. //
  2604. if ( !RtlEqualUnicodeString( &SiteNameInfo->SiteName,
  2605. &SubnetInfo->Subnets[i].SiteName,
  2606. TRUE )) {
  2607. NlPrint(( NL_SITE, "%wZ: Site %wZ is not site this DC is in.\n",
  2608. &SubnetInfo->Subnets[i].SubnetName,
  2609. &SubnetInfo->Subnets[i].SiteName ));
  2610. MoreThanOneSite = TRUE;
  2611. }
  2612. //
  2613. // Add the subnet to out in memory cache.
  2614. //
  2615. NetStatus = NlSitesAddSubnet(
  2616. SubnetInfo->Subnets[i].SiteName.Buffer,
  2617. SubnetInfo->Subnets[i].SubnetName.Buffer );
  2618. if ( NetStatus != NO_ERROR ) {
  2619. NlPrint(( NL_CRITICAL,
  2620. "%wZ: %wZ: Cannot add subnet-to-site mapping to cache: %ld\n",
  2621. &SubnetInfo->Subnets[i].SubnetName,
  2622. &SubnetInfo->Subnets[i].SiteName,
  2623. NetStatus ));
  2624. if ( NetStatus == ERROR_INVALID_NAME ) {
  2625. LPWSTR MsgStrings[1];
  2626. MsgStrings[0] = (LPWSTR) SubnetInfo->Subnets[i].SubnetName.Buffer;
  2627. NlpWriteEventlog( NELOG_NetlogonBadSubnetName,
  2628. EVENTLOG_INFORMATION_TYPE,
  2629. NULL,
  2630. 0,
  2631. MsgStrings,
  2632. 1 );
  2633. }
  2634. }
  2635. }
  2636. //
  2637. // Indicate that all the subnets have been added.
  2638. //
  2639. NlSitesEndSubnetEnum();
  2640. //
  2641. // If there are no subnet entries,
  2642. // and there is only one site in the enterprise,
  2643. // indicate that all client belong to this site.
  2644. //
  2645. // If there are subnet entries,
  2646. // and all of them indicate the same site as our site,
  2647. // indicate that all clients belong to this site.
  2648. //
  2649. EnterCriticalSection( &NlGlobalSiteCritSect );
  2650. if ( LocalSubnetCount == 0) {
  2651. NlGlobalOnlyOneSite = (SubnetInfo->SiteCount == 1);
  2652. } else {
  2653. NlGlobalOnlyOneSite = !MoreThanOneSite;
  2654. }
  2655. if ( NlGlobalOnlyOneSite ) {
  2656. NlPrint(( NL_SITE, "There is only one site. All clients belong to it.\n" ));
  2657. }
  2658. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2659. NetStatus = NO_ERROR;
  2660. //
  2661. // Free locally used resources
  2662. //
  2663. Cleanup:
  2664. if ( SubnetInfo != NULL ) {
  2665. LsaIFree_LSAP_SUBNET_INFO( SubnetInfo );
  2666. }
  2667. if ( SiteNameInfo != NULL ) {
  2668. LsaIFree_LSAP_SITENAME_INFO( SiteNameInfo );
  2669. }
  2670. return NetStatus;
  2671. }
  2672. NET_API_STATUS
  2673. DsrAddressToSiteNamesW(
  2674. IN LPWSTR ComputerName,
  2675. IN DWORD EntryCount,
  2676. IN PNL_SOCKET_ADDRESS SocketAddresses,
  2677. OUT PNL_SITE_NAME_ARRAY *SiteNames
  2678. )
  2679. /*++
  2680. Routine Description:
  2681. The DsAddressToSiteNames API returns the site names that correspond to
  2682. the specified addresses.
  2683. Arguments:
  2684. ComputerName - Specifies the name of the domain controller to remote this API to.
  2685. EntryCount - Number of addresses to convert.
  2686. SocketAddresses - Specifies an array of addresses to convert. EntryCount
  2687. addresses must be specified. Each address must be of type AF_INET.
  2688. SiteNames - Returns an array of pointers to site names. EntryCount entries
  2689. are returned. An entry will be returned as NULL if the corresponding
  2690. address does not map to any site or if the address is malformed.
  2691. The returned buffer must be deallocated using NetApiBufferFree.
  2692. Return Value:
  2693. NO_ERROR - Operation completed successfully;
  2694. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  2695. operation.
  2696. --*/
  2697. {
  2698. NET_API_STATUS NetStatus;
  2699. PNL_SITE_ENTRY *SiteEntries = NULL;
  2700. ULONG i;
  2701. ULONG Size;
  2702. PUNICODE_STRING Strings;
  2703. LPBYTE Where;
  2704. //
  2705. // This API is not supported on workstations.
  2706. //
  2707. *SiteNames = NULL;
  2708. if ( NlGlobalMemberWorkstation ) {
  2709. return ERROR_NOT_SUPPORTED;
  2710. }
  2711. //
  2712. // Initialization
  2713. //
  2714. if ( EntryCount == 0 ) {
  2715. return ERROR_INVALID_PARAMETER;
  2716. }
  2717. //
  2718. // Allocate an array for intermediate results
  2719. //
  2720. SiteEntries = LocalAlloc( LMEM_ZEROINIT, EntryCount*sizeof(PNL_SITE_ENTRY) );
  2721. if ( SiteEntries == NULL ) {
  2722. return ERROR_NOT_ENOUGH_MEMORY;
  2723. }
  2724. //
  2725. // Loop mapping each entry
  2726. //
  2727. for ( i=0; i<EntryCount; i++ ) {
  2728. PSOCKET_ADDRESS SocketAddress;
  2729. PSOCKADDR SockAddr;
  2730. //
  2731. // Validate the entry
  2732. //
  2733. SocketAddress = (PSOCKET_ADDRESS)&SocketAddresses[i];
  2734. SockAddr = SocketAddress->lpSockaddr;
  2735. if (SocketAddress->iSockaddrLength < sizeof(SOCKADDR) ) {
  2736. NlPrint((NL_CRITICAL,
  2737. "DsrAddressToSiteNamesW: Sockaddr is too small %ld (ignoring it)\n",
  2738. SocketAddress->iSockaddrLength ));
  2739. SiteEntries[i] = NULL;
  2740. } else if ( SockAddr->sa_family != AF_INET ) {
  2741. NlPrint((NL_CRITICAL,
  2742. "DsrAddressToSiteNamesW: Address familty isn't AF_INET %ld (ignoring it)\n",
  2743. SockAddr->sa_family ));
  2744. SiteEntries[i] = NULL;
  2745. } else {
  2746. //
  2747. // The SockAddr is valid so map it to a site name.
  2748. //
  2749. SiteEntries[i] = NlFindSiteEntryBySockAddrEx( SockAddr, NULL );
  2750. }
  2751. }
  2752. //
  2753. // Allocate a structure to return to the caller.
  2754. //
  2755. Size = sizeof(NL_SITE_NAME_ARRAY) + EntryCount * sizeof(UNICODE_STRING);
  2756. for ( i=0; i<EntryCount; i++ ) {
  2757. if ( SiteEntries[i] != NULL ) {
  2758. Size += SiteEntries[i]->SiteNameString.Length + sizeof(WCHAR);
  2759. }
  2760. }
  2761. *SiteNames = MIDL_user_allocate( Size );
  2762. if ( *SiteNames == NULL ) {
  2763. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2764. goto Cleanup;
  2765. }
  2766. Strings = (PUNICODE_STRING) ((*SiteNames)+1);
  2767. (*SiteNames)->EntryCount = EntryCount;
  2768. (*SiteNames)->SiteNames = Strings;
  2769. Where = (LPBYTE) &Strings[EntryCount];
  2770. //
  2771. // Loop copying the names into the return buffer.
  2772. //
  2773. for ( i=0; i<EntryCount; i++ ) {
  2774. if ( SiteEntries[i] == NULL ) {
  2775. RtlInitUnicodeString( &Strings[i], NULL );
  2776. } else {
  2777. Strings[i].Length = SiteEntries[i]->SiteNameString.Length;
  2778. Strings[i].MaximumLength = Strings[i].Length + sizeof(WCHAR);
  2779. Strings[i].Buffer = (LPWSTR)Where;
  2780. RtlCopyMemory( Where, SiteEntries[i]->SiteName, Strings[i].MaximumLength );
  2781. Where += Strings[i].Length + sizeof(WCHAR);
  2782. }
  2783. }
  2784. NetStatus = NO_ERROR;
  2785. Cleanup:
  2786. //
  2787. // Derference the site entries.
  2788. //
  2789. if ( SiteEntries != NULL ) {
  2790. for ( i=0; i<EntryCount; i++ ) {
  2791. if ( SiteEntries[i] != NULL ) {
  2792. NlDerefSiteEntry( SiteEntries[i] );
  2793. }
  2794. }
  2795. LocalFree( SiteEntries );
  2796. }
  2797. if ( NetStatus != NO_ERROR ) {
  2798. if ( *SiteNames != NULL ) {
  2799. MIDL_user_free( *SiteNames );
  2800. *SiteNames = NULL;
  2801. }
  2802. }
  2803. return NetStatus;
  2804. UNREFERENCED_PARAMETER( ComputerName );
  2805. }
  2806. NET_API_STATUS
  2807. DsrAddressToSiteNamesExW(
  2808. IN LPWSTR ComputerName,
  2809. IN DWORD EntryCount,
  2810. IN PNL_SOCKET_ADDRESS SocketAddresses,
  2811. OUT PNL_SITE_NAME_EX_ARRAY *SiteNames
  2812. )
  2813. /*++
  2814. Routine Description:
  2815. The DsAddressToSiteNames API returns the site names and subnet names
  2816. that correspond to the specified addresses.
  2817. Arguments:
  2818. ComputerName - Specifies the name of the domain controller to remote this API to.
  2819. EntryCount - Number of addresses to convert.
  2820. SocketAddresses - Specifies an array of addresses to convert. EntryCount
  2821. addresses must be specified. Each address must be of type AF_INET.
  2822. SiteNames - Returns an array of pointers to site names. EntryCount entries
  2823. are returned. An entry will be returned as NULL if the corresponding
  2824. address does not map to any site or if the address is malformed.
  2825. The returned buffer must be deallocated using NetApiBufferFree.
  2826. Return Value:
  2827. NO_ERROR - Operation completed successfully;
  2828. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  2829. operation.
  2830. --*/
  2831. {
  2832. NET_API_STATUS NetStatus;
  2833. PNL_SITE_ENTRY *SiteEntries = NULL;
  2834. PNL_SUBNET *SubnetEntries;
  2835. ULONG i;
  2836. ULONG Size;
  2837. PUNICODE_STRING SiteStrings = NULL;
  2838. PUNICODE_STRING SubnetStrings = NULL;
  2839. //
  2840. // This API is not supported on workstations.
  2841. //
  2842. *SiteNames = NULL;
  2843. if ( NlGlobalMemberWorkstation ) {
  2844. return ERROR_NOT_SUPPORTED;
  2845. }
  2846. //
  2847. // Initialization
  2848. //
  2849. if ( EntryCount == 0 ) {
  2850. return ERROR_INVALID_PARAMETER;
  2851. }
  2852. //
  2853. // Allocate an array for intermediate results
  2854. //
  2855. SiteEntries = LocalAlloc( LMEM_ZEROINIT,
  2856. EntryCount*(sizeof(PNL_SITE_ENTRY)+sizeof(PNL_SUBNET)) );
  2857. if ( SiteEntries == NULL ) {
  2858. return ERROR_NOT_ENOUGH_MEMORY;
  2859. }
  2860. SubnetEntries = (PNL_SUBNET *) (&SiteEntries[EntryCount]);
  2861. //
  2862. // Loop mapping each entry
  2863. //
  2864. for ( i=0; i<EntryCount; i++ ) {
  2865. PSOCKET_ADDRESS SocketAddress;
  2866. PSOCKADDR SockAddr;
  2867. //
  2868. // Validate the entry
  2869. //
  2870. SocketAddress = (PSOCKET_ADDRESS)&SocketAddresses[i];
  2871. SockAddr = SocketAddress->lpSockaddr;
  2872. if (SocketAddress->iSockaddrLength < sizeof(SOCKADDR) ) {
  2873. NlPrint((NL_CRITICAL,
  2874. "DsrAddressToSiteNamesW: Sockaddr is too small %ld (ignoring it)\n",
  2875. SocketAddress->iSockaddrLength ));
  2876. SiteEntries[i] = NULL;
  2877. } else if ( SockAddr->sa_family != AF_INET ) {
  2878. NlPrint((NL_CRITICAL,
  2879. "DsrAddressToSiteNamesW: Address familty isn't AF_INET %ld (ignoring it)\n",
  2880. SockAddr->sa_family ));
  2881. SiteEntries[i] = NULL;
  2882. } else {
  2883. //
  2884. // The SockAddr is valid so map it to a site name.
  2885. //
  2886. SiteEntries[i] = NlFindSiteEntryBySockAddrEx( SockAddr, &SubnetEntries[i] );
  2887. }
  2888. }
  2889. //
  2890. // Allocate a structure to return to the caller.
  2891. //
  2892. *SiteNames = MIDL_user_allocate( sizeof(NL_SITE_NAME_EX_ARRAY) );
  2893. if ( *SiteNames == NULL ) {
  2894. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2895. goto Cleanup;
  2896. }
  2897. SubnetStrings = MIDL_user_allocate( EntryCount * sizeof(UNICODE_STRING) );
  2898. if ( SubnetStrings == NULL ) {
  2899. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2900. goto Cleanup;
  2901. }
  2902. RtlZeroMemory( SubnetStrings, EntryCount * sizeof(UNICODE_STRING) );
  2903. SiteStrings = MIDL_user_allocate( EntryCount * sizeof(UNICODE_STRING) );
  2904. if ( SiteStrings == NULL ) {
  2905. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2906. goto Cleanup;
  2907. }
  2908. RtlZeroMemory( SiteStrings, EntryCount * sizeof(UNICODE_STRING) );
  2909. (*SiteNames)->EntryCount = EntryCount;
  2910. (*SiteNames)->SiteNames = SiteStrings;
  2911. (*SiteNames)->SubnetNames = SubnetStrings;
  2912. //
  2913. // Loop copying the names into the return buffer.
  2914. //
  2915. for ( i=0; i<EntryCount; i++ ) {
  2916. if ( SiteEntries[i] != NULL ) {
  2917. LPWSTR Name;
  2918. Name = NetpAllocWStrFromWStr( SiteEntries[i]->SiteName );
  2919. if ( Name == NULL ) {
  2920. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2921. goto Cleanup;
  2922. }
  2923. RtlInitUnicodeString( &SiteStrings[i], Name );
  2924. }
  2925. if ( SubnetEntries[i] != NULL ) {
  2926. WCHAR SubnetAddressString[NL_IP_ADDRESS_LENGTH+1+2+1];
  2927. ULONG Length;
  2928. UNICODE_STRING NumberString;
  2929. LPWSTR Name;
  2930. //
  2931. // Compute the IP address part of the subnet name
  2932. //
  2933. NetpIpAddressToWStr( SubnetEntries[i]->SubnetAddress,
  2934. SubnetAddressString );
  2935. Length = wcslen(SubnetAddressString);
  2936. SubnetAddressString[Length] = '/';
  2937. Length ++;
  2938. //
  2939. // Compute the bit count part of the subnet name
  2940. //
  2941. NumberString.Buffer = &SubnetAddressString[Length];
  2942. NumberString.MaximumLength = 3 * sizeof(WCHAR);
  2943. RtlIntegerToUnicodeString( SubnetEntries[i]->SubnetBitCount,
  2944. 10,
  2945. &NumberString );
  2946. SubnetAddressString[Length+NumberString.Length/sizeof(WCHAR)] = '\0';
  2947. //
  2948. // Return it to the caller
  2949. //
  2950. Name = NetpAllocWStrFromWStr( SubnetAddressString );
  2951. if ( Name == NULL ) {
  2952. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2953. goto Cleanup;
  2954. }
  2955. RtlInitUnicodeString( &SubnetStrings[i], Name );
  2956. }
  2957. }
  2958. NetStatus = NO_ERROR;
  2959. Cleanup:
  2960. //
  2961. // Derference the site entries.
  2962. //
  2963. if ( SiteEntries != NULL ) {
  2964. for ( i=0; i<EntryCount; i++ ) {
  2965. if ( SiteEntries[i] != NULL ) {
  2966. NlDerefSiteEntry( SiteEntries[i] );
  2967. }
  2968. if ( SubnetEntries[i] != NULL ) {
  2969. NlSitesDerefSubnet( SubnetEntries[i] );
  2970. }
  2971. }
  2972. }
  2973. if ( NetStatus != NO_ERROR ) {
  2974. if ( *SiteNames != NULL ) {
  2975. MIDL_user_free( *SiteNames );
  2976. *SiteNames = NULL;
  2977. }
  2978. if ( SiteStrings != NULL ) {
  2979. for ( i=0; i<EntryCount; i++ ) {
  2980. if ( SiteStrings[i].Buffer != NULL ) {
  2981. MIDL_user_free( SiteStrings[i].Buffer );
  2982. }
  2983. }
  2984. MIDL_user_free( SiteStrings );
  2985. }
  2986. if ( SubnetStrings != NULL ) {
  2987. for ( i=0; i<EntryCount; i++ ) {
  2988. if ( SubnetStrings[i].Buffer != NULL ) {
  2989. MIDL_user_free( SubnetStrings[i].Buffer );
  2990. }
  2991. }
  2992. MIDL_user_free( SubnetStrings );
  2993. }
  2994. }
  2995. return NetStatus;
  2996. UNREFERENCED_PARAMETER( ComputerName );
  2997. }
  2998. NET_API_STATUS
  2999. NlSiteInitialize(
  3000. VOID
  3001. )
  3002. /*++
  3003. Routine Description:
  3004. Initialize this module.
  3005. Calls NlExit upon failure.
  3006. Arguments:
  3007. None.
  3008. Return Value:
  3009. Status of the initialization.
  3010. --*/
  3011. {
  3012. NET_API_STATUS NetStatus;
  3013. try {
  3014. InitializeCriticalSection(&NlGlobalSiteCritSect);
  3015. } except( EXCEPTION_EXECUTE_HANDLER ) {
  3016. NlPrint((NL_CRITICAL, "Cannot InitializeCriticalSection for SiteCritSect\n" ));
  3017. return ERROR_NOT_ENOUGH_MEMORY;
  3018. }
  3019. NlGlobalUnicodeSiteName = NULL;
  3020. NlGlobalSiteEntry = NULL;
  3021. InitializeListHead( &NlGlobalSiteList );
  3022. InitializeListHead( &NlGlobalSubnetList );
  3023. RtlZeroMemory( &NlGlobalSubnetTree, sizeof(NlGlobalSubnetTree) );
  3024. RtlZeroMemory( &NlGlobalNewSubnetTree, sizeof(NlGlobalNewSubnetTree) );
  3025. NlGlobalSiteInitialized = TRUE;
  3026. //
  3027. // Initially set the site name and populate the subnet tree.
  3028. //
  3029. if ( NlGlobalMemberWorkstation ) {
  3030. NetStatus = NlSetSiteName( NlGlobalParameters.SiteName, NULL );
  3031. } else {
  3032. NetStatus = NlSitesAddSubnetFromDs( NULL );
  3033. }
  3034. return NetStatus;
  3035. }
  3036. VOID
  3037. NlSiteTerminate(
  3038. VOID
  3039. )
  3040. /*++
  3041. Routine Description:
  3042. De-Initialize this module.
  3043. Arguments:
  3044. None.
  3045. Return Value:
  3046. None.
  3047. --*/
  3048. {
  3049. PLIST_ENTRY ListEntry;
  3050. //
  3051. // If we've not initialized,
  3052. // we're done.
  3053. //
  3054. if ( !NlGlobalSiteInitialized ) {
  3055. return;
  3056. }
  3057. NlPrint(( NL_SITE_MORE, "NlSiteTerminate: Entered\n" ));
  3058. //
  3059. // Free all entries in NlGlobalSubnetTree and NlGlobalNewSubnetTree
  3060. //
  3061. EnterCriticalSection( &NlGlobalSiteCritSect );
  3062. NlSiteDeleteSubnetTree( &NlGlobalSubnetTree );
  3063. NlSiteDeleteSubnetTree( &NlGlobalNewSubnetTree );
  3064. //
  3065. // Delete the site name.
  3066. //
  3067. NlSetSiteName( NULL, NULL );
  3068. LeaveCriticalSection( &NlGlobalSiteCritSect );
  3069. //
  3070. // There should be no more sites or subnets since all covered sites
  3071. // have been previously dereferenced and all remaining references
  3072. // were from the tree above
  3073. //
  3074. NlAssert( IsListEmpty( &NlGlobalSiteList ) );
  3075. NlAssert( IsListEmpty( &NlGlobalSubnetList ) );
  3076. DeleteCriticalSection(&NlGlobalSiteCritSect);
  3077. NlGlobalSiteInitialized = FALSE;
  3078. NlPrint(( NL_SITE_MORE, "NlSiteTerminate: Exitted\n" ));
  3079. }
  3080. int __cdecl NlpCompareSiteName(
  3081. const void *String1,
  3082. const void *String2
  3083. )
  3084. /*++
  3085. Routine Description:
  3086. String comparison routine for DsrGetDcSiteCoverageW.
  3087. Arguments:
  3088. String1: First string to compare
  3089. String2: Second string to compare
  3090. Return Value:
  3091. --*/
  3092. {
  3093. return RtlCompareUnicodeString(
  3094. (PUNICODE_STRING) String1,
  3095. (PUNICODE_STRING) String2,
  3096. TRUE );
  3097. }
  3098. NET_API_STATUS
  3099. DsrGetDcSiteCoverageW(
  3100. IN LPWSTR ComputerName OPTIONAL,
  3101. OUT PNL_SITE_NAME_ARRAY *SiteNames
  3102. )
  3103. /*++
  3104. Routine Description:
  3105. This API returns the site names of all sites covered by DC.
  3106. Arguments:
  3107. ComputerName - Specifies the name of the domain controller to remote this API to.
  3108. SiteNames - Returns an array of pointers to site names.
  3109. The returned buffer must be deallocated using NetApiBufferFree.
  3110. Return Value:
  3111. NO_ERROR - Operation completed successfully;
  3112. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  3113. operation.
  3114. --*/
  3115. {
  3116. NET_API_STATUS NetStatus;
  3117. PDOMAIN_INFO DomainInfo = NULL;
  3118. if ( NlGlobalMemberWorkstation ) {
  3119. NetStatus = ERROR_NOT_SUPPORTED;
  3120. goto Cleanup;
  3121. }
  3122. //
  3123. // Lookup which domain this call pertains to.
  3124. //
  3125. DomainInfo = NlFindDomainByServerName( ComputerName );
  3126. if ( DomainInfo == NULL ) {
  3127. NetStatus = ERROR_INVALID_COMPUTERNAME;
  3128. goto Cleanup;
  3129. }
  3130. //
  3131. // Get the site names
  3132. //
  3133. NetStatus = NlSitesGetCloseSites( DomainInfo,
  3134. DOM_REAL_DOMAIN,
  3135. SiteNames );
  3136. if ( NetStatus != NO_ERROR ) {
  3137. goto Cleanup;
  3138. }
  3139. //
  3140. // Sort them into alphabetical order
  3141. //
  3142. qsort( (*SiteNames)->SiteNames,
  3143. (*SiteNames)->EntryCount,
  3144. sizeof(UNICODE_STRING),
  3145. NlpCompareSiteName );
  3146. Cleanup:
  3147. if ( DomainInfo != NULL ) {
  3148. NlDereferenceDomain( DomainInfo );
  3149. }
  3150. return NetStatus;
  3151. }
  3152. NET_API_STATUS
  3153. I_NetLogonAddressToSiteName(
  3154. IN PSOCKET_ADDRESS SocketAddress,
  3155. OUT LPWSTR *SiteName
  3156. )
  3157. /*++
  3158. Routine Description:
  3159. This API returns the site name, if any, of the address in SocketAddress.
  3160. See DsrAddressToSiteNamesW for details
  3161. Arguments:
  3162. SocketAddess -- the address to be looked up
  3163. SiteName -- the site name of the address; NULL is returned if no site
  3164. is found.
  3165. Return Value:
  3166. NO_ERROR - Operation completed successfully;
  3167. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  3168. operation.
  3169. --*/
  3170. {
  3171. NET_API_STATUS NetStatus = NO_ERROR;
  3172. PNL_SITE_NAME_ARRAY SiteNameArray = NULL;
  3173. *SiteName = NULL;
  3174. NetStatus = DsrAddressToSiteNamesW( NULL,
  3175. 1,
  3176. (PNL_SOCKET_ADDRESS)SocketAddress,
  3177. &SiteNameArray );
  3178. if ( (NO_ERROR == NetStatus)
  3179. && SiteNameArray->EntryCount > 0
  3180. && SiteNameArray->SiteNames[0].Length > 0 ) {
  3181. ULONG Size = SiteNameArray->SiteNames[0].Length + sizeof(WCHAR);
  3182. *SiteName = MIDL_user_allocate(Size);
  3183. if (*SiteName) {
  3184. RtlZeroMemory(*SiteName, Size);
  3185. RtlCopyMemory(*SiteName, SiteNameArray->SiteNames[0].Buffer, SiteNameArray->SiteNames[0].Length);
  3186. } else {
  3187. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3188. }
  3189. }
  3190. if (SiteNameArray != NULL) {
  3191. MIDL_user_free(SiteNameArray);
  3192. }
  3193. return NetStatus;
  3194. }