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.

980 lines
26 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. dscheck.cxx
  5. Abstract:
  6. Check the consistency of the DS topology for the system volume. The DS
  7. tree for sites\site\nTDSSettings\mSFT-DSA\nTDSConnection is copied into
  8. memory. An RTL Generic Table of the mSFT-DSA objects is built. Duplicates
  9. are kept on a list anchored by the first occurence of a mSFT-DSA. The
  10. tree and the table are used to check the consistency of the topology.
  11. Once the checks have stablized a bit they will be listed here.
  12. Author:
  13. Billy J. Fuller 3-Mar-1997 (From Jim McNelis)
  14. Environment
  15. User mode winnt
  16. --*/
  17. #include <nt.h>
  18. #include <ntrtl.h>
  19. #include <nturtl.h>
  20. #include <windows.h>
  21. #include <assert.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <winldap.h>
  26. #include <dsgetdc.h>
  27. #include <tchar.h>
  28. //
  29. // We save code by recursively scanning the DS's hierarchical directory.
  30. // The following constants tell us where we are in the hierarchy.
  31. //
  32. #define SITES 0
  33. #define SETTINGZ 1
  34. #define SERVERS 2
  35. #define CXTIONS 3
  36. //
  37. // We build an incore copy of the DS hierarchy sites\settings\servers\connections.
  38. // Hence the interrelated structs for site, settings, server, and connection
  39. //
  40. typedef struct cxtion CXTION, * PCXTION;
  41. typedef struct server SERVER, * PSERVER;
  42. typedef struct settings SETTINGS, * PSETTINGS;
  43. typedef struct site SITE, * PSITE;
  44. // Connection
  45. struct cxtion {
  46. PTCHAR CxtionRDN; // relative distinguished name
  47. PSERVER CxtionServer; // address of parent
  48. PCXTION CxtionNext; // peers of this connection
  49. PTCHAR CxtionPartner; // inbound partner's RDN
  50. } * Settingss;
  51. // Server
  52. struct server {
  53. PTCHAR ServerRDN; // relative distinguished name
  54. PSETTINGS ServerSettings; // address of parent
  55. PSERVER ServerNext; // peers of this server
  56. PCXTION ServerOuts; // outbound connections
  57. PCXTION ServerIns; // inbound connections
  58. };
  59. // Settings
  60. struct settings {
  61. PTCHAR SettingsRDN; // relative distinguished name
  62. PSITE SettingsSite; // address of parent
  63. PSETTINGS SettingsNext; // peers of this settings
  64. PSERVER SettingsServers; // children of this settings
  65. };
  66. // Site
  67. struct site {
  68. PTCHAR SiteRDN; // relative distinguished name
  69. PSITE SiteNext; // peers of this site
  70. PSETTINGS SiteSettings; // children of this site
  71. } * Sites;
  72. //
  73. // We avoid N**2 algorithms by using the generic table routines to access
  74. // servers by name.
  75. //
  76. RTL_GENERIC_TABLE ServerTable;
  77. //
  78. // The entry in the generic table points to a sever struct. The entry can
  79. // be looked up by the server's relative distinguished name (ServerRDN).
  80. //
  81. // Although duplicate server names are not allowed, a user may have
  82. // created servers with the same RDN. These duplicates are kept as
  83. // a linked list anchored by RtlServerDups. The duplicates are ignored.
  84. // The first entry encountered while scanning the DS tree is used as
  85. // the "correct" server.
  86. //
  87. typedef struct rtlserver RTLSERVER, * PRTLSERVER;
  88. struct rtlserver {
  89. PRTLSERVER RtlServerDups;
  90. PSERVER RtlServer;
  91. };
  92. RTL_GENERIC_COMPARE_RESULTS
  93. RtlServerCompare(
  94. PRTL_GENERIC_TABLE Table,
  95. PVOID FirstStruct,
  96. PVOID SecondStruct
  97. )
  98. /*++
  99. Routine Description:
  100. Compare two entries in the generic table for servers.
  101. Arguments:
  102. Table - Address of the table (not used).
  103. FirstStruct - PRTLSERVER
  104. SecondStruct - PRTLSERVER
  105. Return Value:
  106. <0 First < Second
  107. =0 First == Second
  108. >0 First > Second
  109. --*/
  110. {
  111. INT Cmp;
  112. Cmp = _tcscmp(((PRTLSERVER)FirstStruct)->RtlServer->ServerRDN,
  113. ((PRTLSERVER)SecondStruct)->RtlServer->ServerRDN);
  114. if (Cmp < 0)
  115. return (GenericLessThan);
  116. if (Cmp == 0)
  117. return (GenericEqual);
  118. return (GenericGreaterThan);
  119. }
  120. PVOID
  121. RtlServerAllocate(
  122. PRTL_GENERIC_TABLE Table,
  123. CLONG ByteSize
  124. )
  125. /*++
  126. Routine Description:
  127. Allocate space for a table entry. The entry includes the user-defined
  128. struct and some overhead used by the generic table routines. The
  129. generic table routines call this function when they need memory.
  130. Arguments:
  131. Table - Address of the table (not used).
  132. ByteSize - Bytes to allocate
  133. Return Value:
  134. Address of newly allocated memory.
  135. --*/
  136. {
  137. return (PVOID)malloc(ByteSize);
  138. }
  139. VOID
  140. RtlServerFree(
  141. PRTL_GENERIC_TABLE Table,
  142. PVOID Buffer
  143. )
  144. /*++
  145. Routine Description:
  146. Free the space allocated by RtlServerAllocate(). The generic table
  147. routines call this function to free memory.
  148. Arguments:
  149. Table - Address of the table (not used).
  150. Buffer - Address of previously allocated memory
  151. Return Value:
  152. None.
  153. --*/
  154. {
  155. free(Buffer);
  156. }
  157. VOID
  158. RtlServerInsert(
  159. PSERVER Server
  160. )
  161. /*++
  162. Routine Description:
  163. Insert a server's name into the table. The new entry in the table
  164. will point to the originating SERVER. If this name is already in the
  165. table, then link the new entry off of the old entry.
  166. Arguments:
  167. Server - Address of SERVER
  168. Return Value:
  169. None.
  170. --*/
  171. {
  172. PRTLSERVER NewServer; // Newly allocated table entry
  173. PRTLSERVER OldServer; // Existing entry in the table
  174. BOOLEAN NewElement; // TRUE if insert found existing entry
  175. // Allocate a new table entry
  176. NewServer = (PRTLSERVER)malloc(sizeof (*NewServer));
  177. NewServer->RtlServer = Server;
  178. NewServer->RtlServerDups = NULL;
  179. // Insert the entry
  180. OldServer = (PRTLSERVER)RtlInsertElementGenericTable(
  181. &ServerTable,
  182. (PVOID)NewServer,
  183. sizeof (*NewServer),
  184. &NewElement);
  185. // NewServer was copied into the table
  186. if (NewElement == TRUE) {
  187. free(NewServer);
  188. } else {
  189. // Entry exists; link NewServer to existing entry
  190. NewServer->RtlServerDups = OldServer->RtlServerDups;
  191. OldServer->RtlServerDups = NewServer;
  192. }
  193. }
  194. PSERVER
  195. RtlServerLookup(
  196. PTCHAR ServerRDN
  197. )
  198. /*++
  199. Routine Description:
  200. Find an entry in the table with the specified name.
  201. Arguments:
  202. Server - Address of SERVER
  203. Return Value:
  204. The address of the SERVER with a matching name or NULL if no
  205. match was found.
  206. --*/
  207. {
  208. PRTLSERVER FoundRtlServer; // Address of matching table entry
  209. SERVER Server; // Part of the search key
  210. RTLSERVER RtlServer; // Search key
  211. // Set up the search key
  212. Server.ServerRDN = ServerRDN;
  213. RtlServer.RtlServer = &Server;
  214. // Search the table
  215. FoundRtlServer = (PRTLSERVER)RtlLookupElementGenericTable(&ServerTable, &RtlServer);
  216. if (FoundRtlServer != NULL)
  217. return FoundRtlServer->RtlServer;
  218. return NULL;
  219. }
  220. VOID
  221. FreeRtlServer()
  222. /*++
  223. Routine Description:
  224. Free every entry in the generic table.
  225. Arguments:
  226. None.
  227. Return Value:
  228. None.
  229. --*/
  230. {
  231. PRTLSERVER RtlServer; // Next entry in table
  232. PRTLSERVER Dups; // scan the entries list of dups
  233. PRTLSERVER NextDups; // copy of freed Dups->RtlServerDups
  234. // For every entry in the table
  235. for (RtlServer = (PRTLSERVER)RtlEnumerateGenericTable(&ServerTable, TRUE);
  236. RtlServer != NULL;
  237. RtlServer = (PRTLSERVER)RtlEnumerateGenericTable(&ServerTable, TRUE)) {
  238. // Free the dups
  239. for (Dups = RtlServer->RtlServerDups; Dups; Dups = NextDups) {
  240. NextDups = Dups->RtlServerDups;
  241. free(Dups);
  242. }
  243. // Delete the entry from the table
  244. RtlDeleteElementGenericTable(&ServerTable, RtlServer);
  245. }
  246. // Didn't get all of them?
  247. if (!RtlIsGenericTableEmpty(&ServerTable)) {
  248. fprintf(stderr, "****** FreeRtlServer: Server Table is not empty\n");
  249. }
  250. }
  251. PLDAP
  252. FrsDsOpenDs()
  253. /*++
  254. Routine Description:
  255. Open and bind to the a primary domain controller.
  256. Arguments:
  257. None.
  258. Return Value:
  259. The address of a open, bound LDAP port or NULL if the operation was
  260. unsuccessful. The caller must free the structure with ldap_unbind().
  261. --*/
  262. {
  263. PLDAP ldap;
  264. LONG Err; // Generic error
  265. PDOMAIN_CONTROLLER_INFO DCInfo = NULL; // Domain Controller Info
  266. ULONG ulOptions;
  267. //
  268. // Get Info about a Primary Domain Controller (just need the IP address)
  269. //
  270. Err = DsGetDcName(
  271. NULL, // Computer to remote to
  272. NULL, // Domain - use our own
  273. NULL, // Domain Guid
  274. NULL, // Site Guid
  275. DS_DIRECTORY_SERVICE_REQUIRED | // Flags
  276. DS_PDC_REQUIRED,
  277. &DCInfo); // Return info
  278. if (Err != ERROR_SUCCESS) {
  279. fprintf(stderr, "DsGetDcName returned error %d\n", Err);
  280. fprintf(stderr, "Could not find a Primary Domain Controller\n");
  281. return NULL;
  282. }
  283. //
  284. // Open and bind to the ldap service (TCP/IP)
  285. // The IP address has a leading "\\" that ldap_open can't handle
  286. //
  287. //
  288. // if ldap_open is called with a server name the api will call DsGetDcName
  289. // passing the server name as the domainname parm...bad, because
  290. // DsGetDcName will make a load of DNS queries based on the server name,
  291. // it is designed to construct these queries from a domain name...so all
  292. // these queries will be bogus, meaning they will waste network bandwidth,
  293. // time to fail, and worst case cause expensive on demand links to come up
  294. // as referrals/forwarders are contacted to attempt to resolve the bogus
  295. // names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
  296. // after the ldap_init but before any other operation using the ldap
  297. // handle from ldap_init, the delayed connection setup will not call
  298. // DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
  299. // will detect that and use the address directly.
  300. //
  301. // ldap = ldap_open(&DCInfo->DomainControllerAddress[2], LDAP_PORT);
  302. ldap = ldap_init(&DCInfo->DomainControllerAddress[2], LDAP_PORT);
  303. if (ldap == NULL) {
  304. fprintf(stderr, "ldap_open: Could not open %ws\n",
  305. &DCInfo->DomainControllerAddress[2]);
  306. return NULL;
  307. }
  308. //
  309. // set the options.
  310. //
  311. ulOptions = PtrToUlong(LDAP_OPT_ON);
  312. ldap_set_option(ldap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
  313. //
  314. // ldap cannot be used until after the bind operation
  315. //
  316. Err = ldap_bind_s(ldap, NULL, NULL, LDAP_AUTH_SSPI);
  317. if (Err != LDAP_SUCCESS) {
  318. fprintf(stderr, "ldap_bind_s: %ws\n", ldap_err2string(Err));
  319. ldap_unbind(ldap); // XXX Is this necessary? Will this free ldap?
  320. return NULL;
  321. }
  322. return ldap;
  323. }
  324. PTCHAR *
  325. GetValues(
  326. IN PLDAP ldap,
  327. IN PTCHAR Base,
  328. IN PTCHAR DesiredAttr
  329. )
  330. /*++
  331. Routine Description:
  332. Return the DS values for one attribute in an object.
  333. Arguments:
  334. ldap - An open, bound ldap port.
  335. Base - The "pathname" of a DS object.
  336. DesiredAttr - Return values for this attribute.
  337. Return Value:
  338. An array of char pointers that represents the values for the attribute.
  339. The caller must free the array with ldap_value_free(). NULL if unsuccessful.
  340. --*/
  341. {
  342. LONG Err; // Generic error
  343. PTCHAR Attr; // Retrieved from an ldap entry
  344. BerElement *Ber; // Needed for scanning attributes
  345. PLDAPMessage Msg = NULL; // Opaque stuff from ldap subsystem
  346. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  347. PTCHAR *Values; // Array of values for desired attribute
  348. PTCHAR Attrs[2]; // Needed for the query
  349. //
  350. // Search Base for all of this attribute + values
  351. //
  352. Attrs[0] = DesiredAttr;
  353. Attrs[1] = NULL;
  354. Err = ldap_search_s(ldap, Base, LDAP_SCOPE_BASE,
  355. TEXT("(objectClass=*)"), Attrs, 0, &Msg);
  356. if (Err != LDAP_SUCCESS) {
  357. fprintf(stderr, "ldap_search_s: %ws: %ws\n", DesiredAttr, ldap_err2string(Err));
  358. if (Msg) {
  359. ldap_msgfree(Msg);
  360. }
  361. return NULL;
  362. }
  363. Entry = ldap_first_entry(ldap, Msg);
  364. Attr = ldap_first_attribute(ldap, Entry, &Ber);
  365. Values = ldap_get_values(ldap, Entry, Attr);
  366. ldap_msgfree(Msg);
  367. return Values;
  368. }
  369. PTCHAR
  370. MakeRDN(
  371. PTCHAR DN
  372. )
  373. /*++
  374. Routine Description:
  375. Extract the base component from a distinguished name. The distinguished
  376. name is assumed to be in DS format (CN=xyz,CN=next one,...)
  377. Arguments:
  378. DN - distinguished name
  379. Return Value:
  380. The address of a base name. The caller must free the memory with free().
  381. --*/
  382. {
  383. LONG RDNLen;
  384. PTCHAR RDN;
  385. // Skip the first CN=; if any
  386. RDN = _tcsstr(DN, TEXT("CN="));
  387. if (RDN == DN)
  388. DN += 3;
  389. // Return the string up to the first delimiter
  390. RDNLen = _tcscspn(DN, TEXT(","));
  391. RDN = (PTCHAR)malloc(sizeof (TCHAR) * (RDNLen + 1));
  392. _tcsncpy(RDN, DN, RDNLen);
  393. RDN[RDNLen] = TEXT('\0');
  394. return RDN;
  395. }
  396. VOID
  397. GetTree(
  398. IN PLDAP ldap,
  399. IN PTCHAR Base,
  400. IN LONG What,
  401. IN PVOID Parent,
  402. IN PTCHAR Filter
  403. )
  404. /*++
  405. Routine Description:
  406. Recursively scan the DS tree beginning at configuration\sites.
  407. Arguments:
  408. ldap - opened and bound ldap connection
  409. Base - Name of object or container in DS
  410. What - Where are we in the DS hierarchy?
  411. Parent - Container which contains Base
  412. Filter - Limits ldap search to these object classes
  413. Return Value:
  414. None.
  415. --*/
  416. {
  417. LONG Err; // Generic error
  418. PLDAPMessage Msg = NULL; // Opaque stuff from ldap subsystem
  419. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  420. PTCHAR DirName; // DS name of this object
  421. PTCHAR RDN; // base name derived from DirName
  422. PTCHAR *Values; // entries for this container
  423. PSITE Site; // copy DS site entries into this struct
  424. PSETTINGS Settings; // copy DS settings entries into this struct
  425. PSERVER Server; // copy DS server entries into this struct
  426. PCXTION Cxtion; // copy DS connection entries into this struct
  427. //
  428. // Search the DS beginning at Base for the entries of class "Filter"
  429. //
  430. Err = ldap_search_s(ldap, Base, LDAP_SCOPE_ONELEVEL, Filter, NULL, 0, &Msg);
  431. if (Err != LDAP_SUCCESS) {
  432. if (Msg) {
  433. ldap_msgfree(Msg);
  434. }
  435. return;
  436. }
  437. //
  438. // Scan the entries returned from ldap_search
  439. //
  440. for (
  441. Entry = ldap_first_entry(ldap, Msg);
  442. Entry != NULL;
  443. Entry = ldap_next_entry(ldap, Entry)) {
  444. // DS pathname of this entry
  445. DirName = ldap_get_dn(ldap, Entry);
  446. if (DirName == NULL)
  447. continue;
  448. // base name of the DS pathname
  449. RDN = MakeRDN(DirName);
  450. // Where are we in the DS directory hierarchy
  451. switch (What) {
  452. case SITES:
  453. // Copy a site entry into our tree
  454. Site = (PSITE)malloc(sizeof (*Site));
  455. Site->SiteRDN = RDN;
  456. Site->SiteSettings = NULL;
  457. Site->SiteNext = Sites;
  458. Sites = Site;
  459. // Get the settings
  460. GetTree(ldap, DirName, SETTINGZ, (PVOID)Site, TEXT("(objectClass=nTDSSettings)"));
  461. break;
  462. case SETTINGZ:
  463. // Copy a settings entry into our tree
  464. Settings = (PSETTINGS)malloc(sizeof (*Settings));
  465. Settings->SettingsRDN = RDN;
  466. Settings->SettingsServers = NULL;
  467. Settings->SettingsSite = (PSITE)Parent;
  468. Settings->SettingsNext = ((PSITE)Parent)->SiteSettings;
  469. ((PSITE)Parent)->SiteSettings = Settings;
  470. // Get the servers
  471. GetTree(ldap, DirName, SERVERS, (PVOID)Settings, TEXT("(objectClass=mSFTDSA)"));
  472. break;
  473. case SERVERS:
  474. // Copy a server entry into our tree
  475. Server = (PSERVER)malloc(sizeof (*Server));
  476. Server->ServerRDN = RDN;
  477. Server->ServerSettings = (PSETTINGS)Parent;
  478. Server->ServerIns = NULL;
  479. Server->ServerOuts = NULL;
  480. Server->ServerNext = ((PSETTINGS)Parent)->SettingsServers;
  481. ((PSETTINGS)Parent)->SettingsServers = Server;
  482. // Put this server into the generic table
  483. RtlServerInsert(Server);
  484. // Get the connections
  485. GetTree(ldap, DirName, CXTIONS, (PVOID)Server, TEXT("(objectClass=nTDSConnection)"));
  486. break;
  487. case CXTIONS:
  488. // Copy a connection entry into our tree
  489. Cxtion = (PCXTION)malloc(sizeof (*Cxtion));
  490. Cxtion->CxtionRDN = RDN;
  491. Cxtion->CxtionServer = (PSERVER)Parent;
  492. Cxtion->CxtionPartner = NULL;
  493. Cxtion->CxtionNext = ((PSERVER)Parent)->ServerIns;
  494. ((PSERVER)Parent)->ServerIns = Cxtion;
  495. Values = GetValues(ldap, DirName, TEXT("fromServer"));
  496. // Get the inbound partner's name
  497. if (Values != NULL) {
  498. Cxtion->CxtionPartner = MakeRDN(Values[0]);
  499. ldap_value_free(Values);
  500. }
  501. break;
  502. default:;
  503. }
  504. free(DirName);
  505. }
  506. ldap_msgfree(Msg);
  507. }
  508. VOID
  509. FrsDsFreeTree(
  510. )
  511. /*++
  512. Routine Description:
  513. Frees our copy of the DS tree.
  514. Arguments:
  515. None.
  516. Return Value:
  517. None.
  518. --*/
  519. {
  520. PSITE Site; // Scan the sites
  521. PSETTINGS Settings; // Scan the settings
  522. PSERVER Server; // Scan the servers
  523. PCXTION Cxtion; // Scan the connections
  524. //
  525. // For every site
  526. //
  527. while ((Site = Sites) != NULL) {
  528. Sites = Site->SiteNext;
  529. //
  530. // For every settings
  531. //
  532. while ((Settings = Site->SiteSettings) != NULL) {
  533. Site->SiteSettings = Settings->SettingsNext;
  534. //
  535. // For every server
  536. //
  537. while ((Server = Settings->SettingsServers) != NULL) {
  538. Settings->SettingsServers = Server->ServerNext;
  539. //
  540. // For every inbound connection
  541. //
  542. while ((Cxtion = Server->ServerIns) != NULL) {
  543. Server->ServerIns = Cxtion->CxtionNext;
  544. // Free inbound connection
  545. free(Cxtion->CxtionRDN);
  546. free(Cxtion->CxtionPartner);
  547. free(Cxtion);
  548. }
  549. //
  550. // For every outbound connection
  551. //
  552. while ((Cxtion = Server->ServerOuts) != NULL) {
  553. Server->ServerOuts = Cxtion->CxtionNext;
  554. // Free outbound connection
  555. free(Cxtion->CxtionRDN);
  556. free(Cxtion->CxtionPartner);
  557. free(Cxtion);
  558. }
  559. // Free server
  560. free(Server->ServerRDN);
  561. free(Server);
  562. }
  563. // Free settings
  564. free(Settings->SettingsRDN);
  565. free(Settings);
  566. }
  567. // Free site
  568. free(Site->SiteRDN);
  569. free(Site);
  570. }
  571. }
  572. VOID
  573. CreateOutBoundPartners(
  574. )
  575. /*++
  576. Routine Description:
  577. Scan our copy of the DS tree. For each server, use the generic
  578. table to find its outbound partners. Update the server's list
  579. of outbound connections.
  580. Arguments:
  581. None.
  582. Return Value:
  583. None.
  584. --*/
  585. {
  586. PSITE Site; // Scan the sites
  587. PSETTINGS Settings; // Scan the settings
  588. PSERVER Server; // Scan the servers
  589. PCXTION Cxtion; // Scan the inbound connections
  590. PSERVER InServer; // My inbound partner
  591. PCXTION InCxtion; // outbound connection added to my inbound partner
  592. PRTLSERVER InRtlServer; // Inbound partner from generic table
  593. //
  594. // For every site
  595. //
  596. for (Site = Sites;
  597. Site != NULL;
  598. Site = Site->SiteNext) {
  599. //
  600. // For every setting
  601. //
  602. for (
  603. Settings = Site->SiteSettings;
  604. Settings != NULL;
  605. Settings = Settings->SettingsNext) {
  606. //
  607. // For every server
  608. //
  609. for (Server = Settings->SettingsServers;
  610. Server != NULL;
  611. Server = Server->ServerNext) {
  612. //
  613. // For every inbound connection
  614. //
  615. for (Cxtion = Server->ServerIns;
  616. Cxtion != NULL;
  617. Cxtion = Cxtion->CxtionNext) {
  618. //
  619. // Find one of our inbound partners and put a copy of
  620. // this inbound connection on his list of outbound
  621. // connections after filling in the "partner" field
  622. // with this server's name. Basically, create the
  623. // outbound connections from the inbound connections.
  624. //
  625. // Find the inbound partner in the generic table
  626. InServer = RtlServerLookup(Cxtion->CxtionPartner);
  627. if (InServer == NULL)
  628. continue;
  629. //
  630. // Dummy up a outbound connection and put it on
  631. // our inbound partner's list of outbound connections.
  632. //
  633. InCxtion = (PCXTION)malloc(sizeof (*InCxtion));
  634. InCxtion->CxtionRDN = _tcsdup(Cxtion->CxtionRDN);
  635. InCxtion->CxtionServer = InServer;
  636. InCxtion->CxtionPartner = _tcsdup(Server->ServerRDN);
  637. InCxtion->CxtionNext = InServer->ServerOuts;
  638. InServer->ServerOuts = InCxtion;
  639. }
  640. }
  641. }
  642. }
  643. }
  644. PTCHAR
  645. GetRoot(
  646. IN PLDAP ldap
  647. )
  648. /*++
  649. Routine Description:
  650. Return the DS pathname of the configuration\sites container.
  651. Arguments:
  652. ldap - An open, bound ldap port.
  653. Return Value:
  654. A malloc'ed string representing the DS pathname of the
  655. configuration\sites container. Or NULL if the container could
  656. not be accessed. The caller must free() the string.
  657. --*/
  658. {
  659. PTCHAR Config; // DS pathname of configuration
  660. PTCHAR Root; // DS pathname of configuration\sites
  661. PTCHAR *Values; // values from the attribute "namingContexts"
  662. LONG NumVals; // number of values
  663. //
  664. // Search Base for the attribute "namingContext"
  665. //
  666. Values = GetValues(ldap, TEXT(""), TEXT("namingContexts"));
  667. if (Values == NULL)
  668. return NULL;
  669. //
  670. // Find the naming context that begins with "CN=configuration"
  671. //
  672. NumVals = ldap_count_values(Values);
  673. while (NumVals--) {
  674. Config = _tcsstr(Values[NumVals], TEXT("CN=configuration"));
  675. if (Config != NULL && Config == Values[NumVals]) {
  676. //
  677. // Build the pathname for "configuration\sites"
  678. //
  679. Root = (PTCHAR)malloc(
  680. sizeof (TCHAR) * _tcslen(Config) +
  681. sizeof (TCHAR) * (_tcslen(TEXT("CN=sites,")) + 1));
  682. _tcscpy(Root, TEXT("CN=sites,"));
  683. _tcscat(Root, Config);
  684. ldap_value_free(Values);
  685. return Root;
  686. }
  687. }
  688. ldap_value_free(Values);
  689. return NULL;
  690. }
  691. VOID
  692. FrsDsCheckTree(
  693. )
  694. /*++
  695. Routine Description:
  696. Scan our copy of the DS tree and check the consistency of sites and
  697. settings. XXX we need a list of checks here.
  698. Arguments:
  699. None.
  700. Return Value:
  701. None.
  702. --*/
  703. {
  704. PSITE Site; // Scan the sites
  705. PSETTINGS Settings; // Scan the settings
  706. //
  707. // No sites
  708. //
  709. if (Sites == NULL) {
  710. fprintf(stderr, "There are no sites\n");
  711. return;
  712. }
  713. //
  714. // For every site
  715. //
  716. for (Site = Sites;
  717. Site != NULL;
  718. Site = Site->SiteNext) {
  719. // No Settings
  720. if (Site->SiteSettings == NULL) {
  721. fprintf(stderr, "%ws has no NTDS Settings\n", Site->SiteRDN);
  722. } else {
  723. // More than one settings
  724. if (Site->SiteSettings->SettingsNext != NULL) {
  725. fprintf(stderr, "%ws has more than one NTDS Settings\n", Site->SiteRDN);
  726. // List the extra settings
  727. for (Settings = Site->SiteSettings;
  728. Settings != NULL;
  729. Settings = Settings->SettingsNext)
  730. fprintf(stderr, "\t%ws\n", Settings->SettingsRDN);
  731. }
  732. }
  733. //
  734. // For every settings
  735. //
  736. for (Settings = Site->SiteSettings;
  737. Settings != NULL;
  738. Settings = Settings->SettingsNext) {
  739. // No servers
  740. if (Settings->SettingsServers == NULL) {
  741. fprintf(stderr, "%ws has no servers\n", Settings->SettingsRDN);
  742. }
  743. }
  744. }
  745. }
  746. VOID
  747. CheckServers(
  748. )
  749. /*++
  750. Routine Description:
  751. Scan the generic table of servers and check the consistency of servers
  752. and connections. XXX we need a list of checks here.
  753. Arguments:
  754. None.
  755. Return Value:
  756. None.
  757. --*/
  758. {
  759. PVOID RestartKey; // Needed for scanning the table
  760. PSERVER Server; // Address of SERVER in copy of DS tree
  761. PCXTION Cxtion; // Address of CXTION in copy of DS tree
  762. PRTLSERVER RtlServer; // Returned by table routines
  763. PRTLSERVER Dups; // Duplicate servers
  764. //
  765. // Scan the generic table of servers. Every server is only listed once.
  766. //
  767. RestartKey = NULL;
  768. for (RtlServer = (PRTLSERVER)RtlEnumerateGenericTableWithoutSplaying(&ServerTable, &RestartKey);
  769. RtlServer != NULL;
  770. RtlServer = (PRTLSERVER)RtlEnumerateGenericTableWithoutSplaying(&ServerTable, &RestartKey)) {
  771. //
  772. // The same server name in multiple sites is not allowed
  773. //
  774. if (RtlServer->RtlServerDups != NULL) {
  775. fprintf(stderr, "%ws is a member of multiple sites\n", RtlServer->RtlServer->ServerRDN);
  776. fprintf(stderr, "\t%ws\n", RtlServer->RtlServer->ServerSettings->SettingsSite->SiteRDN);
  777. for (Dups = RtlServer->RtlServerDups; Dups; Dups = Dups->RtlServerDups)
  778. fprintf(stderr, "\t%ws\n", Dups->RtlServer->ServerSettings->SettingsSite->SiteRDN);
  779. }
  780. Server = RtlServer->RtlServer;
  781. // No inbound connections
  782. if (Server->ServerIns == NULL)
  783. fprintf(stderr, "%ws has no inbound connections\n", Server->ServerRDN);
  784. //
  785. // For every inbound connection
  786. //
  787. for (Cxtion = Server->ServerIns;
  788. Cxtion != NULL;
  789. Cxtion = Cxtion->CxtionNext) {
  790. // Connection doesn't have the partner's name
  791. if (Cxtion->CxtionPartner == NULL)
  792. fprintf(stderr, "%ws has no inbound server\n", Cxtion->CxtionRDN);
  793. // Replicating from ourselves is not allowed
  794. if (_tcscmp(Cxtion->CxtionPartner, Server->ServerRDN) == 0)
  795. fprintf(stderr, "%ws is its own inbound partner\n", Server->ServerRDN);
  796. }
  797. // No outbound connections
  798. if (Server->ServerOuts == NULL)
  799. fprintf(stderr, "%ws has no outbound connections\n", Server->ServerRDN);
  800. //
  801. // For every outbound connection
  802. //
  803. for (Cxtion = Server->ServerOuts;
  804. Cxtion != NULL;
  805. Cxtion = Cxtion->CxtionNext) {
  806. // Connection doesn't have the partner's name
  807. if (Cxtion->CxtionPartner == NULL)
  808. fprintf(stderr, "%ws has no outbound server\n", Cxtion->CxtionRDN);
  809. // Replicating to ourselves is not allowed
  810. if (_tcscmp(Cxtion->CxtionPartner, Server->ServerRDN) == 0)
  811. fprintf(stderr, "%ws is its own outbound partner\n", Server->ServerRDN);
  812. }
  813. }
  814. }
  815. VOID _cdecl
  816. main(
  817. IN LONG argc,
  818. IN PTCHAR *argv
  819. )
  820. /*++
  821. Routine Description:
  822. Open a connection to the DS and copy the DS tree beginning at
  823. configuration\sites. Check the resulting topology for consistency.
  824. The generic table routines are used to avoid N**2 algorithms during
  825. the consistency checks.
  826. Arguments:
  827. None.
  828. Return Value:
  829. exit 0 - No errors
  830. exit 1 - Something went wrong
  831. --*/
  832. {
  833. PLDAP ldap = NULL; // ldap connection
  834. PTCHAR Root = NULL; // DS pathname to ...\configuration\sites
  835. //
  836. // Open and bind a ldap connection to the DS
  837. //
  838. ldap = FrsDsOpenDs();
  839. if (ldap == NULL)
  840. exit(1);
  841. //
  842. // Get the DS pathname down to ...\configuration\sites
  843. //
  844. Root = GetRoot(ldap);
  845. if (Root == NULL)
  846. goto out;
  847. //
  848. // Create incore copy of the complete DS topology
  849. //
  850. // This generic table keeps additional info about the servers
  851. RtlInitializeGenericTable(&ServerTable, RtlServerCompare,
  852. RtlServerAllocate, RtlServerFree, NULL);
  853. // Create copy of the DS tree
  854. GetTree(ldap, Root, SITES, NULL, TEXT("(objectClass=site)"));
  855. // The DS doesn't have connections for outbound partners; create them
  856. CreateOutBoundPartners();
  857. //
  858. // Check consistency
  859. //
  860. FrsDsCheckTree(); // check incore copy of DS tree
  861. CheckServers(); // check generic table of servers
  862. out:
  863. //
  864. // Cleanup
  865. //
  866. if (Sites != NULL) {
  867. FreeRtlServer(); // generic table of servers
  868. FrsDsFreeTree(); // copy of DS tree
  869. }
  870. if (Root != NULL)
  871. free(Root); // DS pathname to ...\configuration\sites
  872. if (ldap != NULL)
  873. ldap_unbind(ldap); // release the connection to the DS
  874. }