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

4328 lines
119 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. BOOL
  636. NlValidateSiteName(
  637. IN LPWSTR SiteName
  638. )
  639. /*++
  640. Routine Description:
  641. This routine validates the site name to be non-mangled and
  642. valid for use as a label in a DNS name. A site with a
  643. mangled name may be created in addition to the site with the
  644. intended name as a result of a site name collision in the DS.
  645. Arguments:
  646. SiteName -- The site name to be validated
  647. Return Value:
  648. TRUE -- Site name is verified as valid for use
  649. FALSE -- Site name is not verified as valid for use
  650. --*/
  651. {
  652. NET_API_STATUS NetStatus;
  653. //
  654. // NULL site name is invalid
  655. //
  656. if ( SiteName == NULL ) {
  657. NlPrint(( NL_CRITICAL,
  658. "NlValidateSiteName: NULL site name is invalid\n" ));
  659. return FALSE;
  660. }
  661. //
  662. // Check if the site name is mangled
  663. //
  664. if ( NlIsMangledRDNExternal(SiteName, wcslen(SiteName), NULL) ) {
  665. NlPrint(( NL_CRITICAL,
  666. "NlValidateSiteName: Site name %ws is mangled\n",
  667. SiteName ));
  668. return FALSE;
  669. }
  670. //
  671. // Sanity check that the site name can be
  672. // used as a label in a DNS name
  673. //
  674. NetStatus = DnsValidateName_W( SiteName, DnsNameDomainLabel );
  675. if ( NetStatus == NO_ERROR || NetStatus == DNS_ERROR_NON_RFC_NAME ) {
  676. return TRUE;
  677. } else {
  678. NlPrint(( NL_CRITICAL,
  679. "NlValidateSiteName: Site name %ws is not valid DNS label\n",
  680. SiteName ));
  681. return FALSE;
  682. }
  683. }
  684. NET_API_STATUS
  685. NlSitesUpdateSiteCoverageForRole(
  686. IN PDOMAIN_INFO DomainInfo,
  687. IN ULONG DomFlags,
  688. IN HANDLE DsHandle,
  689. IN PISM_CONNECTIVITY SiteConnect,
  690. IN LPWSTR ThisSiteName,
  691. IN ULONG ThisSiteIndex,
  692. OUT PBOOLEAN SiteCoverageChanged OPTIONAL
  693. )
  694. /*++
  695. Routine Description:
  696. This routine recomputes the site coverage for this DC based on the costs
  697. associated with the site links.
  698. Basically, for each site that has no DCs for this domain, the site this DC
  699. is in might be chosen to "cover" the site. The following criteria is used:
  700. * Site link cost.
  701. * For sites where the above is equal, the site having the most DCs is chosen.
  702. * For sites where the above is equal, the site having the alphabetically least
  703. name is chosen.
  704. Arguments:
  705. DomainInfo - Hosted domain whose site coverage is to be updated
  706. DomFlags - The role for which we need to update the site coverage
  707. DsHandle - Handle to the DS
  708. SiteConnect - If specified, the site link costs info returned by NlSitesGetIsmConnect
  709. ThisSiteName - The name of the site of this server
  710. ThisSiteIndex - The index of the site of this server in the SiteConnect info
  711. SiteCoverageChanged - If specified and the site covereage changes, returns TRUE.
  712. Otherwise, left intact.
  713. Return Value:
  714. NO_ERROR
  715. --*/
  716. {
  717. NET_API_STATUS NetStatus = NO_ERROR;
  718. WCHAR DnsDomainName[NL_MAX_DNS_LENGTH+1];
  719. WCHAR CapturedDnsForestName[NL_MAX_DNS_LENGTH+1];
  720. ULONG SiteCount = 0;
  721. ULONG TmpSiteCount = 0;
  722. DWORD SiteIndex1;
  723. DWORD SiteIndex2;
  724. PULONG DcsInSite = NULL;
  725. BOOLEAN LocalSiteCoverageChanged = FALSE;
  726. PDS_DOMAIN_CONTROLLER_INFO_1W DcInfo = NULL;
  727. ULONG DcInfoCount;
  728. PDS_NAME_RESULT GcInfo = NULL;
  729. SERVERSITEPAIR *ServerSitePairs = NULL;
  730. PNL_SITE_ENTRY SiteEntry;
  731. PLIST_ENTRY ListEntry;
  732. LPSTR GcOrDcOrNdnc = NULL;
  733. BOOLEAN AtleastOneDc = FALSE;
  734. LPTSTR_ARRAY SiteCoverageParameter = NULL;
  735. PNL_COVERED_SITE CoveredSites = NULL;
  736. ULONG CoveredSitesCount = 0;
  737. ULONG CoveredSitesIndex;
  738. PNL_COVERED_SITE OldCoveredSites = NULL;
  739. ULONG OldCoveredSitesCount = 0;
  740. ULONG OldCoveredSitesIndex;
  741. //
  742. // Local variable initialization
  743. //
  744. if ( DomFlags & DOM_FOREST ) {
  745. GcOrDcOrNdnc = "GC";
  746. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  747. GcOrDcOrNdnc = "DC";
  748. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  749. GcOrDcOrNdnc = "NDNC";
  750. }
  751. //
  752. // Allocate a temporary storage for all possible site entries
  753. //
  754. // Count for the site link cost entries
  755. //
  756. if ( SiteConnect != NULL ) {
  757. TmpSiteCount += SiteConnect->cNumSites;
  758. }
  759. //
  760. // Count for the registry coverage parameter
  761. //
  762. if ( DomFlags & DOM_FOREST ) {
  763. SiteCoverageParameter = NlGlobalParameters.GcSiteCoverage;
  764. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  765. SiteCoverageParameter = NlGlobalParameters.SiteCoverage;
  766. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  767. SiteCoverageParameter = NlGlobalParameters.NdncSiteCoverage;
  768. }
  769. if ( SiteCoverageParameter != NULL ) {
  770. TmpSiteCount += NetpTStrArrayEntryCount( (LPTSTR_ARRAY) SiteCoverageParameter );
  771. }
  772. //
  773. // Count for the site of this machine
  774. //
  775. TmpSiteCount ++;
  776. //
  777. // Allocate the storage
  778. //
  779. CoveredSites = LocalAlloc( LMEM_ZEROINIT, TmpSiteCount * sizeof(NL_COVERED_SITE) );
  780. if ( CoveredSites == NULL ) {
  781. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  782. goto Cleanup;
  783. }
  784. //
  785. // Capture the dns domain name of the hosted
  786. //
  787. NlCaptureDomainInfo ( DomainInfo, DnsDomainName, NULL );
  788. //
  789. // Capture the forest name
  790. //
  791. NlCaptureDnsForestName( CapturedDnsForestName );
  792. //
  793. // If we are to automatically determine site coverage and
  794. // we have the site link costs, build the coverage list
  795. // corresponding to the specified role.
  796. //
  797. if ( NlGlobalParameters.AutoSiteCoverage &&
  798. SiteConnect != NULL &&
  799. SiteConnect->cNumSites != 0 ) {
  800. SiteCount = SiteConnect->cNumSites;
  801. //
  802. // Allocate a buffer for temporary storage.
  803. //
  804. DcsInSite = LocalAlloc( LMEM_ZEROINIT, SiteCount * sizeof(ULONG) );
  805. if ( DcsInSite == NULL ) {
  806. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  807. goto Cleanup;
  808. }
  809. //
  810. // Depending on the role, get the list of GCs/DCs/NDNCs and their sites
  811. //
  812. if ( DsHandle != NULL ) {
  813. //
  814. // Handle building the GC coverage list
  815. //
  816. if ( DomFlags & DOM_FOREST ) {
  817. LPWSTR DummyName = L".";
  818. //
  819. // Get the list of GCs in the forest and their sites.
  820. // Avoid this operation if we are not currently a GC in
  821. // which case update the GC covered site info based
  822. // on the registry setting below.
  823. //
  824. // ??: For multihosting, we will indicate whether we
  825. // are a GC in the forest DOMAIN_INFO struct instead of
  826. // the global as we currently use.
  827. //
  828. if ( (NlGetDomainFlags(DomainInfo) & DS_GC_FLAG) == 0 ) {
  829. NlPrint(( NL_SITE_MORE,
  830. "GC: This DC isn't a GC. So it doesn't auto cover any sites as a GC.\n" ));
  831. } else {
  832. NetStatus = DsCrackNamesW(
  833. DsHandle,
  834. 0,
  835. DS_LIST_GLOBAL_CATALOG_SERVERS,
  836. DS_LIST_GLOBAL_CATALOG_SERVERS,
  837. 1,
  838. &DummyName,
  839. &GcInfo );
  840. if ( NetStatus != NO_ERROR ) {
  841. NlPrint(( NL_CRITICAL,
  842. "NlSitesUpdateSiteCoverage: CrackNames failed: %ld\n",
  843. NetStatus ));
  844. } else {
  845. ULONG GcIndex;
  846. //
  847. // Determine which sites are already covered by GCs.
  848. //
  849. for ( GcIndex=0; GcIndex < GcInfo->cItems; GcIndex++ ) {
  850. LPWSTR GcSiteName = GcInfo->rItems[GcIndex].pName;
  851. LPWSTR GcDnsHostName = GcInfo->rItems[GcIndex].pDomain;
  852. NlPrint(( NL_SITE,
  853. "GC list: %ws %ws\n",
  854. GcSiteName,
  855. GcDnsHostName ));
  856. if ( GcInfo->rItems[GcIndex].status != DS_NAME_NO_ERROR ) {
  857. NlPrint(( NL_CRITICAL,
  858. "NlSitesUpdateSiteCoverage: CrackNameStatus bad: %ws %ws: %ld\n",
  859. GcSiteName,
  860. GcDnsHostName,
  861. GcInfo->rItems[GcIndex].status ));
  862. continue;
  863. }
  864. //
  865. // Count the number of GCs in each site.
  866. //
  867. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  868. if ( GcSiteName != NULL &&
  869. _wcsicmp( GcSiteName,
  870. SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
  871. DcsInSite[SiteIndex1] ++;
  872. AtleastOneDc = TRUE;
  873. break;
  874. }
  875. }
  876. //
  877. // If this DC isn't in any known site,
  878. // simply ignore it.
  879. //
  880. if ( SiteIndex1 >= SiteCount ) {
  881. NlPrint(( NL_CRITICAL,
  882. "GC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
  883. GcSiteName,
  884. GcDnsHostName ));
  885. }
  886. }
  887. }
  888. }
  889. //
  890. // Handle building the DC coverage list
  891. //
  892. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  893. ULONG DcIndex;
  894. //
  895. // Get the list of DCs in the domain and their sites
  896. //
  897. NetStatus = (*NlGlobalpDsGetDomainControllerInfoW)(
  898. DsHandle,
  899. DnsDomainName,
  900. 1,
  901. &DcInfoCount,
  902. &DcInfo );
  903. if ( NetStatus != NO_ERROR ) {
  904. NlPrint(( NL_CRITICAL,
  905. "NlSitesUpdateSiteCoverage: Cannot DsGetDomainControllerInfoW %ld.\n",
  906. NetStatus ));
  907. DcInfoCount = 0;
  908. }
  909. //
  910. // Determine which sites are already covered by DCs.
  911. //
  912. for ( DcIndex=0; DcIndex<DcInfoCount; DcIndex++ ) {
  913. NlPrint(( NL_SITE,
  914. "DC list: %ws %ws\n",
  915. DcInfo[DcIndex].SiteName,
  916. DcInfo[DcIndex].DnsHostName ));
  917. //
  918. // Count the number of DCs in each site.
  919. //
  920. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  921. if ( DcInfo[DcIndex].SiteName != NULL &&
  922. _wcsicmp( DcInfo[DcIndex].SiteName,
  923. SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
  924. DcsInSite[SiteIndex1] ++;
  925. AtleastOneDc = TRUE;
  926. break;
  927. }
  928. }
  929. //
  930. // If this DC isn't in any known site,
  931. // simply ignore it.
  932. //
  933. if ( SiteIndex1 >= SiteCount ) {
  934. NlPrint(( NL_CRITICAL,
  935. "DC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
  936. DcInfo[DcIndex].SiteName,
  937. DcInfo[DcIndex].DnsHostName ));
  938. }
  939. }
  940. //
  941. // Handle building the NDNC coverage list
  942. //
  943. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  944. SERVERSITEPAIR *ServerSitePairEntry;
  945. NetStatus = NlDsGetServersAndSitesForNetLogon( DnsDomainName,
  946. &ServerSitePairs );
  947. //
  948. // Determine which sites are already covered by LDAP servers
  949. //
  950. if ( NetStatus == NO_ERROR ) {
  951. ServerSitePairEntry = ServerSitePairs;
  952. while ( ServerSitePairEntry != NULL &&
  953. ServerSitePairEntry->wszDnsServer != NULL ) {
  954. NlPrint(( NL_SITE,
  955. "NDNC list: %ws %ws\n",
  956. ServerSitePairEntry->wszSite,
  957. ServerSitePairEntry->wszDnsServer ));
  958. //
  959. // Count the number of LDAP servers in each site.
  960. //
  961. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  962. if ( ServerSitePairEntry->wszSite != NULL &&
  963. _wcsicmp( ServerSitePairEntry->wszSite,
  964. SiteConnect->ppSiteDNs[SiteIndex1] ) == 0 ) {
  965. DcsInSite[SiteIndex1] ++;
  966. AtleastOneDc = TRUE;
  967. break;
  968. }
  969. }
  970. //
  971. // If this LDAP server isn't in any known site,
  972. // simply ignore it.
  973. //
  974. if ( SiteIndex1 >= SiteCount ) {
  975. NlPrint(( NL_CRITICAL,
  976. "NDNC: %ws %ws: isn't a site returned from ISM. (ignored)\n",
  977. ServerSitePairEntry->wszSite,
  978. ServerSitePairEntry->wszDnsServer ));
  979. }
  980. ServerSitePairEntry ++;
  981. }
  982. }
  983. }
  984. }
  985. }
  986. //
  987. // If we were able to build the DcsInSite array,
  988. // Compute the auto site coverage.
  989. //
  990. if ( AtleastOneDc ) {
  991. //
  992. // For each site that has no DCs ...
  993. //
  994. for (SiteIndex1 = 0; SiteIndex1 < SiteCount; SiteIndex1++) {
  995. DWORD BestSite;
  996. if ( DcsInSite[SiteIndex1] != 0 ) {
  997. continue;
  998. }
  999. //
  1000. // Skip this site if it's not valid for DNS registrations
  1001. //
  1002. if ( !NlValidateSiteName(SiteConnect->ppSiteDNs[SiteIndex1]) ) {
  1003. continue;
  1004. }
  1005. NlPrint(( NL_SITE_MORE,
  1006. "%s: %ws: Site has no %ss\n",
  1007. GcOrDcOrNdnc,
  1008. SiteConnect->ppSiteDNs[SiteIndex1],
  1009. GcOrDcOrNdnc ));
  1010. //
  1011. // Pick the site that should cover that site
  1012. //
  1013. BestSite = 0xFFFFFFFF;
  1014. for (SiteIndex2 = 0; SiteIndex2 < SiteCount; SiteIndex2++) {
  1015. PISM_LINK Link2 = &(SiteConnect->pLinkValues[SiteIndex1 * SiteCount + SiteIndex2]);
  1016. //
  1017. // A site cannot auto cover itself.
  1018. //
  1019. if ( SiteIndex1 == SiteIndex2 ) {
  1020. #ifdef notdef
  1021. NlPrint(( NL_SITE_MORE,
  1022. "%s: %ws: Site ignoring itself.\n",
  1023. GcOrDcOrNdnc,
  1024. SiteConnect->ppSiteDNs[SiteIndex1] ));
  1025. #endif // notdef
  1026. //
  1027. // An invalid site is not eligible
  1028. //
  1029. } else if ( !NlValidateSiteName(SiteConnect->ppSiteDNs[SiteIndex2]) ) {
  1030. NlPrint(( NL_SITE_MORE,
  1031. "%s: %ws: Site '%ws' is invalid.\n",
  1032. GcOrDcOrNdnc,
  1033. SiteConnect->ppSiteDNs[SiteIndex1],
  1034. SiteConnect->ppSiteDNs[SiteIndex2] ));
  1035. //
  1036. // A site with an infinite cost cannot be reached.
  1037. // so don't consider it.
  1038. //
  1039. } else if ( Link2->ulCost == 0xFFFFFFFF ) {
  1040. NlPrint(( NL_SITE_MORE,
  1041. "%s: %ws: Site '%ws' has infinite cost.\n",
  1042. GcOrDcOrNdnc,
  1043. SiteConnect->ppSiteDNs[SiteIndex1],
  1044. SiteConnect->ppSiteDNs[SiteIndex2] ));
  1045. //
  1046. // A site with no DCs cannot cover any site
  1047. // so don't consider it.
  1048. //
  1049. } else if ( DcsInSite[SiteIndex2] == 0 ) {
  1050. NlPrint(( NL_SITE_MORE,
  1051. "%s: %ws: Site '%ws' has no Dcs.\n",
  1052. GcOrDcOrNdnc,
  1053. SiteConnect->ppSiteDNs[SiteIndex1],
  1054. SiteConnect->ppSiteDNs[SiteIndex2] ));
  1055. //
  1056. // If no best site has yet been picked,
  1057. // use this one.
  1058. //
  1059. } else if ( BestSite == 0xFFFFFFFF ) {
  1060. NlPrint(( NL_SITE_MORE,
  1061. "%s: %ws: Site '%ws' is the first valid site.\n",
  1062. GcOrDcOrNdnc,
  1063. SiteConnect->ppSiteDNs[SiteIndex1],
  1064. SiteConnect->ppSiteDNs[SiteIndex2] ));
  1065. BestSite = SiteIndex2;
  1066. } else {
  1067. PISM_LINK LinkBest = &(SiteConnect->pLinkValues[SiteIndex1 * SiteCount + BestSite]);
  1068. //
  1069. // If the SiteLink cost is less than the current best,
  1070. // use it.
  1071. //
  1072. if ( Link2->ulCost < LinkBest->ulCost ) {
  1073. NlPrint(( NL_SITE_MORE,
  1074. "%s: %ws: '%ws' has cheaper link costs than '%ws'\n",
  1075. GcOrDcOrNdnc,
  1076. SiteConnect->ppSiteDNs[SiteIndex1],
  1077. SiteConnect->ppSiteDNs[SiteIndex2],
  1078. SiteConnect->ppSiteDNs[BestSite] ));
  1079. BestSite = SiteIndex2;
  1080. //
  1081. // If the SiteLink cose is equal ...
  1082. //
  1083. } else if ( Link2->ulCost == LinkBest->ulCost ) {
  1084. //
  1085. // ... then pick the site with the greater number of DCs
  1086. //
  1087. if ( DcsInSite[SiteIndex2] > DcsInSite[BestSite] ) {
  1088. NlPrint(( NL_SITE_MORE,
  1089. "%s: %ws: '%ws' has more DCs than '%ws'\n",
  1090. GcOrDcOrNdnc,
  1091. SiteConnect->ppSiteDNs[SiteIndex1],
  1092. SiteConnect->ppSiteDNs[SiteIndex2],
  1093. SiteConnect->ppSiteDNs[BestSite] ));
  1094. BestSite = SiteIndex2;
  1095. //
  1096. // If the number of DCs is equal ...
  1097. //
  1098. } else if ( DcsInSite[SiteIndex2] == DcsInSite[BestSite] ) {
  1099. //
  1100. // Break the tie by using the site with the alphabetically
  1101. // least name.
  1102. //
  1103. if ( _wcsicmp( SiteConnect->ppSiteDNs[SiteIndex2],
  1104. SiteConnect->ppSiteDNs[BestSite] ) < 0 ) {
  1105. NlPrint(( NL_SITE_MORE,
  1106. "%s: %ws: '%ws' is alphabetically before '%ws'\n",
  1107. GcOrDcOrNdnc,
  1108. SiteConnect->ppSiteDNs[SiteIndex1],
  1109. SiteConnect->ppSiteDNs[SiteIndex2],
  1110. SiteConnect->ppSiteDNs[BestSite] ));
  1111. BestSite = SiteIndex2;
  1112. }
  1113. }
  1114. }
  1115. }
  1116. }
  1117. //
  1118. // If the best site is the site this DC is in,
  1119. // then mark this site as covered.
  1120. //
  1121. if ( BestSite == ThisSiteIndex ) {
  1122. NlPrint(( NL_SITE,
  1123. "%s: %ws: Site is auto covered by our site.\n",
  1124. GcOrDcOrNdnc,
  1125. SiteConnect->ppSiteDNs[SiteIndex1] ));
  1126. NlSitesAddCloseSite( SiteConnect->ppSiteDNs[SiteIndex1],
  1127. CoveredSites,
  1128. &CoveredSitesCount,
  1129. TRUE ); // auto covered
  1130. } else {
  1131. //
  1132. // Note if no site was found
  1133. //
  1134. if ( BestSite == 0xFFFFFFFF ) {
  1135. NlPrint(( NL_SITE,
  1136. "%s: %ws: Site is not auto covered by any site.\n",
  1137. GcOrDcOrNdnc,
  1138. SiteConnect->ppSiteDNs[SiteIndex1] ));
  1139. } else {
  1140. NlPrint(( NL_SITE,
  1141. "%s: %ws: Site is auto covered by site '%ws'.\n",
  1142. GcOrDcOrNdnc,
  1143. SiteConnect->ppSiteDNs[SiteIndex1],
  1144. SiteConnect->ppSiteDNs[BestSite] ));
  1145. }
  1146. }
  1147. }
  1148. }
  1149. //
  1150. // Now that all of the information has been collected.
  1151. // Update the in memory information with the CritSect locked.
  1152. //
  1153. EnterCriticalSection( &NlGlobalSiteCritSect );
  1154. //
  1155. // Merge in the list of covered sites from the registry
  1156. //
  1157. if ( SiteCoverageParameter != NULL ) {
  1158. LPTSTR_ARRAY TStrArray;
  1159. LPWSTR BackSlash = NULL;
  1160. BOOLEAN SkipThisEntry;
  1161. TStrArray = SiteCoverageParameter;
  1162. while (!NetpIsTStrArrayEmpty(TStrArray)) {
  1163. SkipThisEntry = FALSE;
  1164. BackSlash = wcsstr(TStrArray, L"\\");
  1165. //
  1166. // If backslash is present, then the covered site is specified
  1167. // for a given domain/forest/NDNC. The domain/forest/NDNC name
  1168. // precedes the backslash while the site name follows after the
  1169. // backslash. If backslash is absent, the site is covered for
  1170. // all domains/forests/NDNCs.
  1171. //
  1172. if ( BackSlash != NULL ) {
  1173. *BackSlash = UNICODE_NULL;
  1174. //
  1175. // Check the appropriate name depending on whether this is
  1176. // a forest or a domain or an NDNC
  1177. //
  1178. if ( DomFlags & DOM_FOREST ) {
  1179. if ( !NlEqualDnsName(TStrArray, CapturedDnsForestName) ) {
  1180. SkipThisEntry = TRUE;
  1181. }
  1182. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1183. if ( !NlEqualDnsName(TStrArray, DnsDomainName) &&
  1184. NlNameCompare(TStrArray, DomainInfo->DomUnicodeDomainName, NAMETYPE_DOMAIN) != 0 ) {
  1185. SkipThisEntry = TRUE;
  1186. }
  1187. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1188. if ( !NlEqualDnsName(TStrArray, DnsDomainName) ) {
  1189. SkipThisEntry = TRUE;
  1190. }
  1191. }
  1192. }
  1193. //
  1194. // Add this site to the current list of covered sites
  1195. //
  1196. if ( !SkipThisEntry ) {
  1197. if ( BackSlash != NULL ) {
  1198. if ( *(BackSlash+1) != UNICODE_NULL ) {
  1199. NlPrint(( NL_SITE,
  1200. "%s: %ws: Site is covered by our site (regkey).\n",
  1201. GcOrDcOrNdnc,
  1202. BackSlash+1 ));
  1203. NlSitesAddCloseSite( BackSlash+1, CoveredSites, &CoveredSitesCount, FALSE );
  1204. }
  1205. } else {
  1206. NlPrint(( NL_SITE,
  1207. "%s: %ws: Site is covered by our site (regkey).\n",
  1208. GcOrDcOrNdnc,
  1209. TStrArray ));
  1210. NlSitesAddCloseSite( TStrArray, CoveredSites, &CoveredSitesCount, FALSE );
  1211. }
  1212. }
  1213. if ( BackSlash != NULL ) {
  1214. *BackSlash = L'\\';
  1215. }
  1216. TStrArray = NetpNextTStrArrayEntry(TStrArray);
  1217. }
  1218. }
  1219. //
  1220. // The site this DC is in is covered by definition
  1221. //
  1222. NlSitesAddCloseSite( ThisSiteName, CoveredSites, &CoveredSitesCount, FALSE );
  1223. //
  1224. // Determine if the site coverages changes.
  1225. // Log info if auto site coverage changes.
  1226. //
  1227. if ( (DomFlags & DOM_FOREST) != 0 ) {
  1228. OldCoveredSites = DomainInfo->GcCoveredSites;
  1229. OldCoveredSitesCount = DomainInfo->GcCoveredSitesCount;
  1230. } else if ( (DomFlags & DOM_REAL_DOMAIN) != 0 ||
  1231. (DomFlags & DOM_NON_DOMAIN_NC) != 0 ) {
  1232. OldCoveredSites = DomainInfo->CoveredSites;
  1233. OldCoveredSitesCount = DomainInfo->CoveredSitesCount;
  1234. }
  1235. //
  1236. // Determine if new sites get covered and log events for newly auto
  1237. // covered sites
  1238. //
  1239. for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
  1240. DWORD EventId = 0;
  1241. LPWSTR MsgStrings[3];
  1242. MsgStrings[0] = ThisSiteName;
  1243. MsgStrings[1] = CoveredSites[CoveredSitesIndex].CoveredSite->SiteName;
  1244. //
  1245. // Determine if we didn't cover this site in the past
  1246. //
  1247. for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
  1248. if ( RtlEqualUnicodeString( &CoveredSites[CoveredSitesIndex].CoveredSite->SiteNameString,
  1249. &OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteNameString,
  1250. TRUE ) ) {
  1251. break;
  1252. }
  1253. }
  1254. //
  1255. // Indicate if this is a new covered site
  1256. //
  1257. if ( OldCoveredSitesIndex == OldCoveredSitesCount ) {
  1258. LocalSiteCoverageChanged = TRUE;
  1259. //
  1260. // If the new site is auto covered, log the event
  1261. //
  1262. if ( CoveredSites[CoveredSitesIndex].CoveredAuto ) {
  1263. //
  1264. // Log for GC that this is a newly auto covered site
  1265. //
  1266. if ( DomFlags & DOM_FOREST ) {
  1267. EventId = NELOG_NetlogonGcSiteCovered;
  1268. MsgStrings[2] = CapturedDnsForestName;
  1269. //
  1270. // Log for DC that this is a newly auto covered site
  1271. //
  1272. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1273. EventId = NELOG_NetlogonDcSiteCovered;
  1274. MsgStrings[2] = DomainInfo->DomUnicodeDomainName;
  1275. //
  1276. // Log for NDNC that this is a newly auto covered site
  1277. //
  1278. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1279. EventId = NELOG_NetlogonNdncSiteCovered;
  1280. MsgStrings[2] = DnsDomainName;
  1281. }
  1282. }
  1283. //
  1284. // If we had this site not auto covered in the past and it is
  1285. // auto covered now, log the event
  1286. //
  1287. } else if ( CoveredSites[CoveredSitesIndex].CoveredAuto &&
  1288. !OldCoveredSites[OldCoveredSitesIndex].CoveredAuto ) {
  1289. //
  1290. // Log for GC that this old manually covered site is now auto covered
  1291. //
  1292. if ( DomFlags & DOM_FOREST ) {
  1293. EventId = NELOG_NetlogonGcOldSiteCovered;
  1294. MsgStrings[2] = CapturedDnsForestName;
  1295. //
  1296. // Log for DC that this old manually covered site is now auto covered
  1297. //
  1298. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1299. EventId = NELOG_NetlogonDcOldSiteCovered;
  1300. MsgStrings[2] = DomainInfo->DomUnicodeDomainName;
  1301. //
  1302. // Log for NDNC that this old manually covered site is now auto covered
  1303. //
  1304. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1305. EventId = NELOG_NetlogonNdncOldSiteCovered;
  1306. MsgStrings[2] = DnsDomainName;
  1307. }
  1308. }
  1309. //
  1310. // Log the event if needed
  1311. //
  1312. if ( EventId != 0 ) {
  1313. NlpWriteEventlog ( EventId,
  1314. EVENTLOG_INFORMATION_TYPE,
  1315. NULL,
  1316. 0,
  1317. MsgStrings,
  1318. 3 | NETP_ALLOW_DUPLICATE_EVENTS );
  1319. }
  1320. }
  1321. //
  1322. // Determine if old sites are nolonger covered and log events for old auto
  1323. // covered sites which are nolonger auto covered
  1324. //
  1325. for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
  1326. DWORD EventId = 0;
  1327. LPWSTR MsgStrings[2];
  1328. MsgStrings[0] = OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteName;
  1329. //
  1330. // Determine if we no longer cover this site
  1331. //
  1332. for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
  1333. if ( RtlEqualUnicodeString( &OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteNameString,
  1334. &CoveredSites[CoveredSitesIndex].CoveredSite->SiteNameString,
  1335. TRUE ) ) {
  1336. break;
  1337. }
  1338. }
  1339. //
  1340. // Indicate if this site is no longer covered
  1341. //
  1342. if ( CoveredSitesIndex == CoveredSitesCount ) {
  1343. LocalSiteCoverageChanged = TRUE;
  1344. //
  1345. // If the old site was auto covered, log the event
  1346. //
  1347. if ( OldCoveredSites[OldCoveredSitesIndex].CoveredAuto ) {
  1348. //
  1349. // Log for GC that this old auto covered site is no longer auto covered
  1350. //
  1351. if ( DomFlags & DOM_FOREST ) {
  1352. EventId = NELOG_NetlogonGcSiteNotCovered;
  1353. MsgStrings[1] = CapturedDnsForestName;
  1354. //
  1355. // Log for DC that this old auto covered site is no longer auto covered
  1356. //
  1357. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1358. EventId = NELOG_NetlogonDcSiteNotCovered;
  1359. MsgStrings[1] = DomainInfo->DomUnicodeDomainName;
  1360. //
  1361. // Log for NDNC that this old auto covered site is no longer auto covered
  1362. //
  1363. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1364. EventId = NELOG_NetlogonNdncSiteNotCovered;
  1365. MsgStrings[1] = DnsDomainName;
  1366. }
  1367. }
  1368. //
  1369. // If we had this site auto covered in the past and it is
  1370. // no longer auto covered, log the event
  1371. //
  1372. } else if ( OldCoveredSites[OldCoveredSitesIndex].CoveredAuto &&
  1373. !CoveredSites[CoveredSitesIndex].CoveredAuto ) {
  1374. //
  1375. // Log for GC that this old auto covered site is now manually covered
  1376. //
  1377. if ( DomFlags & DOM_FOREST ) {
  1378. EventId = NELOG_NetlogonGcSiteNotCoveredAuto;
  1379. MsgStrings[1] = CapturedDnsForestName;
  1380. //
  1381. // Log for DC that this old auto covered site is now manually covered
  1382. //
  1383. } else if ( DomFlags & DOM_REAL_DOMAIN ) {
  1384. EventId = NELOG_NetlogonDcSiteNotCoveredAuto;
  1385. MsgStrings[1] = DomainInfo->DomUnicodeDomainName;
  1386. //
  1387. // Log for NDNC that this old auto covered site is now manually covered
  1388. //
  1389. } else if ( DomFlags & DOM_NON_DOMAIN_NC ) {
  1390. EventId = NELOG_NetlogonNdncSiteNotCoveredAuto;
  1391. MsgStrings[1] = DnsDomainName;
  1392. }
  1393. }
  1394. //
  1395. // Log the event if needed
  1396. //
  1397. if ( EventId != 0 ) {
  1398. NlPrint(( NL_SITE,
  1399. "%s: %ws: Site is no longer auto covered by our site.\n",
  1400. GcOrDcOrNdnc,
  1401. OldCoveredSites[OldCoveredSitesIndex].CoveredSite->SiteName ));
  1402. NlpWriteEventlog ( EventId,
  1403. EVENTLOG_INFORMATION_TYPE,
  1404. NULL,
  1405. 0,
  1406. MsgStrings,
  1407. 2 | NETP_ALLOW_DUPLICATE_EVENTS );
  1408. }
  1409. }
  1410. //
  1411. // Dereference the old entries.
  1412. //
  1413. for ( OldCoveredSitesIndex = 0; OldCoveredSitesIndex < OldCoveredSitesCount; OldCoveredSitesIndex++ ) {
  1414. NlDerefSiteEntry( OldCoveredSites[OldCoveredSitesIndex].CoveredSite );
  1415. }
  1416. //
  1417. // Finally update the site coverage list.
  1418. // Free the old list and allocate the new one.
  1419. //
  1420. if ( DomFlags & DOM_FOREST ) {
  1421. if ( DomainInfo->GcCoveredSites != NULL ) {
  1422. LocalFree( DomainInfo->GcCoveredSites );
  1423. DomainInfo->GcCoveredSites = NULL;
  1424. DomainInfo->GcCoveredSitesCount = 0;
  1425. }
  1426. if ( CoveredSitesCount != 0 ) {
  1427. DomainInfo->GcCoveredSites = LocalAlloc( 0, CoveredSitesCount * sizeof(NL_COVERED_SITE ) );
  1428. if ( DomainInfo->GcCoveredSites != NULL ) {
  1429. RtlCopyMemory( DomainInfo->GcCoveredSites,
  1430. CoveredSites,
  1431. CoveredSitesCount * sizeof(NL_COVERED_SITE) );
  1432. DomainInfo->GcCoveredSitesCount = CoveredSitesCount;
  1433. } else {
  1434. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1435. }
  1436. }
  1437. //
  1438. // Reference the newly added entries, if any
  1439. //
  1440. for ( CoveredSitesIndex = 0; CoveredSitesIndex < DomainInfo->GcCoveredSitesCount; CoveredSitesIndex++ ) {
  1441. NlRefSiteEntry( (DomainInfo->GcCoveredSites)[CoveredSitesIndex].CoveredSite );
  1442. }
  1443. } else if ( (DomFlags & DOM_REAL_DOMAIN) != 0 ||
  1444. (DomFlags & DOM_NON_DOMAIN_NC) != 0 ) {
  1445. if ( DomainInfo->CoveredSites != NULL ) {
  1446. LocalFree( DomainInfo->CoveredSites );
  1447. DomainInfo->CoveredSites = NULL;
  1448. DomainInfo->CoveredSitesCount = 0;
  1449. }
  1450. if ( CoveredSitesCount != 0 ) {
  1451. DomainInfo->CoveredSites = LocalAlloc( 0, CoveredSitesCount * sizeof(NL_COVERED_SITE) );
  1452. if ( DomainInfo->CoveredSites != NULL ) {
  1453. RtlCopyMemory( DomainInfo->CoveredSites,
  1454. CoveredSites,
  1455. CoveredSitesCount * sizeof(NL_COVERED_SITE) );
  1456. DomainInfo->CoveredSitesCount = CoveredSitesCount;
  1457. } else {
  1458. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1459. }
  1460. }
  1461. //
  1462. // Reference the newly added entries, if any
  1463. //
  1464. for ( CoveredSitesIndex = 0; CoveredSitesIndex < DomainInfo->CoveredSitesCount; CoveredSitesIndex++ ) {
  1465. NlRefSiteEntry( (DomainInfo->CoveredSites)[CoveredSitesIndex].CoveredSite );
  1466. }
  1467. }
  1468. //
  1469. // Now that the datebase is consistent, drop the lock for the next pass
  1470. //
  1471. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1472. Cleanup:
  1473. if ( DcsInSite != NULL ) {
  1474. LocalFree( DcsInSite );
  1475. }
  1476. if ( DcInfo != NULL ) {
  1477. (*NlGlobalpDsFreeDomainControllerInfoW)( 1, DcInfoCount, DcInfo );
  1478. }
  1479. if ( GcInfo != NULL ) {
  1480. DsFreeNameResultW( GcInfo );
  1481. }
  1482. if ( ServerSitePairs != NULL ) {
  1483. NlDsFreeServersAndSitesForNetLogon( ServerSitePairs );
  1484. }
  1485. //
  1486. // Free the temprory list of covered sites.
  1487. // Deref each temp entry.
  1488. //
  1489. if ( CoveredSites != NULL ) {
  1490. for ( CoveredSitesIndex = 0; CoveredSitesIndex < CoveredSitesCount; CoveredSitesIndex++ ) {
  1491. NlDerefSiteEntry( CoveredSites[CoveredSitesIndex].CoveredSite );
  1492. }
  1493. LocalFree( CoveredSites );
  1494. }
  1495. //
  1496. // Update the site coverage change info only if it indeed changed
  1497. //
  1498. if ( NetStatus == NO_ERROR && SiteCoverageChanged != NULL && LocalSiteCoverageChanged ) {
  1499. *SiteCoverageChanged = TRUE;
  1500. }
  1501. return NO_ERROR;
  1502. }
  1503. PNL_SITE_ENTRY
  1504. NlFindSiteEntry(
  1505. IN LPWSTR SiteName
  1506. )
  1507. /*++
  1508. Routine Description:
  1509. This routine finds a site entry for a particular site name. If one
  1510. does not exist, one is created.
  1511. Arguments:
  1512. SiteName - Name of the site.
  1513. Return Value:
  1514. Pointer to the Site entry for the site.
  1515. NULL: Memory could not be allocated.
  1516. --*/
  1517. {
  1518. PLIST_ENTRY ListEntry;
  1519. ULONG SiteNameSize;
  1520. PNL_SITE_ENTRY SiteEntry;
  1521. UNICODE_STRING SiteNameString;
  1522. //
  1523. // If the site entry already exists,
  1524. // return a pointer to it.
  1525. //
  1526. RtlInitUnicodeString( &SiteNameString, SiteName );
  1527. EnterCriticalSection( &NlGlobalSiteCritSect );
  1528. for ( ListEntry = NlGlobalSiteList.Flink ;
  1529. ListEntry != &NlGlobalSiteList;
  1530. ListEntry = ListEntry->Flink) {
  1531. SiteEntry =
  1532. CONTAINING_RECORD( ListEntry, NL_SITE_ENTRY, Next );
  1533. if ( RtlEqualUnicodeString( &SiteEntry->SiteNameString,
  1534. &SiteNameString,
  1535. TRUE ) ) {
  1536. NlRefSiteEntry( SiteEntry );
  1537. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1538. return SiteEntry;
  1539. }
  1540. }
  1541. //
  1542. // If not,
  1543. // allocate one.
  1544. //
  1545. SiteNameSize = SiteNameString.Length + sizeof(WCHAR);
  1546. SiteEntry = LocalAlloc( 0, sizeof(NL_SITE_ENTRY) + SiteNameSize );
  1547. if ( SiteEntry == NULL ) {
  1548. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1549. return NULL;
  1550. }
  1551. //
  1552. // Fill it in.
  1553. //
  1554. // Being in global list is not a reference.
  1555. SiteEntry->ReferenceCount = 1;
  1556. SiteEntry->SiteNameString.Length = SiteNameString.Length;
  1557. SiteEntry->SiteNameString.MaximumLength = SiteNameString.Length + sizeof(WCHAR);
  1558. SiteEntry->SiteNameString.Buffer = SiteEntry->SiteName;
  1559. RtlCopyMemory( &SiteEntry->SiteName, SiteName, SiteNameSize );
  1560. InsertHeadList( &NlGlobalSiteList, &SiteEntry->Next );
  1561. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1562. return SiteEntry;
  1563. }
  1564. VOID
  1565. NlSitesRefSubnet(
  1566. IN PNL_SUBNET Subnet
  1567. )
  1568. /*++
  1569. Routine Description:
  1570. Reference a subnet
  1571. NlGlobalSiteCritSect must be locked.
  1572. Arguments:
  1573. Subnet - Entry to be Referenced.
  1574. Return Value:
  1575. None.
  1576. --*/
  1577. {
  1578. Subnet->ReferenceCount++;
  1579. }
  1580. PNL_SUBNET
  1581. NlFindSubnetEntry(
  1582. IN LPWSTR SiteName,
  1583. IN ULONG SubnetAddress,
  1584. IN ULONG SubnetMask,
  1585. IN BYTE SubnetBitCount
  1586. )
  1587. /*++
  1588. Routine Description:
  1589. This routine finds a subnet entry for a particular subnet name. If one
  1590. does not exist, one is created.
  1591. Arguments:
  1592. SiteName - Name of the site the subnet covers.
  1593. SubnetAddress - Subnet Address for the subnet to find.
  1594. SubnetMask - Subnet mask for the subnet to find.
  1595. SubnetBitCount - Subnet bit count for the subnet to find.
  1596. Return Value:
  1597. Pointer to the Subnet entry for the site.
  1598. Entry should be dereferenced using NlSitesDerefSubnet
  1599. NULL: Memory could not be allocated.
  1600. --*/
  1601. {
  1602. PLIST_ENTRY ListEntry;
  1603. ULONG SiteNameSize;
  1604. PNL_SUBNET Subnet;
  1605. //
  1606. // If the subnet entry already exists,
  1607. // return a pointer to it.
  1608. //
  1609. EnterCriticalSection( &NlGlobalSiteCritSect );
  1610. for ( ListEntry = NlGlobalSubnetList.Flink ;
  1611. ListEntry != &NlGlobalSubnetList;
  1612. ListEntry = ListEntry->Flink) {
  1613. Subnet =
  1614. CONTAINING_RECORD( ListEntry, NL_SUBNET, Next );
  1615. if ( Subnet->SubnetAddress == SubnetAddress &&
  1616. Subnet->SubnetBitCount == SubnetBitCount &&
  1617. Subnet->SubnetMask == SubnetMask &&
  1618. _wcsicmp( Subnet->SiteEntry->SiteName, SiteName ) == 0 ) {
  1619. #if NETLOGONDBG
  1620. {
  1621. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1622. NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
  1623. NlPrint(( NL_SITE, "%s/%ld: Re-adding Subnet for site '%ws'\n", IpAddress, Subnet->SubnetBitCount, SiteName ));
  1624. }
  1625. #endif // NETLOGONDBG
  1626. NlSitesRefSubnet( Subnet );
  1627. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1628. return Subnet;
  1629. }
  1630. }
  1631. //
  1632. // If not,
  1633. // allocate one.
  1634. //
  1635. Subnet = LocalAlloc( 0, sizeof(NL_SUBNET) );
  1636. if ( Subnet == NULL ) {
  1637. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1638. return NULL;
  1639. }
  1640. //
  1641. // Fill it in.
  1642. //
  1643. // Being in global list is not a reference.
  1644. Subnet->ReferenceCount = 1;
  1645. Subnet->SubnetAddress = SubnetAddress;
  1646. Subnet->SubnetMask = SubnetMask;
  1647. Subnet->SubnetBitCount = SubnetBitCount;
  1648. Subnet->SiteEntry = NlFindSiteEntry( SiteName );
  1649. if ( Subnet->SiteEntry == NULL ) {
  1650. LocalFree( Subnet );
  1651. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1652. return NULL;
  1653. }
  1654. #if NETLOGONDBG
  1655. {
  1656. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1657. NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
  1658. NlPrint(( NL_SITE, "%s/%ld: Adding Subnet for site '%ws'\n",
  1659. IpAddress,
  1660. Subnet->SubnetBitCount,
  1661. SiteName ));
  1662. }
  1663. #endif // NETLOGONDBG
  1664. InsertHeadList( &NlGlobalSubnetList, &Subnet->Next );
  1665. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1666. return Subnet;
  1667. }
  1668. VOID
  1669. NlSitesDerefSubnet(
  1670. IN PNL_SUBNET Subnet
  1671. )
  1672. /*++
  1673. Routine Description:
  1674. Dereference a subnet
  1675. If the reference count goes to zero,
  1676. the subnet entry will be deleted.
  1677. Arguments:
  1678. Subnet - Entry to be dereferenced.
  1679. Return Value:
  1680. None.
  1681. --*/
  1682. {
  1683. EnterCriticalSection( &NlGlobalSiteCritSect );
  1684. if ( (--(Subnet->ReferenceCount)) == 0 ) {
  1685. #if NETLOGONDBG
  1686. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1687. NetpIpAddressToStr( Subnet->SubnetAddress, IpAddress );
  1688. NlPrint(( NL_SITE, "%s/%ld: Subnet deleted\n", IpAddress, Subnet->SubnetBitCount ));
  1689. #endif // NETLOGONDBG
  1690. //
  1691. // If there is a site associated with this subnet,
  1692. // dereference it.
  1693. //
  1694. if ( Subnet->SiteEntry != NULL ) {
  1695. NlDerefSiteEntry( Subnet->SiteEntry );
  1696. }
  1697. //
  1698. // Remove the subnet from the global list
  1699. //
  1700. RemoveEntryList( &Subnet->Next );
  1701. //
  1702. // Free the Subnet entry itself.
  1703. //
  1704. LocalFree( Subnet );
  1705. }
  1706. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1707. }
  1708. VOID
  1709. NlSiteDeleteSubnetTree(
  1710. IN PNL_SUBNET_TREE_ENTRY SubnetTreeEntry
  1711. )
  1712. /*++
  1713. Routine Description:
  1714. Delete everything pointed to by this SubnetTreeEntry
  1715. Enter with NlGlobalSiteCritSect locked.
  1716. Arguments:
  1717. SubnetTreeEntry - SubnetTreeEntry to de-initialize
  1718. Return Value:
  1719. TRUE: SubnetTreeEntry is now empty
  1720. FALSE: SubnetTreeEntry still has entries.
  1721. --*/
  1722. {
  1723. //
  1724. // If there are children,
  1725. // delete them.
  1726. //
  1727. if ( SubnetTreeEntry->Subtree != NULL ) {
  1728. ULONG i;
  1729. for ( i=0; i<256; i++ ) {
  1730. NlSiteDeleteSubnetTree( &SubnetTreeEntry->Subtree->Subtree[i] );
  1731. }
  1732. NlPrint(( NL_SITE_MORE, "Deleting subtree\n" ));
  1733. LocalFree( SubnetTreeEntry->Subtree );
  1734. SubnetTreeEntry->Subtree = NULL;
  1735. }
  1736. //
  1737. // If there is a subnet,
  1738. // dereference it.
  1739. //
  1740. if ( SubnetTreeEntry->Subnet != NULL ) {
  1741. // NlPrint(( NL_SITE_MORE, "Derefing subnet upon tree deletion\n" ));
  1742. NlSitesDerefSubnet( SubnetTreeEntry->Subnet );
  1743. SubnetTreeEntry->Subnet = NULL;
  1744. }
  1745. return;
  1746. }
  1747. VOID
  1748. NlSitesEndSubnetEnum(
  1749. VOID
  1750. )
  1751. /*++
  1752. Routine Description:
  1753. This routine is called at the end of a set of NlSitesAddSubnet calls.
  1754. The sequence is:
  1755. loop for each subnet
  1756. NlSitesAddSubnet
  1757. NlSitesEndSubnetEnum
  1758. NlSiteAddSubnet adds the entries to a temporary tree. This routine
  1759. swaps the temporary tree into the permanent location. This mechanism
  1760. does the following:
  1761. a) Allows the old subnet tree to be used while the new tree is being built.
  1762. b) Allows me to not permanently grab the SiteCritSect for the entire
  1763. enumeration of subnet/site objects from the DS.
  1764. c) Reuse the in-memory subnet/site structures in the old and new tree. This
  1765. avoids re-allocation of these structures (or worse temporarily doubling
  1766. of memory usage).
  1767. Arguments:
  1768. SiteName - Name of the site the subnet is in.
  1769. SubnetName - subnet to be added
  1770. Return Value:
  1771. NO_ERROR: success
  1772. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  1773. --*/
  1774. {
  1775. //
  1776. // Free all old entries in NlGlobalSubnetTree.
  1777. //
  1778. NlPrint(( NL_SITE_MORE, "NlSitesEndSubnetEnum: Entered\n" ));
  1779. EnterCriticalSection( &NlGlobalSiteCritSect );
  1780. NlSiteDeleteSubnetTree( &NlGlobalSubnetTree );
  1781. //
  1782. // Make the "new" subnet tree the real subnet tree.
  1783. //
  1784. NlGlobalSubnetTree = NlGlobalNewSubnetTree;
  1785. RtlZeroMemory( &NlGlobalNewSubnetTree, sizeof(NlGlobalNewSubnetTree) );
  1786. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1787. NlPrint(( NL_SITE_MORE, "NlSitesEndSubnetEnum: Exitted\n" ));
  1788. }
  1789. NET_API_STATUS
  1790. NlSitesAddSubnet(
  1791. IN LPWSTR SiteName,
  1792. IN LPWSTR SubnetName
  1793. )
  1794. /*++
  1795. Routine Description:
  1796. This routine adds a subnet to the tree of subnets.
  1797. Arguments:
  1798. SiteName - Name of the site the subnet is in.
  1799. SubnetName - subnet to be added
  1800. Return Value:
  1801. NO_ERROR: success
  1802. ERROR_INVALID_NAME: Subnet Name is not valid
  1803. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  1804. --*/
  1805. {
  1806. NET_API_STATUS NetStatus;
  1807. PNL_SUBNET Subnet = NULL;
  1808. PNL_SUBNET_TREE_ENTRY SubnetTreeEntry;
  1809. LPBYTE SubnetBytePointer;
  1810. ULONG i;
  1811. ULONG SubnetAddress;
  1812. ULONG SubnetMask;
  1813. BYTE SubnetBitCount;
  1814. //
  1815. // Parse the subnet name
  1816. //
  1817. EnterCriticalSection( &NlGlobalSiteCritSect );
  1818. NetStatus = NlParseSubnetString( SubnetName,
  1819. &SubnetAddress,
  1820. &SubnetMask,
  1821. &SubnetBitCount );
  1822. if ( NetStatus != NO_ERROR ) {
  1823. goto Cleanup;
  1824. }
  1825. //
  1826. // Find or allocate an entry for the subnet
  1827. //
  1828. Subnet = NlFindSubnetEntry( SiteName,
  1829. SubnetAddress,
  1830. SubnetMask,
  1831. SubnetBitCount );
  1832. if ( Subnet == NULL ) {
  1833. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1834. goto Cleanup;
  1835. }
  1836. //
  1837. // Loop for each byte in the subnet address
  1838. //
  1839. SubnetTreeEntry = &NlGlobalNewSubnetTree;
  1840. SubnetBytePointer = (LPBYTE) (&Subnet->SubnetAddress);
  1841. while ( SubnetBitCount != 0 ) {
  1842. NlPrint(( NL_SITE_MORE, "%ld: Doing byte\n", *SubnetBytePointer ));
  1843. //
  1844. // If there isn't a tree branch for the current node,
  1845. // create one.
  1846. //
  1847. if ( SubnetTreeEntry->Subtree == NULL ) {
  1848. NlPrint(( NL_SITE_MORE, "%ld: Creating subtree\n", *SubnetBytePointer ));
  1849. SubnetTreeEntry->Subtree = LocalAlloc( LMEM_ZEROINIT, sizeof(NL_SUBNET_TREE) );
  1850. if ( SubnetTreeEntry->Subtree == NULL ) {
  1851. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1852. goto Cleanup;
  1853. }
  1854. }
  1855. //
  1856. // If this is the last byte of the subnet address,
  1857. // link the subnet onto the tree here.
  1858. //
  1859. if ( SubnetBitCount <= 8 ) {
  1860. ULONG LoopCount;
  1861. //
  1862. // The caller indexes into this array with an IP address.
  1863. // Create a link to our subnet for each possible IP addresses
  1864. // that map onto this subnet.
  1865. //
  1866. // Between 1 and 128 IP addresses map onto this subnet address.
  1867. //
  1868. LoopCount = 1 << (8-SubnetBitCount);
  1869. for ( i=0; i<LoopCount; i++ ) {
  1870. PNL_SUBNET_TREE_ENTRY Subtree;
  1871. ULONG SubnetIndex;
  1872. //
  1873. // Compute which entry is to be updated.
  1874. //
  1875. SubnetIndex = (*SubnetBytePointer) + i;
  1876. NlPrint(( NL_SITE_MORE, "%ld: Doing sub-byte\n", SubnetIndex ));
  1877. NlAssert( SubnetIndex <= 255 );
  1878. Subtree = &SubnetTreeEntry->Subtree->Subtree[SubnetIndex];
  1879. //
  1880. // If there already is a subnet linked off the tree here,
  1881. // handle it.
  1882. //
  1883. if ( Subtree->Subnet != NULL ) {
  1884. NlPrint(( NL_SITE_MORE, "%ld: Subnet already exists %ld\n",
  1885. SubnetIndex,
  1886. Subtree->Subnet->SubnetBitCount ));
  1887. //
  1888. // If the entry is for a less specific subnet
  1889. // delete the current entry.
  1890. //
  1891. if ( Subtree->Subnet->SubnetBitCount < Subnet->SubnetBitCount ) {
  1892. NlPrint(( NL_SITE_MORE, "%ld: Deref previous subnet\n",
  1893. SubnetIndex ));
  1894. NlSitesDerefSubnet( Subtree->Subnet );
  1895. Subtree->Subnet = NULL;
  1896. //
  1897. // Otherwise,
  1898. // use the current entry since it is better than this one.
  1899. //
  1900. } else {
  1901. NlPrint(( NL_SITE_MORE, "%ld: Use previous subnet\n",
  1902. SubnetIndex ));
  1903. continue;
  1904. }
  1905. }
  1906. //
  1907. // Link the subnet into the tree.
  1908. // Increment the reference count.
  1909. //
  1910. NlSitesRefSubnet( Subnet );
  1911. Subtree->Subnet = Subnet;
  1912. }
  1913. break;
  1914. }
  1915. //
  1916. // Move on to the next byte of the subnet address
  1917. //
  1918. SubnetTreeEntry = &SubnetTreeEntry->Subtree->Subtree[*SubnetBytePointer];
  1919. SubnetBitCount -= 8;
  1920. SubnetBytePointer ++;
  1921. }
  1922. NetStatus = NO_ERROR;
  1923. //
  1924. // Free locally used resources.
  1925. //
  1926. Cleanup:
  1927. if ( Subnet != NULL ) {
  1928. NlSitesDerefSubnet( Subnet );
  1929. }
  1930. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1931. return NetStatus;
  1932. }
  1933. PNL_SITE_ENTRY
  1934. NlFindSiteEntryBySockAddrEx(
  1935. IN PSOCKADDR SockAddr,
  1936. OUT PNL_SUBNET *RetSubnet OPTIONAL
  1937. )
  1938. /*++
  1939. Routine Description:
  1940. This routine look up the specified socket address and translate it to a
  1941. site name.
  1942. Arguments:
  1943. SockAddr - Socket Address to lookup
  1944. RetSubnet - If specified, returns a pointer to the subnet object used to do
  1945. the mapping.
  1946. Might return NULL indicating a subnet object wasn't used.
  1947. Entry should be dereferenced using NlSitesDerefSubnet.
  1948. Return Value:
  1949. NULL: No site can be found for this SockAddr.
  1950. Non-NULL: Site corresponding to the SockAddr.
  1951. Entry should be derefenced using NlDerefSiteEntry
  1952. --*/
  1953. {
  1954. PNL_SITE_ENTRY SiteEntry = NULL;
  1955. PNL_SUBNET Subnet = NULL;
  1956. PNL_SUBNET_TREE_ENTRY SubnetTreeEntry;
  1957. ULONG ByteIndex;
  1958. ULONG IpAddress;
  1959. //
  1960. // Convert SockAddr to IP address.
  1961. //
  1962. if ( ARGUMENT_PRESENT(RetSubnet) ) {
  1963. *RetSubnet = NULL;
  1964. }
  1965. if ( SockAddr->sa_family != AF_INET ) {
  1966. return NULL;
  1967. }
  1968. IpAddress = ((PSOCKADDR_IN)SockAddr)->sin_addr.S_un.S_addr;
  1969. //
  1970. // If there are no subnet entries and only one site,
  1971. // then all clients belong to that site.
  1972. // Don't bother mapping.
  1973. //
  1974. EnterCriticalSection( &NlGlobalSiteCritSect );
  1975. if ( NlGlobalOnlyOneSite ) {
  1976. if ( NlGlobalSiteEntry == NULL ) {
  1977. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1978. return NULL;
  1979. }
  1980. SiteEntry = NlGlobalSiteEntry;
  1981. NlRefSiteEntry( SiteEntry );
  1982. //
  1983. // If the caller isn't interested in the subnet name,
  1984. // we are done
  1985. //
  1986. if ( RetSubnet == NULL ) {
  1987. LeaveCriticalSection( &NlGlobalSiteCritSect );
  1988. return SiteEntry;
  1989. }
  1990. }
  1991. //
  1992. // Loop for each byte in the Ip address
  1993. //
  1994. SubnetTreeEntry = &NlGlobalSubnetTree;
  1995. for ( ByteIndex=0; ByteIndex<sizeof(IpAddress); ByteIndex++) {
  1996. ULONG SubnetIndex;
  1997. //
  1998. // If there is no subtree,
  1999. // we're done.
  2000. //
  2001. SubnetIndex = ((LPBYTE)(&IpAddress))[ByteIndex];
  2002. NlPrint(( NL_SITE_MORE, "%ld: Lookup: Doing byte\n", SubnetIndex ));
  2003. if ( SubnetTreeEntry->Subtree == NULL ) {
  2004. break;
  2005. }
  2006. //
  2007. // Compute which entry is being referenced
  2008. //
  2009. SubnetTreeEntry = &SubnetTreeEntry->Subtree->Subtree[SubnetIndex];
  2010. //
  2011. // If there already is a subnet linked off here,
  2012. // use it.
  2013. //
  2014. // (but continue walking down the tree trying to find a more explicit entry.)
  2015. //
  2016. if ( SubnetTreeEntry->Subnet != NULL ) {
  2017. NlPrint(( NL_SITE_MORE, "%ld: Lookup: saving subnet at this level\n", SubnetIndex ));
  2018. Subnet = SubnetTreeEntry->Subnet;
  2019. }
  2020. }
  2021. //
  2022. // If we found a subnet,
  2023. // return the site associated with the subnet.
  2024. if ( Subnet != NULL ) {
  2025. //
  2026. // If we already know the site name (because there is
  2027. // only one site), this subnet must map to it
  2028. //
  2029. if ( SiteEntry != NULL ) {
  2030. NlAssert( SiteEntry == Subnet->SiteEntry );
  2031. } else {
  2032. SiteEntry = Subnet->SiteEntry;
  2033. NlRefSiteEntry( SiteEntry );
  2034. }
  2035. if ( ARGUMENT_PRESENT(RetSubnet) ) {
  2036. NlSitesRefSubnet( Subnet );
  2037. *RetSubnet = Subnet;
  2038. }
  2039. }
  2040. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2041. return SiteEntry;
  2042. }
  2043. PNL_SITE_ENTRY
  2044. NlFindSiteEntryBySockAddr(
  2045. IN PSOCKADDR SockAddr
  2046. )
  2047. /*++
  2048. Routine Description:
  2049. This routine look up the specified socket address and translate it to a
  2050. site name.
  2051. Arguments:
  2052. SockAddr - Socket Address to lookup
  2053. RetSubnet - If specified, returns a pointer to the subnet object used to do
  2054. the mapping.
  2055. Might return NULL indicating a subnet object wasn't used.
  2056. Entry should be dereferenced using NlSitesDerefSubnet.
  2057. Return Value:
  2058. NULL: No site can be found for this SockAddr.
  2059. Non-NULL: Site corresponding to the SockAddr.
  2060. Entry should be derefenced using NlDerefSiteEntry
  2061. --*/
  2062. {
  2063. return NlFindSiteEntryBySockAddrEx( SockAddr, NULL );
  2064. }
  2065. BOOL
  2066. NlCaptureSiteName(
  2067. WCHAR CapturedSiteName[NL_MAX_DNS_LABEL_LENGTH+1]
  2068. )
  2069. /*++
  2070. Routine Description:
  2071. Capture the current sitename of the site this machine is in.
  2072. Arguments:
  2073. CapturedSiteName - Returns the name of the site this machine is in.
  2074. Return Value:
  2075. TRUE - if there is a site name.
  2076. FALSE - if there is no site name.
  2077. --*/
  2078. {
  2079. BOOL RetVal;
  2080. EnterCriticalSection( &NlGlobalSiteCritSect );
  2081. if ( NlGlobalUnicodeSiteName == NULL ) {
  2082. CapturedSiteName[0] = L'\0';
  2083. RetVal = FALSE;
  2084. } else {
  2085. wcscpy( CapturedSiteName, NlGlobalUnicodeSiteName );
  2086. RetVal = TRUE;
  2087. }
  2088. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2089. return RetVal;
  2090. }
  2091. NET_API_STATUS
  2092. DsrGetSiteName(
  2093. IN LPWSTR ComputerName OPTIONAL,
  2094. OUT LPWSTR *SiteName
  2095. )
  2096. /*++
  2097. Routine Description:
  2098. Same as DsGetSiteNameW except:
  2099. * This is the RPC server side implementation.
  2100. Arguments:
  2101. Same as DsGetSiteNameW except as above.
  2102. Return Value:
  2103. Same as DsGetSiteNameW except as above.
  2104. Note: On a workstation or a member server, this function makes a
  2105. reasonable attempt to retrieve a valid name of the site ComputerName
  2106. is in. If the locally stored name is too old, the function receives
  2107. the name from a DC. If any error occurs during this process, the local
  2108. value for the name is returned. It is possible that the name received
  2109. from the DC is out of date. In that case the function will return it
  2110. anyway.
  2111. --*/
  2112. {
  2113. NET_API_STATUS NetStatus;
  2114. NTSTATUS Status;
  2115. UNICODE_STRING DomainNameString;
  2116. PDOMAIN_INFO DomainInfo = NULL;
  2117. PCLIENT_SESSION ClientSession = NULL;
  2118. PNL_DC_CACHE_ENTRY NlDcCacheEntry;
  2119. BOOL AmWriter = FALSE;
  2120. //
  2121. // Lookup which domain this call pertains to.
  2122. //
  2123. DomainInfo = NlFindDomainByServerName( ComputerName );
  2124. if ( DomainInfo == NULL ) {
  2125. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2126. goto Cleanup;
  2127. }
  2128. EnterCriticalSection( &NlGlobalSiteCritSect );
  2129. //
  2130. // On a workstation or a member server, update the site name if it's not
  2131. // statically configured and is old.
  2132. // However, do not update if we are in NT4 domain since there is no site
  2133. // concept in NT4.
  2134. //
  2135. if ( NlGlobalMemberWorkstation &&
  2136. !NlGlobalParameters.SiteNameConfigured &&
  2137. DomainInfo->DomUnicodeDnsDomainNameString.Length != 0 &&
  2138. NetpLogonTimeHasElapsed(
  2139. NlGlobalSiteNameSetTime,
  2140. NlGlobalParameters.SiteNameTimeout * 1000 ) ) {
  2141. NlPrint(( NL_SITE, "DsrGetSiteName: Site name '%ws' is old. Getting a new one from DC.\n",
  2142. NlGlobalUnicodeSiteName ));
  2143. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2144. //
  2145. // Fill in the primary domain name.
  2146. //
  2147. RtlInitUnicodeString( &DomainNameString, DomainInfo->DomUnicodeDomainName );
  2148. //
  2149. // On the PDC or BDC,
  2150. // find the Client session for the domain.
  2151. // On workstations,
  2152. // find the primary domain client session.
  2153. //
  2154. ClientSession = NlFindNamedClientSession( DomainInfo,
  2155. &DomainNameString,
  2156. NL_DIRECT_TRUST_REQUIRED,
  2157. NULL );
  2158. if ( ClientSession == NULL ) {
  2159. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2160. "DsrGetSiteName: %wZ: No such trusted domain\n",
  2161. &DomainNameString ));
  2162. NetStatus = ERROR_NO_SUCH_DOMAIN;
  2163. goto Cleanup;
  2164. }
  2165. //
  2166. // Become a writer of the client session.
  2167. //
  2168. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  2169. NlPrintCs(( NL_CRITICAL, ClientSession,
  2170. "DsrGetSiteName: Can't become writer of client session.\n" ));
  2171. NetStatus = ERROR_NO_LOGON_SERVERS;
  2172. goto Cleanup;
  2173. }
  2174. AmWriter = TRUE;
  2175. //
  2176. // Get the DC info from the server
  2177. //
  2178. Status = NlGetAnyDCName( ClientSession,
  2179. TRUE, // Require IP be used to determine site correctly
  2180. FALSE, // Don't do with-account discovery
  2181. &NlDcCacheEntry,
  2182. NULL ); // don't care if the DC was rediscovered
  2183. //
  2184. // Do not error out on failure. Rather, use local cache.
  2185. // Use the response only if it is from an NT5 DC (that
  2186. // knows about the site of the client)
  2187. //
  2188. EnterCriticalSection( &NlGlobalSiteCritSect );
  2189. if ( NT_SUCCESS(Status) ) {
  2190. if ( (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) != 0 ) {
  2191. NlSetDynamicSiteName( NlDcCacheEntry->UnicodeClientSiteName );
  2192. } else {
  2193. NlPrint(( NL_SITE,
  2194. "DsrGetSiteName: NlGetAnyDCName returned NT4 DC. Returning site '%ws' from local cache\n",
  2195. NlGlobalUnicodeSiteName ));
  2196. }
  2197. NetpDcDerefCacheEntry( NlDcCacheEntry );
  2198. } else {
  2199. NlPrint(( NL_CRITICAL,
  2200. "DsrGetSiteName: NlGetAnyDCName failed. Returning site '%ws' from local cache.\n",
  2201. NlGlobalUnicodeSiteName ));
  2202. }
  2203. } else {
  2204. NlPrint(( NL_SITE, "DsrGetSiteName: Returning site name '%ws' from local cache.\n",
  2205. NlGlobalUnicodeSiteName ));
  2206. }
  2207. if ( NlGlobalUnicodeSiteName == NULL ) {
  2208. *SiteName = NULL;
  2209. NetStatus = ERROR_NO_SITENAME;
  2210. } else {
  2211. *SiteName = NetpAllocWStrFromWStr( NlGlobalUnicodeSiteName );
  2212. if ( *SiteName == NULL ) {
  2213. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2214. } else {
  2215. NetStatus = NO_ERROR;
  2216. }
  2217. }
  2218. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2219. Cleanup:
  2220. if ( DomainInfo != NULL ) {
  2221. NlDereferenceDomain( DomainInfo );
  2222. }
  2223. if ( ClientSession != NULL ) {
  2224. if ( AmWriter ) {
  2225. NlResetWriterClientSession( ClientSession );
  2226. }
  2227. NlUnrefClientSession( ClientSession );
  2228. }
  2229. return NetStatus;
  2230. }
  2231. NET_API_STATUS
  2232. NlSetSiteName(
  2233. IN LPWSTR SiteName OPTIONAL,
  2234. OUT PBOOLEAN SiteNameChanged OPTIONAL
  2235. )
  2236. /*++
  2237. Routine Description:
  2238. This routine set the current site name in a global.
  2239. Any bogus site name is truncated to be a valid site name.
  2240. Arguments:
  2241. SiteName - Name of the site this machine is in.
  2242. NULL: machine is no longer in a site.
  2243. SiteNameChanged - If specified, returns TRUE if the site name changed
  2244. Return Value:
  2245. NO_ERROR: success
  2246. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  2247. --*/
  2248. {
  2249. NET_API_STATUS NetStatus;
  2250. LPWSTR TempUnicodeSiteName = NULL;
  2251. LPWSTR LocalUnicodeSiteName = NULL;
  2252. LPSTR LocalUtf8SiteName = NULL;
  2253. PNL_SITE_ENTRY LocalSiteEntry = NULL;
  2254. //
  2255. // Initialization
  2256. //
  2257. if ( ARGUMENT_PRESENT( SiteNameChanged )) {
  2258. *SiteNameChanged = FALSE;
  2259. }
  2260. //
  2261. // If the site name hasn't changed,
  2262. // early out.
  2263. // (Case sensitive compare to allow case changes.)
  2264. //
  2265. EnterCriticalSection( &NlGlobalSiteCritSect );
  2266. if ( SiteName != NULL &&
  2267. NlGlobalUnicodeSiteName != NULL &&
  2268. wcscmp( NlGlobalUnicodeSiteName, SiteName ) == 0 ) {
  2269. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2270. NetStatus = NO_ERROR;
  2271. goto Cleanup;
  2272. }
  2273. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2274. //
  2275. // Copy the site name into a Locally allocated buffer.
  2276. //
  2277. NlPrint(( NL_SITE, "Setting site name to '%ws'\n", SiteName ));
  2278. if ( SiteName == NULL ) {
  2279. LocalUnicodeSiteName = NULL;
  2280. LocalUtf8SiteName = NULL;
  2281. LocalSiteEntry = NULL;
  2282. } else {
  2283. BOOLEAN LogMessage = FALSE;
  2284. UNICODE_STRING UnicodeStringOfSiteName;
  2285. LPWSTR Period;
  2286. DNS_STATUS DnsStatus;
  2287. //
  2288. // Ditch any period in the site name.
  2289. //
  2290. RtlInitUnicodeString( &UnicodeStringOfSiteName, SiteName );
  2291. Period = wcschr( SiteName, L'.' );
  2292. if ( Period != NULL ) {
  2293. UnicodeStringOfSiteName.Length = (USHORT)(Period-SiteName) * sizeof(WCHAR);
  2294. NlPrint(( NL_CRITICAL,
  2295. "Site name '%ws' contains a period (truncated to '%wZ').\n",
  2296. SiteName,
  2297. &UnicodeStringOfSiteName ));
  2298. if ( UnicodeStringOfSiteName.Length == 0 ) {
  2299. NlPrint(( NL_CRITICAL,
  2300. "Site name '%ws' truncated to zero characters (Set to '1').\n",
  2301. SiteName ));
  2302. RtlInitUnicodeString( &UnicodeStringOfSiteName, L"1" );
  2303. }
  2304. LogMessage = TRUE;
  2305. }
  2306. //
  2307. // Loop truncating the name until it is short enough.
  2308. //
  2309. // The length restriction only makes sense in the UTF-8 character set.
  2310. // UTF-8 has multibyte characters so only truncate the UNICODE string
  2311. // and test the UTF-8 string.
  2312. //
  2313. for (;;) {
  2314. LocalUtf8SiteName = NetpAllocUtf8StrFromUnicodeString( &UnicodeStringOfSiteName );
  2315. if ( LocalUtf8SiteName == NULL ) {
  2316. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2317. goto Cleanup;
  2318. }
  2319. //
  2320. // If the site name is OK, we're done.
  2321. //
  2322. if ( strlen(LocalUtf8SiteName) <= NL_MAX_DNS_LABEL_LENGTH ) {
  2323. break;
  2324. }
  2325. //
  2326. // Truncate the site name (and press on)
  2327. //
  2328. UnicodeStringOfSiteName.Length -= sizeof(WCHAR);
  2329. NlPrint(( NL_CRITICAL,
  2330. "Site name '%ws' is too long (trucated to '%wZ')\n",
  2331. SiteName,
  2332. &UnicodeStringOfSiteName ));
  2333. LogMessage = TRUE;
  2334. }
  2335. //
  2336. // Validate the character set of the site name.
  2337. // (If invalid, map the bogus characters)
  2338. //
  2339. DnsStatus = DnsValidateName_UTF8( LocalUtf8SiteName, DnsNameDomain );
  2340. if ( DnsStatus != ERROR_SUCCESS &&
  2341. DnsStatus != DNS_ERROR_NON_RFC_NAME ) {
  2342. ULONG i;
  2343. //
  2344. // Grab a copy of the string to map into
  2345. //
  2346. TempUnicodeSiteName = NetpAllocWStrFromWStr( SiteName );
  2347. if ( TempUnicodeSiteName == NULL ) {
  2348. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2349. goto Cleanup;
  2350. }
  2351. UnicodeStringOfSiteName.Buffer = TempUnicodeSiteName;
  2352. //
  2353. // Map the bogus characters
  2354. //
  2355. for ( i=0; i<UnicodeStringOfSiteName.Length/sizeof(WCHAR); i++) {
  2356. WCHAR JustOneChar[2];
  2357. //
  2358. // Test one character at a time.
  2359. //
  2360. JustOneChar[0] = UnicodeStringOfSiteName.Buffer[i];
  2361. JustOneChar[1] = '\0';
  2362. DnsStatus = DnsValidateName_W( JustOneChar, DnsNameDomain );
  2363. if ( DnsStatus != ERROR_SUCCESS &&
  2364. DnsStatus != DNS_ERROR_NON_RFC_NAME ) {
  2365. UnicodeStringOfSiteName.Buffer[i] = L'-';
  2366. }
  2367. }
  2368. //
  2369. // Map back to UTF-8
  2370. //
  2371. NetpMemoryFree( LocalUtf8SiteName );
  2372. LocalUtf8SiteName = NetpAllocUtf8StrFromUnicodeString( &UnicodeStringOfSiteName );
  2373. if ( LocalUtf8SiteName == NULL ) {
  2374. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2375. goto Cleanup;
  2376. }
  2377. NlPrint(( NL_CRITICAL,
  2378. "Site name '%ws' has invalid character (set to '%wZ')\n",
  2379. SiteName,
  2380. &UnicodeStringOfSiteName ));
  2381. LogMessage = TRUE;
  2382. }
  2383. //
  2384. // If any munging of the name occurred,
  2385. // log the failure.
  2386. //
  2387. if ( LogMessage ) {
  2388. LPWSTR MsgStrings[1];
  2389. MsgStrings[0] = (LPWSTR) SiteName;
  2390. NlpWriteEventlog( NELOG_NetlogonBadSiteName,
  2391. EVENTLOG_ERROR_TYPE,
  2392. NULL,
  2393. 0,
  2394. MsgStrings,
  2395. 1 );
  2396. }
  2397. LocalUnicodeSiteName = NetpAllocWStrFromUtf8Str( LocalUtf8SiteName );
  2398. if ( LocalUnicodeSiteName == NULL ) {
  2399. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2400. goto Cleanup;
  2401. }
  2402. NlAssert( wcslen(LocalUnicodeSiteName) <= NL_MAX_DNS_LABEL_LENGTH );
  2403. LocalSiteEntry = NlFindSiteEntry( LocalUnicodeSiteName );
  2404. if ( LocalSiteEntry == NULL ) {
  2405. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2406. goto Cleanup;
  2407. }
  2408. }
  2409. //
  2410. // If the site name hasn't changed (Using modified site name),
  2411. // early out.
  2412. // (Case sensitive compare to allow case changes.)
  2413. //
  2414. EnterCriticalSection( &NlGlobalSiteCritSect );
  2415. if ( LocalUnicodeSiteName != NULL &&
  2416. NlGlobalUnicodeSiteName != NULL &&
  2417. wcscmp( NlGlobalUnicodeSiteName, LocalUnicodeSiteName ) == 0 ) {
  2418. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2419. NetStatus = NO_ERROR;
  2420. goto Cleanup;
  2421. }
  2422. //
  2423. // Free any previous entry
  2424. //
  2425. if ( NlGlobalUnicodeSiteName != NULL ) {
  2426. NetpMemoryFree( NlGlobalUnicodeSiteName );
  2427. }
  2428. if ( NlGlobalUtf8SiteName != NULL ) {
  2429. NetpMemoryFree( NlGlobalUtf8SiteName );
  2430. }
  2431. if ( NlGlobalSiteEntry != NULL ) {
  2432. NlDerefSiteEntry( NlGlobalSiteEntry );
  2433. }
  2434. //
  2435. // Save the new site name.
  2436. //
  2437. NlGlobalUnicodeSiteName = LocalUnicodeSiteName;
  2438. LocalUnicodeSiteName = NULL;
  2439. NlGlobalUtf8SiteName = LocalUtf8SiteName;
  2440. LocalUtf8SiteName = NULL;
  2441. NlGlobalSiteEntry = LocalSiteEntry;
  2442. LocalSiteEntry = NULL;
  2443. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2444. if ( ARGUMENT_PRESENT( SiteNameChanged )) {
  2445. *SiteNameChanged = TRUE;
  2446. }
  2447. NetStatus = NO_ERROR;
  2448. //
  2449. // Cleanup local data.
  2450. //
  2451. Cleanup:
  2452. if ( TempUnicodeSiteName != NULL ) {
  2453. NetpMemoryFree( TempUnicodeSiteName );
  2454. }
  2455. if ( LocalUnicodeSiteName != NULL ) {
  2456. NetApiBufferFree( LocalUnicodeSiteName );
  2457. }
  2458. if ( LocalUtf8SiteName != NULL ) {
  2459. NetpMemoryFree( LocalUtf8SiteName );
  2460. }
  2461. if ( LocalSiteEntry != NULL ) {
  2462. NlDerefSiteEntry( LocalSiteEntry );
  2463. }
  2464. //
  2465. // Set the time when the site name was updated
  2466. //
  2467. if ( NetStatus == NO_ERROR ) {
  2468. NlQuerySystemTime( &NlGlobalSiteNameSetTime );
  2469. }
  2470. return NetStatus;
  2471. }
  2472. VOID
  2473. NlSetDynamicSiteName(
  2474. IN LPWSTR SiteName OPTIONAL
  2475. )
  2476. /*++
  2477. Routine Description:
  2478. This routine set the current site name of this machine in the registry
  2479. and in Netlogon globals
  2480. Arguments:
  2481. SiteName - Name of the site this machine is in.
  2482. NULL: machine is no longer in a site.
  2483. Return Value:
  2484. None.
  2485. --*/
  2486. {
  2487. NET_API_STATUS NetStatus;
  2488. HKEY ParmHandle = NULL;
  2489. ULONG SiteNameSize;
  2490. //
  2491. // Avoid changing the site name on DCs.
  2492. //
  2493. if ( !NlGlobalMemberWorkstation ) {
  2494. return;
  2495. }
  2496. //
  2497. // Don't change the sitename back to its current value.
  2498. // (Case sensitive compare to allow case changes.)
  2499. //
  2500. EnterCriticalSection( &NlGlobalSiteCritSect );
  2501. if ( NlGlobalUnicodeSiteName != NULL &&
  2502. SiteName != NULL &&
  2503. wcscmp(SiteName, NlGlobalUnicodeSiteName) == 0 ) {
  2504. NlPrint(( NL_SITE_MORE, "NlSetDynamicSiteName: Old and new site names '%ws' are identical.\n",
  2505. SiteName ));
  2506. NlQuerySystemTime( &NlGlobalSiteNameSetTime );
  2507. goto Cleanup;
  2508. }
  2509. //
  2510. // If the site name was explicitly configured,
  2511. // don't set the site name.
  2512. //
  2513. if ( NlGlobalParameters.SiteNameConfigured ) {
  2514. NlPrint(( NL_SITE_MORE,
  2515. "Cannot set site name to %ws from %ws since it is statically configured\n",
  2516. SiteName,
  2517. NlGlobalUnicodeSiteName ));
  2518. goto Cleanup;
  2519. }
  2520. //
  2521. // Save the name in globals.
  2522. //
  2523. NlSetSiteName( SiteName, NULL );
  2524. //
  2525. // Save the name in the registry to keep it across boots.
  2526. //
  2527. //
  2528. // Open the key for Netlogon\Parameters
  2529. //
  2530. ParmHandle = NlOpenNetlogonKey( NL_PARAM_KEY );
  2531. if (ParmHandle == NULL) {
  2532. NlPrint(( NL_CRITICAL,
  2533. "Cannot NlOpenNetlogonKey to set site name to %ws from %ws\n",
  2534. SiteName,
  2535. NlGlobalUnicodeSiteName ));
  2536. goto Cleanup;
  2537. }
  2538. //
  2539. // If the we're no longer in a site,
  2540. // delete the value.
  2541. //
  2542. if ( SiteName == NULL ) {
  2543. NetStatus = RegDeleteValueW( ParmHandle,
  2544. NETLOGON_KEYWORD_DYNAMICSITENAME );
  2545. if ( NetStatus != ERROR_SUCCESS ) {
  2546. if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
  2547. NlPrint(( NL_CRITICAL,
  2548. "NlSetDynamicSiteName: Cannot delete '" NL_PARAM_KEY "\\%ws' %ld.\n",
  2549. NETLOGON_KEYWORD_DYNAMICSITENAME,
  2550. NetStatus ));
  2551. }
  2552. goto Cleanup;
  2553. }
  2554. //
  2555. // Set the value in the registry.
  2556. //
  2557. } else {
  2558. SiteNameSize = (wcslen(SiteName)+1) * sizeof(WCHAR);
  2559. NetStatus = RegSetValueExW( ParmHandle,
  2560. NETLOGON_KEYWORD_DYNAMICSITENAME,
  2561. 0, // Reserved
  2562. REG_SZ,
  2563. (LPBYTE)SiteName,
  2564. SiteNameSize+1 );
  2565. if ( NetStatus != ERROR_SUCCESS ) {
  2566. NlPrint(( NL_CRITICAL,
  2567. "NlSetDynamicSiteName: Cannot Set '" NL_PARAM_KEY "\\%ws' %ld.\n",
  2568. NETLOGON_KEYWORD_DYNAMICSITENAME,
  2569. NetStatus ));
  2570. goto Cleanup;
  2571. }
  2572. }
  2573. Cleanup:
  2574. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2575. if ( ParmHandle != NULL ) {
  2576. RegCloseKey( ParmHandle );
  2577. }
  2578. return;
  2579. }
  2580. NET_API_STATUS
  2581. NlSitesAddSubnetFromDs(
  2582. OUT PBOOLEAN SiteNameChanged OPTIONAL
  2583. )
  2584. /*++
  2585. Routine Description:
  2586. This routine reads the subnet\site mapping from the DS and populates
  2587. Netlogon's cache with that information
  2588. Arguments:
  2589. SiteNameChanged - If specified, returns TRUE if the site name changed
  2590. Return Value:
  2591. NO_ERROR: success
  2592. ERROR_NOT_ENOUGH_MEMORY: Not enough memory for the subnet structure.
  2593. --*/
  2594. {
  2595. NET_API_STATUS NetStatus;
  2596. NTSTATUS Status;
  2597. PLSAP_SUBNET_INFO SubnetInfo = NULL;
  2598. PLSAP_SITENAME_INFO SiteNameInfo = NULL;
  2599. ULONG i;
  2600. BOOLEAN MoreThanOneSite = FALSE;
  2601. ULONG LocalSubnetCount = 0;
  2602. //
  2603. // Get the site name of this site.
  2604. //
  2605. Status = LsaIGetSiteName( &SiteNameInfo );
  2606. if ( !NT_SUCCESS(Status) ) {
  2607. //
  2608. // If the DS simply isn't running,
  2609. // skip this.
  2610. //
  2611. if ( Status == STATUS_INVALID_DOMAIN_STATE ) {
  2612. NlPrint(( NL_SITE,
  2613. "DS isn't running so site to subnet mapping ignored\n" ));
  2614. NetStatus = NO_ERROR;
  2615. goto Cleanup;
  2616. }
  2617. NlPrint(( NL_CRITICAL,
  2618. "Cannot LsaIGetSiteName %lx\n", Status ));
  2619. NetStatus = NetpNtStatusToApiStatus(Status);
  2620. goto Cleanup;
  2621. }
  2622. NlGlobalDsaGuid = SiteNameInfo->DsaGuid;
  2623. NetStatus = NlSetSiteName( SiteNameInfo->SiteName.Buffer, SiteNameChanged );
  2624. if ( NetStatus != NO_ERROR ) {
  2625. NlPrint(( NL_CRITICAL,
  2626. "Cannot NlSetSiteName %ld\n", NetStatus ));
  2627. goto Cleanup;
  2628. }
  2629. //
  2630. // If this machine is marked as a GC,
  2631. // flag it so.
  2632. //
  2633. // Really this is only needed if netlogon.dll is unloaded via nltest /unload.
  2634. // Otherwise the flag is saved across starts/stops in a global.
  2635. //
  2636. if ( NlGlobalNetlogonUnloaded &&
  2637. (SiteNameInfo->DsaOptions & NTDSDSA_OPT_IS_GC) != 0 ) {
  2638. NlPrint((NL_INIT,
  2639. "Set GC-running bit after netlogon.dll unload\n" ));
  2640. I_NetLogonSetServiceBits( DS_GC_FLAG, DS_GC_FLAG );
  2641. }
  2642. //
  2643. // Get the list of subnet to site mappings from the DS
  2644. //
  2645. NlPrint(( NL_SITE, "Adding subnet to site mappings from the DS\n" ));
  2646. Status = LsaIQuerySubnetInfo( &SubnetInfo );
  2647. if ( !NT_SUCCESS(Status) ) {
  2648. NlPrint((NL_CRITICAL, "Cannot LsaIQuerySubnetInfo %lx\n", Status ));
  2649. NetStatus = NetpNtStatusToApiStatus( Status );
  2650. goto Cleanup;
  2651. }
  2652. //
  2653. // Put them in our in-memory cache.
  2654. //
  2655. for ( i=0; i<SubnetInfo->SubnetCount; i++ ) {
  2656. //
  2657. // If there is no site associated with the subnet,
  2658. // silently ignore it.
  2659. //
  2660. if ( SubnetInfo->Subnets[i].SiteName.Length == 0 ) {
  2661. NlPrint(( NL_SITE, "%wZ: Subnet has no associated site (ignored)\n",
  2662. &SubnetInfo->Subnets[i].SubnetName ));
  2663. continue;
  2664. }
  2665. LocalSubnetCount ++;
  2666. //
  2667. // Determine if there are multiple sites in the enterprise
  2668. //
  2669. if ( !RtlEqualUnicodeString( &SiteNameInfo->SiteName,
  2670. &SubnetInfo->Subnets[i].SiteName,
  2671. TRUE )) {
  2672. NlPrint(( NL_SITE, "%wZ: Site %wZ is not site this DC is in.\n",
  2673. &SubnetInfo->Subnets[i].SubnetName,
  2674. &SubnetInfo->Subnets[i].SiteName ));
  2675. MoreThanOneSite = TRUE;
  2676. }
  2677. //
  2678. // Add the subnet to out in memory cache.
  2679. //
  2680. NetStatus = NlSitesAddSubnet(
  2681. SubnetInfo->Subnets[i].SiteName.Buffer,
  2682. SubnetInfo->Subnets[i].SubnetName.Buffer );
  2683. if ( NetStatus != NO_ERROR ) {
  2684. NlPrint(( NL_CRITICAL,
  2685. "%wZ: %wZ: Cannot add subnet-to-site mapping to cache: %ld\n",
  2686. &SubnetInfo->Subnets[i].SubnetName,
  2687. &SubnetInfo->Subnets[i].SiteName,
  2688. NetStatus ));
  2689. if ( NetStatus == ERROR_INVALID_NAME ) {
  2690. LPWSTR MsgStrings[1];
  2691. MsgStrings[0] = (LPWSTR) SubnetInfo->Subnets[i].SubnetName.Buffer;
  2692. NlpWriteEventlog( NELOG_NetlogonBadSubnetName,
  2693. EVENTLOG_INFORMATION_TYPE,
  2694. NULL,
  2695. 0,
  2696. MsgStrings,
  2697. 1 );
  2698. }
  2699. }
  2700. }
  2701. //
  2702. // Indicate that all the subnets have been added.
  2703. //
  2704. NlSitesEndSubnetEnum();
  2705. //
  2706. // If there are no subnet entries,
  2707. // and there is only one site in the enterprise,
  2708. // indicate that all client belong to this site.
  2709. //
  2710. // If there are subnet entries,
  2711. // and all of them indicate the same site as our site,
  2712. // indicate that all clients belong to this site.
  2713. //
  2714. EnterCriticalSection( &NlGlobalSiteCritSect );
  2715. if ( LocalSubnetCount == 0) {
  2716. NlGlobalOnlyOneSite = (SubnetInfo->SiteCount == 1);
  2717. } else {
  2718. NlGlobalOnlyOneSite = !MoreThanOneSite;
  2719. }
  2720. if ( NlGlobalOnlyOneSite ) {
  2721. NlPrint(( NL_SITE, "There is only one site. All clients belong to it.\n" ));
  2722. }
  2723. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2724. NetStatus = NO_ERROR;
  2725. //
  2726. // Free locally used resources
  2727. //
  2728. Cleanup:
  2729. if ( SubnetInfo != NULL ) {
  2730. LsaIFree_LSAP_SUBNET_INFO( SubnetInfo );
  2731. }
  2732. if ( SiteNameInfo != NULL ) {
  2733. LsaIFree_LSAP_SITENAME_INFO( SiteNameInfo );
  2734. }
  2735. return NetStatus;
  2736. }
  2737. NET_API_STATUS
  2738. DsrAddressToSiteNamesW(
  2739. IN LPWSTR ComputerName,
  2740. IN DWORD EntryCount,
  2741. IN PNL_SOCKET_ADDRESS SocketAddresses,
  2742. OUT PNL_SITE_NAME_ARRAY *SiteNames
  2743. )
  2744. /*++
  2745. Routine Description:
  2746. The DsAddressToSiteNames API returns the site names that correspond to
  2747. the specified addresses.
  2748. Arguments:
  2749. ComputerName - Specifies the name of the domain controller to remote this API to.
  2750. EntryCount - Number of addresses to convert.
  2751. SocketAddresses - Specifies an array of addresses to convert. EntryCount
  2752. addresses must be specified. Each address must be of type AF_INET.
  2753. SiteNames - Returns an array of pointers to site names. EntryCount entries
  2754. are returned. An entry will be returned as NULL if the corresponding
  2755. address does not map to any site or if the address is malformed.
  2756. The returned buffer must be deallocated using NetApiBufferFree.
  2757. Return Value:
  2758. NO_ERROR - Operation completed successfully;
  2759. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  2760. operation.
  2761. --*/
  2762. {
  2763. NET_API_STATUS NetStatus;
  2764. PNL_SITE_ENTRY *SiteEntries = NULL;
  2765. ULONG i;
  2766. ULONG Size;
  2767. PUNICODE_STRING Strings;
  2768. LPBYTE Where;
  2769. //
  2770. // This API is not supported on workstations.
  2771. //
  2772. *SiteNames = NULL;
  2773. if ( NlGlobalMemberWorkstation ) {
  2774. return ERROR_NOT_SUPPORTED;
  2775. }
  2776. //
  2777. // Initialization
  2778. //
  2779. if ( EntryCount == 0 ) {
  2780. return ERROR_INVALID_PARAMETER;
  2781. }
  2782. //
  2783. // Allocate an array for intermediate results
  2784. //
  2785. SiteEntries = LocalAlloc( LMEM_ZEROINIT, EntryCount*sizeof(PNL_SITE_ENTRY) );
  2786. if ( SiteEntries == NULL ) {
  2787. return ERROR_NOT_ENOUGH_MEMORY;
  2788. }
  2789. //
  2790. // Loop mapping each entry
  2791. //
  2792. for ( i=0; i<EntryCount; i++ ) {
  2793. PSOCKET_ADDRESS SocketAddress;
  2794. PSOCKADDR SockAddr;
  2795. //
  2796. // Validate the entry
  2797. //
  2798. SocketAddress = (PSOCKET_ADDRESS)&SocketAddresses[i];
  2799. SockAddr = SocketAddress->lpSockaddr;
  2800. if ( (SocketAddress->iSockaddrLength < sizeof(SOCKADDR) ) ||
  2801. (SockAddr == NULL) ) {
  2802. NlPrint((NL_CRITICAL,
  2803. "DsrAddressToSiteNamesW: Sockaddr is too small %ld (ignoring it)\n",
  2804. SocketAddress->iSockaddrLength ));
  2805. SiteEntries[i] = NULL;
  2806. } else if ( SockAddr->sa_family != AF_INET ) {
  2807. NlPrint((NL_CRITICAL,
  2808. "DsrAddressToSiteNamesW: Address familty isn't AF_INET %ld (ignoring it)\n",
  2809. SockAddr->sa_family ));
  2810. SiteEntries[i] = NULL;
  2811. } else {
  2812. //
  2813. // The SockAddr is valid so map it to a site name.
  2814. //
  2815. SiteEntries[i] = NlFindSiteEntryBySockAddrEx( SockAddr, NULL );
  2816. }
  2817. }
  2818. //
  2819. // Allocate a structure to return to the caller.
  2820. //
  2821. Size = sizeof(NL_SITE_NAME_ARRAY) + EntryCount * sizeof(UNICODE_STRING);
  2822. for ( i=0; i<EntryCount; i++ ) {
  2823. if ( SiteEntries[i] != NULL ) {
  2824. Size += SiteEntries[i]->SiteNameString.Length + sizeof(WCHAR);
  2825. }
  2826. }
  2827. *SiteNames = MIDL_user_allocate( Size );
  2828. if ( *SiteNames == NULL ) {
  2829. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2830. goto Cleanup;
  2831. }
  2832. Strings = (PUNICODE_STRING) ((*SiteNames)+1);
  2833. (*SiteNames)->EntryCount = EntryCount;
  2834. (*SiteNames)->SiteNames = Strings;
  2835. Where = (LPBYTE) &Strings[EntryCount];
  2836. //
  2837. // Loop copying the names into the return buffer.
  2838. //
  2839. for ( i=0; i<EntryCount; i++ ) {
  2840. if ( SiteEntries[i] == NULL ) {
  2841. RtlInitUnicodeString( &Strings[i], NULL );
  2842. } else {
  2843. Strings[i].Length = SiteEntries[i]->SiteNameString.Length;
  2844. Strings[i].MaximumLength = Strings[i].Length + sizeof(WCHAR);
  2845. Strings[i].Buffer = (LPWSTR)Where;
  2846. RtlCopyMemory( Where, SiteEntries[i]->SiteName, Strings[i].MaximumLength );
  2847. Where += Strings[i].Length + sizeof(WCHAR);
  2848. }
  2849. }
  2850. NetStatus = NO_ERROR;
  2851. Cleanup:
  2852. //
  2853. // Derference the site entries.
  2854. //
  2855. if ( SiteEntries != NULL ) {
  2856. for ( i=0; i<EntryCount; i++ ) {
  2857. if ( SiteEntries[i] != NULL ) {
  2858. NlDerefSiteEntry( SiteEntries[i] );
  2859. }
  2860. }
  2861. LocalFree( SiteEntries );
  2862. }
  2863. if ( NetStatus != NO_ERROR ) {
  2864. if ( *SiteNames != NULL ) {
  2865. MIDL_user_free( *SiteNames );
  2866. *SiteNames = NULL;
  2867. }
  2868. }
  2869. return NetStatus;
  2870. UNREFERENCED_PARAMETER( ComputerName );
  2871. }
  2872. NET_API_STATUS
  2873. DsrAddressToSiteNamesExW(
  2874. IN LPWSTR ComputerName,
  2875. IN DWORD EntryCount,
  2876. IN PNL_SOCKET_ADDRESS SocketAddresses,
  2877. OUT PNL_SITE_NAME_EX_ARRAY *SiteNames
  2878. )
  2879. /*++
  2880. Routine Description:
  2881. The DsAddressToSiteNames API returns the site names and subnet names
  2882. that correspond to the specified addresses.
  2883. Arguments:
  2884. ComputerName - Specifies the name of the domain controller to remote this API to.
  2885. EntryCount - Number of addresses to convert.
  2886. SocketAddresses - Specifies an array of addresses to convert. EntryCount
  2887. addresses must be specified. Each address must be of type AF_INET.
  2888. SiteNames - Returns an array of pointers to site names. EntryCount entries
  2889. are returned. An entry will be returned as NULL if the corresponding
  2890. address does not map to any site or if the address is malformed.
  2891. The returned buffer must be deallocated using NetApiBufferFree.
  2892. Return Value:
  2893. NO_ERROR - Operation completed successfully;
  2894. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  2895. operation.
  2896. --*/
  2897. {
  2898. NET_API_STATUS NetStatus;
  2899. PNL_SITE_ENTRY *SiteEntries = NULL;
  2900. PNL_SUBNET *SubnetEntries;
  2901. ULONG i;
  2902. ULONG Size;
  2903. PUNICODE_STRING SiteStrings = NULL;
  2904. PUNICODE_STRING SubnetStrings = NULL;
  2905. //
  2906. // This API is not supported on workstations.
  2907. //
  2908. *SiteNames = NULL;
  2909. if ( NlGlobalMemberWorkstation ) {
  2910. return ERROR_NOT_SUPPORTED;
  2911. }
  2912. //
  2913. // Initialization
  2914. //
  2915. if ( EntryCount == 0 ) {
  2916. return ERROR_INVALID_PARAMETER;
  2917. }
  2918. //
  2919. // Allocate an array for intermediate results
  2920. //
  2921. SiteEntries = LocalAlloc( LMEM_ZEROINIT,
  2922. EntryCount*(sizeof(PNL_SITE_ENTRY)+sizeof(PNL_SUBNET)) );
  2923. if ( SiteEntries == NULL ) {
  2924. return ERROR_NOT_ENOUGH_MEMORY;
  2925. }
  2926. SubnetEntries = (PNL_SUBNET *) (&SiteEntries[EntryCount]);
  2927. //
  2928. // Loop mapping each entry
  2929. //
  2930. for ( i=0; i<EntryCount; i++ ) {
  2931. PSOCKET_ADDRESS SocketAddress;
  2932. PSOCKADDR SockAddr;
  2933. //
  2934. // Validate the entry
  2935. //
  2936. SocketAddress = (PSOCKET_ADDRESS)&SocketAddresses[i];
  2937. SockAddr = SocketAddress->lpSockaddr;
  2938. if ( (SocketAddress->iSockaddrLength < sizeof(SOCKADDR) ) ||
  2939. (SockAddr == NULL) ) {
  2940. NlPrint((NL_CRITICAL,
  2941. "DsrAddressToSiteNamesW: Sockaddr is too small %ld (ignoring it)\n",
  2942. SocketAddress->iSockaddrLength ));
  2943. SiteEntries[i] = NULL;
  2944. } else if ( SockAddr->sa_family != AF_INET ) {
  2945. NlPrint((NL_CRITICAL,
  2946. "DsrAddressToSiteNamesW: Address familty isn't AF_INET %ld (ignoring it)\n",
  2947. SockAddr->sa_family ));
  2948. SiteEntries[i] = NULL;
  2949. } else {
  2950. //
  2951. // The SockAddr is valid so map it to a site name.
  2952. //
  2953. SiteEntries[i] = NlFindSiteEntryBySockAddrEx( SockAddr, &SubnetEntries[i] );
  2954. }
  2955. }
  2956. //
  2957. // Allocate a structure to return to the caller.
  2958. //
  2959. *SiteNames = MIDL_user_allocate( sizeof(NL_SITE_NAME_EX_ARRAY) );
  2960. if ( *SiteNames == NULL ) {
  2961. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2962. goto Cleanup;
  2963. }
  2964. SubnetStrings = MIDL_user_allocate( EntryCount * sizeof(UNICODE_STRING) );
  2965. if ( SubnetStrings == NULL ) {
  2966. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2967. goto Cleanup;
  2968. }
  2969. RtlZeroMemory( SubnetStrings, EntryCount * sizeof(UNICODE_STRING) );
  2970. SiteStrings = MIDL_user_allocate( EntryCount * sizeof(UNICODE_STRING) );
  2971. if ( SiteStrings == NULL ) {
  2972. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2973. goto Cleanup;
  2974. }
  2975. RtlZeroMemory( SiteStrings, EntryCount * sizeof(UNICODE_STRING) );
  2976. (*SiteNames)->EntryCount = EntryCount;
  2977. (*SiteNames)->SiteNames = SiteStrings;
  2978. (*SiteNames)->SubnetNames = SubnetStrings;
  2979. //
  2980. // Loop copying the names into the return buffer.
  2981. //
  2982. for ( i=0; i<EntryCount; i++ ) {
  2983. if ( SiteEntries[i] != NULL ) {
  2984. LPWSTR Name;
  2985. Name = NetpAllocWStrFromWStr( SiteEntries[i]->SiteName );
  2986. if ( Name == NULL ) {
  2987. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2988. goto Cleanup;
  2989. }
  2990. RtlInitUnicodeString( &SiteStrings[i], Name );
  2991. }
  2992. if ( SubnetEntries[i] != NULL ) {
  2993. WCHAR SubnetAddressString[NL_IP_ADDRESS_LENGTH+1+2+1];
  2994. ULONG Length;
  2995. UNICODE_STRING NumberString;
  2996. LPWSTR Name;
  2997. //
  2998. // Compute the IP address part of the subnet name
  2999. //
  3000. NetpIpAddressToWStr( SubnetEntries[i]->SubnetAddress,
  3001. SubnetAddressString );
  3002. Length = wcslen(SubnetAddressString);
  3003. SubnetAddressString[Length] = '/';
  3004. Length ++;
  3005. //
  3006. // Compute the bit count part of the subnet name
  3007. //
  3008. NumberString.Buffer = &SubnetAddressString[Length];
  3009. NumberString.MaximumLength = 3 * sizeof(WCHAR);
  3010. RtlIntegerToUnicodeString( SubnetEntries[i]->SubnetBitCount,
  3011. 10,
  3012. &NumberString );
  3013. SubnetAddressString[Length+NumberString.Length/sizeof(WCHAR)] = '\0';
  3014. //
  3015. // Return it to the caller
  3016. //
  3017. Name = NetpAllocWStrFromWStr( SubnetAddressString );
  3018. if ( Name == NULL ) {
  3019. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3020. goto Cleanup;
  3021. }
  3022. RtlInitUnicodeString( &SubnetStrings[i], Name );
  3023. }
  3024. }
  3025. NetStatus = NO_ERROR;
  3026. Cleanup:
  3027. //
  3028. // Derference the site entries.
  3029. //
  3030. if ( SiteEntries != NULL ) {
  3031. for ( i=0; i<EntryCount; i++ ) {
  3032. if ( SiteEntries[i] != NULL ) {
  3033. NlDerefSiteEntry( SiteEntries[i] );
  3034. }
  3035. if ( SubnetEntries[i] != NULL ) {
  3036. NlSitesDerefSubnet( SubnetEntries[i] );
  3037. }
  3038. }
  3039. }
  3040. if ( NetStatus != NO_ERROR ) {
  3041. if ( *SiteNames != NULL ) {
  3042. MIDL_user_free( *SiteNames );
  3043. *SiteNames = NULL;
  3044. }
  3045. if ( SiteStrings != NULL ) {
  3046. for ( i=0; i<EntryCount; i++ ) {
  3047. if ( SiteStrings[i].Buffer != NULL ) {
  3048. MIDL_user_free( SiteStrings[i].Buffer );
  3049. }
  3050. }
  3051. MIDL_user_free( SiteStrings );
  3052. }
  3053. if ( SubnetStrings != NULL ) {
  3054. for ( i=0; i<EntryCount; i++ ) {
  3055. if ( SubnetStrings[i].Buffer != NULL ) {
  3056. MIDL_user_free( SubnetStrings[i].Buffer );
  3057. }
  3058. }
  3059. MIDL_user_free( SubnetStrings );
  3060. }
  3061. }
  3062. return NetStatus;
  3063. UNREFERENCED_PARAMETER( ComputerName );
  3064. }
  3065. NET_API_STATUS
  3066. NlSiteInitialize(
  3067. VOID
  3068. )
  3069. /*++
  3070. Routine Description:
  3071. Initialize this module.
  3072. Calls NlExit upon failure.
  3073. Arguments:
  3074. None.
  3075. Return Value:
  3076. Status of the initialization.
  3077. --*/
  3078. {
  3079. NET_API_STATUS NetStatus;
  3080. try {
  3081. InitializeCriticalSection(&NlGlobalSiteCritSect);
  3082. } except( EXCEPTION_EXECUTE_HANDLER ) {
  3083. NlPrint((NL_CRITICAL, "Cannot InitializeCriticalSection for SiteCritSect\n" ));
  3084. return ERROR_NOT_ENOUGH_MEMORY;
  3085. }
  3086. NlGlobalUnicodeSiteName = NULL;
  3087. NlGlobalSiteEntry = NULL;
  3088. InitializeListHead( &NlGlobalSiteList );
  3089. InitializeListHead( &NlGlobalSubnetList );
  3090. RtlZeroMemory( &NlGlobalSubnetTree, sizeof(NlGlobalSubnetTree) );
  3091. RtlZeroMemory( &NlGlobalNewSubnetTree, sizeof(NlGlobalNewSubnetTree) );
  3092. NlGlobalSiteInitialized = TRUE;
  3093. //
  3094. // Initially set the site name and populate the subnet tree.
  3095. //
  3096. if ( NlGlobalMemberWorkstation ) {
  3097. NetStatus = NlSetSiteName( NlGlobalParameters.SiteName, NULL );
  3098. } else {
  3099. NetStatus = NlSitesAddSubnetFromDs( NULL );
  3100. }
  3101. return NetStatus;
  3102. }
  3103. VOID
  3104. NlSiteTerminate(
  3105. VOID
  3106. )
  3107. /*++
  3108. Routine Description:
  3109. De-Initialize this module.
  3110. Arguments:
  3111. None.
  3112. Return Value:
  3113. None.
  3114. --*/
  3115. {
  3116. PLIST_ENTRY ListEntry;
  3117. //
  3118. // If we've not initialized,
  3119. // we're done.
  3120. //
  3121. if ( !NlGlobalSiteInitialized ) {
  3122. return;
  3123. }
  3124. NlPrint(( NL_SITE_MORE, "NlSiteTerminate: Entered\n" ));
  3125. //
  3126. // Free all entries in NlGlobalSubnetTree and NlGlobalNewSubnetTree
  3127. //
  3128. EnterCriticalSection( &NlGlobalSiteCritSect );
  3129. NlSiteDeleteSubnetTree( &NlGlobalSubnetTree );
  3130. NlSiteDeleteSubnetTree( &NlGlobalNewSubnetTree );
  3131. //
  3132. // Delete the site name.
  3133. //
  3134. NlSetSiteName( NULL, NULL );
  3135. LeaveCriticalSection( &NlGlobalSiteCritSect );
  3136. //
  3137. // There should be no more sites or subnets since all covered sites
  3138. // have been previously dereferenced and all remaining references
  3139. // were from the tree above
  3140. //
  3141. NlAssert( IsListEmpty( &NlGlobalSiteList ) );
  3142. NlAssert( IsListEmpty( &NlGlobalSubnetList ) );
  3143. DeleteCriticalSection(&NlGlobalSiteCritSect);
  3144. NlGlobalSiteInitialized = FALSE;
  3145. NlPrint(( NL_SITE_MORE, "NlSiteTerminate: Exitted\n" ));
  3146. }
  3147. int __cdecl NlpCompareSiteName(
  3148. const void *String1,
  3149. const void *String2
  3150. )
  3151. /*++
  3152. Routine Description:
  3153. String comparison routine for DsrGetDcSiteCoverageW.
  3154. Arguments:
  3155. String1: First string to compare
  3156. String2: Second string to compare
  3157. Return Value:
  3158. --*/
  3159. {
  3160. return RtlCompareUnicodeString(
  3161. (PUNICODE_STRING) String1,
  3162. (PUNICODE_STRING) String2,
  3163. TRUE );
  3164. }
  3165. NET_API_STATUS
  3166. DsrGetDcSiteCoverageW(
  3167. IN LPWSTR ComputerName OPTIONAL,
  3168. OUT PNL_SITE_NAME_ARRAY *SiteNames
  3169. )
  3170. /*++
  3171. Routine Description:
  3172. This API returns the site names of all sites covered by DC.
  3173. Arguments:
  3174. ComputerName - Specifies the name of the domain controller to remote this API to.
  3175. SiteNames - Returns an array of pointers to site names.
  3176. The returned buffer must be deallocated using NetApiBufferFree.
  3177. Return Value:
  3178. NO_ERROR - Operation completed successfully;
  3179. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  3180. operation.
  3181. --*/
  3182. {
  3183. NET_API_STATUS NetStatus;
  3184. PDOMAIN_INFO DomainInfo = NULL;
  3185. if ( NlGlobalMemberWorkstation ) {
  3186. NetStatus = ERROR_NOT_SUPPORTED;
  3187. goto Cleanup;
  3188. }
  3189. //
  3190. // Lookup which domain this call pertains to.
  3191. //
  3192. DomainInfo = NlFindDomainByServerName( ComputerName );
  3193. if ( DomainInfo == NULL ) {
  3194. NetStatus = ERROR_INVALID_COMPUTERNAME;
  3195. goto Cleanup;
  3196. }
  3197. //
  3198. // Get the site names
  3199. //
  3200. NetStatus = NlSitesGetCloseSites( DomainInfo,
  3201. DOM_REAL_DOMAIN,
  3202. SiteNames );
  3203. if ( NetStatus != NO_ERROR ) {
  3204. goto Cleanup;
  3205. }
  3206. //
  3207. // Sort them into alphabetical order
  3208. //
  3209. qsort( (*SiteNames)->SiteNames,
  3210. (*SiteNames)->EntryCount,
  3211. sizeof(UNICODE_STRING),
  3212. NlpCompareSiteName );
  3213. Cleanup:
  3214. if ( DomainInfo != NULL ) {
  3215. NlDereferenceDomain( DomainInfo );
  3216. }
  3217. return NetStatus;
  3218. }
  3219. NET_API_STATUS
  3220. I_NetLogonAddressToSiteName(
  3221. IN PSOCKET_ADDRESS SocketAddress,
  3222. OUT LPWSTR *SiteName
  3223. )
  3224. /*++
  3225. Routine Description:
  3226. This API returns the site name, if any, of the address in SocketAddress.
  3227. It is provided for in-process callers. See DsrAddressToSiteNamesW for details.
  3228. Arguments:
  3229. SocketAddess -- the address to be looked up
  3230. SiteName -- the site name of the address; NULL is returned if no site
  3231. is found.
  3232. Return Value:
  3233. NO_ERROR - Operation completed successfully;
  3234. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
  3235. operation.
  3236. ERROR_NETLOGON_NOT_STARTED - Netlogon is stopped.
  3237. --*/
  3238. {
  3239. NET_API_STATUS NetStatus = NO_ERROR;
  3240. PNL_SITE_NAME_ARRAY SiteNameArray = NULL;
  3241. //
  3242. // If caller is calling when the netlogon service isn't running,
  3243. // tell it so.
  3244. //
  3245. if ( !NlStartNetlogonCall() ) {
  3246. return ERROR_NETLOGON_NOT_STARTED;
  3247. }
  3248. *SiteName = NULL;
  3249. NetStatus = DsrAddressToSiteNamesW( NULL,
  3250. 1,
  3251. (PNL_SOCKET_ADDRESS)SocketAddress,
  3252. &SiteNameArray );
  3253. if ( (NO_ERROR == NetStatus)
  3254. && SiteNameArray->EntryCount > 0
  3255. && SiteNameArray->SiteNames[0].Length > 0 ) {
  3256. ULONG Size = SiteNameArray->SiteNames[0].Length + sizeof(WCHAR);
  3257. *SiteName = MIDL_user_allocate(Size);
  3258. if (*SiteName) {
  3259. RtlZeroMemory(*SiteName, Size);
  3260. RtlCopyMemory(*SiteName, SiteNameArray->SiteNames[0].Buffer, SiteNameArray->SiteNames[0].Length);
  3261. } else {
  3262. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3263. }
  3264. }
  3265. if (SiteNameArray != NULL) {
  3266. MIDL_user_free(SiteNameArray);
  3267. }
  3268. //
  3269. // Indicate that the calling thread has left netlogon.dll
  3270. //
  3271. NlEndNetlogonCall();
  3272. return NetStatus;
  3273. }