Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

13263 lines
406 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. ds.c
  5. Abstract:
  6. This command server manages the topology retrieved from the DS.
  7. The entire DS tree is checked for consistency. But only a copy of
  8. the information about this server is sent on to the replica control
  9. command server.
  10. The consistency of the DS topology is verified before the info
  11. is merged into the current working topology. N**2 scans are
  12. prevented by using several temporary tables during the verification.
  13. Author:
  14. Billy J. Fuller 3-Mar-1997
  15. Environment
  16. User mode winnt
  17. --*/
  18. #include <ntreppch.h>
  19. #pragma hdrstop
  20. #include <perrepsr.h>
  21. #undef DEBSUB
  22. #define DEBSUB "DS:"
  23. #include <frs.h>
  24. #include <ntdsapi.h>
  25. #include <ntdsapip.h> // ms internal flags for DsCrackNames()
  26. #include <ntfrsapi.h>
  27. #include <tablefcn.h>
  28. #include <lmaccess.h>
  29. #include <dsrole.h>
  30. #include <lmapibuf.h>
  31. #ifdef SECURITY_WIN32
  32. #include <security.h>
  33. #else
  34. #define SECURITY_WIN32
  35. #include <security.h>
  36. #undef SECURITY_WIN32
  37. #endif
  38. #include <winsock2.h>
  39. //
  40. // No reason to hold memory if all we are doing is periodically
  41. // polling the DS to find there is no replication work to be
  42. // performed
  43. //
  44. // extern BOOL MainInitSucceeded;
  45. //
  46. // We will re-read the DS every so often (adjustable with registry)
  47. //
  48. ULONG DsPollingInterval;
  49. ULONG DsPollingShortInterval;
  50. ULONG DsPollingLongInterval;
  51. //
  52. // Don't use a noisy DS; wait until it settles out
  53. //
  54. ULONGLONG ThisChange;
  55. ULONGLONG LastChange;
  56. //
  57. // Dont't bother processing the same topology again
  58. //
  59. ULONGLONG NextChange;
  60. ULONGLONG ActiveChange;
  61. //
  62. // Try to keep the same binding forever
  63. //
  64. PLDAP gLdap = NULL;
  65. HANDLE DsHandle = NULL;
  66. PWCHAR SitesDn = NULL;
  67. PWCHAR ServicesDn = NULL;
  68. PWCHAR SystemDn = NULL;
  69. PWCHAR ComputersDn = NULL;
  70. PWCHAR DomainControllersDn = NULL;
  71. PWCHAR DefaultNcDn = NULL;
  72. BOOL DsBindingsAreValid = FALSE;
  73. BOOL DsCreateSysVolsHasRun = FALSE;
  74. //
  75. // Globals for the comm test
  76. //
  77. PWCHAR DsDomainControllerName;
  78. //
  79. // Have we initialized the rest of the service?
  80. //
  81. extern BOOL MainInitHasRun;
  82. //
  83. // Directory and file filter lists from registry.
  84. //
  85. extern PWCHAR RegistryFileExclFilterList;
  86. extern PWCHAR RegistryFileInclFilterList;
  87. extern PWCHAR RegistryDirExclFilterList;
  88. extern PWCHAR RegistryDirInclFilterList;
  89. //
  90. // Stop polling the DS
  91. //
  92. BOOL DsIsShuttingDown;
  93. HANDLE DsShutDownComplete;
  94. //
  95. // Remember the computer's DN to save calls to GetComputerObjectName().
  96. //
  97. PWCHAR ComputerCachedFqdn;
  98. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  99. PGEN_TABLE SubscriberTable = NULL;
  100. PGEN_TABLE SetTable = NULL;
  101. PGEN_TABLE CxtionTable = NULL;
  102. PGEN_TABLE AllCxtionsTable = NULL;
  103. PGEN_TABLE PartnerComputerTable = NULL;
  104. PGEN_TABLE MemberTable = NULL;
  105. PGEN_TABLE VolSerialNumberToDriveTable = NULL; // Mapping of VolumeSerial Number to drive.
  106. PWCHAR MemberSearchFilter = NULL;
  107. //
  108. // Collect the errors encountered during polling in this buffer and write it to the eventlog at the
  109. // end of poll.
  110. //
  111. PWCHAR DsPollSummaryBuf = NULL;
  112. DWORD DsPollSummaryBufLen = 0;
  113. DWORD DsPollSummaryMaxBufLen = 0;
  114. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  115. //
  116. // Role information
  117. //
  118. PWCHAR Roles[DsRole_RolePrimaryDomainController + 1] = {
  119. L"DsRole_RoleStandaloneWorkstation",
  120. L"DsRole_RoleMemberWorkstation",
  121. L"DsRole_RoleStandaloneServer",
  122. L"DsRole_RoleMemberServer",
  123. L"DsRole_RoleBackupDomainController",
  124. L"DsRole_RolePrimaryDomainController"
  125. };
  126. //
  127. // Flags to passed into DsGetDcName (see sdk\inc\dsgetdc.h)
  128. //
  129. FLAG_NAME_TABLE DsGetDcNameFlagNameTable[] = {
  130. {DS_FORCE_REDISCOVERY , "FORCE_REDISCOVERY " },
  131. {DS_DIRECTORY_SERVICE_REQUIRED , "DIRECTORY_SERVICE_REQUIRED " },
  132. {DS_DIRECTORY_SERVICE_PREFERRED , "DIRECTORY_SERVICE_PREFERRED " },
  133. {DS_GC_SERVER_REQUIRED , "GC_SERVER_REQUIRED " },
  134. {DS_PDC_REQUIRED , "PDC_REQUIRED " },
  135. {DS_BACKGROUND_ONLY , "DS_BACKGROUND_ONLY " },
  136. {DS_IP_REQUIRED , "IP_REQUIRED " },
  137. {DS_KDC_REQUIRED , "KDC_REQUIRED " },
  138. {DS_TIMESERV_REQUIRED , "TIMESERV_REQUIRED " },
  139. {DS_WRITABLE_REQUIRED , "WRITABLE_REQUIRED " },
  140. {DS_GOOD_TIMESERV_PREFERRED , "GOOD_TIMESERV_PREFERRED " },
  141. {DS_AVOID_SELF , "AVOID_SELF " },
  142. {DS_ONLY_LDAP_NEEDED , "ONLY_LDAP_NEEDED " },
  143. {DS_IS_FLAT_NAME , "IS_FLAT_NAME " },
  144. {DS_IS_DNS_NAME , "IS_DNS_NAME " },
  145. {DS_RETURN_DNS_NAME , "RETURN_DNS_NAME " },
  146. {DS_RETURN_FLAT_NAME , "RETURN_FLAT_NAME " },
  147. {0, NULL}
  148. };
  149. //
  150. // return flags from DsGetDCInfo() & DsGetDcName() too?
  151. //
  152. FLAG_NAME_TABLE DsGetDcInfoFlagNameTable[] = {
  153. {DS_PDC_FLAG , "DCisPDCofDomain " },
  154. {DS_GC_FLAG , "DCIsGCofForest " },
  155. {DS_LDAP_FLAG , "ServerSupportsLDAP_Server " },
  156. {DS_DS_FLAG , "DCSupportsDSAndIsA_DC " },
  157. {DS_KDC_FLAG , "DCIsRunningKDCSvc " },
  158. {DS_TIMESERV_FLAG , "DCIsRunningTimeSvc " },
  159. {DS_CLOSEST_FLAG , "DCIsInClosestSiteToClient " },
  160. {DS_WRITABLE_FLAG , "DCHasWritableDS " },
  161. {DS_GOOD_TIMESERV_FLAG , "DCRunningTimeSvcWithClockHW " },
  162. {DS_DNS_CONTROLLER_FLAG , "DCNameIsDNSName " },
  163. {DS_DNS_DOMAIN_FLAG , "DomainNameIsDNSName " },
  164. {DS_DNS_FOREST_FLAG , "DnsForestNameIsDNSName " },
  165. {0, NULL}
  166. };
  167. //
  168. // Name strings for config node object types. NOTE: Order must match enum in frs.h
  169. //
  170. PWCHAR DsConfigTypeName[] = {
  171. L" ",
  172. L"NTDS-Connection (in)",
  173. L"NTFRS-Member",
  174. L"NTFRS-Replica-Set",
  175. L"NTFRS-Settings",
  176. L"NTDS-Settings",
  177. L"NTFRS-Subscriber",
  178. L"NTFRS-Subscriptions",
  179. L"NTDS-DSA",
  180. L"COMPUTER",
  181. L"USER",
  182. L"SERVER",
  183. L"<<SERVICES_ROOT>>",
  184. L"<<Connection (Out)>>"
  185. };
  186. //
  187. // Client side ldap_connect timeout in seconds. Reg value "Ldap Bind Timeout In Seconds". Default is 30 seconds.
  188. //
  189. extern DWORD LdapBindTimeoutInSeconds;
  190. /******************************************************************************
  191. *******************************************************************************
  192. ** **
  193. ** **
  194. ** F R S _ L D A P _ S E A R C H _ C O N T E X T **
  195. ** **
  196. ** **
  197. *******************************************************************************
  198. ******************************************************************************/
  199. //
  200. // Client side ldap search timeout in minutes. Reg value "Ldap Search Timeout In Minutes". Default is 10 minutes.
  201. //
  202. extern DWORD LdapSearchTimeoutInMinutes;
  203. //
  204. // Ldap client timeout structure. Value is overwritten by the value of LdapSearchTimeoutInMinutes.
  205. //
  206. LDAP_TIMEVAL LdapTimeout = { 10 * 60 * 60, 0 }; //Default ldap timeout value. Overridden by registry param Ldap Search Timeout Value In Minutes
  207. #define FRS_LDAP_SEARCH_PAGESIZE 1000
  208. typedef struct _FRS_LDAP_SEARCH_CONTEXT {
  209. ULONG EntriesInPage; // Number of entries in the current page.
  210. ULONG CurrentEntry; // Location of the pointer into the page.
  211. LDAPMessage * LdapMsg; // Returned from ldap_search_ext_s()
  212. LDAPMessage * CurrentLdapMsg; // Current entry from current page.
  213. PWCHAR Filter; // Filter to add to the DS query.
  214. PWCHAR BaseDn; // Dn to start the query from.
  215. DWORD Scope; // Scope of the search.
  216. PWCHAR * Attrs; // Attributes requested by the search.
  217. } FRS_LDAP_SEARCH_CONTEXT, *PFRS_LDAP_SEARCH_CONTEXT;
  218. //
  219. // Registry Command codes for FrsDsEnumerateSysVolKeys()
  220. //
  221. #define REGCMD_CREATE_PRIMARY_DOMAIN (1)
  222. #define REGCMD_CREATE_MEMBERS (2)
  223. #define REGCMD_DELETE_MEMBERS (3)
  224. #define REGCMD_DELETE_KEYS (4)
  225. #define MK_ATTRS_1(_attr_, _a1) \
  226. _attr_[0] = _a1; _attr_[1] = NULL;
  227. #define MK_ATTRS_2(_attr_, _a1, _a2) \
  228. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = NULL;
  229. #define MK_ATTRS_3(_attr_, _a1, _a2, _a3) \
  230. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = NULL;
  231. #define MK_ATTRS_4(_attr_, _a1, _a2, _a3, _a4) \
  232. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  233. _attr_[4] = NULL;
  234. #define MK_ATTRS_5(_attr_, _a1, _a2, _a3, _a4, _a5) \
  235. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  236. _attr_[4] = _a5; _attr_[5] = NULL;
  237. #define MK_ATTRS_6(_attr_, _a1, _a2, _a3, _a4, _a5, _a6) \
  238. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  239. _attr_[4] = _a5; _attr_[5] = _a6; _attr_[6] = NULL;
  240. #define MK_ATTRS_7(_attr_, _a1, _a2, _a3, _a4, _a5, _a6, _a7) \
  241. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  242. _attr_[4] = _a5; _attr_[5] = _a6; _attr_[6] = _a7; _attr_[7] = NULL;
  243. #define MK_ATTRS_8(_attr_, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) \
  244. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  245. _attr_[4] = _a5; _attr_[5] = _a6; _attr_[6] = _a7; _attr_[7] = _a8; \
  246. _attr_[8] = NULL;
  247. //
  248. // Merging the information from the Ds with the active replicas.
  249. //
  250. CRITICAL_SECTION MergingReplicasWithDs;
  251. VOID
  252. FrsBuildVolSerialNumberToDriveTable(
  253. VOID
  254. );
  255. ULONG
  256. FrsProcessBackupRestore(
  257. VOID
  258. );
  259. RcsSetSysvolReady(
  260. IN DWORD NewSysvolReady
  261. );
  262. LONG
  263. PmInitPerfmonRegistryKeys (
  264. VOID
  265. );
  266. VOID
  267. DbgQueryDynamicConfigParams(
  268. );
  269. DWORD
  270. FrsDsGetRole(
  271. VOID
  272. );
  273. VOID
  274. FrsDsAddToPollSummary3ws(
  275. IN DWORD idsCode,
  276. IN PWCHAR WStr1,
  277. IN PWCHAR WStr2,
  278. IN PWCHAR WStr3
  279. )
  280. /*++
  281. Routine Description:
  282. Add to the poll summary event log.
  283. Arguments:
  284. idsCode - Code of data string from string.rc
  285. WStr1 - Argument1
  286. WStr2 - Argument2
  287. WStr3 - Argument3
  288. Return Value:
  289. None.
  290. --*/
  291. {
  292. #undef DEBSUB
  293. #define DEBSUB "FrsDsAddToPollSummary3ws:"
  294. PWCHAR ResStr = NULL;
  295. PWCHAR tempMessage = NULL;
  296. DWORD tempMessageLen = 0;
  297. ResStr = FrsGetResourceStr(idsCode);
  298. tempMessageLen = (wcslen(ResStr) - wcslen(L"%ws%ws%ws") +
  299. wcslen(WStr1) + wcslen(WStr2) +
  300. wcslen(WStr3) + 1) * sizeof(WCHAR);
  301. tempMessage = FrsAlloc(tempMessageLen);
  302. wsprintf(tempMessage, ResStr, WStr1, WStr2, WStr3);
  303. //
  304. // Don't want to copy the trailing null to the event log buffer or else
  305. // the next message will not be printed.
  306. //
  307. FRS_DS_ADD_TO_POLL_SUMMARY(DsPollSummaryBuf, tempMessage, tempMessageLen - 2);
  308. FrsFree(ResStr);
  309. FrsFree(tempMessage);
  310. return;
  311. }
  312. VOID
  313. FrsDsAddToPollSummary(
  314. IN DWORD idsCode
  315. )
  316. /*++
  317. Routine Description:
  318. Add to the poll summary event log.
  319. Arguments:
  320. idsCode - Code of data string from string.rc
  321. Return Value:
  322. None.
  323. --*/
  324. {
  325. #undef DEBSUB
  326. #define DEBSUB "FrsDsAddToPollSummary:"
  327. PWCHAR ResStr = NULL;
  328. ResStr = FrsGetResourceStr(idsCode);
  329. //
  330. // Don't want to copy the trailing null to the event log buffer or else
  331. // the next message will not be printed.
  332. //
  333. FRS_DS_ADD_TO_POLL_SUMMARY(DsPollSummaryBuf, ResStr, wcslen(ResStr) * sizeof(WCHAR));
  334. FrsFree(ResStr);
  335. return;
  336. }
  337. PVOID *
  338. FrsDsFindValues(
  339. IN PLDAP Ldap,
  340. IN PLDAPMessage Entry,
  341. IN PWCHAR DesiredAttr,
  342. IN BOOL DoBerVals
  343. )
  344. /*++
  345. Routine Description:
  346. Return the DS values for one attribute in an entry.
  347. Arguments:
  348. Ldap - An open, bound Ldap port.
  349. Entry - An Ldap entry returned by Ldap_search_s()
  350. DesiredAttr - Return values for this attribute.
  351. DoBerVals - Return the bervals (for binary data, v.s. WCHAR data)
  352. Return Value:
  353. An array of char pointers that represents the values for the attribute.
  354. The caller must free the array with LDAP_FREE_VALUES().
  355. NULL if unsuccessful.
  356. --*/
  357. {
  358. #undef DEBSUB
  359. #define DEBSUB "FrsDsFindValues:"
  360. PWCHAR Attr; // Retrieved from an Ldap entry
  361. BerElement *Ber; // Needed for scanning attributes
  362. //
  363. // Search the entry for the desired attribute
  364. //
  365. for (Attr = ldap_first_attribute(Ldap, Entry, &Ber);
  366. Attr != NULL;
  367. Attr = ldap_next_attribute(Ldap, Entry, Ber)) {
  368. if (WSTR_EQ(DesiredAttr, Attr)) {
  369. //
  370. // Return the values for DesiredAttr
  371. //
  372. if (DoBerVals) {
  373. return ldap_get_values_len(Ldap, Entry, Attr);
  374. } else {
  375. return ldap_get_values(Ldap, Entry, Attr);
  376. }
  377. }
  378. }
  379. return NULL;
  380. }
  381. PWCHAR
  382. FrsDsFindValue(
  383. IN PLDAP Ldap,
  384. IN PLDAPMessage Entry,
  385. IN PWCHAR DesiredAttr
  386. )
  387. /*++
  388. Routine Description:
  389. Return a copy of the first DS value for one attribute in an entry.
  390. Arguments:
  391. ldap - An open, bound ldap port.
  392. Entry - An ldap entry returned by ldap_search_s()
  393. DesiredAttr - Return values for this attribute.
  394. Return Value:
  395. A zero-terminated string or NULL if the attribute or its value
  396. doesn't exist. The string is freed with FREE_NO_HEADER().
  397. --*/
  398. {
  399. #undef DEBSUB
  400. #define DEBSUB "FrsDsFindValue:"
  401. PWCHAR Val;
  402. PWCHAR *Values;
  403. // Get ldap's array of values
  404. Values = (PWCHAR *)FrsDsFindValues(Ldap, Entry, DesiredAttr, FALSE);
  405. // Copy the first value (if any)
  406. Val = (Values) ? FrsWcsDup(Values[0]) : NULL;
  407. // Free ldap's array of values
  408. LDAP_FREE_VALUES(Values);
  409. return Val;
  410. }
  411. GUID *
  412. FrsDsFindGuid(
  413. IN PLDAP Ldap,
  414. IN PLDAPMessage LdapEntry
  415. )
  416. /*++
  417. Routine Description:
  418. Return a copy of the object's guid
  419. Arguments:
  420. ldap - An open, bound ldap port.
  421. Entry - An ldap entry returned by ldap_search_s()
  422. Return Value:
  423. The address of a guid or NULL. Free with FrsFree().
  424. --*/
  425. {
  426. #undef DEBSUB
  427. #define DEBSUB "FrsDsFindGuid:"
  428. GUID *Guid;
  429. PLDAP_BERVAL *Values;
  430. // Get ldap's array of values
  431. Values = (PLDAP_BERVAL *)FrsDsFindValues(Ldap, LdapEntry, ATTR_OBJECT_GUID, TRUE);
  432. // Copy the first value (if any)
  433. Guid = (Values) ? FrsDupGuid((GUID *)Values[0]->bv_val) : NULL;
  434. // Free ldap's array of values
  435. LDAP_FREE_BER_VALUES(Values);
  436. return Guid;
  437. }
  438. PSCHEDULE
  439. FrsDsFindSchedule(
  440. IN PLDAP Ldap,
  441. IN PLDAPMessage LdapEntry,
  442. OUT PULONG Len
  443. )
  444. /*++
  445. Routine Description:
  446. Return a copy of the object's schedule
  447. Arguments:
  448. Ldap - An open, bound ldap port.
  449. LdapEntry - An ldap entry returned by ldap_search_s()
  450. Len - length of schedule blob
  451. Return Value:
  452. The address of a schedule or NULL. Free with FrsFree().
  453. --*/
  454. {
  455. #undef DEBSUB
  456. #define DEBSUB "FrsDsFindSchedule:"
  457. PLDAP_BERVAL *Values;
  458. PSCHEDULE Schedule;
  459. //
  460. // Get ldap's array of values
  461. //
  462. Values = (PLDAP_BERVAL *)FrsDsFindValues(Ldap, LdapEntry, ATTR_SCHEDULE, TRUE);
  463. if (!Values)
  464. return NULL;
  465. //
  466. // Return a copy of the schedule
  467. //
  468. *Len = Values[0]->bv_len;
  469. if (*Len) {
  470. //
  471. // Need to check if *Len == 0 as FrsAlloc asserts if called with 0 as the first parameter (prefix fix).
  472. //
  473. Schedule = FrsAlloc(*Len);
  474. CopyMemory(Schedule, Values[0]->bv_val, *Len);
  475. } else {
  476. Schedule = NULL;
  477. }
  478. LDAP_FREE_BER_VALUES(Values);
  479. return Schedule;
  480. }
  481. BOOL
  482. FrsDsLdapSearch(
  483. IN PLDAP Ldap,
  484. IN PWCHAR Base,
  485. IN ULONG Scope,
  486. IN PWCHAR Filter,
  487. IN PWCHAR Attrs[],
  488. IN ULONG AttrsOnly,
  489. IN LDAPMessage **Msg
  490. )
  491. /*++
  492. Routine Description:
  493. Issue the ldap ldap_search_s call, check for errors, and check for
  494. a shutdown in progress.
  495. Arguments:
  496. ldap Session handle to Ldap server.
  497. Base The distinguished name of the entry at which to start the search
  498. Scope
  499. LDAP_SCOPE_BASE Search the base entry only.
  500. LDAP_SCOPE_ONELEVEL Search the base entry and all entries in the first
  501. level below the base.
  502. LDAP_SCOPE_SUBTREE Search the base entry and all entries in the tree
  503. below the base.
  504. Filter The search filter.
  505. Attrs A null-terminated array of strings indicating the attributes
  506. to return for each matching entry. Pass NULL to retrieve all
  507. available attributes.
  508. AttrsOnly A boolean value that should be zero if both attribute types
  509. and values are to be returned, nonzero if only types are wanted.
  510. mSG Contains the results of the search upon completion of the call.
  511. The ldap array of values or NULL if the Base, DesiredAttr, or its
  512. values does not exist.
  513. The ldap array is freed with LDAP_FREE_VALUES().
  514. Return Value:
  515. TRUE if not shutting down.
  516. --*/
  517. {
  518. #undef DEBSUB
  519. #define DEBSUB "FrsDsLdapSearch:"
  520. DWORD LStatus;
  521. *Msg = NULL;
  522. //
  523. // Increment the DS Searches counter
  524. //
  525. PM_INC_CTR_SERVICE(PMTotalInst, DSSearches, 1);
  526. //
  527. // Issue the ldap search
  528. //
  529. // LStatus = ldap_search_s(Ldap, Base, Scope, Filter, Attrs, AttrsOnly, Msg);
  530. LStatus = ldap_search_ext_s(Ldap,
  531. Base,
  532. Scope,
  533. Filter,
  534. Attrs,
  535. AttrsOnly,
  536. NULL,
  537. NULL,
  538. &LdapTimeout,
  539. 0,
  540. Msg);
  541. //
  542. // Check for errors
  543. //
  544. if (LStatus != LDAP_SUCCESS) {
  545. DPRINT2_LS(1, ":DS: WARN - Error searching %ws for %ws;", Base, Filter, LStatus);
  546. //
  547. // Increment the DS Searches in Error counter
  548. //
  549. PM_INC_CTR_SERVICE(PMTotalInst, DSSearchesError, 1);
  550. //
  551. // Add to the poll summary event log.
  552. //
  553. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_SEARCH_ERROR, Filter, Base,
  554. ldap_err2string(LStatus));
  555. LDAP_FREE_MSG(*Msg);
  556. return FALSE;
  557. }
  558. //
  559. // Return FALSE if shutting down.
  560. //
  561. if (FrsIsShuttingDown || DsIsShuttingDown) {
  562. LDAP_FREE_MSG(*Msg);
  563. return FALSE;
  564. }
  565. return TRUE;
  566. }
  567. BOOL
  568. FrsDsLdapSearchInit(
  569. PLDAP ldap,
  570. PWCHAR Base,
  571. ULONG Scope,
  572. PWCHAR Filter,
  573. PWCHAR Attrs[],
  574. ULONG AttrsOnly,
  575. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  576. )
  577. /*++
  578. Routine Description:
  579. Issue the ldap_create_page_control and ldap_search_ext_s calls,
  580. FrsDsLdapSearchInit(), and FrsDsLdapSearchNext() APIs are used to
  581. make ldap queries and retrieve the results in paged form.
  582. Arguments:
  583. ldap Session handle to Ldap server.
  584. Base The distinguished name of the entry at which to start the search.
  585. A copy of base is kept in the context.
  586. Scope
  587. LDAP_SCOPE_BASE Search the base entry only.
  588. LDAP_SCOPE_ONELEVEL Search the base entry and all entries in the first
  589. level below the base.
  590. LDAP_SCOPE_SUBTREE Search the base entry and all entries in the tree
  591. below the base.
  592. Filter The search filter. A copy of filter is kept in the context.
  593. Attrs A null-terminated array of strings indicating the attributes
  594. to return for each matching entry. Pass NULL to retrieve all
  595. available attributes.
  596. AttrsOnly A boolean value that should be zero if both attribute types
  597. and values are to be returned, nonzero if only types are wanted.
  598. FrsSearchContext
  599. An opaques structure that links the FrsDsLdapSearchInit() and
  600. FrsDsLdapSearchNext() calls together. The structure contains
  601. the information required to retrieve query results across pages.
  602. Return Value:
  603. BOOL result.
  604. --*/
  605. {
  606. #undef DEBSUB
  607. #define DEBSUB "FrsDsLdapSearchInit:"
  608. DWORD LStatus = LDAP_SUCCESS;
  609. PLDAPControl ServerControls[2];
  610. PLDAPControl ServerControl = NULL;
  611. UINT i;
  612. LDAP_BERVAL cookie1 = { 0, NULL };
  613. FrsSearchContext->LdapMsg = NULL;
  614. FrsSearchContext->CurrentLdapMsg = NULL;
  615. FrsSearchContext->EntriesInPage = 0;
  616. FrsSearchContext->CurrentEntry = 0;
  617. FrsSearchContext->BaseDn = FrsWcsDup(Base);
  618. FrsSearchContext->Filter = FrsWcsDup(Filter);
  619. FrsSearchContext->Scope = Scope;
  620. FrsSearchContext->Attrs = Attrs;
  621. LStatus = ldap_create_page_control(ldap,
  622. FRS_LDAP_SEARCH_PAGESIZE,
  623. &cookie1,
  624. FALSE, // is critical
  625. &ServerControl
  626. );
  627. ServerControls[0] = ServerControl;
  628. ServerControls[1] = NULL;
  629. if (LStatus != LDAP_SUCCESS) {
  630. DPRINT2_LS(2, ":DS: WARN - Error creating page control %ws for %ws;", Base, Filter, LStatus);
  631. FrsSearchContext->BaseDn = FrsFree(FrsSearchContext->BaseDn);
  632. FrsSearchContext->Filter = FrsFree(FrsSearchContext->Filter);
  633. return FALSE;
  634. }
  635. LStatus = ldap_search_ext_s(ldap,
  636. FrsSearchContext->BaseDn,
  637. FrsSearchContext->Scope,
  638. FrsSearchContext->Filter,
  639. FrsSearchContext->Attrs,
  640. FALSE,
  641. ServerControls,
  642. NULL,
  643. &LdapTimeout,
  644. 0,
  645. &FrsSearchContext->LdapMsg);
  646. ldap_control_free(ServerControl);
  647. if (LStatus == LDAP_SUCCESS) {
  648. FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
  649. FrsSearchContext->CurrentEntry = 0;
  650. }
  651. if (LStatus != LDAP_SUCCESS) {
  652. DPRINT2_LS(2, ":DS: WARN - Error searching %ws for %ws;", Base, Filter, LStatus);
  653. FrsSearchContext->BaseDn = FrsFree(FrsSearchContext->BaseDn);
  654. FrsSearchContext->Filter = FrsFree(FrsSearchContext->Filter);
  655. return FALSE;
  656. }
  657. return TRUE;
  658. }
  659. PLDAPMessage
  660. FrsDsLdapSearchGetNextEntry(
  661. PLDAP ldap,
  662. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  663. )
  664. /*++
  665. Routine Description:
  666. Get the next entry form the current page of the results
  667. returned. This call is only made if there is a entry
  668. in the current page.
  669. Arguments:
  670. ldap Session handle to Ldap server.
  671. FrsSearchContext
  672. An opaques structure that links the FrsDsLdapSearchInit() and
  673. FrsDsLdapSearchNext() calls together. The structure contains
  674. the information required to retrieve query results across pages.
  675. Return Value:
  676. The first or the next entry from the current page.
  677. --*/
  678. {
  679. #undef DEBSUB
  680. #define DEBSUB "FrsDsLdapSearchGetNextEntry:"
  681. FrsSearchContext->CurrentEntry += 1;
  682. if ( FrsSearchContext->CurrentEntry == 1 ) {
  683. FrsSearchContext->CurrentLdapMsg = ldap_first_entry(ldap ,FrsSearchContext->LdapMsg);
  684. } else {
  685. FrsSearchContext->CurrentLdapMsg = ldap_next_entry(ldap ,FrsSearchContext->CurrentLdapMsg);
  686. }
  687. return FrsSearchContext->CurrentLdapMsg;
  688. }
  689. DWORD
  690. FrsDsLdapSearchGetNextPage(
  691. PLDAP ldap,
  692. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  693. )
  694. /*++
  695. Routine Description:
  696. Get the next page from the results returned by ldap_search_ext_s..
  697. Arguments:
  698. ldap Session handle to Ldap server.
  699. FrsSearchContext
  700. An opaques structure that links the FrsDsLdapSearchInit() and
  701. FrsDsLdapSearchNext() calls together. The structure contains
  702. the information required to retrieve query results across pages.
  703. Return Value:
  704. WINSTATUS
  705. --*/
  706. {
  707. #undef DEBSUB
  708. #define DEBSUB "FrsDsLdapSearchGetNextPage:"
  709. DWORD LStatus = LDAP_SUCCESS;
  710. LDAP_BERVAL * CurrCookie = NULL;
  711. PLDAPControl * CurrControls = NULL;
  712. ULONG retcode = 0;
  713. ULONG TotalEntries = 0;
  714. PLDAPControl ServerControls[2];
  715. PLDAPControl ServerControl= NULL;
  716. // Get the server control from the message, and make a new control with the cookie from the server
  717. LStatus = ldap_parse_result(ldap, FrsSearchContext->LdapMsg, &retcode,NULL,NULL,NULL,&CurrControls,FALSE);
  718. LDAP_FREE_MSG(FrsSearchContext->LdapMsg);
  719. if (LStatus != LDAP_SUCCESS) {
  720. DPRINT2_LS(2, ":DS: WARN - Error in ldap_parse_result %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  721. return LdapMapErrorToWin32(LStatus);
  722. }
  723. LStatus = ldap_parse_page_control(ldap, CurrControls, &TotalEntries, &CurrCookie);
  724. if (LStatus != LDAP_SUCCESS) {
  725. DPRINT2_LS(2, ":DS: WARN - Error in ldap_parse_page_control %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  726. return LdapMapErrorToWin32(LStatus);
  727. }
  728. if ( CurrCookie->bv_len == 0 && CurrCookie->bv_val == 0 ) {
  729. LStatus = LDAP_CONTROL_NOT_FOUND;
  730. ldap_controls_free(CurrControls);
  731. ber_bvfree(CurrCookie);
  732. return LdapMapErrorToWin32(LStatus);
  733. }
  734. LStatus = ldap_create_page_control(ldap,
  735. FRS_LDAP_SEARCH_PAGESIZE,
  736. CurrCookie,
  737. FALSE,
  738. &ServerControl);
  739. ServerControls[0] = ServerControl;
  740. ServerControls[1] = NULL;
  741. ldap_controls_free(CurrControls);
  742. CurrControls = NULL;
  743. ber_bvfree(CurrCookie);
  744. CurrCookie = NULL;
  745. if (LStatus != LDAP_SUCCESS) {
  746. DPRINT2_LS(2, ":DS: WARN - Error in ldap_parse_page_control %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  747. return LdapMapErrorToWin32(LStatus);
  748. }
  749. // continue the search with the new cookie
  750. LStatus = ldap_search_ext_s(ldap,
  751. FrsSearchContext->BaseDn,
  752. FrsSearchContext->Scope,
  753. FrsSearchContext->Filter,
  754. FrsSearchContext->Attrs,
  755. FALSE,
  756. ServerControls,
  757. NULL,
  758. &LdapTimeout,
  759. 0,
  760. &FrsSearchContext->LdapMsg);
  761. ldap_control_free(ServerControl);
  762. //
  763. // LDAP_CONTROL_NOT_FOUND means that we have reached the end of the search results
  764. //
  765. if ( (LStatus != LDAP_SUCCESS) && (LStatus != LDAP_CONTROL_NOT_FOUND) ) {
  766. DPRINT2_LS(2, ":DS: WARN - Error searching %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  767. }
  768. if (LStatus == LDAP_SUCCESS) {
  769. FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
  770. FrsSearchContext->CurrentEntry = 0;
  771. }
  772. return LdapMapErrorToWin32(LStatus);
  773. }
  774. PLDAPMessage
  775. FrsDsLdapSearchNext(
  776. PLDAP ldap,
  777. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  778. )
  779. /*++
  780. Routine Description:
  781. Get the next entry form the current page of the results
  782. returned or from the next page if we are at the end of the.
  783. current page.
  784. Arguments:
  785. ldap Session handle to Ldap server.
  786. FrsSearchContext
  787. An opaques structure that links the FrsDsLdapSearchInit() and
  788. FrsDsLdapSearchNext() calls together. The structure contains
  789. the information required to retrieve query results across pages.
  790. Return Value:
  791. The next entry on this page or the first entry from the next page.
  792. NULL if there are no more entries to return.
  793. --*/
  794. {
  795. #undef DEBSUB
  796. #define DEBSUB "FrsDsLdapSearchNext:"
  797. DWORD WStatus = ERROR_SUCCESS;
  798. PLDAPMessage NextEntry = NULL;
  799. if (FrsSearchContext->EntriesInPage > FrsSearchContext->CurrentEntry )
  800. {
  801. // return the next entry from the current page
  802. return FrsDsLdapSearchGetNextEntry(ldap, FrsSearchContext);
  803. }
  804. else
  805. {
  806. // see if there are more pages of results to get
  807. WStatus = FrsDsLdapSearchGetNextPage(ldap, FrsSearchContext);
  808. if (WStatus == ERROR_SUCCESS)
  809. {
  810. return FrsDsLdapSearchGetNextEntry(ldap, FrsSearchContext);
  811. }
  812. }
  813. return NextEntry;
  814. }
  815. VOID
  816. FrsDsLdapSearchClose(
  817. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  818. )
  819. /*++
  820. Routine Description:
  821. The search is complete. Free the elemetns of the context and reset
  822. them so the same context can be used for another search.
  823. Arguments:
  824. FrsSearchContext
  825. An opaques structure that links the FrsDsLdapSearchInit() and
  826. FrsDsLdapSearchNext() calls together. The structure contains
  827. the information required to retrieve query results across pages.
  828. Return Value:
  829. NONE
  830. --*/
  831. {
  832. #undef DEBSUB
  833. #define DEBSUB "FrsDsLdapSearchClose:"
  834. FrsSearchContext->EntriesInPage = 0;
  835. FrsSearchContext->CurrentEntry = 0;
  836. FrsSearchContext->BaseDn = FrsFree(FrsSearchContext->BaseDn);
  837. FrsSearchContext->Filter = FrsFree(FrsSearchContext->Filter);
  838. LDAP_FREE_MSG(FrsSearchContext->LdapMsg);
  839. }
  840. PWCHAR *
  841. FrsDsGetValues(
  842. IN PLDAP Ldap,
  843. IN PWCHAR Base,
  844. IN PWCHAR DesiredAttr
  845. )
  846. /*++
  847. Routine Description:
  848. Return all of the DS values for one attribute in an object.
  849. Arguments:
  850. ldap - An open, bound ldap port.
  851. Base - The "pathname" of a DS object.
  852. DesiredAttr - Return values for this attribute.
  853. Return Value:
  854. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  855. does not exist. The ldap array is freed with LDAP_FREE_VALUES().
  856. --*/
  857. {
  858. #undef DEBSUB
  859. #define DEBSUB "FrsDsGetValues:"
  860. PLDAPMessage Msg = NULL; // Opaque stuff from ldap subsystem
  861. PWCHAR *Values; // Array of values for desired attribute
  862. //
  863. // Search Base for all of this attribute + values (objectCategory=*)
  864. //
  865. if (!FrsDsLdapSearch(Ldap, Base, LDAP_SCOPE_BASE, CATEGORY_ANY,
  866. NULL, 0, &Msg)) {
  867. return NULL;
  868. }
  869. //
  870. // Return the values for the desired attribute
  871. //
  872. Values = (PWCHAR *)FrsDsFindValues(Ldap,
  873. ldap_first_entry(Ldap, Msg),
  874. DesiredAttr,
  875. FALSE);
  876. LDAP_FREE_MSG(Msg);
  877. return Values;
  878. }
  879. PWCHAR
  880. FrsDsMakeParentDn(
  881. IN PWCHAR Dn
  882. )
  883. /*++
  884. Routine Description:
  885. Return a cop of Dn with the last component removed.
  886. NULL if no more components
  887. Arguments:
  888. Dn - Distinguished name in DS format
  889. Return Value:
  890. Return a cop of Dn with the last component removed.
  891. NULL if no more components
  892. --*/
  893. {
  894. #undef DEBSUB
  895. #define DEBSUB "FrsDsMakeParentDn:"
  896. DWORD PLen;
  897. //
  898. // Done
  899. //
  900. if (Dn == NULL) {
  901. return NULL;
  902. }
  903. //
  904. // Index of parent component
  905. //
  906. PLen = wcscspn(Dn, L",");
  907. //
  908. // No parent component
  909. //
  910. if (Dn[PLen] != L',') {
  911. return NULL;
  912. }
  913. //
  914. // Copy of parent component
  915. //
  916. return FrsWcsDup(&Dn[PLen + 1]);
  917. }
  918. PWCHAR
  919. FrsDsExtendDn(
  920. IN PWCHAR Dn,
  921. IN PWCHAR Cn
  922. )
  923. /*++
  924. Routine Description:
  925. Extend an existing DN with a new CN= component.
  926. Arguments:
  927. Dn - distinguished name
  928. Cn - common name
  929. Return Value:
  930. CN=Cn,Dn
  931. --*/
  932. {
  933. #undef DEBSUB
  934. #define DEBSUB "FrsDsExtendDn:"
  935. ULONG Len;
  936. PWCHAR NewDn;
  937. if ((Dn == NULL) || (Cn == NULL)) {
  938. return NULL;
  939. }
  940. Len = wcslen(L"CN=,") + wcslen(Dn) + wcslen(Cn) + 1;
  941. NewDn = (PWCHAR)FrsAlloc(Len * sizeof(WCHAR));
  942. wcscpy(NewDn, L"CN=");
  943. wcscat(NewDn, Cn);
  944. wcscat(NewDn, L",");
  945. wcscat(NewDn, Dn);
  946. return NewDn;
  947. }
  948. PWCHAR
  949. FrsDsExtendDnOu(
  950. IN PWCHAR Dn,
  951. IN PWCHAR Ou
  952. )
  953. /*++
  954. Routine Description:
  955. Extend an existing DN with a new OU= component.
  956. Arguments:
  957. Dn - distinguished name
  958. Ou - orginizational name
  959. Return Value:
  960. OU=Ou,Dn
  961. --*/
  962. {
  963. #undef DEBSUB
  964. #define DEBSUB "FrsDsExtendDnOu:"
  965. ULONG Len;
  966. PWCHAR NewDn;
  967. if ((Dn == NULL) || (Ou == NULL)) {
  968. return NULL;
  969. }
  970. Len = wcslen(L"OU=,") + wcslen(Dn) + wcslen(Ou) + 1;
  971. NewDn = (PWCHAR)FrsAlloc(Len * sizeof(WCHAR));
  972. wcscpy(NewDn, L"OU=");
  973. wcscat(NewDn, Ou);
  974. wcscat(NewDn, L",");
  975. wcscat(NewDn, Dn);
  976. return NewDn;
  977. }
  978. PWCHAR
  979. FrsDsMakeRdnX(
  980. IN PWCHAR DN,
  981. IN LONG Index
  982. )
  983. /*++
  984. Routine Description:
  985. Extract the RDN component (relative distinguished name) selected by
  986. the Index arg. Index 0 is first RDN in the string, 1 is the next, etc.
  987. The (DN) distinguished name is assumed to be in
  988. DS format (CN=xyz,CN=next one,...). In this case, the returned
  989. RDN for index 0 is "xyz".
  990. Arguments:
  991. DN - distinguished name
  992. Index - The index number of the desired RDN.
  993. Return Value:
  994. A zero-terminated string. The string is freed with FrsFree().
  995. NULL if selected RDN was not found.
  996. --*/
  997. {
  998. #undef DEBSUB
  999. #define DEBSUB "FrsDsMakeRdnX:"
  1000. DWORD RDNLen;
  1001. PWCHAR RDN;
  1002. if (DN == NULL) {
  1003. return NULL;
  1004. }
  1005. //
  1006. // Skip the first CN=; if any
  1007. //
  1008. RDN = wcsstr(DN, L"cn=");
  1009. if (RDN == DN) {
  1010. DN += 3;
  1011. }
  1012. while (Index > 0) {
  1013. DN = wcsstr(DN, L"cn=");
  1014. if (DN == NULL) {
  1015. return NULL;
  1016. }
  1017. DN = DN + 3;
  1018. Index -= 1;
  1019. }
  1020. // Return the string up to the first delimiter or EOS
  1021. RDNLen = wcscspn(DN, L",");
  1022. RDN = (PWCHAR)FrsAlloc(sizeof(WCHAR) * (RDNLen + 1));
  1023. wcsncpy(RDN, DN, RDNLen);
  1024. RDN[RDNLen] = L'\0';
  1025. return _wcsupr(RDN);
  1026. }
  1027. PWCHAR
  1028. FrsDsMakeRdn(
  1029. IN PWCHAR DN
  1030. )
  1031. /*++
  1032. Routine Description:
  1033. Extract the base component (relative distinguished name) from a
  1034. distinguished name. The distinguished name is assumed to be in
  1035. DS format (CN=xyz,CN=next one,...). In this case, the returned
  1036. RDN is "xyz".
  1037. Arguments:
  1038. DN - distinguished name
  1039. Return Value:
  1040. A zero-terminated string. The string is freed with FrsFree().
  1041. --*/
  1042. {
  1043. #undef DEBSUB
  1044. #define DEBSUB "FrsDsMakeRdn:"
  1045. DWORD RDNLen;
  1046. PWCHAR RDN;
  1047. if (DN == NULL) {
  1048. return NULL;
  1049. }
  1050. //
  1051. // Skip the first CN=; if any
  1052. //
  1053. RDN = wcsstr(DN, L"cn=");
  1054. if (RDN == DN) {
  1055. DN += 3;
  1056. }
  1057. // Return the string up to the first delimiter or EOS
  1058. RDNLen = wcscspn(DN, L",");
  1059. RDN = (PWCHAR)FrsAlloc(sizeof(WCHAR) * (RDNLen + 1));
  1060. wcsncpy(RDN, DN, RDNLen);
  1061. RDN[RDNLen] = L'\0';
  1062. return _wcsupr(RDN);
  1063. }
  1064. VOID
  1065. FrsDsCloseDs(
  1066. VOID
  1067. )
  1068. /*++
  1069. Routine Description:
  1070. Unbind from the DS.
  1071. Arguments:
  1072. None.
  1073. Return Value:
  1074. None.
  1075. --*/
  1076. {
  1077. #undef DEBSUB
  1078. #define DEBSUB "FrsDsCloseDs:"
  1079. DsBindingsAreValid = FALSE;
  1080. if (gLdap) {
  1081. ldap_unbind_s(gLdap);
  1082. gLdap = NULL;
  1083. }
  1084. if (HANDLE_IS_VALID(DsHandle)) {
  1085. DsUnBind(&DsHandle);
  1086. DsHandle = NULL;
  1087. }
  1088. SitesDn = FrsFree(SitesDn);
  1089. ServicesDn = FrsFree(ServicesDn);
  1090. SystemDn = FrsFree(SystemDn);
  1091. ComputersDn = FrsFree(ComputersDn);
  1092. DomainControllersDn = FrsFree(DomainControllersDn);
  1093. DefaultNcDn = FrsFree(DefaultNcDn);
  1094. }
  1095. DWORD
  1096. FrsDsGetDcInfo(
  1097. IN PDOMAIN_CONTROLLER_INFO *DcInfo,
  1098. IN DWORD Flags
  1099. )
  1100. /*++
  1101. Routine Description:
  1102. Open and bind to a dc
  1103. Arguments:
  1104. DcInfo - Dc Info
  1105. Flags - DsGetDcName(Flags)
  1106. Return Value:
  1107. DsGetDcName
  1108. --*/
  1109. {
  1110. #undef DEBSUB
  1111. #define DEBSUB "FrsDsGetDcInfo:"
  1112. DWORD WStatus;
  1113. PWCHAR DcName;
  1114. DWORD InfoFlags;
  1115. CHAR FlagBuffer[220];
  1116. FrsFlagsToStr(Flags, DsGetDcNameFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  1117. DPRINT2(4, ":DS: DsGetDcName (%08x) Flags [%s]\n", Flags, FlagBuffer);
  1118. WStatus = DsGetDcName(NULL, // Computer to remote to
  1119. NULL, // Domain - use our own
  1120. NULL, // Domain Guid
  1121. NULL, // Site Guid
  1122. Flags,
  1123. DcInfo); // Return info
  1124. CLEANUP1_WS(0, ":DS: ERROR - Could not get DC Info for %ws;",
  1125. ComputerName, WStatus, RETURN);
  1126. DcName = (*DcInfo)->DomainControllerName;
  1127. FrsFlagsToStr(Flags, DsGetDcNameFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  1128. DPRINT2(4, ":DS: DcInfo (flags are %08x) Flags [%s]\n", Flags, FlagBuffer);
  1129. DPRINT1(4, ":DS: DomainControllerName : %ws\n", DcName);
  1130. DPRINT1(4, ":DS: DomainControllerAddress: %ws\n", (*DcInfo)->DomainControllerAddress);
  1131. DPRINT1(4, ":DS: DomainControllerType : %08x\n",(*DcInfo)->DomainControllerAddressType);
  1132. DPRINT1(4, ":DS: DomainName : %ws\n", (*DcInfo)->DomainName);
  1133. DPRINT1(4, ":DS: DnsForestName : %ws\n", (*DcInfo)->DnsForestName);
  1134. DPRINT1(4, ":DS: DcSiteName : %ws\n", (*DcInfo)->DcSiteName);
  1135. DPRINT1(4, ":DS: ClientSiteName : %ws\n", (*DcInfo)->ClientSiteName);
  1136. InfoFlags = (*DcInfo)->Flags;
  1137. FrsFlagsToStr(InfoFlags, DsGetDcInfoFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  1138. DPRINT2(4, ":DS: InfoFlags : %08x Flags [%s]\n",InfoFlags, FlagBuffer);
  1139. DsDomainControllerName = FrsFree(DsDomainControllerName);
  1140. DsDomainControllerName = FrsWcsDup(DcName);
  1141. //
  1142. // DCs should bind to the local DS to avoid ACL problems.
  1143. //
  1144. if (IsADc && DcName && (wcslen(DcName) > 2) &&
  1145. _wcsnicmp(&DcName[2], ComputerName, wcslen(ComputerName))) {
  1146. DPRINT3(0, ":DS: ERROR - The DC %ws is using the DS on DC %ws "
  1147. "Some of the information in the DS"
  1148. " may be unavailable to %ws; possibly disabling "
  1149. "replication with some partners.\n",
  1150. ComputerName, &DcName[2], ComputerName);
  1151. }
  1152. RETURN:
  1153. return WStatus;
  1154. }
  1155. VOID
  1156. FrsDsRegisterSpn(
  1157. IN PLDAP Ldap,
  1158. IN PCONFIG_NODE Computer
  1159. )
  1160. /*++
  1161. Routine Description:
  1162. Register the NtFrs SPN so that authenticated RPC calls can
  1163. use SPN/FQDN as the target principal name
  1164. Arguments:
  1165. Computer - Computer node from the ds
  1166. Return Value:
  1167. None.
  1168. --*/
  1169. {
  1170. #undef DEBSUB
  1171. #define DEBSUB "FrsDsRegisterSpn:"
  1172. DWORD WStatus;
  1173. PWCHAR Spn = NULL;
  1174. PWCHAR SpnPrefix= NULL;
  1175. PWCHAR *SpnList = NULL;
  1176. DWORD SpnNum = 0;
  1177. static BOOL RegisteredSpn = FALSE;
  1178. //
  1179. // No Ds binding or no computer or already registered
  1180. //
  1181. if (RegisteredSpn ||
  1182. (ComputerDnsName[0] == L'\0') ||
  1183. !DsBindingsAreValid ||
  1184. !Computer ||
  1185. !Computer->Dn) {
  1186. return;
  1187. }
  1188. //
  1189. // Register the NtFrs SPN so that authenticated RPC calls can
  1190. // use SPN/FQDN as the target principal name
  1191. //
  1192. Spn = FrsAlloc((wcslen(ComputerDnsName) + wcslen(SERVICE_PRINCIPAL_NAME) + 2) * sizeof(WCHAR));
  1193. wcscpy(Spn, SERVICE_PRINCIPAL_NAME);
  1194. wcscat(Spn, L"/");
  1195. wcscat(Spn, ComputerDnsName);
  1196. SpnPrefix = FrsAlloc((wcslen(SERVICE_PRINCIPAL_NAME) + 1) * sizeof(WCHAR));
  1197. wcscpy(SpnPrefix, SERVICE_PRINCIPAL_NAME);
  1198. SpnList = FrsDsGetValues(Ldap, Computer->Dn, ATTR_SERVICE_PRINCIPAL_NAME);
  1199. SpnNum=0;
  1200. while ((SpnList != NULL)&& (SpnList[SpnNum] != NULL)) {
  1201. DPRINT2(5, "Spn list from DS[%d] = %ws\n", SpnNum, SpnList[SpnNum]);
  1202. if (!_wcsicmp(SpnList[SpnNum], Spn)) {
  1203. // Spn found for NtFrs.
  1204. DPRINT1(4, "SPN already registered for Ntfrs: %ws\n", SpnList[SpnNum]);
  1205. RegisteredSpn = TRUE;
  1206. } else if (!_wcsnicmp(SpnList[SpnNum], SpnPrefix, wcslen(SpnPrefix))) {
  1207. //
  1208. // An older SPN exists. Delete it.
  1209. //
  1210. DPRINT1(4, "Deleting stale SPN for Ntfrs: %ws\n", SpnList[SpnNum]);
  1211. WStatus = DsWriteAccountSpn(DsHandle, DS_SPN_DELETE_SPN_OP, Computer->Dn, 1, &SpnList[SpnNum]);
  1212. if (!WIN_SUCCESS(WStatus)) {
  1213. DPRINT2_WS(1, "WARN - Delete DsWriteAccountSpn(%ws, %ws);", SpnList[SpnNum], Computer->Dn, WStatus);
  1214. } else {
  1215. DPRINT2(5, "Delete DsWriteAccountSpn(%ws, %ws); success\n", SpnList[SpnNum], Computer->Dn);
  1216. }
  1217. }
  1218. ++SpnNum;
  1219. }
  1220. if (!RegisteredSpn) {
  1221. DPRINT1(4, "Registering SPN for Ntfrs; %ws\n", Spn);
  1222. WStatus = DsWriteAccountSpn(DsHandle, DS_SPN_ADD_SPN_OP, Computer->Dn, 1, &Spn);
  1223. if (!WIN_SUCCESS(WStatus)) {
  1224. DPRINT2_WS(1, "WARN - Add DsWriteAccountSpn(%ws, %ws);", Spn, Computer->Dn, WStatus);
  1225. } else {
  1226. DPRINT2(5, "Add DsWriteAccountSpn(%ws, %ws); success\n", Spn, Computer->Dn);
  1227. RegisteredSpn = TRUE;
  1228. }
  1229. }
  1230. FrsFree(Spn);
  1231. FrsFree(SpnPrefix);
  1232. //
  1233. // Free ldap's array of values
  1234. //
  1235. LDAP_FREE_VALUES(SpnList);
  1236. }
  1237. BOOL
  1238. FrsDsBindDs(
  1239. IN DWORD Flags
  1240. )
  1241. /*++
  1242. Routine Description:
  1243. Open and bind to a domain controller.
  1244. Arguments:
  1245. Flags - For FrsDsGetDcInfo()
  1246. Return Value:
  1247. None. Sets global handles for FrsDsOpenDs().
  1248. --*/
  1249. {
  1250. #undef DEBSUB
  1251. #define DEBSUB "FrsDsBindDs:"
  1252. DWORD WStatus;
  1253. DWORD LStatus;
  1254. PWCHAR DcAddr;
  1255. PWCHAR DcName;
  1256. PWCHAR DcDnsName;
  1257. BOOL Bound = FALSE;
  1258. PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
  1259. struct l_timeval Timeout;
  1260. #define MAX_DC_NAMELIST 8
  1261. ULONG NameListx, i;
  1262. PWCHAR NameList[MAX_DC_NAMELIST];
  1263. ULONG ulOptions;
  1264. //
  1265. // Bind to the local DS if this computer is a DC
  1266. //
  1267. gLdap = NULL;
  1268. if (IsADc) {
  1269. DcAddr = NULL;
  1270. DcName = ComputerName;
  1271. DcDnsName = ComputerDnsName;
  1272. } else {
  1273. //
  1274. // Not a DC; find any DC for this domain
  1275. //
  1276. WStatus = FrsDsGetDcInfo(&DcInfo, Flags);
  1277. CLEANUP2_WS(0, ":DS: ERROR - FrsDsGetDcInfo(%08x, %ws);",
  1278. Flags, ComputerName, WStatus, CLEANUP);
  1279. //
  1280. // Binding address
  1281. //
  1282. DcAddr = DcInfo->DomainControllerAddress;
  1283. DcName = NULL;
  1284. DcDnsName = DcInfo->DomainControllerName;
  1285. }
  1286. FRS_ASSERT(DcDnsName || DcName || DcAddr);
  1287. //
  1288. // Open the ldap server using various forms of the DC's name
  1289. //
  1290. NameListx = 0;
  1291. if (DcDnsName &&
  1292. (wcslen(DcDnsName) > 2) && DcDnsName[0] == L'\\' && DcDnsName[1] == L'\\') {
  1293. // Trim the "\\"
  1294. NameList[NameListx++] = DcDnsName + 2;
  1295. }
  1296. if (DcAddr &&
  1297. (wcslen(DcAddr) > 2) && DcAddr[0] == L'\\' && DcAddr[1] == L'\\') {
  1298. // Trim the "\\"
  1299. NameList[NameListx++] = DcAddr + 2;
  1300. }
  1301. NameList[NameListx++] = DcDnsName;
  1302. NameList[NameListx++] = DcName;
  1303. NameList[NameListx++] = DcAddr;
  1304. FRS_ASSERT(NameListx <= MAX_DC_NAMELIST);
  1305. ulOptions = PtrToUlong(LDAP_OPT_ON);
  1306. Timeout.tv_sec = LdapBindTimeoutInSeconds;
  1307. Timeout.tv_usec = 0;
  1308. for (i=0; i<NameListx; i++) {
  1309. if (NameList[i] != NULL) {
  1310. //
  1311. // if ldap_open is called with a server name the api will call DsGetDcName
  1312. // passing the server name as the domainname parm...bad, because
  1313. // DsGetDcName will make a load of DNS queries based on the server name,
  1314. // it is designed to construct these queries from a domain name...so all
  1315. // these queries will be bogus, meaning they will waste network bandwidth,
  1316. // time to fail, and worst case cause expensive on demand links to come up
  1317. // as referrals/forwarders are contacted to attempt to resolve the bogus
  1318. // names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
  1319. // after the ldap_init but before any other operation using the ldap
  1320. // handle from ldap_init, the delayed connection setup will not call
  1321. // DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
  1322. // will detect that and use the address directly.
  1323. //
  1324. // gLdap = ldap_open(NameList[i], LDAP_PORT);
  1325. gLdap = ldap_init(NameList[i], LDAP_PORT);
  1326. if (gLdap != NULL) {
  1327. ldap_set_option(gLdap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
  1328. LStatus = ldap_connect(gLdap, &Timeout);
  1329. if (LStatus != LDAP_SUCCESS) {
  1330. DPRINT1_LS(1, ":DS: WARN - ldap_connect(%ws);", NameList[i], LStatus);
  1331. ldap_unbind_s(gLdap);
  1332. gLdap = NULL;
  1333. } else {
  1334. //
  1335. // Successfully connected.
  1336. //
  1337. DPRINT1(5, ":DS: ldap_connect(%ws) succeeded\n", NameList[i]);
  1338. break;
  1339. }
  1340. }
  1341. }
  1342. }
  1343. //
  1344. // Whatever it is, we can't find it.
  1345. //
  1346. if (!gLdap) {
  1347. // DPRINT3_WS(0, ":DS: ERROR - ldap_open(DNS %ws, BIOS %ws, IP %ws);",
  1348. // DcDnsName, DcName, DcAddr, WStatus);
  1349. DPRINT3_WS(0, ":DS: ERROR - ldap_init(DNS %ws, BIOS %ws, IP %ws);",
  1350. DcDnsName, DcName, DcAddr, WStatus);
  1351. goto CLEANUP;
  1352. }
  1353. //
  1354. // Bind to the ldap server
  1355. //
  1356. LStatus = ldap_bind_s(gLdap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  1357. CLEANUP_LS(0, ":DS: ERROR - Binding to DS.", LStatus, CLEANUP);
  1358. //
  1359. // Bind to the Ds (for various Ds calls such as DsCrackName())
  1360. //
  1361. NameListx = 0;
  1362. NameList[NameListx++] = DcDnsName;
  1363. NameList[NameListx++] = DcName;
  1364. NameList[NameListx++] = DcAddr;
  1365. FRS_ASSERT(NameListx <= MAX_DC_NAMELIST);
  1366. WStatus = ERROR_RETRY;
  1367. for (i=0; i<NameListx; i++) {
  1368. if (NameList[i] != NULL) {
  1369. WStatus = DsBind(NameList[i], NULL, &DsHandle);
  1370. if (!WIN_SUCCESS(WStatus)) {
  1371. DsHandle = NULL;
  1372. DPRINT1_WS(1, ":DS: WARN - DsBind(%ws);", NameList[i], WStatus);
  1373. } else {
  1374. DPRINT1(5, ":DS: DsBind(%ws) succeeded\n", NameList[i]);
  1375. break;
  1376. }
  1377. }
  1378. }
  1379. //
  1380. // Whatever it is, we can't find it
  1381. //
  1382. CLEANUP3_WS(0, ":DS: ERROR - DsBind(DNS %ws, BIOS %ws, IP %ws);",
  1383. DcDnsName, DcName, DcAddr, WStatus, CLEANUP);
  1384. //
  1385. // SUCCESS
  1386. //
  1387. Bound = TRUE;
  1388. CLEANUP:
  1389. //
  1390. // Cleanup
  1391. //
  1392. if (!Bound) {
  1393. //
  1394. // Close the connection to release resources if the above failed.
  1395. //
  1396. if (gLdap) {
  1397. ldap_unbind_s(gLdap);
  1398. gLdap = NULL;
  1399. }
  1400. }
  1401. if (DcInfo) {
  1402. NetApiBufferFree(DcInfo);
  1403. DcInfo = NULL;
  1404. }
  1405. return Bound;
  1406. }
  1407. BOOL
  1408. FrsDsOpenDs(
  1409. VOID
  1410. )
  1411. /*++
  1412. Routine Description:
  1413. Open and bind to a primary domain controller. The DN of the
  1414. sites container is a sideeffect.
  1415. Arguments:
  1416. DefaultDn
  1417. Return Value:
  1418. Bound ldap structure or NULL
  1419. --*/
  1420. {
  1421. #undef DEBSUB
  1422. #define DEBSUB "FrsDsOpenDs:"
  1423. DWORD WStatus;
  1424. DWORD LStatus;
  1425. DWORD NumVals;
  1426. PWCHAR Config;
  1427. PLDAPMessage LdapEntry;
  1428. PLDAPMessage LdapMsg = NULL;
  1429. PWCHAR *Values = NULL;
  1430. PWCHAR Attrs[3];
  1431. //
  1432. // Time to clean up and exit
  1433. //
  1434. if (FrsIsShuttingDown || DsIsShuttingDown) {
  1435. goto ERROR_BINDING;
  1436. }
  1437. //
  1438. // Use existing bindings if possible
  1439. //
  1440. if (DsBindingsAreValid) {
  1441. return TRUE;
  1442. }
  1443. //
  1444. // The previous poll might have set DsBindingsAreValid to FALSE because one
  1445. // of the handle became invalid. In that case the other handles still need to be
  1446. // closed to prevent leak.
  1447. //
  1448. FrsDsCloseDs();
  1449. //
  1450. // Increment the DS Bindings counter
  1451. //
  1452. PM_INC_CTR_SERVICE(PMTotalInst, DSBindings, 1);
  1453. //
  1454. // Bind to a DS.
  1455. //
  1456. // Note the behavior of DsGetDcName for the following four flag combinations.
  1457. //
  1458. // DS_BACKGROUND_ONLY (as of 10/10/99)
  1459. // DS_FORCE_REDISCOVERY
  1460. // Zero Zero Netlogon will attempt to satisfy the request via
  1461. // cached info, negative cache entries will be
  1462. // returned if they are less than 5 minutes old.
  1463. // If it can't it will do a full discovery (dns
  1464. // queries, udp pings, poss netbt queries,
  1465. // mailslot datagrams, etc)
  1466. //
  1467. // Zero One Netlogon will do a full discovery.
  1468. //
  1469. // One Zero Netlogon will satisfy from the cache, unless the
  1470. // backoff routine allows for a retry at this time
  1471. // *and* the cache is insufficient.
  1472. //
  1473. // One One The DS_BACKGROUND_ONY flag is ignored, treated
  1474. // as a FORCE call.
  1475. //
  1476. if (!FrsDsBindDs(DS_DIRECTORY_SERVICE_REQUIRED |
  1477. DS_WRITABLE_REQUIRED |
  1478. DS_BACKGROUND_ONLY)) {
  1479. //
  1480. // Flush the cache and try again
  1481. //
  1482. DPRINT(1, ":DS: WARN - FrsDsBindDs(no force) failed\n");
  1483. //
  1484. // Because of the use of Dial-up lines at sites without a DC we don't
  1485. // want to use DS_FORCE_REDISCOVERY since that will defeat the generic
  1486. // DC discovery backoff algorithm thus causing FRS to constantly bring
  1487. // up the line. Bug 412620.
  1488. //FrsDsCloseDs(); // close ldap handle before doing reopen.
  1489. //if (!FrsDsBindDs(DS_DIRECTORY_SERVICE_REQUIRED |
  1490. // DS_WRITABLE_REQUIRED |
  1491. // DS_FORCE_REDISCOVERY)) {
  1492. // DPRINT(1, ":DS: WARN - FrsDsBindDs(force) failed\n");
  1493. goto ERROR_BINDING;
  1494. //}
  1495. }
  1496. DPRINT(4, ":DS: FrsDsBindDs() succeeded\n");
  1497. //
  1498. // Find the naming contexts and the default naming context (objectCategory=*)
  1499. //
  1500. MK_ATTRS_2(Attrs, ATTR_NAMING_CONTEXTS, ATTR_DEFAULT_NAMING_CONTEXT);
  1501. if (!FrsDsLdapSearch(gLdap, CN_ROOT, LDAP_SCOPE_BASE, CATEGORY_ANY,
  1502. Attrs, 0, &LdapMsg)) {
  1503. goto ERROR_BINDING;
  1504. }
  1505. LdapEntry = ldap_first_entry(gLdap, LdapMsg);
  1506. if (LdapEntry == NULL) {
  1507. goto ERROR_BINDING;
  1508. }
  1509. Values = (PWCHAR *)FrsDsFindValues(gLdap, LdapEntry, ATTR_NAMING_CONTEXTS, FALSE);
  1510. if (Values == NULL) {
  1511. goto ERROR_BINDING;
  1512. }
  1513. //
  1514. // Now, find the naming context that begins with "CN=Configuration"
  1515. //
  1516. NumVals = ldap_count_values(Values);
  1517. while (NumVals--) {
  1518. FRS_WCSLWR(Values[NumVals]);
  1519. Config = wcsstr(Values[NumVals], CONFIG_NAMING_CONTEXT);
  1520. if (Config && Config == Values[NumVals]) {
  1521. //
  1522. // Build the pathname for "configuration\sites & services"
  1523. //
  1524. SitesDn = FrsDsExtendDn(Config, CN_SITES);
  1525. ServicesDn = FrsDsExtendDn(Config, CN_SERVICES);
  1526. break;
  1527. }
  1528. }
  1529. LDAP_FREE_VALUES(Values);
  1530. //
  1531. // Finally, find the default naming context
  1532. //
  1533. Values = (PWCHAR *)FrsDsFindValues(gLdap, LdapEntry, ATTR_DEFAULT_NAMING_CONTEXT, FALSE);
  1534. if (Values == NULL) {
  1535. goto ERROR_BINDING;
  1536. }
  1537. DefaultNcDn = FrsWcsDup(Values[0]);
  1538. ComputersDn = FrsDsExtendDn(DefaultNcDn, CN_COMPUTERS);
  1539. SystemDn = FrsDsExtendDn(DefaultNcDn, CN_SYSTEM);
  1540. DomainControllersDn = FrsDsExtendDnOu(DefaultNcDn, CN_DOMAIN_CONTROLLERS);
  1541. LDAP_FREE_VALUES(Values);
  1542. LDAP_FREE_MSG(LdapMsg);
  1543. //
  1544. // Polling the ds requires all these distinguished names
  1545. //
  1546. if ((SitesDn == NULL) || (ServicesDn == NULL) || (SystemDn == NULL) ||
  1547. (DefaultNcDn == NULL) || (ComputersDn == NULL) || (DomainControllersDn == NULL)) {
  1548. goto ERROR_BINDING;
  1549. }
  1550. //
  1551. // SUCCESS
  1552. //
  1553. DsBindingsAreValid = TRUE;
  1554. return TRUE;
  1555. ERROR_BINDING:
  1556. //
  1557. // avoid extraneous error messages during shutdown
  1558. //
  1559. if (!FrsIsShuttingDown && !DsIsShuttingDown) {
  1560. DPRINT(0, ":DS: ERROR - Could not open the DS\n");
  1561. }
  1562. //
  1563. // Cleanup
  1564. //
  1565. LDAP_FREE_VALUES(Values);
  1566. LDAP_FREE_MSG(LdapMsg);
  1567. //
  1568. // No ds bindings
  1569. //
  1570. FrsDsCloseDs();
  1571. //
  1572. // Increment the DS Bindings in Error counter
  1573. //
  1574. PM_INC_CTR_SERVICE(PMTotalInst, DSBindingsError, 1);
  1575. return FALSE;
  1576. }
  1577. #if DBG
  1578. #define FRS_PRINT_TREE(_Hdr_, _Sites_) FrsDsFrsPrintTree(_Hdr_, _Sites_)
  1579. VOID
  1580. FrsDsFrsPrintTree(
  1581. IN PWCHAR Hdr,
  1582. IN PCONFIG_NODE Sites
  1583. )
  1584. /*++
  1585. Routine Description:
  1586. print the tree.
  1587. Arguments:
  1588. Hdr - prettyprint
  1589. Sites
  1590. Return Value:
  1591. None.
  1592. --*/
  1593. {
  1594. #undef DEBSUB
  1595. #define DEBSUB "FrsDsFrsPrintTree:"
  1596. PCONFIG_NODE Site;
  1597. PCONFIG_NODE Settings;
  1598. PCONFIG_NODE Set;
  1599. PCONFIG_NODE Server;
  1600. PCONFIG_NODE Cxtion;
  1601. CHAR Guid[GUID_CHAR_LEN + 1];
  1602. if (Sites == NULL) {
  1603. return;
  1604. }
  1605. if (Hdr) {
  1606. DPRINT1(5, ":DS: %ws\n", Hdr);
  1607. }
  1608. //
  1609. // Print the tree
  1610. //
  1611. for (Site = Sites; Site; Site = Site->Peer) {
  1612. GuidToStr(Site->Name->Guid, Guid);
  1613. DPRINT2(5, ":DS: %ws (%ws)\n", Site->Name->Name,
  1614. (Site->Consistent) ? L"Consistent" : L"InConsistent");
  1615. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  1616. if (Settings->Name) {
  1617. GuidToStr(Settings->Name->Guid, Guid);
  1618. DPRINT2(5, ":DS: %ws (%ws)\n", Settings->Name->Name,
  1619. (Settings->Consistent) ? L"Consistent" : L"InConsistent");
  1620. } else {
  1621. DPRINT(5, ":DS: nTDSSettings\n");
  1622. }
  1623. for (Set = Settings->Children; Set; Set = Set->Peer) {
  1624. GuidToStr(Set->Name->Guid, Guid);
  1625. DPRINT2(5, ":DS: %ws (%ws)\n", Set->Name->Name,
  1626. (Set->Consistent) ? L"Consistent" : L"InConsistent");
  1627. for (Server = Set->Children; Server; Server = Server->Peer) {
  1628. GuidToStr(Server->Name->Guid, Guid);
  1629. DPRINT3(5, ":DS: %ws %ws (%ws)\n",
  1630. Server->Name->Name, Server->Root,
  1631. (Server->Consistent) ? L"Consistent" : L"InConsistent");
  1632. for (Cxtion = Server->Children; Cxtion; Cxtion = Cxtion->Peer) {
  1633. GuidToStr(Cxtion->Name->Guid, Guid);
  1634. DPRINT4(5, ":DS: %ws %ws %ws) (%ws)\n",
  1635. Cxtion->Name->Name,
  1636. (Cxtion->Inbound) ? L"IN (From" : L"OUT (To",
  1637. Cxtion->PartnerName->Name,
  1638. (Cxtion->Consistent) ? L"Consistent" : L"InConsistent");
  1639. }
  1640. }
  1641. }
  1642. }
  1643. }
  1644. if (Hdr) {
  1645. DPRINT1(5, ":DS: %ws DONE\n", Hdr);
  1646. } else {
  1647. DPRINT(5, ":DS: DONE\n");
  1648. }
  1649. }
  1650. #else DBG
  1651. #define FRS_PRINT_TREE(_Hdr_, _Sites_)
  1652. #endif DBG
  1653. VOID
  1654. FrsDsTreeLink(
  1655. IN PCONFIG_NODE Parent,
  1656. IN PCONFIG_NODE Node
  1657. )
  1658. /*++
  1659. Routine Description:
  1660. Link the node into the tree and keep a running "change checksum"
  1661. to compare with the previous tree. We don't use a DS that is in
  1662. flux. We wait until two polling cycles return the same "change
  1663. checksum" before using the DS data.
  1664. Arguments:
  1665. Entry - Current entry from the DS
  1666. Parent - Container which contains Base
  1667. Return Value:
  1668. None.
  1669. --*/
  1670. {
  1671. #undef DEBSUB
  1672. #define DEBSUB "FrsDsTreeLink:"
  1673. ULONG i;
  1674. ULONG LenChanged; // length of Changed
  1675. DPRINT3(5, ":DS: Linking node type %ws, node name %ws to parent %ws\n",
  1676. DsConfigTypeName[Node->DsObjectType],
  1677. (Node->Name) ? Node->Name->Name : L"null",
  1678. (Parent->Name) ? Parent->Name->Name : L"null");
  1679. //
  1680. // Link into config
  1681. //
  1682. ++Parent->NumChildren;
  1683. Node->Parent = Parent;
  1684. Node->Peer = Parent->Children;
  1685. Parent->Children = Node;
  1686. //
  1687. // Some indication that the DS is stable
  1688. //
  1689. if (Node->UsnChanged) {
  1690. LenChanged = wcslen(Node->UsnChanged);
  1691. for (i = 0; i < LenChanged; ++i) {
  1692. ThisChange += *(Node->UsnChanged + i); // sum
  1693. NextChange += ThisChange; // sum of sums (order dependent)
  1694. }
  1695. }
  1696. }
  1697. PCONFIG_NODE
  1698. FrsDsAllocBasicNode(
  1699. IN PLDAP Ldap,
  1700. IN PLDAPMessage LdapEntry,
  1701. IN ULONG NodeType
  1702. )
  1703. /*++
  1704. Routine Description:
  1705. Allocate a Node and fill in the fields common to all or most nodes.
  1706. (guid, name, dn, schedule, and usnchanged)
  1707. Arguments:
  1708. Ldap - opened and bound ldap connection
  1709. LdapEntry - from ldap_first/next_entry
  1710. NodeType - Internal type code for the object represented by this node.
  1711. Return Value:
  1712. NULL if basic node cannot be allocated
  1713. --*/
  1714. {
  1715. #undef DEBSUB
  1716. #define DEBSUB "FrsDsAllocBasicNode:"
  1717. PCONFIG_NODE Node;
  1718. //
  1719. // Increment the DS Objects counter
  1720. //
  1721. PM_INC_CTR_SERVICE(PMTotalInst, DSObjects, 1);
  1722. //
  1723. // Initially, the node is assumed to be consistent
  1724. //
  1725. Node = FrsAllocType(CONFIG_NODE_TYPE);
  1726. Node->Consistent = TRUE;
  1727. Node->DsObjectType = NodeType;
  1728. //
  1729. // A dummy entry can be created by passing NULL for LdapEntry.
  1730. //
  1731. if (LdapEntry == NULL) {
  1732. return Node;
  1733. }
  1734. //
  1735. // Distinguished name
  1736. //
  1737. Node->Dn = FrsDsFindValue(Ldap, LdapEntry, ATTR_DN);
  1738. FRS_WCSLWR(Node->Dn);
  1739. //
  1740. // Name = RDN + Object Guid
  1741. //
  1742. Node->Name = FrsBuildGName(FrsDsFindGuid(Ldap, LdapEntry),
  1743. FrsDsMakeRdn(Node->Dn));
  1744. //
  1745. // Schedule, if any
  1746. //
  1747. Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
  1748. //
  1749. // USN Changed
  1750. //
  1751. Node->UsnChanged = FrsDsFindValue(Ldap, LdapEntry, ATTR_USN_CHANGED);
  1752. if (!Node->Dn || !Node->Name->Name || !Node->Name->Guid) {
  1753. //
  1754. // Increment the DS Objects in Error counter
  1755. //
  1756. PM_INC_CTR_SERVICE(PMTotalInst, DSObjectsError, 1);
  1757. DPRINT3(0, ":DS: ERROR - Ignoring node; lacks dn (%08x), rdn (%08x), or guid (%08x)\n",
  1758. Node->Dn, Node->Name->Name, Node->Name->Guid);
  1759. Node = FrsFreeType(Node);
  1760. }
  1761. return Node;
  1762. }
  1763. #define NUM_EQUALS (4)
  1764. ULONG
  1765. FrsDsSameSite(
  1766. IN PWCHAR NtDsSettings1,
  1767. IN PWCHAR NtDsSettings2
  1768. )
  1769. /*++
  1770. Routine Description:
  1771. Are the ntds settings in the same site?
  1772. Arguments:
  1773. NtDsSettings1 - NtDs Settings FQDN
  1774. NtDsSettings2 - NtDs Settings FQDN
  1775. Return Value:
  1776. TRUE - Same site
  1777. FALSE - Not
  1778. --*/
  1779. {
  1780. #undef DEBSUB
  1781. #define DEBSUB "FrsDsSameSite:"
  1782. PWCHAR Equal1 = NULL;
  1783. PWCHAR Equal2 = NULL;
  1784. DWORD EqualsFound;
  1785. if (!NtDsSettings1 || !NtDsSettings2) {
  1786. return TRUE;
  1787. }
  1788. //
  1789. // Forth equals sign
  1790. //
  1791. for (EqualsFound = 0; *NtDsSettings1 != L'\0'; ++NtDsSettings1) {
  1792. if (*NtDsSettings1 != L'=') {
  1793. continue;
  1794. }
  1795. if (++EqualsFound == NUM_EQUALS) {
  1796. Equal1 = NtDsSettings1;
  1797. break;
  1798. }
  1799. }
  1800. //
  1801. // Forth equals sign
  1802. //
  1803. for (EqualsFound = 0; *NtDsSettings2 != L'\0'; ++NtDsSettings2) {
  1804. if (*NtDsSettings2 != L'=') {
  1805. continue;
  1806. }
  1807. if (++EqualsFound == NUM_EQUALS) {
  1808. Equal2 = NtDsSettings2;
  1809. break;
  1810. }
  1811. }
  1812. //
  1813. // Not the same length
  1814. //
  1815. if (!Equal1 || !Equal2) {
  1816. return TRUE;
  1817. }
  1818. //
  1819. // Compare up to the first comma
  1820. //
  1821. while (*Equal1 == *Equal2 && (*Equal1 && *Equal1 != L',')) {
  1822. ++Equal1;
  1823. ++Equal2;
  1824. }
  1825. DPRINT3(4, ":DS: %s: %ws %ws\n",
  1826. (*Equal1 == *Equal2) ? "SAME SITE" : "DIFF SITE", Equal1, Equal2);
  1827. return (*Equal1 == *Equal2);
  1828. }
  1829. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  1830. DWORD
  1831. FrsDsResolveCxtionConflict(
  1832. IN PCONFIG_NODE OldCxtion,
  1833. IN PCONFIG_NODE NewCxtion,
  1834. IN PCONFIG_NODE *Winner,
  1835. IN PCONFIG_NODE *Loser
  1836. )
  1837. /*++
  1838. Routine Description:
  1839. Resolve the connection conflict.
  1840. Arguments:
  1841. OldCxtion
  1842. NewCxtion
  1843. Winner
  1844. Loser
  1845. Return Value:
  1846. WIN32 Status
  1847. --*/
  1848. {
  1849. //
  1850. // Compare the guids and pick a connection. This ensures that both the members
  1851. // at the ends of each connection pick the same one.
  1852. //
  1853. if ((OldCxtion != NULL) && (NewCxtion != NULL) &&
  1854. (OldCxtion->Name != NULL) && (NewCxtion->Name != NULL) &&
  1855. (memcmp(OldCxtion->Name->Guid, NewCxtion->Name->Guid, sizeof(GUID)) > 0) ) {
  1856. *Winner = NewCxtion;
  1857. *Loser = OldCxtion;
  1858. } else {
  1859. *Winner = OldCxtion;
  1860. *Loser = NewCxtion;
  1861. }
  1862. //
  1863. // Add to the poll summary event log.
  1864. //
  1865. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_CXTION_CONFLICT, (*Winner)->Dn,
  1866. (*Loser)->Dn, (*Winner)->Dn);
  1867. return ERROR_SUCCESS;
  1868. }
  1869. DWORD
  1870. FrsDsResolveSubscriberConflict(
  1871. IN PCONFIG_NODE OldSubscriber,
  1872. IN PCONFIG_NODE NewSubscriber,
  1873. IN PCONFIG_NODE *Winner,
  1874. IN PCONFIG_NODE *Loser
  1875. )
  1876. /*++
  1877. Routine Description:
  1878. Resolve the subscriber conflict.
  1879. Arguments:
  1880. OldSubscriber
  1881. NewSubscriber
  1882. Winner
  1883. Loser
  1884. Return Value:
  1885. WIN32 Status
  1886. --*/
  1887. {
  1888. *Winner = OldSubscriber;
  1889. *Loser = NewSubscriber;
  1890. //
  1891. // Add to the poll summary event log.
  1892. //
  1893. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_SUBSCRIBER_CONFLICT, (*Winner)->Dn,
  1894. (*Loser)->Dn, (*Winner)->Dn);
  1895. return ERROR_SUCCESS;
  1896. }
  1897. ULONG
  1898. FrsNewDsGetNonSysvolInboundCxtions(
  1899. IN PLDAP Ldap,
  1900. IN PWCHAR SetDn,
  1901. IN PWCHAR MemberRef
  1902. )
  1903. /*++
  1904. Routine Description:
  1905. Fetch the non-sysvol inbound connections and add them
  1906. to the CxtionTable. Check for multiple connections between the
  1907. same partners and resolve the conflict.
  1908. Arguments:
  1909. ldap - opened and bound ldap connection.
  1910. SetDn - Dn of the set being processed.
  1911. MemberRef - Member reference from the subscriber object.
  1912. Return Value:
  1913. ERROR_SUCCESS - config fetched successfully
  1914. Otherwise - couldn't get the DS config
  1915. --*/
  1916. {
  1917. #undef DEBSUB
  1918. #define DEBSUB "FrsNewDsGetNonSysvolInboundCxtions:"
  1919. PWCHAR Attrs[8];
  1920. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  1921. PCONFIG_NODE Node; // generic node for the tree
  1922. PWCHAR TempFilter = NULL;
  1923. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  1924. PWCHAR PartnerCn = NULL;
  1925. PGEN_ENTRY ConflictingNodeEntry = NULL;
  1926. PCONFIG_NODE ConflictingNode = NULL;
  1927. PCONFIG_NODE Winner = NULL;
  1928. PCONFIG_NODE Loser = NULL;
  1929. BOOL Inbound;
  1930. PWCHAR Options = NULL;
  1931. //
  1932. // Look for all the connections under our member object.
  1933. //
  1934. MK_ATTRS_7(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  1935. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION, ATTR_OPTIONS);
  1936. if (!FrsDsLdapSearchInit(Ldap, MemberRef, LDAP_SCOPE_ONELEVEL, CATEGORY_CXTION,
  1937. Attrs, 0, &FrsSearchContext)) {
  1938. return ERROR_ACCESS_DENIED;
  1939. }
  1940. if (FrsSearchContext.EntriesInPage == 0) {
  1941. DPRINT1(1, ":DS: WARN - There are no connection objects in %ws!\n", MemberRef);
  1942. }
  1943. //
  1944. // Scan the entries returned from ldap_search
  1945. //
  1946. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  1947. Entry != NULL;
  1948. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  1949. //
  1950. // Basic node info (guid, name, dn, schedule, and usnchanged)
  1951. //
  1952. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  1953. if (!Node) {
  1954. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  1955. continue;
  1956. }
  1957. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  1958. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  1959. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  1960. Node->EnabledCxtion, Node->Name->Name);
  1961. Node = FrsFreeType(Node);
  1962. continue;
  1963. }
  1964. //
  1965. // Read the options value on the connection object.
  1966. // We are intersted in the NTDSCONN_OPT_TWOWAY_SYNC flag and the
  1967. // priority on connections.
  1968. //
  1969. Options = FrsDsFindValue(Ldap, Entry, ATTR_OPTIONS);
  1970. if (Options != NULL) {
  1971. Node->CxtionOptions = _wtoi(Options);
  1972. Options = FrsFree(Options);
  1973. } else {
  1974. Node->CxtionOptions = 0;
  1975. }
  1976. //
  1977. // These are inbound connections.
  1978. //
  1979. Node->Inbound = TRUE;
  1980. //
  1981. // Node's partner's name.
  1982. //
  1983. Node->PartnerDn = FrsDsFindValue(Ldap, Entry, ATTR_FROM_SERVER);
  1984. FRS_WCSLWR(Node->PartnerDn);
  1985. //
  1986. // Add the Inbound cxtion to the cxtion table.
  1987. //
  1988. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  1989. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  1990. if (ConflictingNodeEntry) {
  1991. ConflictingNode = ConflictingNodeEntry->Data;
  1992. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  1993. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  1994. //
  1995. // The new one is the winner. Remove old one and insert new one.
  1996. //
  1997. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  1998. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  1999. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2000. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2001. FrsFreeType(ConflictingNode);
  2002. } else {
  2003. //
  2004. // The old one is the winner. Leave it in the table.
  2005. //
  2006. FrsFreeType(Node);
  2007. continue;
  2008. }
  2009. } else {
  2010. //
  2011. // If there is no conflict then we need to add this Member to the MemberSearchFilter
  2012. // if it is not already there. It could have been added while processing the oubound connections.
  2013. //
  2014. Inbound = FALSE;
  2015. if (GTabLookupTableString(CxtionTable, Node->PartnerDn, (PWCHAR)&Inbound) == NULL) {
  2016. PartnerCn = FrsDsMakeRdn(Node->PartnerDn);
  2017. if (MemberSearchFilter != NULL) {
  2018. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_CN) +
  2019. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2020. wcscpy(TempFilter, MemberSearchFilter);
  2021. wcscat(TempFilter, L"(" ATTR_CN L"=" );
  2022. wcscat(TempFilter, PartnerCn);
  2023. wcscat(TempFilter, L")");
  2024. FrsFree(MemberSearchFilter);
  2025. MemberSearchFilter = TempFilter;
  2026. TempFilter = NULL;
  2027. } else {
  2028. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_CN) +
  2029. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2030. wcscpy(MemberSearchFilter, L"(|(" ATTR_CN L"=" );
  2031. wcscat(MemberSearchFilter, PartnerCn);
  2032. wcscat(MemberSearchFilter, L")");
  2033. }
  2034. FrsFree(PartnerCn);
  2035. }
  2036. }
  2037. }
  2038. FrsDsLdapSearchClose(&FrsSearchContext);
  2039. return ERROR_SUCCESS;
  2040. }
  2041. ULONG
  2042. FrsNewDsGetNonSysvolOutboundCxtions(
  2043. IN PLDAP Ldap,
  2044. IN PWCHAR SetDn,
  2045. IN PWCHAR MemberRef
  2046. )
  2047. /*++
  2048. Routine Description:
  2049. Fetch the non-sysvol outbound connections and add them
  2050. to the CxtionTable. Check for multiple connections between the
  2051. same partners and resolve the conflict.
  2052. Arguments:
  2053. ldap - opened and bound ldap connection.
  2054. SetDn - Dn of the set being processed.
  2055. MemberRef - Member reference from the subscriber object.
  2056. Return Value:
  2057. ERROR_SUCCESS - config fetched successfully
  2058. Otherwise - couldn't get the DS config
  2059. --*/
  2060. {
  2061. #undef DEBSUB
  2062. #define DEBSUB "FrsNewDsGetNonSysvolOutboundCxtions:"
  2063. PWCHAR Attrs[8];
  2064. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2065. PCONFIG_NODE Node; // generic node for the tree
  2066. PWCHAR SearchFilter = NULL;
  2067. PWCHAR TempFilter = NULL;
  2068. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2069. PWCHAR PartnerCn = NULL;
  2070. PGEN_ENTRY ConflictingNodeEntry = NULL;
  2071. PCONFIG_NODE ConflictingNode = NULL;
  2072. PCONFIG_NODE Winner = NULL;
  2073. PCONFIG_NODE Loser = NULL;
  2074. PWCHAR Options = NULL;
  2075. //
  2076. // Look for all the connections that have our member as the from server.
  2077. // Filter will look like (&(objectCategory=nTDSConnection)(fromServer=cn=member1,cn=set1,...))
  2078. //
  2079. MK_ATTRS_7(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  2080. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION, ATTR_OPTIONS);
  2081. SearchFilter = FrsAlloc((wcslen(L"(&(=))" CATEGORY_CXTION ATTR_FROM_SERVER) +
  2082. wcslen(MemberRef) + 1) * sizeof(WCHAR));
  2083. wcscpy(SearchFilter,L"(&" CATEGORY_CXTION L"(" ATTR_FROM_SERVER L"=" );
  2084. wcscat(SearchFilter,MemberRef);
  2085. wcscat(SearchFilter,L"))");
  2086. if (!FrsDsLdapSearchInit(Ldap, SetDn, LDAP_SCOPE_SUBTREE, SearchFilter,
  2087. Attrs, 0, &FrsSearchContext)) {
  2088. SearchFilter = FrsFree(SearchFilter);
  2089. return ERROR_ACCESS_DENIED;
  2090. }
  2091. if (FrsSearchContext.EntriesInPage == 0) {
  2092. DPRINT1(1, ":DS: WARN - No outbound connections found for member %ws!\n", MemberRef);
  2093. }
  2094. SearchFilter = FrsFree(SearchFilter);
  2095. //
  2096. // Scan the entries returned from ldap_search
  2097. //
  2098. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2099. Entry != NULL;
  2100. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2101. //
  2102. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2103. //
  2104. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  2105. if (!Node) {
  2106. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  2107. continue;
  2108. }
  2109. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  2110. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  2111. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  2112. Node->EnabledCxtion, Node->Name->Name);
  2113. Node = FrsFreeType(Node);
  2114. continue;
  2115. }
  2116. //
  2117. // Read the options value on the connection object.
  2118. // We are only intersted in the NTDSCONN_OPT_TWOWAY_SYNC flag.
  2119. //
  2120. Options = FrsDsFindValue(Ldap, Entry, ATTR_OPTIONS);
  2121. if (Options != NULL) {
  2122. Node->CxtionOptions = _wtoi(Options);
  2123. Options = FrsFree(Options);
  2124. } else {
  2125. Node->CxtionOptions = 0;
  2126. }
  2127. //
  2128. // These are outbound connections.
  2129. //
  2130. Node->Inbound = FALSE;
  2131. //
  2132. // Node's partner's name. This is an outbound connection. Get the
  2133. // partners Dn by going one level up from the connection to the
  2134. // member Dn.
  2135. //
  2136. Node->PartnerDn = FrsWcsDup(wcsstr(Node->Dn + 3, L"cn="));
  2137. FRS_WCSLWR(Node->PartnerDn);
  2138. //
  2139. // Add the outbound cxtion to the cxtion table.
  2140. //
  2141. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2142. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2143. if (ConflictingNodeEntry) {
  2144. ConflictingNode = ConflictingNodeEntry->Data;
  2145. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  2146. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  2147. //
  2148. // The new one is the winner. Remove old one and insert new one.
  2149. //
  2150. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  2151. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2152. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2153. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2154. FrsFreeType(ConflictingNode);
  2155. } else {
  2156. //
  2157. // The old one is the winner. Leave it in the table.
  2158. //
  2159. FrsFreeType(Node);
  2160. continue;
  2161. }
  2162. } else {
  2163. //
  2164. // If there is no conflict then we need to add this Member to the MemberSearchFilter.
  2165. //
  2166. PartnerCn = FrsDsMakeRdn(Node->PartnerDn);
  2167. if (MemberSearchFilter != NULL) {
  2168. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_CN) +
  2169. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2170. wcscpy(TempFilter, MemberSearchFilter);
  2171. wcscat(TempFilter, L"(" ATTR_CN L"=");
  2172. wcscat(TempFilter, PartnerCn);
  2173. wcscat(TempFilter, L")");
  2174. FrsFree(MemberSearchFilter);
  2175. MemberSearchFilter = TempFilter;
  2176. TempFilter = NULL;
  2177. } else {
  2178. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_CN) +
  2179. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2180. wcscpy(MemberSearchFilter, L"(|(" ATTR_CN L"=");
  2181. wcscat(MemberSearchFilter, PartnerCn);
  2182. wcscat(MemberSearchFilter, L")");
  2183. }
  2184. FrsFree(PartnerCn);
  2185. }
  2186. }
  2187. FrsDsLdapSearchClose(&FrsSearchContext);
  2188. return ERROR_SUCCESS;
  2189. }
  2190. ULONG
  2191. FrsNewDsGetSysvolInboundCxtions(
  2192. IN PLDAP Ldap,
  2193. IN PWCHAR SettingsDn
  2194. )
  2195. /*++
  2196. Routine Description:
  2197. Fetch the sysvol inbound connections and add them
  2198. to the CxtionTable. Check for multiple connections between the
  2199. same partners and resolve the conflict.
  2200. Arguments:
  2201. ldap - opened and bound ldap connection.
  2202. SettingsDn - server reference from the member object.
  2203. Return Value:
  2204. ERROR_SUCCESS - config fetched successfully
  2205. Otherwise - couldn't get the DS config
  2206. --*/
  2207. {
  2208. #undef DEBSUB
  2209. #define DEBSUB "FrsNewDsGetSysvolInboundCxtions:"
  2210. PWCHAR Attrs[7];
  2211. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2212. PCONFIG_NODE Node; // generic node for the tree
  2213. PWCHAR TempFilter = NULL;
  2214. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2215. PGEN_ENTRY ConflictingNodeEntry = NULL;
  2216. PCONFIG_NODE ConflictingNode = NULL;
  2217. PCONFIG_NODE Winner = NULL;
  2218. PCONFIG_NODE Loser = NULL;
  2219. BOOL Inbound;
  2220. //
  2221. // Look for all the connections under our member object.
  2222. //
  2223. MK_ATTRS_6(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  2224. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION);
  2225. if (!FrsDsLdapSearchInit(Ldap, SettingsDn, LDAP_SCOPE_ONELEVEL, CATEGORY_CXTION,
  2226. Attrs, 0, &FrsSearchContext)) {
  2227. return ERROR_ACCESS_DENIED;
  2228. }
  2229. if (FrsSearchContext.EntriesInPage == 0) {
  2230. DPRINT1(1, ":DS: WARN - No sysvol inbound connections found for object %ws!\n", SettingsDn);
  2231. }
  2232. //
  2233. // Scan the entries returned from ldap_search
  2234. //
  2235. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2236. Entry != NULL;
  2237. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2238. //
  2239. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2240. //
  2241. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  2242. if (!Node) {
  2243. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  2244. continue;
  2245. }
  2246. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  2247. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  2248. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  2249. Node->EnabledCxtion, Node->Name->Name);
  2250. Node = FrsFreeType(Node);
  2251. continue;
  2252. }
  2253. //
  2254. // These are inbound connections.
  2255. //
  2256. Node->Inbound = TRUE;
  2257. //
  2258. // Node's partner's name.
  2259. //
  2260. Node->PartnerDn = FrsDsFindValue(Ldap, Entry, ATTR_FROM_SERVER);
  2261. FRS_WCSLWR(Node->PartnerDn);
  2262. //
  2263. // Add the Inbound cxtion to the cxtion table.
  2264. //
  2265. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2266. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2267. if (ConflictingNodeEntry) {
  2268. ConflictingNode = ConflictingNodeEntry->Data;
  2269. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  2270. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  2271. //
  2272. // The new one is the winner. Remove old one and insert new one.
  2273. //
  2274. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  2275. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2276. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2277. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2278. FrsFreeType(ConflictingNode);
  2279. } else {
  2280. //
  2281. // The old one is the winner. Leave it in the table.
  2282. //
  2283. FrsFreeType(Node);
  2284. continue;
  2285. }
  2286. } else {
  2287. //
  2288. // If there is no conflict then we need to add this Member to the MemberSearchFilter
  2289. // if it is not already there. It could have been added while processing the oubound connections.
  2290. //
  2291. Inbound = FALSE;
  2292. if (GTabLookupTableString(CxtionTable, Node->PartnerDn, (PWCHAR)&Inbound) == NULL) {
  2293. if (MemberSearchFilter != NULL) {
  2294. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_SERVER_REF) +
  2295. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2296. wcscpy(TempFilter, MemberSearchFilter);
  2297. wcscat(TempFilter, L"(" ATTR_SERVER_REF L"=");
  2298. wcscat(TempFilter, Node->PartnerDn);
  2299. wcscat(TempFilter, L")");
  2300. FrsFree(MemberSearchFilter);
  2301. MemberSearchFilter = TempFilter;
  2302. TempFilter = NULL;
  2303. } else {
  2304. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_SERVER_REF) +
  2305. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2306. wcscpy(MemberSearchFilter, L"(|(" ATTR_SERVER_REF L"=");
  2307. wcscat(MemberSearchFilter, Node->PartnerDn);
  2308. wcscat(MemberSearchFilter, L")");
  2309. }
  2310. }
  2311. }
  2312. //
  2313. // If sysvol, always on within a site
  2314. // Trigger schedule otherwise.
  2315. //
  2316. Node->SameSite = FrsDsSameSite(SettingsDn, Node->PartnerDn);
  2317. if (Node->SameSite) {
  2318. Node->Schedule = FrsFree(Node->Schedule);
  2319. }
  2320. }
  2321. FrsDsLdapSearchClose(&FrsSearchContext);
  2322. return ERROR_SUCCESS;
  2323. }
  2324. ULONG
  2325. FrsNewDsGetSysvolOutboundCxtions(
  2326. IN PLDAP Ldap,
  2327. IN PWCHAR SettingsDn
  2328. )
  2329. /*++
  2330. Routine Description:
  2331. Fetch the sysvol outbound connections and add them
  2332. to the CxtionTable. Check for multiple connections between the
  2333. same partners and resolve the conflict.
  2334. Arguments:
  2335. ldap - opened and bound ldap connection.
  2336. SettingsDn - server reference from the member object.
  2337. Return Value:
  2338. ERROR_SUCCESS - config fetched successfully
  2339. Otherwise - couldn't get the DS config
  2340. --*/
  2341. {
  2342. #undef DEBSUB
  2343. #define DEBSUB "FrsNewDsGetSysvolOutboundCxtions:"
  2344. PWCHAR Attrs[7];
  2345. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2346. PCONFIG_NODE Node; // generic node for the tree
  2347. PWCHAR SearchFilter = NULL;
  2348. PWCHAR TempFilter = NULL;
  2349. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2350. PGEN_ENTRY ConflictingNodeEntry = NULL;
  2351. PCONFIG_NODE ConflictingNode = NULL;
  2352. PCONFIG_NODE Winner = NULL;
  2353. PCONFIG_NODE Loser = NULL;
  2354. //
  2355. // Look for all the connections that have our member as the from server.
  2356. // Filter will look like (&(objectCategory=nTDSConnection)(fromServer=cn=member1,cn=set1,...))
  2357. //
  2358. MK_ATTRS_6(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  2359. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION);
  2360. SearchFilter = FrsAlloc((wcslen(L"(&(=))" CATEGORY_CXTION ATTR_FROM_SERVER) +
  2361. wcslen(SettingsDn) + 1) * sizeof(WCHAR));
  2362. wcscpy(SearchFilter,L"(&" CATEGORY_CXTION L"(" ATTR_FROM_SERVER L"=");
  2363. wcscat(SearchFilter,SettingsDn);
  2364. wcscat(SearchFilter,L"))");
  2365. if (!FrsDsLdapSearchInit(Ldap, SitesDn, LDAP_SCOPE_SUBTREE, SearchFilter,
  2366. Attrs, 0, &FrsSearchContext)) {
  2367. SearchFilter = FrsFree(SearchFilter);
  2368. return ERROR_ACCESS_DENIED;
  2369. }
  2370. if (FrsSearchContext.EntriesInPage == 0) {
  2371. DPRINT1(1, ":DS: WARN - No sysvol outbound connections found for member %ws!\n", SettingsDn);
  2372. }
  2373. SearchFilter = FrsFree(SearchFilter);
  2374. //
  2375. // Scan the entries returned from ldap_search
  2376. //
  2377. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2378. Entry != NULL;
  2379. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2380. //
  2381. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2382. //
  2383. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  2384. if (!Node) {
  2385. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  2386. continue;
  2387. }
  2388. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  2389. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  2390. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  2391. Node->EnabledCxtion, Node->Name->Name);
  2392. Node = FrsFreeType(Node);
  2393. continue;
  2394. }
  2395. //
  2396. // These are outbound connections.
  2397. //
  2398. Node->Inbound = FALSE;
  2399. //
  2400. // Node's partner's name. This is an outbound connection. Get the
  2401. // partners Dn by going one level up from the connection to the
  2402. // member Dn.
  2403. //
  2404. Node->PartnerDn = FrsWcsDup(wcsstr(Node->Dn + 3, L"cn="));
  2405. FRS_WCSLWR(Node->PartnerDn);
  2406. //
  2407. // Add the outbound cxtion to the cxtion table.
  2408. //
  2409. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2410. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2411. if (ConflictingNodeEntry) {
  2412. ConflictingNode = ConflictingNodeEntry->Data;
  2413. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  2414. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  2415. //
  2416. // The new one is the winner. Remove old one and insert new one.
  2417. //
  2418. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  2419. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2420. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2421. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2422. FrsFreeType(ConflictingNode);
  2423. } else {
  2424. //
  2425. // The old one is the winner. Leave it in the table.
  2426. //
  2427. FrsFreeType(Node);
  2428. continue;
  2429. }
  2430. } else {
  2431. //
  2432. // If there is no conflict then we need to add this Member to the MemberSearchFilter.
  2433. //
  2434. if (MemberSearchFilter != NULL) {
  2435. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_SERVER_REF) +
  2436. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2437. wcscpy(TempFilter, MemberSearchFilter);
  2438. wcscat(TempFilter, L"(" ATTR_SERVER_REF L"=");
  2439. wcscat(TempFilter, Node->PartnerDn);
  2440. wcscat(TempFilter, L")");
  2441. FrsFree(MemberSearchFilter);
  2442. MemberSearchFilter = TempFilter;
  2443. TempFilter = NULL;
  2444. } else {
  2445. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_SERVER_REF) +
  2446. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2447. wcscpy(MemberSearchFilter, L"(|(" ATTR_SERVER_REF L"=");
  2448. wcscat(MemberSearchFilter, Node->PartnerDn);
  2449. wcscat(MemberSearchFilter, L")");
  2450. }
  2451. }
  2452. //
  2453. // If sysvol, always on within a site
  2454. //
  2455. Node->SameSite = FrsDsSameSite(SettingsDn, Node->PartnerDn);
  2456. if (Node->SameSite) {
  2457. Node->Schedule = FrsFree(Node->Schedule);
  2458. }
  2459. }
  2460. FrsDsLdapSearchClose(&FrsSearchContext);
  2461. return ERROR_SUCCESS;
  2462. }
  2463. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  2464. ULONG
  2465. FrsDsGetCxtions(
  2466. IN PLDAP Ldap,
  2467. IN PWCHAR Base,
  2468. IN PCONFIG_NODE Parent,
  2469. IN BOOL IsSysvol
  2470. )
  2471. /*++
  2472. Routine Description:
  2473. Fetch the cxtions for the server identified by Base
  2474. Arguments:
  2475. ldap - opened and bound ldap connection
  2476. Base - Name of object or container in DS
  2477. Parent - Container which contains Base
  2478. Return Value:
  2479. ERROR_SUCCESS - config fetched successfully
  2480. Otherwise - couldn't get the DS config
  2481. --*/
  2482. {
  2483. #undef DEBSUB
  2484. #define DEBSUB "FrsDsGetCxtions:"
  2485. PWCHAR Attrs[7];
  2486. PLDAPMessage Msg = NULL; // opaque stuff from ldap subsystem
  2487. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2488. PCONFIG_NODE Node; // generic node for the tree
  2489. //
  2490. // Search the DS for cxtions (objectCategory=nTDSConnection)
  2491. //
  2492. MK_ATTRS_6(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  2493. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION);
  2494. if (!FrsDsLdapSearch(Ldap, Base, LDAP_SCOPE_ONELEVEL, CATEGORY_CXTION,
  2495. Attrs, 0, &Msg)) {
  2496. return ERROR_ACCESS_DENIED;
  2497. }
  2498. //
  2499. // Scan the entries returned from ldap_search
  2500. //
  2501. for (Entry = ldap_first_entry(Ldap, Msg);
  2502. Entry != NULL;
  2503. Entry = ldap_next_entry(Ldap, Entry)) {
  2504. //
  2505. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2506. //
  2507. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  2508. if (!Node) {
  2509. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  2510. continue;
  2511. }
  2512. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  2513. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  2514. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  2515. Node->EnabledCxtion, Node->Name->Name);
  2516. Node = FrsFreeType(Node);
  2517. continue;
  2518. }
  2519. //
  2520. // All cxtions in the the DS are inbound cxtions
  2521. //
  2522. Node->Inbound = TRUE;
  2523. //
  2524. // Node's partner's name
  2525. //
  2526. Node->PartnerDn = FrsDsFindValue(Ldap, Entry, ATTR_FROM_SERVER);
  2527. FRS_WCSLWR(Node->PartnerDn);
  2528. Node->PartnerName = FrsBuildGName(NULL, FrsDsMakeRdn(Node->PartnerDn));
  2529. //
  2530. // If sysvol, always on within a site
  2531. // Trigger schedule otherwise.
  2532. //
  2533. Node->SameSite = FrsDsSameSite(Base, Node->PartnerDn);
  2534. if (IsSysvol && Node->SameSite) {
  2535. Node->Schedule = FrsFree(Node->Schedule);
  2536. }
  2537. //
  2538. // Link into config and add to the running change checksum
  2539. //
  2540. FrsDsTreeLink(Parent, Node);
  2541. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeCxtion", Node);
  2542. }
  2543. LDAP_FREE_MSG(Msg);
  2544. return ERROR_SUCCESS;
  2545. }
  2546. #if 0
  2547. // currently unused.
  2548. PCONFIG_NODE
  2549. FrsDsFindNodeByDn(
  2550. PCONFIG_NODE Root,
  2551. PWCHAR Dn
  2552. )
  2553. /*++
  2554. Routine Description:
  2555. Find the node whose Dn matches Dn. Start at Root.
  2556. Arguments:
  2557. Root
  2558. Dn
  2559. Return Value:
  2560. PCONFIG_NODE contained in Root or NULL.
  2561. --*/
  2562. {
  2563. #undef DEBSUB
  2564. #define DEBSUB "FrsDsFindNodeByDn:"
  2565. PCONFIG_NODE Node;
  2566. PCONFIG_NODE RetNode;
  2567. //
  2568. // Check peers
  2569. //
  2570. for (Node = Root; Node; Node = Node->Peer) {
  2571. if (WSTR_EQ(Node->Dn, Dn)) {
  2572. return Node;
  2573. }
  2574. //
  2575. // Check children
  2576. //
  2577. if (RetNode = FrsDsFindNodeByDn(Node->Children, Dn)) {
  2578. return RetNode;
  2579. }
  2580. }
  2581. return NULL;
  2582. }
  2583. #endif
  2584. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  2585. VOID
  2586. FrsDsMergeTwoWaySchedules(
  2587. IN PSCHEDULE *pISchedule,
  2588. IN DWORD *pIScheduleLen,
  2589. IN OUT PSCHEDULE *pOSchedule,
  2590. IN OUT DWORD *pOScheduleLen,
  2591. IN PSCHEDULE *pRSchedule
  2592. )
  2593. /*++
  2594. Routine Description:
  2595. Set the output schedule by merging the input schedule with the.
  2596. output schedule.
  2597. Schedules are merged to support NTDSCONN_OPT_TWOWAY_SYNC flag
  2598. on the connection object.
  2599. This function only merges the interval schedule (SCHEDULE_INTERVAL).
  2600. Other schedules are ignored and may be overwritten during merging.
  2601. Input Output Replica Resultant output schedule.
  2602. ----- ------ ----------------------------------
  2603. 0 0 0 Schedule is absent. Considered to be always on.
  2604. 0 0 1 Schedule is absent. Use replica sets schedule.
  2605. 0 1 0 Schedule is absent. Considered to be always on.
  2606. 0 1 1 Schedule is present.Merge replica set schedule with the schedule on the output.
  2607. 1 0 0 Schedule is present.Same as the one on input.
  2608. 1 0 1 Schedule is present.Merge replica set schedule with the schedule on the input.
  2609. 1 1 0 Schedule is present.Merge the input and output schedule.
  2610. 1 1 1 Schedule is present.Merge the input and output schedule.
  2611. Arguments:
  2612. pISchedule - Input schedule.
  2613. pIScheduleLen - Input schedule length.
  2614. pOSchedule - Resultant schedule.
  2615. pOScheduleLen - Resultant schedule length.
  2616. pRSchedule - Default replica set schedule.
  2617. Return Value:
  2618. NONE
  2619. --*/
  2620. {
  2621. #undef DEBSUB
  2622. #define DEBSUB "FrsDsMergeTwoWaySchedules:"
  2623. UINT i;
  2624. PUCHAR IScheduleData = NULL;
  2625. PUCHAR OScheduleData = NULL;
  2626. //
  2627. // Set the location of the data in the schedule structures that are
  2628. // non-null.
  2629. //
  2630. if (*pISchedule != NULL){
  2631. for (i=0; i< (*pISchedule)->NumberOfSchedules ; ++i) {
  2632. if ((*pISchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2633. IScheduleData = ((PUCHAR)*pISchedule) + (*pISchedule)->Schedules[i].Offset;
  2634. break;
  2635. }
  2636. }
  2637. }
  2638. if (*pOSchedule != NULL){
  2639. for (i=0; i< (*pOSchedule)->NumberOfSchedules ; ++i) {
  2640. if ((*pOSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2641. OScheduleData = ((PUCHAR)*pOSchedule) + (*pOSchedule)->Schedules[i].Offset;
  2642. break;
  2643. }
  2644. }
  2645. }
  2646. //
  2647. // If there is no output schedule then copy the schedule
  2648. // from input to output if there is one on input. Now if there
  2649. // is a schedule on the replica set merge it with the new ouput
  2650. // schedule.
  2651. //
  2652. if (*pOSchedule == NULL || OScheduleData == NULL) {
  2653. if (*pISchedule == NULL) {
  2654. return;
  2655. }
  2656. *pOScheduleLen = *pIScheduleLen;
  2657. *pOSchedule = FrsAlloc(*pOScheduleLen);
  2658. CopyMemory(*pOSchedule, *pISchedule, *pOScheduleLen);
  2659. if (*pRSchedule == NULL) {
  2660. return;
  2661. }
  2662. //
  2663. // Update the location of output schedule data.
  2664. //
  2665. for (i=0; i< (*pOSchedule)->NumberOfSchedules ; ++i) {
  2666. if ((*pOSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2667. OScheduleData = ((PUCHAR)*pOSchedule) + (*pOSchedule)->Schedules[i].Offset;
  2668. break;
  2669. }
  2670. }
  2671. //
  2672. // Update the location of input schedule data.
  2673. //
  2674. for (i=0; i< (*pRSchedule)->NumberOfSchedules ; ++i) {
  2675. if ((*pRSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2676. IScheduleData = ((PUCHAR)*pRSchedule) + (*pRSchedule)->Schedules[i].Offset;
  2677. break;
  2678. }
  2679. }
  2680. }
  2681. //
  2682. // If there is no input schedule then check if there is a schedule
  2683. // on the replica set. If there is then merge that with the output schedule.
  2684. //
  2685. if ((*pISchedule == NULL || IScheduleData == NULL)) {
  2686. //
  2687. // Update the location of input schedule data. Pick it from replica set.
  2688. //
  2689. if (*pRSchedule != NULL) {
  2690. for (i=0; i< (*pRSchedule)->NumberOfSchedules ; ++i) {
  2691. if ((*pRSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2692. IScheduleData = ((PUCHAR)*pRSchedule) + (*pRSchedule)->Schedules[i].Offset;
  2693. break;
  2694. }
  2695. }
  2696. } else {
  2697. *pOSchedule = FrsFree(*pOSchedule);
  2698. *pOScheduleLen = 0;
  2699. return;
  2700. }
  2701. }
  2702. for (i=0 ; i<7*24 ; ++i) {
  2703. *(OScheduleData + i) = *(OScheduleData + i) | *(IScheduleData + i);
  2704. }
  2705. return;
  2706. }
  2707. DWORD
  2708. FrsNewDsGetSysvolCxtions(
  2709. IN PLDAP Ldap,
  2710. IN PWCHAR SetDn,
  2711. IN PWCHAR MemberRef,
  2712. IN PCONFIG_NODE Parent,
  2713. IN PCONFIG_NODE Computer
  2714. )
  2715. /*++
  2716. Routine Description:
  2717. Fetch the members for the replica set identified by Base.
  2718. Arguments:
  2719. ldap : Handle to DS.
  2720. SetDn : Dn of the set being processed.
  2721. MemberRef : MemberRef from the subscriber object.
  2722. Parent : Pointer to the set node in the config tree that is being built,
  2723. Return Value:
  2724. WIN32 Status
  2725. --*/
  2726. {
  2727. #undef DEBSUB
  2728. #define DEBSUB "FrsNewDsGetSysvolCxtions:"
  2729. PWCHAR Attrs[7];
  2730. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2731. PCONFIG_NODE Node; // generic node for the tree
  2732. PCONFIG_NODE Subscriber;
  2733. PCONFIG_NODE PartnerNode = NULL;
  2734. PCONFIG_NODE MemberNode = NULL;
  2735. PCONFIG_NODE Cxtion = NULL;
  2736. DWORD WStatus = ERROR_SUCCESS;
  2737. PVOID Key = NULL;
  2738. PWCHAR TempFilter = NULL;
  2739. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2740. PWCHAR SettingsDn = NULL;
  2741. MK_ATTRS_6(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  2742. ATTR_SERVER_REF, ATTR_COMPUTER_REF);
  2743. //
  2744. // Initialize the CxtionTable. We discard the table once we have
  2745. // loaded the replica set. We use the same variables for
  2746. // every replica set.
  2747. //
  2748. if (CxtionTable != NULL) {
  2749. CxtionTable = GTabFreeTable(CxtionTable, NULL);
  2750. }
  2751. CxtionTable = GTabAllocStringTable();
  2752. //
  2753. // Initialize the MemberTable. We discard the table once we have
  2754. // loaded the replica set. We use the same variables for
  2755. // every replica set.
  2756. //
  2757. if (MemberTable != NULL) {
  2758. MemberTable = GTabFreeTable(MemberTable, NULL);
  2759. }
  2760. MemberTable = GTabAllocStringTable();
  2761. //
  2762. // We will form the MemberSearchFilter for this replica set.
  2763. //
  2764. if (MemberSearchFilter != NULL) {
  2765. MemberSearchFilter = FrsFree(MemberSearchFilter);
  2766. }
  2767. //
  2768. // We have to first get our member object to get the serverreference to
  2769. // know where to go to get the connections.
  2770. //
  2771. if (!FrsDsLdapSearchInit(Ldap, MemberRef, LDAP_SCOPE_BASE, CATEGORY_ANY,
  2772. Attrs, 0, &FrsSearchContext)) {
  2773. return ERROR_ACCESS_DENIED;
  2774. }
  2775. if (FrsSearchContext.EntriesInPage == 0) {
  2776. DPRINT1(1, ":DS: WARN - No member object found for member %ws!\n", MemberRef);
  2777. }
  2778. //
  2779. // Scan the entries returned from ldap_search
  2780. //
  2781. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2782. Entry != NULL && WIN_SUCCESS(WStatus);
  2783. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2784. //
  2785. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2786. //
  2787. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_MEMBER);
  2788. if (!Node) {
  2789. DPRINT(0, ":DS: Member lacks basic info; skipping\n");
  2790. continue;
  2791. }
  2792. //
  2793. // NTDS Settings (DSA) Reference.
  2794. //
  2795. Node->SettingsDn = FrsDsFindValue(Ldap, Entry, ATTR_SERVER_REF);
  2796. if (Node->SettingsDn == NULL) {
  2797. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks server reference; skipping\n", Node->Dn);
  2798. Node->Consistent = FALSE;
  2799. //
  2800. // Add to the poll summary event log.
  2801. //
  2802. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2803. Node->Dn, ATTR_SERVER_REF);
  2804. Node = FrsFreeType(Node);
  2805. continue;
  2806. }
  2807. FRS_WCSLWR(Node->SettingsDn);
  2808. SettingsDn = FrsWcsDup(Node->SettingsDn);
  2809. //
  2810. // Computer Reference
  2811. //
  2812. Node->ComputerDn = FrsDsFindValue(Ldap, Entry, ATTR_COMPUTER_REF);
  2813. if (Node->ComputerDn == NULL) {
  2814. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks computer reference; skipping\n", Node->Dn);
  2815. Node->Consistent = FALSE;
  2816. //
  2817. // Add to the poll summary event log.
  2818. //
  2819. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2820. Node->Dn, ATTR_COMPUTER_REF);
  2821. Node = FrsFreeType(Node);
  2822. continue;
  2823. }
  2824. FRS_WCSLWR(Node->ComputerDn);
  2825. //
  2826. // Link into config and add to the running checksum
  2827. //
  2828. FrsDsTreeLink(Parent, Node);
  2829. //
  2830. // Insert the new member in the member table only if it is not there already.
  2831. // For sysvols insert the members with their settingsdn as the primary key
  2832. // because that is what is stored in the cxtion->PartnerDn structure at this time.
  2833. //
  2834. GTabInsertUniqueEntry(MemberTable, Node, Node->SettingsDn, NULL);
  2835. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeMember", Node);
  2836. }
  2837. FrsDsLdapSearchClose(&FrsSearchContext);
  2838. //
  2839. // We can't do any further processing if the Node is not consistent.
  2840. //
  2841. if (Node == NULL || !Node->Consistent) {
  2842. FrsFree(SettingsDn);
  2843. return ERROR_INVALID_DATA;
  2844. }
  2845. //
  2846. // Get the outbound connections.
  2847. //
  2848. WStatus = FrsNewDsGetSysvolOutboundCxtions(Ldap, SettingsDn);
  2849. if (!WIN_SUCCESS(WStatus)) {
  2850. FrsFree(SettingsDn);
  2851. return WStatus;
  2852. }
  2853. //
  2854. // Get the inbound connections.
  2855. //
  2856. WStatus = FrsNewDsGetSysvolInboundCxtions(Ldap, SettingsDn);
  2857. if (!WIN_SUCCESS(WStatus)) {
  2858. FrsFree(SettingsDn);
  2859. return WStatus;
  2860. }
  2861. //
  2862. // The above two calls build the MemberFilter.
  2863. // MemberFilter is used to search the DS for all the member objects of
  2864. // interest. If there are no connections from or to this member then
  2865. // the filter will be NULL.
  2866. //
  2867. if (MemberSearchFilter == NULL) {
  2868. //
  2869. // Is this member linked to this computer
  2870. //
  2871. MemberNode = Node;
  2872. Subscriber = GTabLookupTableString(SubscriberTable, MemberNode->Dn, NULL);
  2873. //
  2874. // Yep; have a suscriber
  2875. //
  2876. if (Subscriber != NULL) {
  2877. MemberNode->ThisComputer = TRUE;
  2878. MemberNode->Root = FrsWcsDup(Subscriber->Root);
  2879. MemberNode->Stage = FrsWcsDup(Subscriber->Stage);
  2880. FRS_WCSLWR(MemberNode->Root);
  2881. FRS_WCSLWR(MemberNode->Stage);
  2882. MemberNode->DnsName = FrsWcsDup(Computer->DnsName);
  2883. }
  2884. FrsFree(SettingsDn);
  2885. return ERROR_SUCCESS;
  2886. } else {
  2887. //
  2888. // Add the closing ')' to the MemberSearchFilter.
  2889. //
  2890. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L")") + 1 ) * sizeof(WCHAR));
  2891. wcscpy(TempFilter, MemberSearchFilter);
  2892. wcscat(TempFilter, L")");
  2893. FrsFree(MemberSearchFilter);
  2894. MemberSearchFilter = TempFilter;
  2895. TempFilter = NULL;
  2896. }
  2897. if (!FrsDsLdapSearchInit(Ldap, SetDn, LDAP_SCOPE_ONELEVEL, MemberSearchFilter,
  2898. Attrs, 0, &FrsSearchContext)) {
  2899. FrsFree(SettingsDn);
  2900. return ERROR_ACCESS_DENIED;
  2901. }
  2902. if (FrsSearchContext.EntriesInPage == 0) {
  2903. DPRINT1(1, ":DS: WARN - No member objects of interest found under %ws!\n", SetDn);
  2904. }
  2905. //
  2906. // Scan the entries returned from ldap_search
  2907. //
  2908. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2909. Entry != NULL && WIN_SUCCESS(WStatus);
  2910. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2911. //
  2912. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2913. //
  2914. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_MEMBER);
  2915. if (!Node) {
  2916. DPRINT(0, ":DS: Member lacks basic info; skipping\n");
  2917. continue;
  2918. }
  2919. //
  2920. // NTDS Settings (DSA) Reference.
  2921. //
  2922. Node->SettingsDn = FrsDsFindValue(Ldap, Entry, ATTR_SERVER_REF);
  2923. if (Node->SettingsDn == NULL) {
  2924. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks server reference; skipping\n", Node->Dn);
  2925. Node->Consistent = FALSE;
  2926. //
  2927. // Add to the poll summary event log.
  2928. //
  2929. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2930. Node->Dn, ATTR_SERVER_REF);
  2931. Node = FrsFreeType(Node);
  2932. continue;
  2933. }
  2934. FRS_WCSLWR(Node->SettingsDn);
  2935. //
  2936. // Computer Reference
  2937. //
  2938. Node->ComputerDn = FrsDsFindValue(Ldap, Entry, ATTR_COMPUTER_REF);
  2939. if (Node->ComputerDn == NULL) {
  2940. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks computer reference; skipping\n", Node->Dn);
  2941. //
  2942. // Add to the poll summary event log.
  2943. //
  2944. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2945. Node->Dn, ATTR_COMPUTER_REF);
  2946. Node = FrsFreeType(Node);
  2947. continue;
  2948. }
  2949. FRS_WCSLWR(Node->ComputerDn);
  2950. //
  2951. // Link into config and add to the running checksum
  2952. //
  2953. FrsDsTreeLink(Parent, Node);
  2954. //
  2955. // Insert the new member in the member table only if it is not there already.
  2956. // For sysvols insert the members with their settingsdn as the primary key
  2957. // because that is what is stored in the cxtion->PartnerDn structure at this time.
  2958. //
  2959. GTabInsertUniqueEntry(MemberTable, Node, Node->SettingsDn, NULL);
  2960. //
  2961. // Make a table of computers of interest to us so we can search for all
  2962. // the computers of interest at one time after we have polled all
  2963. // replica sets. Put empty entries in the table at this point.
  2964. // Do not add our computer in this table as we already have info about
  2965. // our computer.
  2966. //
  2967. if (WSTR_NE(Node->ComputerDn, Computer->Dn)) {
  2968. //
  2969. // This is not our computer. Add it to the table if it isn't already in the table.
  2970. //
  2971. PartnerNode = GTabLookupTableString(PartnerComputerTable, Node->ComputerDn, NULL);
  2972. if (PartnerNode == NULL) {
  2973. //
  2974. // There are no duplicates so enter this computer name in the table.
  2975. //
  2976. PartnerNode = FrsDsAllocBasicNode(Ldap, NULL, CONFIG_TYPE_COMPUTER);
  2977. PartnerNode->Dn = FrsWcsDup(Node->ComputerDn);
  2978. PartnerNode->MemberDn = FrsWcsDup(Node->Dn);
  2979. GTabInsertUniqueEntry(PartnerComputerTable, PartnerNode, PartnerNode->Dn, NULL);
  2980. }
  2981. }
  2982. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeMember", Node);
  2983. }
  2984. FrsDsLdapSearchClose(&FrsSearchContext);
  2985. //
  2986. // Link the inbound and outbound connections to our member node.
  2987. //
  2988. MemberNode = GTabLookupTableString(MemberTable, SettingsDn, NULL);
  2989. if (MemberNode != NULL) {
  2990. //
  2991. // Is this member linked to this computer
  2992. //
  2993. Subscriber = GTabLookupTableString(SubscriberTable, MemberNode->Dn, NULL);
  2994. //
  2995. // Yep; have a suscriber
  2996. //
  2997. if (Subscriber != NULL) {
  2998. MemberNode->ThisComputer = TRUE;
  2999. MemberNode->Root = FrsWcsDup(Subscriber->Root);
  3000. MemberNode->Stage = FrsWcsDup(Subscriber->Stage);
  3001. FRS_WCSLWR(MemberNode->Root);
  3002. FRS_WCSLWR(MemberNode->Stage);
  3003. MemberNode->DnsName = FrsWcsDup(Computer->DnsName);
  3004. //
  3005. // This is us. Link all the cxtions to this Member.
  3006. //
  3007. if (CxtionTable != NULL) {
  3008. Key = NULL;
  3009. while ((Cxtion = GTabNextDatum(CxtionTable, &Key)) != NULL) {
  3010. //
  3011. // Get our Partners Node from the member table.
  3012. //
  3013. PartnerNode = GTabLookupTableString(MemberTable, Cxtion->PartnerDn, NULL);
  3014. if (PartnerNode != NULL) {
  3015. Cxtion->PartnerName = FrsDupGName(PartnerNode->Name);
  3016. Cxtion->PartnerCoDn = FrsWcsDup(PartnerNode->ComputerDn);
  3017. } else {
  3018. //
  3019. // This Cxtion does not have a valid member object for its
  3020. // partner. E.g. A sysvol topology that has connections under
  3021. // the NTDSSettings objects but there are no corresponding
  3022. // member objects.
  3023. //
  3024. DPRINT1(0, ":DS: Marking connection inconsistent.(%ws)\n",Cxtion->Dn);
  3025. Cxtion->Consistent = FALSE;
  3026. }
  3027. FrsDsTreeLink(MemberNode, Cxtion);
  3028. }
  3029. CxtionTable = GTabFreeTable(CxtionTable,NULL);
  3030. }
  3031. }
  3032. }
  3033. FrsFree(SettingsDn);
  3034. return WStatus;
  3035. }
  3036. DWORD
  3037. FrsNewDsGetNonSysvolCxtions(
  3038. IN PLDAP Ldap,
  3039. IN PWCHAR SetDn,
  3040. IN PWCHAR MemberRef,
  3041. IN PCONFIG_NODE Parent,
  3042. IN PCONFIG_NODE Computer
  3043. )
  3044. /*++
  3045. Routine Description:
  3046. Fetch the members and connections for the replica set identified by Base.
  3047. Arguments:
  3048. ldap : Handle to DS.
  3049. SetDn : Dn of the set being processed.
  3050. MemberRef : MemberRef from the subscriber object.
  3051. Parent : Pointer to the set node in the config tree that is being built,
  3052. Return Value:
  3053. WIN32 Status
  3054. --*/
  3055. {
  3056. #undef DEBSUB
  3057. #define DEBSUB "FrsNewDsGetNonSysvolCxtions:"
  3058. PWCHAR Attrs[7];
  3059. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3060. PCONFIG_NODE Node; // generic node for the tree
  3061. PCONFIG_NODE Subscriber;
  3062. PCONFIG_NODE PartnerNode = NULL;
  3063. PCONFIG_NODE MemberNode = NULL;
  3064. PCONFIG_NODE Cxtion = NULL;
  3065. DWORD WStatus = ERROR_SUCCESS;
  3066. PVOID Key = NULL;
  3067. PWCHAR MemberCn = NULL;
  3068. PWCHAR TempFilter = NULL;
  3069. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  3070. //
  3071. // Initialize the CxtionTable. We discard the table once we have
  3072. // loaded the replica set. We use the same variables for
  3073. // every replica set.
  3074. //
  3075. if (CxtionTable != NULL) {
  3076. CxtionTable = GTabFreeTable(CxtionTable, NULL);
  3077. }
  3078. CxtionTable = GTabAllocStringTable();
  3079. //
  3080. // Initialize the MemberTable. We discard the table once we have
  3081. // loaded the replica set. We use the same variables for
  3082. // every replica set.
  3083. //
  3084. if (MemberTable != NULL) {
  3085. MemberTable = GTabFreeTable(MemberTable, NULL);
  3086. }
  3087. MemberTable = GTabAllocStringTable();
  3088. //
  3089. // We will form the MemberSearchFilter for this replica set.
  3090. //
  3091. if (MemberSearchFilter != NULL) {
  3092. MemberSearchFilter = FrsFree(MemberSearchFilter);
  3093. }
  3094. //
  3095. // Add this members name to the member search filter.
  3096. //
  3097. MemberCn = FrsDsMakeRdn(MemberRef);
  3098. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_CN) +
  3099. wcslen(MemberCn) + 1 ) * sizeof(WCHAR));
  3100. wcscpy(MemberSearchFilter, L"(|(" ATTR_CN L"=");
  3101. wcscat(MemberSearchFilter, MemberCn);
  3102. wcscat(MemberSearchFilter, L")");
  3103. MemberCn = FrsFree(MemberCn);
  3104. //
  3105. // Get the outbound connections.
  3106. //
  3107. WStatus = FrsNewDsGetNonSysvolOutboundCxtions(Ldap, SetDn, MemberRef);
  3108. if (!WIN_SUCCESS(WStatus)) {
  3109. return WStatus;
  3110. }
  3111. //
  3112. // Get the inbound connections.
  3113. //
  3114. WStatus = FrsNewDsGetNonSysvolInboundCxtions(Ldap, SetDn, MemberRef);
  3115. if (!WIN_SUCCESS(WStatus)) {
  3116. return WStatus;
  3117. }
  3118. //
  3119. // The above twp calls build the MemberFilter.
  3120. // MemberFilter is used to search the DS for all the member objects of
  3121. // interest. If there are no connections from or to this member then
  3122. // the filter will will just have 1 entry.
  3123. //
  3124. //
  3125. // Add the closing ')' to the MemberSearchFilter.
  3126. //
  3127. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L")") + 1 ) * sizeof(WCHAR));
  3128. wcscpy(TempFilter, MemberSearchFilter);
  3129. wcscat(TempFilter, L")");
  3130. FrsFree(MemberSearchFilter);
  3131. MemberSearchFilter = TempFilter;
  3132. TempFilter = NULL;
  3133. MK_ATTRS_6(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  3134. ATTR_SERVER_REF, ATTR_COMPUTER_REF);
  3135. if (!FrsDsLdapSearchInit(Ldap, SetDn, LDAP_SCOPE_ONELEVEL, MemberSearchFilter,
  3136. Attrs, 0, &FrsSearchContext)) {
  3137. return ERROR_ACCESS_DENIED;
  3138. }
  3139. if (FrsSearchContext.EntriesInPage == 0) {
  3140. DPRINT1(1, ":DS: WARN - No member objects of interest found under %ws!\n", SetDn);
  3141. }
  3142. //
  3143. // Scan the entries returned from ldap_search
  3144. //
  3145. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  3146. Entry != NULL && WIN_SUCCESS(WStatus);
  3147. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  3148. //
  3149. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3150. //
  3151. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_MEMBER);
  3152. if (!Node) {
  3153. DPRINT(4, ":DS: Member lacks basic info; skipping\n");
  3154. continue;
  3155. }
  3156. //
  3157. // Computer Reference
  3158. //
  3159. Node->ComputerDn = FrsDsFindValue(Ldap, Entry, ATTR_COMPUTER_REF);
  3160. if (Node->ComputerDn == NULL) {
  3161. DPRINT1(4, ":DS: WARN - Member (%ws) lacks computer reference; skipping\n", Node->Dn);
  3162. //
  3163. // Add to the poll summary event log.
  3164. //
  3165. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  3166. Node->Dn, ATTR_COMPUTER_REF);
  3167. Node = FrsFreeType(Node);
  3168. continue;
  3169. }
  3170. FRS_WCSLWR(Node->ComputerDn);
  3171. //
  3172. // Link into config and add to the running checksum
  3173. //
  3174. FrsDsTreeLink(Parent, Node);
  3175. //
  3176. // Insert the new member in the member table only if it is not there already.
  3177. //
  3178. GTabInsertUniqueEntry(MemberTable, Node, Node->Dn, NULL);
  3179. //
  3180. // Make a table of computers of interest to us so we can search for all
  3181. // the computers of interest at one time after we have polled all
  3182. // replica sets. Put empty entries in the table at this point.
  3183. // Do not add our computer in this table as we already have info about
  3184. // our computer.
  3185. //
  3186. if (WSTR_NE(Node->ComputerDn, Computer->Dn)) {
  3187. //
  3188. // This is not our computer. Add it to the table if it isn't already in the table.
  3189. //
  3190. PartnerNode = GTabLookupTableString(PartnerComputerTable, Node->ComputerDn, NULL);
  3191. if (PartnerNode == NULL) {
  3192. //
  3193. // There are no duplicates so enter this computer name in the table.
  3194. //
  3195. PartnerNode = FrsDsAllocBasicNode(Ldap, NULL, CONFIG_TYPE_COMPUTER);
  3196. PartnerNode->Dn = FrsWcsDup(Node->ComputerDn);
  3197. PartnerNode->MemberDn = FrsWcsDup(Node->Dn);
  3198. GTabInsertUniqueEntry(PartnerComputerTable, PartnerNode, PartnerNode->Dn, NULL);
  3199. }
  3200. }
  3201. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeMember", Node);
  3202. }
  3203. FrsDsLdapSearchClose(&FrsSearchContext);
  3204. //
  3205. // Link the inbound and outbound connections to our member node.
  3206. //
  3207. MemberNode = GTabLookupTableString(MemberTable, MemberRef, NULL);
  3208. if (MemberNode != NULL) {
  3209. //
  3210. // Is this member linked to this computer
  3211. //
  3212. Subscriber = GTabLookupTableString(SubscriberTable, MemberNode->Dn, NULL);
  3213. //
  3214. // Yep; have a suscriber
  3215. //
  3216. if (Subscriber != NULL) {
  3217. MemberNode->ThisComputer = TRUE;
  3218. MemberNode->Root = FrsWcsDup(Subscriber->Root);
  3219. MemberNode->Stage = FrsWcsDup(Subscriber->Stage);
  3220. FRS_WCSLWR(MemberNode->Root);
  3221. FRS_WCSLWR(MemberNode->Stage);
  3222. MemberNode->DnsName = FrsWcsDup(Computer->DnsName);
  3223. //
  3224. // This is us. Link all the cxtions to this Member.
  3225. //
  3226. if (CxtionTable != NULL) {
  3227. Key = NULL;
  3228. while ((Cxtion = GTabNextDatum(CxtionTable, &Key)) != NULL) {
  3229. //
  3230. // Get our Partners Node from the member table.
  3231. //
  3232. PartnerNode = GTabLookupTableString(MemberTable, Cxtion->PartnerDn, NULL);
  3233. if (PartnerNode != NULL) {
  3234. Cxtion->PartnerName = FrsDupGName(PartnerNode->Name);
  3235. Cxtion->PartnerCoDn = FrsWcsDup(PartnerNode->ComputerDn);
  3236. } else {
  3237. //
  3238. // This Cxtion does not have a valid member object for its
  3239. // partner. E.g. A sysvol topology that has connections under
  3240. // the NTDSSettings objects but there are no corresponding
  3241. // member objects.
  3242. //
  3243. DPRINT1(0, ":DS: Marking connection inconsistent.(%ws)\n",Cxtion->Dn);
  3244. Cxtion->Consistent = FALSE;
  3245. }
  3246. FrsDsTreeLink(MemberNode, Cxtion);
  3247. }
  3248. CxtionTable = GTabFreeTable(CxtionTable,NULL);
  3249. }
  3250. }
  3251. }
  3252. return WStatus;
  3253. }
  3254. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  3255. DWORD
  3256. FrsDsGetMembers(
  3257. IN PLDAP Ldap,
  3258. IN PWCHAR Base,
  3259. IN PCONFIG_NODE Parent,
  3260. IN PCONFIG_NODE Computer
  3261. )
  3262. /*++
  3263. Routine Description:
  3264. Fetch the members for the replica set identified by Base.
  3265. Arguments:
  3266. ldap - opened and bound ldap connection
  3267. Base - Name of object or container in DS
  3268. Parent - Container which contains Base
  3269. Computer
  3270. Return Value:
  3271. WIN32 Status
  3272. --*/
  3273. {
  3274. #undef DEBSUB
  3275. #define DEBSUB "FrsDsGetMembers:"
  3276. PWCHAR Attrs[7];
  3277. PLDAPMessage Msg = NULL; // Opaque stuff from ldap subsystem
  3278. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3279. PCONFIG_NODE Node; // generic node for the tree
  3280. PCONFIG_NODE Subscriptions;
  3281. PCONFIG_NODE Subscriber;
  3282. DWORD WStatus = ERROR_SUCCESS;
  3283. //
  3284. // Search the DS for members (objectCategory=nTFRSMember)
  3285. //
  3286. MK_ATTRS_6(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  3287. ATTR_SERVER_REF, ATTR_COMPUTER_REF);
  3288. if (!FrsDsLdapSearch(Ldap, Base, LDAP_SCOPE_ONELEVEL, CATEGORY_MEMBER,
  3289. Attrs, 0, &Msg)) {
  3290. return ERROR_ACCESS_DENIED;
  3291. }
  3292. //
  3293. // Scan the entries returned from ldap_search
  3294. //
  3295. for (Entry = ldap_first_entry(Ldap, Msg);
  3296. Entry != NULL && WIN_SUCCESS(WStatus);
  3297. Entry = ldap_next_entry(Ldap, Entry)) {
  3298. //
  3299. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3300. //
  3301. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_MEMBER);
  3302. if (!Node) {
  3303. DPRINT(4, ":DS: Member lacks basic info; skipping\n");
  3304. continue;
  3305. }
  3306. //
  3307. // NTDS Settings (DSA) Reference
  3308. //
  3309. Node->SettingsDn = FrsDsFindValue(Ldap, Entry, ATTR_SERVER_REF);
  3310. FRS_WCSLWR(Node->SettingsDn);
  3311. //
  3312. // Computer Reference
  3313. //
  3314. Node->ComputerDn = FrsDsFindValue(Ldap, Entry, ATTR_COMPUTER_REF);
  3315. FRS_WCSLWR(Node->ComputerDn);
  3316. //
  3317. // Is this member linked to this computer
  3318. //
  3319. if (Node->ComputerDn &&
  3320. WSTR_EQ(Node->ComputerDn, Computer->Dn)) {
  3321. //
  3322. // Yep; so does this computer have a subscriber for this member?
  3323. //
  3324. Subscriber = NULL;
  3325. for (Subscriptions = Computer->Children;
  3326. Subscriptions;
  3327. Subscriptions = Subscriptions->Peer) {
  3328. for (Subscriber = Subscriptions->Children;
  3329. Subscriber;
  3330. Subscriber = Subscriber->Peer) {
  3331. if (!Subscriber->MemberDn) {
  3332. continue;
  3333. }
  3334. if (WSTR_EQ(Subscriber->MemberDn, Node->Dn)) {
  3335. break;
  3336. }
  3337. }
  3338. if (Subscriber) {
  3339. break;
  3340. }
  3341. }
  3342. //
  3343. // Yep; have a suscriber
  3344. //
  3345. if (Subscriber) {
  3346. Node->ThisComputer = TRUE;
  3347. Node->Root = FrsWcsDup(Subscriber->Root);
  3348. Node->Stage = FrsWcsDup(Subscriber->Stage);
  3349. FRS_WCSLWR(Node->Root);
  3350. FRS_WCSLWR(Node->Stage);
  3351. Node->DnsName = FrsWcsDup(Computer->DnsName);
  3352. }
  3353. }
  3354. //
  3355. // Link into config and add to the running checksum
  3356. //
  3357. FrsDsTreeLink(Parent, Node);
  3358. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeMember", Node);
  3359. //
  3360. // Get the inbound cxtions
  3361. //
  3362. WStatus = FrsDsGetCxtions(Ldap, Node->Dn, Node, FALSE);
  3363. }
  3364. LDAP_FREE_MSG(Msg);
  3365. return WStatus;
  3366. }
  3367. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  3368. DWORD
  3369. FrsNewDsGetSets(
  3370. IN PLDAP Ldap,
  3371. IN PWCHAR SetDnAddr,
  3372. IN PWCHAR MemberRef,
  3373. IN PCONFIG_NODE Parent,
  3374. IN PCONFIG_NODE Computer
  3375. )
  3376. /*++
  3377. Routine Description:
  3378. Recursively scan the DS tree beginning at
  3379. configuration\sites\settings\sets.
  3380. Arguments:
  3381. ldap - opened and bound ldap connection
  3382. SetDnAddr - From member reference from subscriber
  3383. Parent - Container which contains Base
  3384. Computer - for member back links
  3385. Return Value:
  3386. ERROR_SUCCESS - config fetched successfully
  3387. Otherwise - couldn't get the DS config
  3388. --*/
  3389. {
  3390. #undef DEBSUB
  3391. #define DEBSUB "FrsNewDsGetSets:"
  3392. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3393. PCONFIG_NODE Node; // generic node for the tree
  3394. DWORD i;
  3395. DWORD WStatus = ERROR_SUCCESS;
  3396. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  3397. PWCHAR Attrs[9];
  3398. //
  3399. // Have we processed this set before? If we have then don't process
  3400. // it again. This check prevents two subscribers to point to
  3401. // different member objects that are members of the same set.
  3402. //
  3403. Node = GTabLookupTableString(SetTable, SetDnAddr, NULL);
  3404. if (Node) {
  3405. return ERROR_SUCCESS;
  3406. }
  3407. //
  3408. // Search the DS beginning at Base for sets (objectCategory=nTFRSReplicaSet)
  3409. //
  3410. MK_ATTRS_8(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  3411. ATTR_SET_TYPE, ATTR_PRIMARY_MEMBER, ATTR_FILE_FILTER, ATTR_DIRECTORY_FILTER);
  3412. if (!FrsDsLdapSearchInit(Ldap, SetDnAddr, LDAP_SCOPE_BASE, CATEGORY_REPLICA_SET,
  3413. Attrs, 0, &FrsSearchContext)) {
  3414. return ERROR_ACCESS_DENIED;
  3415. }
  3416. if (FrsSearchContext.EntriesInPage == 0) {
  3417. DPRINT1(1, ":DS: WARN - No replica set objects found under %ws!\n", SetDnAddr);
  3418. }
  3419. //
  3420. // Scan the entries returned from ldap_search
  3421. //
  3422. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  3423. Entry != NULL && WIN_SUCCESS(WStatus);
  3424. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  3425. //
  3426. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3427. //
  3428. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_REPLICA_SET);
  3429. if (!Node) {
  3430. DPRINT(4, ":DS: Set lacks basic info; skipping\n");
  3431. continue;
  3432. }
  3433. //
  3434. // Replica set type
  3435. //
  3436. Node->SetType = FrsDsFindValue(Ldap, Entry, ATTR_SET_TYPE);
  3437. //
  3438. // Check the set type. It has to be one that we recognize.
  3439. //
  3440. if ((Node->SetType == NULL) ||
  3441. (WSTR_NE(Node->SetType, FRS_RSTYPE_OTHERW) &&
  3442. WSTR_NE(Node->SetType, FRS_RSTYPE_DFSW) &&
  3443. WSTR_NE(Node->SetType, FRS_RSTYPE_DOMAIN_SYSVOLW) &&
  3444. WSTR_NE(Node->SetType, FRS_RSTYPE_ENTERPRISE_SYSVOLW))){
  3445. DPRINT1(4, ":DS: ERROR - Invalid Set type for (%ws)\n", Node->Dn);
  3446. //
  3447. // Add to the poll summary event log.
  3448. //
  3449. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_REPLICA_SET,
  3450. Node->Dn, ATTR_SET_TYPE);
  3451. Node = FrsFreeType(Node);
  3452. continue;
  3453. }
  3454. //
  3455. // Primary member
  3456. //
  3457. Node->MemberDn = FrsDsFindValue(Ldap, Entry, ATTR_PRIMARY_MEMBER);
  3458. //
  3459. // File filter
  3460. //
  3461. Node->FileFilterList = FrsDsFindValue(Ldap, Entry, ATTR_FILE_FILTER);
  3462. //
  3463. // Directory filter
  3464. //
  3465. Node->DirFilterList = FrsDsFindValue(Ldap, Entry, ATTR_DIRECTORY_FILTER);
  3466. //
  3467. // Link into config and add to the running checksum
  3468. //
  3469. FrsDsTreeLink(Parent, Node);
  3470. //
  3471. // Insert into the table of sets. We checked for duplicates above with
  3472. // GTabLookupTableString so there should not be any duplicates.
  3473. //
  3474. FRS_ASSERT(GTabInsertUniqueEntry(SetTable, Node, Node->Dn, NULL) == NULL);
  3475. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSet", Node);
  3476. //
  3477. // Get the replica set topology. We have to look at different places
  3478. // in the DS depending on the type of replica set. The cxtions for sysvol
  3479. // replica set are generated by KCC and they reside under the server object
  3480. // for the DC. We use the serverReference from the member object to get
  3481. // there.
  3482. //
  3483. if (FRS_RSTYPE_IS_SYSVOLW(Node->SetType)) {
  3484. WStatus = FrsNewDsGetSysvolCxtions(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3485. } else {
  3486. WStatus = FrsNewDsGetNonSysvolCxtions(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3487. }
  3488. }
  3489. FrsDsLdapSearchClose(&FrsSearchContext);
  3490. return WStatus;
  3491. }
  3492. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  3493. DWORD
  3494. FrsDsGetSets(
  3495. IN PLDAP Ldap,
  3496. IN PWCHAR SetDnAddr,
  3497. IN PCONFIG_NODE Parent,
  3498. IN PCONFIG_NODE Computer
  3499. )
  3500. /*++
  3501. Routine Description:
  3502. Recursively scan the DS tree beginning at
  3503. configuration\sites\settings\sets.
  3504. Arguments:
  3505. ldap - opened and bound ldap connection
  3506. SetDnAddr - From member reference from subscriber
  3507. Parent - Container which contains Base
  3508. Computer - for member back links
  3509. Return Value:
  3510. ERROR_SUCCESS - config fetched successfully
  3511. Otherwise - couldn't get the DS config
  3512. --*/
  3513. {
  3514. #undef DEBSUB
  3515. #define DEBSUB "FrsDsGetSets:"
  3516. PLDAPMessage Msg = NULL; // Opaque stuff from ldap subsystem
  3517. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3518. PCONFIG_NODE Node; // generic node for the tree
  3519. DWORD i;
  3520. DWORD WStatus = ERROR_SUCCESS;
  3521. PWCHAR Attrs[9];
  3522. //
  3523. // Have we processed this set before?
  3524. //
  3525. for (Node = Parent->Children; Node; Node = Node->Peer) {
  3526. if (WSTR_EQ(Node->Dn, SetDnAddr)) {
  3527. DPRINT1(4, ":DS: Set hit on %ws\n", SetDnAddr);
  3528. break;
  3529. }
  3530. }
  3531. //
  3532. // Yep; get the members
  3533. //
  3534. if (Node) {
  3535. return FrsDsGetMembers(Ldap, Node->Dn, Node, Computer);
  3536. }
  3537. //
  3538. // Search the DS beginning at Base for sets (objectCategory=nTFRSReplicaSet)
  3539. //
  3540. MK_ATTRS_8(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  3541. ATTR_SET_TYPE, ATTR_PRIMARY_MEMBER, ATTR_FILE_FILTER, ATTR_DIRECTORY_FILTER);
  3542. if (!FrsDsLdapSearch(Ldap, SetDnAddr, LDAP_SCOPE_BASE, CATEGORY_REPLICA_SET,
  3543. Attrs, 0, &Msg)) {
  3544. return ERROR_ACCESS_DENIED;
  3545. }
  3546. //
  3547. // Scan the entries returned from ldap_search
  3548. //
  3549. for (Entry = ldap_first_entry(Ldap, Msg);
  3550. Entry != NULL && WIN_SUCCESS(WStatus);
  3551. Entry = ldap_next_entry(Ldap, Entry)) {
  3552. //
  3553. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3554. //
  3555. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_REPLICA_SET);
  3556. if (!Node) {
  3557. DPRINT(4, ":DS: Set lacks basic info; skipping\n");
  3558. continue;
  3559. }
  3560. //
  3561. // Replica set type
  3562. //
  3563. Node->SetType = FrsDsFindValue(Ldap, Entry, ATTR_SET_TYPE);
  3564. //
  3565. // Primary member
  3566. //
  3567. Node->MemberDn = FrsDsFindValue(Ldap, Entry, ATTR_PRIMARY_MEMBER);
  3568. //
  3569. // File filter
  3570. //
  3571. Node->FileFilterList = FrsDsFindValue(Ldap, Entry, ATTR_FILE_FILTER);
  3572. //
  3573. // Directory filter
  3574. //
  3575. Node->DirFilterList = FrsDsFindValue(Ldap, Entry, ATTR_DIRECTORY_FILTER);
  3576. //
  3577. // Link into config and add to the running checksum
  3578. //
  3579. FrsDsTreeLink(Parent, Node);
  3580. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSet", Node);
  3581. //
  3582. // Recurse to the next level in the DS hierarchy
  3583. //
  3584. WStatus = FrsDsGetMembers(Ldap, Node->Dn, Node, Computer);
  3585. }
  3586. LDAP_FREE_MSG(Msg);
  3587. return WStatus;
  3588. }
  3589. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  3590. DWORD
  3591. FrsNewDsGetSettings(
  3592. IN PLDAP Ldap,
  3593. IN PWCHAR MemberRef,
  3594. IN PCONFIG_NODE Parent,
  3595. IN PCONFIG_NODE Computer
  3596. )
  3597. /*++
  3598. Routine Description:
  3599. Scan the DS tree for NTFRS-Settings objects and their servers
  3600. Arguments:
  3601. ldap - opened and bound ldap connection
  3602. MemberRef - From the subscriber member reference
  3603. Parent - Container which contains Base
  3604. Computer
  3605. Return Value:
  3606. WIN32 Status
  3607. --*/
  3608. {
  3609. #undef DEBSUB
  3610. #define DEBSUB "FrsNewDsGetSettings:"
  3611. PWCHAR Attrs[5];
  3612. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3613. PCONFIG_NODE Node; // generic node for the tree
  3614. PWCHAR MemberDnAddr;
  3615. PWCHAR SetDnAddr;
  3616. PWCHAR SettingsDnAddr;
  3617. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  3618. DWORD WStatus = ERROR_SUCCESS;
  3619. //
  3620. // Find the member component
  3621. //
  3622. MemberDnAddr = wcsstr(MemberRef, L"cn=");
  3623. if (!MemberDnAddr) {
  3624. DPRINT1(0, ":DS: ERROR - Missing member component in %ws\n", MemberRef);
  3625. return ERROR_ACCESS_DENIED;
  3626. }
  3627. //
  3628. // Find the set component
  3629. //
  3630. SetDnAddr = wcsstr(MemberDnAddr + 3, L"cn=");
  3631. if (!SetDnAddr) {
  3632. DPRINT1(0, ":DS: ERROR - Missing set component in %ws\n", MemberRef);
  3633. return ERROR_ACCESS_DENIED;
  3634. }
  3635. //
  3636. // Find the settings component
  3637. //
  3638. SettingsDnAddr = wcsstr(SetDnAddr + 3, L"cn=");
  3639. if (!SettingsDnAddr) {
  3640. DPRINT1(0, ":DS: ERROR - Missing settings component in %ws\n", MemberRef);
  3641. return ERROR_ACCESS_DENIED;
  3642. }
  3643. //
  3644. // Have we processed this settings before?
  3645. //
  3646. for (Node = Parent->Children; Node; Node = Node->Peer) {
  3647. if (WSTR_EQ(Node->Dn, SettingsDnAddr)) {
  3648. DPRINT1(4, ":DS: Settings hit on %ws\n", MemberRef);
  3649. break;
  3650. }
  3651. }
  3652. //
  3653. // Yep; get the sets
  3654. //
  3655. if (Node) {
  3656. return FrsNewDsGetSets(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3657. }
  3658. //
  3659. // Search the DS beginning at Base for settings (objectCategory=nTFRSSettings)
  3660. //
  3661. MK_ATTRS_4(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED);
  3662. if (!FrsDsLdapSearchInit(Ldap, SettingsDnAddr, LDAP_SCOPE_BASE, CATEGORY_NTFRS_SETTINGS,
  3663. Attrs, 0, &FrsSearchContext)) {
  3664. return ERROR_ACCESS_DENIED;
  3665. }
  3666. if (FrsSearchContext.EntriesInPage == 0) {
  3667. DPRINT1(1, ":DS: WARN - No NTFRSSettings objects found under %ws!\n", SettingsDnAddr);
  3668. }
  3669. //
  3670. // Scan the entries returned from ldap_search
  3671. //
  3672. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  3673. Entry != NULL && WIN_SUCCESS(WStatus);
  3674. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  3675. //
  3676. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3677. //
  3678. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_NTFRS_SETTINGS);
  3679. if (!Node) {
  3680. DPRINT(4, ":DS: Frs Settings lacks basic info; skipping\n");
  3681. continue;
  3682. }
  3683. //
  3684. // Link into config and add to the running checksum
  3685. //
  3686. FrsDsTreeLink(Parent, Node);
  3687. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSettings", Node);
  3688. //
  3689. // Recurse to the next level in the DS hierarchy
  3690. //
  3691. WStatus = FrsNewDsGetSets(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3692. }
  3693. FrsDsLdapSearchClose(&FrsSearchContext);
  3694. return WStatus;
  3695. }
  3696. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  3697. DWORD
  3698. FrsDsGetSettings(
  3699. IN PLDAP Ldap,
  3700. IN PWCHAR MemberDn,
  3701. IN PCONFIG_NODE Parent,
  3702. IN PCONFIG_NODE Computer
  3703. )
  3704. /*++
  3705. Routine Description:
  3706. Scan the DS tree for NTFRS-Settings objects and their servers
  3707. Arguments:
  3708. ldap - opened and bound ldap connection
  3709. MemberDn - From the subscriber member reference
  3710. Parent - Container which contains Base
  3711. Computer
  3712. Return Value:
  3713. WIN32 Status
  3714. --*/
  3715. {
  3716. #undef DEBSUB
  3717. #define DEBSUB "FrsDsGetSettings:"
  3718. PWCHAR Attrs[5];
  3719. PLDAPMessage Msg = NULL; // Opaque stuff from ldap subsystem
  3720. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3721. PCONFIG_NODE Node; // generic node for the tree
  3722. PWCHAR MemberDnAddr;
  3723. PWCHAR SetDnAddr;
  3724. PWCHAR SettingsDnAddr;
  3725. DWORD WStatus = ERROR_SUCCESS;
  3726. //
  3727. // Find the member component
  3728. //
  3729. MemberDnAddr = wcsstr(MemberDn, L"cn=");
  3730. if (!MemberDnAddr) {
  3731. DPRINT1(0, ":DS: ERROR - Missing member component in %ws\n", MemberDn);
  3732. return ERROR_ACCESS_DENIED;
  3733. }
  3734. //
  3735. // Find the set component
  3736. //
  3737. SetDnAddr = wcsstr(MemberDnAddr + 3, L"cn=");
  3738. if (!SetDnAddr) {
  3739. DPRINT1(0, ":DS: ERROR - Missing set component in %ws\n", MemberDn);
  3740. return ERROR_ACCESS_DENIED;
  3741. }
  3742. //
  3743. // Find the settings component
  3744. //
  3745. SettingsDnAddr = wcsstr(SetDnAddr + 3, L"cn=");
  3746. if (!SettingsDnAddr) {
  3747. DPRINT1(0, ":DS: ERROR - Missing settings component in %ws\n", MemberDn);
  3748. return ERROR_ACCESS_DENIED;
  3749. }
  3750. //
  3751. // Have we processed this settings before?
  3752. //
  3753. for (Node = Parent->Children; Node; Node = Node->Peer) {
  3754. if (WSTR_EQ(Node->Dn, SettingsDnAddr)) {
  3755. DPRINT1(4, ":DS: Settings hit on %ws\n", MemberDn);
  3756. break;
  3757. }
  3758. }
  3759. //
  3760. // Yep; get the sets
  3761. //
  3762. if (Node) {
  3763. return FrsDsGetSets(Ldap, SetDnAddr, Node, Computer);
  3764. }
  3765. //
  3766. // Search the DS beginning at Base for settings (objectCategory=nTFRSSettings)
  3767. //
  3768. MK_ATTRS_4(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED);
  3769. if (!FrsDsLdapSearch(Ldap, SettingsDnAddr, LDAP_SCOPE_BASE, CATEGORY_NTFRS_SETTINGS,
  3770. Attrs, 0, &Msg)) {
  3771. return ERROR_ACCESS_DENIED;
  3772. }
  3773. //
  3774. // Scan the entries returned from ldap_search
  3775. //
  3776. for (Entry = ldap_first_entry(Ldap, Msg);
  3777. Entry != NULL && WIN_SUCCESS(WStatus);
  3778. Entry = ldap_next_entry(Ldap, Entry)) {
  3779. //
  3780. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3781. //
  3782. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_NTFRS_SETTINGS);
  3783. if (!Node) {
  3784. DPRINT(4, ":DS: Frs Settings lacks basic info; skipping\n");
  3785. continue;
  3786. }
  3787. //
  3788. // Link into config and add to the running checksum
  3789. //
  3790. FrsDsTreeLink(Parent, Node);
  3791. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSettings", Node);
  3792. //
  3793. // Recurse to the next level in the DS hierarchy
  3794. //
  3795. WStatus = FrsDsGetSets(Ldap, SetDnAddr, Node, Computer);
  3796. }
  3797. LDAP_FREE_MSG(Msg);
  3798. return WStatus;
  3799. }
  3800. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  3801. DWORD
  3802. FrsNewDsGetServices(
  3803. IN PLDAP Ldap,
  3804. IN PCONFIG_NODE Computer,
  3805. OUT PCONFIG_NODE *Services
  3806. )
  3807. /*++
  3808. Routine Description:
  3809. Recursively scan the DS tree beginning at the settings from
  3810. the subscriber nodes.
  3811. The name is a misnomer because of evolution.
  3812. Arguments:
  3813. ldap - opened and bound ldap connection
  3814. Computer
  3815. Services - returned list of all Settings
  3816. Return Value:
  3817. WIN32 Status
  3818. --*/
  3819. {
  3820. #undef DEBSUB
  3821. #define DEBSUB "FrsNewDsGetServices:"
  3822. PCONFIG_NODE Node;
  3823. PCONFIG_NODE Subscriptions;
  3824. PCONFIG_NODE Subscriber;
  3825. PVOID SubKey = NULL;
  3826. DWORD WStatus = ERROR_SUCCESS;
  3827. *Services = NULL;
  3828. //
  3829. // Initialize the SubscriberTable.
  3830. //
  3831. if (SetTable != NULL) {
  3832. SetTable = GTabFreeTable(SetTable,NULL);
  3833. }
  3834. SetTable = GTabAllocStringTable();
  3835. //
  3836. // Initially, the node is assumed to be consistent
  3837. //
  3838. Node = FrsAllocType(CONFIG_NODE_TYPE);
  3839. Node->DsObjectType = CONFIG_TYPE_SERVICES_ROOT;
  3840. Node->Consistent = TRUE;
  3841. //
  3842. // Distinguished name
  3843. //
  3844. Node->Dn = FrsWcsDup(L"<<replica ds root>>");
  3845. FRS_WCSLWR(Node->Dn);
  3846. //
  3847. // Name = RDN + Object Guid
  3848. //
  3849. Node->Name = FrsBuildGName(FrsAlloc(sizeof(GUID)),
  3850. FrsWcsDup(L"<<replica ds root>>"));
  3851. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeService", Node);
  3852. SubKey = NULL;
  3853. while ((Subscriber = GTabNextDatum(SubscriberTable, &SubKey)) != NULL) {
  3854. //
  3855. // Recurse to the next level in the DS hierarchy
  3856. //
  3857. WStatus = FrsNewDsGetSettings(Ldap, Subscriber->MemberDn, Node, Computer);
  3858. DPRINT1_WS(2, ":DS: WARN - Error getting topology for replica root (%ws);", Subscriber->Root, WStatus);
  3859. }
  3860. *Services = Node;
  3861. return WStatus;
  3862. }
  3863. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  3864. DWORD
  3865. FrsDsGetServices(
  3866. IN PLDAP Ldap,
  3867. IN PCONFIG_NODE Computer,
  3868. OUT PCONFIG_NODE *Services
  3869. )
  3870. /*++
  3871. Routine Description:
  3872. Recursively scan the DS tree beginning at the settings from
  3873. the subscriber nodes.
  3874. The name is a misnomer because of evolution.
  3875. Arguments:
  3876. ldap - opened and bound ldap connection
  3877. Computer
  3878. Services - returned list of all Settings
  3879. Return Value:
  3880. WIN32 Status
  3881. --*/
  3882. {
  3883. #undef DEBSUB
  3884. #define DEBSUB "FrsDsGetServices:"
  3885. PCONFIG_NODE Node;
  3886. PCONFIG_NODE Subscriptions;
  3887. PCONFIG_NODE Subscriber;
  3888. DWORD WStatus = ERROR_SUCCESS;
  3889. *Services = NULL;
  3890. //
  3891. // Initially, the node is assumed to be consistent
  3892. //
  3893. Node = FrsAllocType(CONFIG_NODE_TYPE);
  3894. Node->DsObjectType = CONFIG_TYPE_SERVICES_ROOT;
  3895. Node->Consistent = TRUE;
  3896. //
  3897. // Distinguished name
  3898. //
  3899. Node->Dn = FrsWcsDup(L"<<replica ds root>>");
  3900. FRS_WCSLWR(Node->Dn);
  3901. //
  3902. // Name = RDN + Object Guid
  3903. //
  3904. Node->Name = FrsBuildGName(FrsAlloc(sizeof(GUID)),
  3905. FrsWcsDup(L"<<replica ds root>>"));
  3906. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeService", Node);
  3907. for (Subscriptions = Computer->Children;
  3908. Subscriptions;
  3909. Subscriptions = Subscriptions->Peer) {
  3910. for (Subscriber = Subscriptions->Children;
  3911. Subscriber;
  3912. Subscriber = Subscriber->Peer) {
  3913. //
  3914. // No member link; nevermind
  3915. //
  3916. if (!Subscriber->MemberDn) {
  3917. continue;
  3918. }
  3919. //
  3920. // Recurse to the next level in the DS hierarchy
  3921. //
  3922. WStatus = FrsDsGetSettings(Ldap, Subscriber->MemberDn, Node, Computer);
  3923. if (!WIN_SUCCESS(WStatus)) {
  3924. break;
  3925. }
  3926. }
  3927. }
  3928. *Services = Node;
  3929. return WStatus;
  3930. }
  3931. PWCHAR
  3932. FrsDsGetDnsName(
  3933. IN PLDAP Ldap,
  3934. IN PWCHAR Dn
  3935. )
  3936. /*++
  3937. Routine Description:
  3938. Read the dNSHostName attribute from Dn
  3939. Arguments:
  3940. Ldap - opened and bound ldap connection
  3941. Dn - Base Dn for search
  3942. Return Value:
  3943. WIN32 Status
  3944. --*/
  3945. {
  3946. #undef DEBSUB
  3947. #define DEBSUB "FrsDsGetDnsName:"
  3948. PLDAPMessage LdapMsg = NULL;
  3949. PLDAPMessage LdapEntry;
  3950. PWCHAR DnsName = NULL;
  3951. PWCHAR Attrs[2];
  3952. DWORD WStatus = ERROR_SUCCESS;
  3953. //
  3954. // Search the DS beginning at Base for the entries of class (objectCategory=*)
  3955. //
  3956. MK_ATTRS_1(Attrs, ATTR_DNS_HOST_NAME);
  3957. //
  3958. // Note: Is it safe to turn off referrals re: back links?
  3959. // if so, use ldap_get/set_option in winldap.h
  3960. //
  3961. if (!FrsDsLdapSearch(Ldap, Dn, LDAP_SCOPE_BASE, CATEGORY_ANY,
  3962. Attrs, 0, &LdapMsg)) {
  3963. goto CLEANUP;
  3964. }
  3965. //
  3966. // Scan the entries returned from ldap_search
  3967. //
  3968. LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  3969. if (!LdapEntry) {
  3970. goto CLEANUP;
  3971. }
  3972. //
  3973. // DNS name
  3974. //
  3975. DnsName = FrsDsFindValue(Ldap, LdapEntry, ATTR_DNS_HOST_NAME);
  3976. CLEANUP:
  3977. LDAP_FREE_MSG(LdapMsg);
  3978. DPRINT2(4, ":DS: DN %ws -> DNS %ws\n", Dn, DnsName);
  3979. return DnsName;
  3980. }
  3981. PWCHAR
  3982. FrsDsGuessPrincName(
  3983. IN PWCHAR Dn
  3984. )
  3985. /*++
  3986. Routine Description:
  3987. Derive the NT4 account name for Dn. Dn should be the Dn
  3988. of a computer object.
  3989. Arguments:
  3990. Dn
  3991. Return Value:
  3992. NT4 Account Name or NULL
  3993. --*/
  3994. {
  3995. #undef DEBSUB
  3996. #define DEBSUB "FrsDsGuessPrincName:"
  3997. DWORD Len = 0;
  3998. WCHAR HackPrincName[MAX_PATH];
  3999. PWCHAR Rdn;
  4000. PWCHAR Dc;
  4001. DPRINT1(4, ":DS: WARN: Guess NT4 Account Name for %ws\n", Dn);
  4002. //
  4003. // Computer's Dn not available
  4004. //
  4005. if (!Dn) {
  4006. return NULL;
  4007. }
  4008. Dc = wcsstr(Dn, L"dc=");
  4009. //
  4010. // No DC=?
  4011. //
  4012. if (!Dc) {
  4013. DPRINT1(4, ":DS: No DC= in %ws\n", Dn);
  4014. return NULL;
  4015. }
  4016. //
  4017. // DC= at eol?
  4018. //
  4019. Dc += 3;
  4020. if (!*Dc) {
  4021. DPRINT1(4, ":DS: No DC= at eol in %ws\n", Dn);
  4022. return NULL;
  4023. }
  4024. while (*Dc && *Dc != L',') {
  4025. HackPrincName[Len++] = *Dc++;
  4026. }
  4027. HackPrincName[Len++] = L'\\';
  4028. HackPrincName[Len++] = L'\0';
  4029. Rdn = FrsDsMakeRdn(Dn);
  4030. wcscat(HackPrincName, Rdn);
  4031. wcscat(HackPrincName, L"$");
  4032. DPRINT1(4, ":DS: Guessing %ws\n", HackPrincName);
  4033. FrsFree(Rdn);
  4034. return FrsWcsDup(HackPrincName);
  4035. }
  4036. PWCHAR
  4037. FrsDsFormUPN(
  4038. IN PWCHAR NT4AccountName,
  4039. IN PWCHAR DomainDnsName
  4040. )
  4041. /*++
  4042. Routine Description:
  4043. Forms the User Principal Name by combining the
  4044. Sam account name and the domain dns name in the form
  4045. shown below.
  4046. <SamAccountName>@<DnsDomainName>
  4047. You can get <SamAccountName> from the string to the right of the "\"
  4048. of the NT4AccountName.
  4049. Arguments:
  4050. NT4AccountName - DS_NT4_ACCOUNT_NAME returned from DsCrackNames.
  4051. DomainDnsName - Dns name of the domain.
  4052. Return Value:
  4053. Copy of name in desired format; free with FrsFree()
  4054. --*/
  4055. {
  4056. #undef DEBSUB
  4057. #define DEBSUB "FrsDsFormUPN:"
  4058. PWCHAR SamBegin = NULL;
  4059. PWCHAR FormedUPN = NULL;
  4060. if ((NT4AccountName == NULL ) || (DomainDnsName == NULL)) {
  4061. return NULL;
  4062. }
  4063. //
  4064. // Find the sam account name.
  4065. //
  4066. for (SamBegin = NT4AccountName; *SamBegin && *SamBegin != L'\\'; ++SamBegin);
  4067. if (*SamBegin && *(SamBegin+1)) {
  4068. SamBegin++;
  4069. } else {
  4070. return NULL;
  4071. }
  4072. FormedUPN = FrsAlloc((wcslen(SamBegin) + wcslen(DomainDnsName) + 2) * sizeof(WCHAR));
  4073. wcscpy(FormedUPN, SamBegin);
  4074. wcscat(FormedUPN, L"@");
  4075. wcscat(FormedUPN, DomainDnsName);
  4076. DPRINT1(0, "SUDARC-DEV - UPN formed is %ws\n", FormedUPN);
  4077. return FormedUPN;
  4078. }
  4079. PWCHAR
  4080. FrsDsConvertName(
  4081. IN HANDLE Handle,
  4082. IN PWCHAR InputName,
  4083. IN DWORD InputFormat,
  4084. IN PWCHAR DomainDnsName,
  4085. IN DWORD DesiredFormat
  4086. )
  4087. /*++
  4088. Routine Description:
  4089. Translate the input name into the desired format.
  4090. Arguments:
  4091. Handle - From DsBind
  4092. InputName - Supplied name.
  4093. InputFormat - Format of the supplied name.
  4094. DomainDnsName - If !NULL, produce new local handle
  4095. DesiredFormat - desired format. Eg. DS_USER_PRINCIPAL_NAME
  4096. Return Value:
  4097. Copy of name in desired format; free with FrsFree()
  4098. --*/
  4099. {
  4100. #undef DEBSUB
  4101. #define DEBSUB "FrsDsConvertName:"
  4102. DWORD WStatus;
  4103. DS_NAME_RESULT *Cracked = NULL;
  4104. HANDLE LocalHandle = NULL;
  4105. PWCHAR CrackedName = NULL;
  4106. PWCHAR CrackedDomain = NULL;
  4107. PWCHAR CrackedUPN = NULL;
  4108. DWORD RequestedFormat = 0;
  4109. DPRINT3(4, ":DS: Convert Name %ws From %08x To %08x\n", InputName, InputFormat, DesiredFormat);
  4110. //
  4111. // Input name not available.
  4112. //
  4113. if (!InputName) {
  4114. return NULL;
  4115. }
  4116. //
  4117. // Need something to go on!
  4118. //
  4119. if (!HANDLE_IS_VALID(Handle) && !DomainDnsName) {
  4120. return NULL;
  4121. }
  4122. //
  4123. // Bind to Ds
  4124. //
  4125. if (DomainDnsName) {
  4126. DPRINT3(4, ":DS: Get %08x Name from %ws for %ws\n",
  4127. DesiredFormat, DomainDnsName, InputName);
  4128. WStatus = DsBind(NULL, DomainDnsName, &LocalHandle);
  4129. CLEANUP2_WS(0, ":DS: ERROR - DsBind(%ws, %08x);",
  4130. DomainDnsName, DesiredFormat, WStatus, RETURN);
  4131. Handle = LocalHandle;
  4132. }
  4133. //
  4134. // Crack the computer's distinguished name into its NT4 Account Name
  4135. //
  4136. // If the Desired format is DS_USER_PRINCIPAL_NAME then we form it by
  4137. // getting the name from DS_NT4_ACCOUNT_NAME and the dns domain name
  4138. // from the "Cracked->rItems->pDomain"
  4139. // We could ask for DS_USER_PRINCIPAL_NAME directly but we don't because.
  4140. // Object can have implicit or explicit UPNs. If the object has an explicit UPN,
  4141. // the DsCrackNames will work. If the object has an implicit UPN,
  4142. // then you need to build it.
  4143. //
  4144. if (DesiredFormat == DS_USER_PRINCIPAL_NAME) {
  4145. RequestedFormat = DS_NT4_ACCOUNT_NAME;
  4146. } else {
  4147. RequestedFormat = DesiredFormat;
  4148. }
  4149. WStatus = DsCrackNames(Handle, // in hDS,
  4150. DS_NAME_NO_FLAGS, // in flags,
  4151. InputFormat , // in formatOffered,
  4152. RequestedFormat, // in formatDesired,
  4153. 1, // in cNames,
  4154. &InputName, // in *rpNames,
  4155. &Cracked); // out *ppResult
  4156. if (!WIN_SUCCESS(WStatus)) {
  4157. DPRINT2_WS(0, ":DS: ERROR - DsCrackNames(%ws, %08x);", InputName, DesiredFormat, WStatus);
  4158. //
  4159. // Set DsBindingsAreValid to FALSE if the handle has become invalid.
  4160. // That will force us to rebind at the next poll. The guess below might still
  4161. // work so continue processing.
  4162. //
  4163. if (WStatus == ERROR_INVALID_HANDLE) {
  4164. DPRINT1(4, ":DS: Marking binding to %ws as invalid.\n",
  4165. (DsDomainControllerName) ? DsDomainControllerName : L"<null>");
  4166. DsBindingsAreValid = FALSE;
  4167. }
  4168. //
  4169. // What else can we do?
  4170. //
  4171. if (HANDLE_IS_VALID(LocalHandle)) {
  4172. DsUnBind(&LocalHandle);
  4173. LocalHandle = NULL;
  4174. }
  4175. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  4176. return FrsDsGuessPrincName(InputName);
  4177. } else {
  4178. return NULL;
  4179. }
  4180. }
  4181. //
  4182. // Might have it
  4183. //
  4184. if (Cracked && Cracked->cItems && Cracked->rItems) {
  4185. //
  4186. // Got it!
  4187. //
  4188. if (Cracked->rItems->status == DS_NAME_NO_ERROR) {
  4189. DPRINT1(4, ":DS: Cracked Domain : %ws\n", Cracked->rItems->pDomain);
  4190. DPRINT2(4, ":DS: Cracked Name : %08x %ws\n",
  4191. DesiredFormat, Cracked->rItems->pName);
  4192. CrackedDomain = FrsWcsDup(Cracked->rItems->pDomain);
  4193. CrackedName = FrsWcsDup(Cracked->rItems->pName);
  4194. //
  4195. // Only got the domain; rebind and try again
  4196. //
  4197. } else
  4198. if (Cracked->rItems->status == DS_NAME_ERROR_DOMAIN_ONLY) {
  4199. CrackedName = FrsDsConvertName(NULL, InputName, InputFormat, Cracked->rItems->pDomain, DesiredFormat);
  4200. } else {
  4201. DPRINT3(0, ":DS: ERROR - DsCrackNames(%ws, %08x); internal status %d\n",
  4202. InputName, DesiredFormat, Cracked->rItems->status);
  4203. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  4204. CrackedName = FrsDsGuessPrincName(InputName);
  4205. }
  4206. }
  4207. } else {
  4208. DPRINT2(0, ":DS: ERROR - DsCrackNames(%ws, %08x); no status\n",
  4209. InputName, DesiredFormat);
  4210. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  4211. CrackedName = FrsDsGuessPrincName(InputName);
  4212. }
  4213. }
  4214. if (Cracked) {
  4215. DsFreeNameResult(Cracked);
  4216. Cracked = NULL;
  4217. }
  4218. if (HANDLE_IS_VALID(LocalHandle)) {
  4219. DsUnBind(&LocalHandle);
  4220. LocalHandle = NULL;
  4221. }
  4222. RETURN:
  4223. if ((DesiredFormat == DS_USER_PRINCIPAL_NAME) && (CrackedName != NULL) && (CrackedDomain != NULL)) {
  4224. CrackedUPN = FrsDsFormUPN(CrackedName, CrackedDomain);
  4225. FrsFree(CrackedName);
  4226. FrsFree(CrackedDomain);
  4227. return CrackedUPN;
  4228. } else {
  4229. return CrackedName;
  4230. }
  4231. }
  4232. PWCHAR
  4233. FrsDsGetName(
  4234. IN PWCHAR Dn,
  4235. IN HANDLE Handle,
  4236. IN PWCHAR DomainDnsName,
  4237. IN DWORD DesiredFormat
  4238. )
  4239. /*++
  4240. Routine Description:
  4241. Translate the Dn into the desired format. Dn should be the Dn
  4242. of a computer object.
  4243. Arguments:
  4244. Dn - Of computer object
  4245. Handle - From DsBind
  4246. DomainDnsName - If !NULL, produce new local handle
  4247. DesiredFormat - DS_NT4_ACCOUNT_NAME or DS_STRING_SID_NAME
  4248. Return Value:
  4249. Copy of name in desired format; free with FrsFree()
  4250. --*/
  4251. {
  4252. #undef DEBSUB
  4253. #define DEBSUB "FrsDsGetName:"
  4254. DWORD WStatus;
  4255. DS_NAME_RESULT *Cracked = NULL;
  4256. HANDLE LocalHandle = NULL;
  4257. PWCHAR CrackedName = NULL;
  4258. PWCHAR CrackedDomain = NULL;
  4259. PWCHAR CrackedUPN = NULL;
  4260. DWORD RequestedFormat = 0;
  4261. DPRINT2(4, ":DS: Get %08x Name for %ws\n", DesiredFormat, Dn);
  4262. //
  4263. // Computer's Dn not available
  4264. //
  4265. if (!Dn) {
  4266. return NULL;
  4267. }
  4268. //
  4269. // Need something to go on!
  4270. //
  4271. if (!HANDLE_IS_VALID(Handle) && !DomainDnsName) {
  4272. return NULL;
  4273. }
  4274. //
  4275. // Bind to Ds
  4276. //
  4277. if (DomainDnsName) {
  4278. DPRINT3(4, ":DS: Get %08x Name from %ws for %ws\n",
  4279. DesiredFormat, DomainDnsName, Dn);
  4280. WStatus = DsBind(NULL, DomainDnsName, &LocalHandle);
  4281. CLEANUP2_WS(0, ":DS: ERROR - DsBind(%ws, %08x);",
  4282. DomainDnsName, DesiredFormat, WStatus, RETURN);
  4283. Handle = LocalHandle;
  4284. }
  4285. //
  4286. // Crack the computer's distinguished name into its NT4 Account Name
  4287. //
  4288. // If the Desired format is DS_USER_PRINCIPAL_NAME then we form it by
  4289. // getting the name from DS_NT4_ACCOUNT_NAME and the dns domain name
  4290. // from the "Cracked->rItems->pDomain"
  4291. // We could ask for DS_USER_PRINCIPAL_NAME directly but we don't because.
  4292. // Object can have implicit or explicit UPNs. If the object has an explicit UPN,
  4293. // the DsCrackNames will work. If the object has an implicit UPN,
  4294. // then you need to build it.
  4295. //
  4296. if (DesiredFormat == DS_USER_PRINCIPAL_NAME) {
  4297. RequestedFormat = DS_NT4_ACCOUNT_NAME;
  4298. } else {
  4299. RequestedFormat = DesiredFormat;
  4300. }
  4301. WStatus = DsCrackNames(Handle, // in hDS,
  4302. DS_NAME_NO_FLAGS, // in flags,
  4303. DS_FQDN_1779_NAME, // in formatOffered,
  4304. RequestedFormat, // in formatDesired,
  4305. 1, // in cNames,
  4306. &Dn, // in *rpNames,
  4307. &Cracked); // out *ppResult
  4308. if (!WIN_SUCCESS(WStatus)) {
  4309. DPRINT2_WS(0, ":DS: ERROR - DsCrackNames(%ws, %08x);", Dn, DesiredFormat, WStatus);
  4310. //
  4311. // Set DsBindingsAreValid to FALSE if the handle has become invalid.
  4312. // That will force us to rebind at the next poll. The guess below might still
  4313. // work so continue processing.
  4314. //
  4315. if (WStatus == ERROR_INVALID_HANDLE) {
  4316. DPRINT1(4, ":DS: Marking binding to %ws as invalid.\n",
  4317. (DsDomainControllerName) ? DsDomainControllerName : L"<null>");
  4318. DsBindingsAreValid = FALSE;
  4319. }
  4320. //
  4321. // What else can we do?
  4322. //
  4323. if (HANDLE_IS_VALID(LocalHandle)) {
  4324. DsUnBind(&LocalHandle);
  4325. LocalHandle = NULL;
  4326. }
  4327. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  4328. return FrsDsGuessPrincName(Dn);
  4329. } else {
  4330. return NULL;
  4331. }
  4332. }
  4333. //
  4334. // Might have it
  4335. //
  4336. if (Cracked && Cracked->cItems && Cracked->rItems) {
  4337. //
  4338. // Got it!
  4339. //
  4340. if (Cracked->rItems->status == DS_NAME_NO_ERROR) {
  4341. DPRINT1(4, ":DS: Cracked Domain : %ws\n", Cracked->rItems->pDomain);
  4342. DPRINT2(4, ":DS: Cracked Name : %08x %ws\n",
  4343. DesiredFormat, Cracked->rItems->pName);
  4344. CrackedDomain = FrsWcsDup(Cracked->rItems->pDomain);
  4345. CrackedName = FrsWcsDup(Cracked->rItems->pName);
  4346. //
  4347. // Only got the domain; rebind and try again
  4348. //
  4349. } else
  4350. if (Cracked->rItems->status == DS_NAME_ERROR_DOMAIN_ONLY) {
  4351. CrackedName = FrsDsGetName(Dn, NULL, Cracked->rItems->pDomain, DesiredFormat);
  4352. } else {
  4353. DPRINT3(0, ":DS: ERROR - DsCrackNames(%ws, %08x); internal status %d\n",
  4354. Dn, DesiredFormat, Cracked->rItems->status);
  4355. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  4356. CrackedName = FrsDsGuessPrincName(Dn);
  4357. }
  4358. }
  4359. } else {
  4360. DPRINT2(0, ":DS: ERROR - DsCrackNames(%ws, %08x); no status\n",
  4361. Dn, DesiredFormat);
  4362. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  4363. CrackedName = FrsDsGuessPrincName(Dn);
  4364. }
  4365. }
  4366. if (Cracked) {
  4367. DsFreeNameResult(Cracked);
  4368. Cracked = NULL;
  4369. }
  4370. if (HANDLE_IS_VALID(LocalHandle)) {
  4371. DsUnBind(&LocalHandle);
  4372. LocalHandle = NULL;
  4373. }
  4374. RETURN:
  4375. if ((DesiredFormat == DS_USER_PRINCIPAL_NAME) && (CrackedName != NULL) && (CrackedDomain != NULL)) {
  4376. CrackedUPN = FrsDsFormUPN(CrackedName, CrackedDomain);
  4377. FrsFree(CrackedName);
  4378. FrsFree(CrackedDomain);
  4379. return CrackedUPN;
  4380. } else {
  4381. return CrackedName;
  4382. }
  4383. }
  4384. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  4385. VOID
  4386. FrsNewDsCreatePartnerPrincName(
  4387. IN PCONFIG_NODE Sites
  4388. )
  4389. /*++
  4390. Routine Description:
  4391. Construct the server principal names for our partners.
  4392. Must be called after FrsDsCheckAndCreatePartners()
  4393. Arguments:
  4394. Sites
  4395. Return Value:
  4396. None.
  4397. --*/
  4398. {
  4399. #undef DEBSUB
  4400. #define DEBSUB "FrsNewDsCreatePartnerPrincName:"
  4401. PCONFIG_NODE Cxtion;
  4402. PCONFIG_NODE Partner;
  4403. PCONFIG_NODE Site;
  4404. PCONFIG_NODE Settings;
  4405. PCONFIG_NODE Set;
  4406. PCONFIG_NODE Server;
  4407. PVOID Key;
  4408. //
  4409. // Get all the required information for every computer in the PartnerComputerTable.
  4410. //
  4411. Key = NULL;
  4412. while ((Partner = GTabNextDatum(PartnerComputerTable, &Key)) != NULL) {
  4413. //
  4414. // Get the Server Principal Name.
  4415. //
  4416. if ((Partner->PrincName == NULL) ||
  4417. (*Partner->PrincName == UNICODE_NULL)) {
  4418. Partner->PrincName = FrsDsGetName(Partner->Dn, DsHandle, NULL, DS_NT4_ACCOUNT_NAME);
  4419. if ((Partner->PrincName == NULL) ||
  4420. (*Partner->PrincName == UNICODE_NULL)) {
  4421. //
  4422. // Setting active change to 0 will cause this code to be
  4423. // repeated at the next ds polling cycle. We do this because
  4424. // the partner's principal name may appear later.
  4425. //
  4426. ActiveChange = 0;
  4427. Partner->Consistent = FALSE;
  4428. continue;
  4429. }
  4430. }
  4431. //
  4432. // Get the partners dnsHostName.
  4433. //
  4434. if (!Partner->DnsName) {
  4435. Partner->DnsName = FrsDsGetDnsName(gLdap, Partner->Dn);
  4436. }
  4437. //
  4438. // Get the partners SID.
  4439. //
  4440. if (!Partner->Sid) {
  4441. Partner->Sid = FrsDsGetName(Partner->Dn, DsHandle, NULL, DS_STRING_SID_NAME);
  4442. }
  4443. }
  4444. //
  4445. // For every cxtion in every replica set.
  4446. //
  4447. Key = NULL;
  4448. while((Cxtion = GTabNextDatum(AllCxtionsTable, &Key)) != NULL) {
  4449. //
  4450. // Ignore inconsistent cxtions
  4451. //
  4452. if (!Cxtion->Consistent) {
  4453. continue;
  4454. }
  4455. //
  4456. // Look for the Cxtion's partner using the PartnerCoDn.
  4457. //
  4458. //
  4459. // Mark this connection inconsistent if it lacks a PartnerCoDn.
  4460. //
  4461. if (Cxtion->PartnerCoDn == NULL) {
  4462. Cxtion->Consistent = FALSE;
  4463. continue;
  4464. }
  4465. Partner = GTabLookupTableString(PartnerComputerTable, Cxtion->PartnerCoDn, NULL);
  4466. //
  4467. // Inconsistent partner; continue
  4468. //
  4469. if (Partner == NULL || !Partner->Consistent) {
  4470. Cxtion->Consistent = FALSE;
  4471. continue;
  4472. }
  4473. //
  4474. // Get out partner's server principal name
  4475. //
  4476. if (!Cxtion->PrincName) {
  4477. Cxtion->PrincName = FrsWcsDup(Partner->PrincName);
  4478. }
  4479. //
  4480. // Get our partner's dns name
  4481. //
  4482. if (!Cxtion->PartnerDnsName) {
  4483. //
  4484. // The partner's DNS name is not critical; we can fall
  4485. // back on our partner's NetBios name.
  4486. //
  4487. if (Partner->DnsName) {
  4488. Cxtion->PartnerDnsName = FrsWcsDup(Partner->DnsName);
  4489. }
  4490. }
  4491. //
  4492. // Get our partner's Sid
  4493. //
  4494. if (!Cxtion->PartnerSid) {
  4495. //
  4496. // The partner's DNS name is not critical; we can fall
  4497. // back on our partner's NetBios name.
  4498. //
  4499. if (Partner->Sid) {
  4500. Cxtion->PartnerSid = FrsWcsDup(Partner->Sid);
  4501. }
  4502. }
  4503. } // cxtion scan
  4504. }
  4505. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  4506. VOID
  4507. FrsDsCreatePartnerPrincName(
  4508. IN PCONFIG_NODE Sites
  4509. )
  4510. /*++
  4511. Routine Description:
  4512. Construct the server principal names for our partners.
  4513. Must be called after FrsDsCheckAndCreatePartners()
  4514. Arguments:
  4515. Sites
  4516. Return Value:
  4517. None.
  4518. --*/
  4519. {
  4520. #undef DEBSUB
  4521. #define DEBSUB "FrsDsCreatePartnerPrincName:"
  4522. PCONFIG_NODE Cxtion;
  4523. PCONFIG_NODE Partner;
  4524. PCONFIG_NODE Site;
  4525. PCONFIG_NODE Settings;
  4526. PCONFIG_NODE Set;
  4527. PCONFIG_NODE Server;
  4528. //
  4529. // For every server on this machine
  4530. //
  4531. for (Site = Sites; Site; Site = Site->Peer) {
  4532. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  4533. for (Set = Settings->Children; Set; Set = Set->Peer) {
  4534. for (Server = Set->Children; Server; Server = Server->Peer) {
  4535. if (!Server->ThisComputer) {
  4536. continue;
  4537. }
  4538. //
  4539. // Ignore inconsistent servers
  4540. //
  4541. if (!Server->Consistent) {
  4542. continue;
  4543. }
  4544. //
  4545. // For every cxtion
  4546. //
  4547. for (Cxtion = Server->Children; Cxtion; Cxtion = Cxtion->Peer) {
  4548. //
  4549. // Ignore inconsistent cxtions
  4550. //
  4551. if (!Cxtion->Consistent) {
  4552. continue;
  4553. }
  4554. //
  4555. // Cxtion's partner
  4556. //
  4557. Partner = Cxtion->Partner;
  4558. //
  4559. // Inconsistent partner; continue
  4560. //
  4561. if (!Partner->Consistent) {
  4562. Cxtion->Consistent = FALSE;
  4563. continue;
  4564. }
  4565. //
  4566. // Get our partner's server principal name
  4567. //
  4568. if (!Cxtion->PrincName) {
  4569. if ((Partner->PrincName == NULL) ||
  4570. (*Partner->PrincName == UNICODE_NULL)) {
  4571. Partner->PrincName =
  4572. FrsDsGetName(Partner->ComputerDn, DsHandle, NULL, DS_NT4_ACCOUNT_NAME);
  4573. if ((Partner->PrincName == NULL) ||
  4574. (*Partner->PrincName == UNICODE_NULL)) {
  4575. //
  4576. // Setting active change to 0 will cause this code to be
  4577. // repeated at the next ds polling cycle. We do this
  4578. // because the partner's principal name may appear
  4579. // later.
  4580. //
  4581. ActiveChange = 0;
  4582. Cxtion->Consistent = FALSE;
  4583. Partner->Consistent = FALSE;
  4584. continue;
  4585. }
  4586. }
  4587. Cxtion->PrincName = FrsWcsDup(Partner->PrincName);
  4588. }
  4589. //
  4590. // Get our partner's dns name
  4591. //
  4592. if (!Cxtion->PartnerDnsName) {
  4593. if (!Partner->DnsName) {
  4594. Partner->DnsName = FrsDsGetDnsName(gLdap, Partner->ComputerDn);
  4595. }
  4596. //
  4597. // The partner's DNS name is not critical; we can fall
  4598. // back on our partner's NetBios name.
  4599. //
  4600. if (Partner->DnsName) {
  4601. Cxtion->PartnerDnsName = FrsWcsDup(Partner->DnsName);
  4602. }
  4603. }
  4604. //
  4605. // Get our partner's Sid
  4606. //
  4607. if (!Cxtion->PartnerSid) {
  4608. if (!Partner->Sid) {
  4609. Partner->Sid =
  4610. FrsDsGetName(Partner->ComputerDn, DsHandle, NULL, DS_STRING_SID_NAME);
  4611. }
  4612. //
  4613. // The partner's DNS name is not critical; we can fall
  4614. // back on our partner's NetBios name.
  4615. //
  4616. if (Partner->Sid) {
  4617. Cxtion->PartnerSid = FrsWcsDup(Partner->Sid);
  4618. }
  4619. }
  4620. //
  4621. // Get our partner's computer name
  4622. //
  4623. if (!Cxtion->PartnerCoDn) {
  4624. if (Partner->ComputerDn) {
  4625. Cxtion->PartnerCoDn = FrsWcsDup(Partner->ComputerDn);
  4626. }
  4627. }
  4628. } // cxtion scan
  4629. } } } } // nested for's
  4630. }
  4631. VOID
  4632. FrsDsCheckAndCreatePartners(
  4633. IN PCONFIG_NODE Sites
  4634. )
  4635. /*++
  4636. Routine Description:
  4637. Construct the outbound partners from the inbound partners.
  4638. For every inbound cxtion, find the inbound partner's server node
  4639. and attach a dummy outbound cxtion that points back at this server.
  4640. Arguments:
  4641. Sites
  4642. Return Value:
  4643. None.
  4644. --*/
  4645. {
  4646. #undef DEBSUB
  4647. #define DEBSUB "FrsDsCheckAndCreatePartners:"
  4648. PCONFIG_NODE InCxtion; // Scan the inbound connections
  4649. PCONFIG_NODE OutCxtion;
  4650. PCONFIG_NODE Site;
  4651. PCONFIG_NODE Settings;
  4652. PCONFIG_NODE Set;
  4653. PCONFIG_NODE Server;
  4654. PCONFIG_NODE OutServer; // My inbound partner
  4655. PGEN_TABLE Servers; // memberNAME\SettingsGUID
  4656. //
  4657. // Table of servers needed for fast lookups
  4658. //
  4659. Servers = GTabAllocTable();
  4660. for (Site = Sites; Site; Site = Site->Peer) {
  4661. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  4662. for (Set = Settings->Children; Set; Set = Set->Peer) {
  4663. for (Server = Set->Children; Server; Server = Server->Peer) {
  4664. GTabInsertEntry(Servers, Server, Set->Name->Guid, Server->Name->Name);
  4665. }
  4666. }
  4667. }
  4668. }
  4669. //
  4670. // For every server
  4671. //
  4672. for (Site = Sites; Site; Site = Site->Peer) {
  4673. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  4674. for (Set = Settings->Children; Set; Set = Set->Peer) {
  4675. for (Server = Set->Children; Server; Server = Server->Peer) {
  4676. //
  4677. // For every inbound connection
  4678. //
  4679. for (InCxtion = Server->Children;
  4680. InCxtion;
  4681. InCxtion = InCxtion->Peer) {
  4682. if (!InCxtion->Inbound) {
  4683. continue;
  4684. }
  4685. //
  4686. // No partner; continue
  4687. //
  4688. if (!InCxtion->PartnerDn ||
  4689. !InCxtion->PartnerName ||
  4690. !InCxtion->PartnerName->Name) {
  4691. InCxtion->Consistent = FALSE;
  4692. continue;
  4693. }
  4694. //
  4695. // Find the server node of our inbound partner
  4696. //
  4697. OutServer = GTabLookup(Servers,
  4698. Set->Name->Guid,
  4699. InCxtion->PartnerName->Name);
  4700. //
  4701. // Partner doesn't exist in this set
  4702. //
  4703. if (OutServer == NULL) {
  4704. InCxtion->Consistent = FALSE;
  4705. continue;
  4706. }
  4707. //
  4708. // We need our partner's guid
  4709. //
  4710. InCxtion->Partner = OutServer;
  4711. FrsFree(InCxtion->PartnerName->Guid);
  4712. InCxtion->PartnerName->Guid = FrsDupGuid(OutServer->Name->Guid);
  4713. //
  4714. // Don't construct outbound cxtions for other machines
  4715. //
  4716. if (!OutServer->ThisComputer) {
  4717. continue;
  4718. }
  4719. //
  4720. // Dummy up an outbound cxtion and put it on our inbound
  4721. // partner's list of outbound cxtions.
  4722. //
  4723. OutCxtion = FrsAllocType(CONFIG_NODE_TYPE);
  4724. OutCxtion->DsObjectType = CONFIG_TYPE_OUT_CXTION;
  4725. OutCxtion->Name = FrsDupGName(InCxtion->Name);
  4726. OutCxtion->Partner = Server;
  4727. OutCxtion->Inbound = FALSE;
  4728. OutCxtion->SameSite = InCxtion->SameSite;
  4729. OutCxtion->Consistent = TRUE;
  4730. OutCxtion->PartnerName = FrsDupGName(Server->Name);
  4731. if (InCxtion->Schedule) {
  4732. OutCxtion->ScheduleLength = InCxtion->ScheduleLength;
  4733. OutCxtion->Schedule = FrsAlloc(InCxtion->ScheduleLength);
  4734. CopyMemory(OutCxtion->Schedule, InCxtion->Schedule, InCxtion->ScheduleLength);
  4735. }
  4736. ++OutServer->NumChildren;
  4737. OutCxtion->Parent = OutServer;
  4738. OutCxtion->Peer = OutServer->Children;
  4739. OutServer->Children = OutCxtion;
  4740. }
  4741. }
  4742. }
  4743. }
  4744. }
  4745. Servers = GTabFreeTable(Servers, NULL);
  4746. }
  4747. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  4748. BOOL
  4749. FrsNewDsDoesUserWantReplication(
  4750. IN PCONFIG_NODE Computer
  4751. )
  4752. /*++
  4753. Routine Description:
  4754. Does the topology imply that the user wants this server to replicate?
  4755. Arguments
  4756. Computer
  4757. Return Value:
  4758. TRUE - server may be replicating
  4759. FALSE - server is not replicating
  4760. --*/
  4761. {
  4762. #undef DEBSUB
  4763. #define DEBSUB "FrsNewDsDoesUserWantReplication:"
  4764. DWORD WStatus;
  4765. PCONFIG_NODE Subscriptions;
  4766. PCONFIG_NODE Subscriber;
  4767. //
  4768. // Ds polling thread is shutting down
  4769. //
  4770. if (DsIsShuttingDown) {
  4771. DPRINT(0, ":DS: Ds polling thread is shutting down\n");
  4772. return FALSE;
  4773. }
  4774. //
  4775. // Can't find our computer; something is wrong. Don't start
  4776. //
  4777. if (!Computer) {
  4778. DPRINT(0, ":DS: no computer\n");
  4779. return FALSE;
  4780. } else {
  4781. DPRINT(4, ":DS: have a computer\n");
  4782. }
  4783. //
  4784. // We need to process the topology further if there is at least
  4785. // 1 valid subscriber.
  4786. //
  4787. if (SubscriberTable != NULL) {
  4788. return TRUE;
  4789. }
  4790. //
  4791. // Database exists; once was a member of a replica set
  4792. //
  4793. WStatus = FrsDoesFileExist(JetFile);
  4794. if (WIN_SUCCESS(WStatus)) {
  4795. DPRINT(4, ":DS: database exists\n");
  4796. return TRUE;
  4797. } else {
  4798. DPRINT(4, ":DS: database does not exists\n");
  4799. }
  4800. DPRINT1(4, ":DS: Not starting on %ws; nothing to do\n", ComputerName);
  4801. return FALSE;
  4802. }
  4803. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  4804. BOOL
  4805. FrsDsDoesUserWantReplication(
  4806. IN PCONFIG_NODE Computer
  4807. )
  4808. /*++
  4809. Routine Description:
  4810. Does the topology imply that the user wants this server to replicate?
  4811. Arguments
  4812. Computer
  4813. Return Value:
  4814. TRUE - server may be replicating
  4815. FALSE - server is not replicating
  4816. --*/
  4817. {
  4818. #undef DEBSUB
  4819. #define DEBSUB "FrsDsDoesUserWantReplication:"
  4820. DWORD WStatus;
  4821. PCONFIG_NODE Subscriptions;
  4822. PCONFIG_NODE Subscriber;
  4823. //
  4824. // Ds polling thread is shutting down
  4825. //
  4826. if (DsIsShuttingDown) {
  4827. DPRINT(4, ":DS: Ds polling thread is shutting down\n");
  4828. return FALSE;
  4829. }
  4830. //
  4831. // Can't find our computer; something is wrong. Don't start
  4832. //
  4833. if (!Computer) {
  4834. DPRINT(0, ":DS: no computer\n");
  4835. return FALSE;
  4836. } else {
  4837. DPRINT(4, ":DS: have a computer\n");
  4838. }
  4839. //
  4840. // Member of a replica set
  4841. //
  4842. Subscriber = NULL;
  4843. for (Subscriptions = Computer->Children;
  4844. Subscriptions;
  4845. Subscriptions = Subscriptions->Peer) {
  4846. for (Subscriber = Subscriptions->Children;
  4847. Subscriber;
  4848. Subscriber = Subscriber->Peer) {
  4849. if (Subscriber->MemberDn) {
  4850. break;
  4851. }
  4852. }
  4853. if (Subscriber) {
  4854. break;
  4855. }
  4856. }
  4857. if (Subscriber) {
  4858. DPRINT(4, ":DS: has a valid subscriber object\n");
  4859. return TRUE;
  4860. } else {
  4861. DPRINT(0, ":DS: does not have a valid subscriber object\n");
  4862. }
  4863. //
  4864. // Database exists implies once was a member of a replica set.
  4865. //
  4866. WStatus = FrsDoesFileExist(JetFile);
  4867. CLEANUP_WS(4, ":DS: database does not exists. ", WStatus, ERROR_RETURN);
  4868. DPRINT(4, ":DS: database exists\n");
  4869. return TRUE;
  4870. ERROR_RETURN:
  4871. DPRINT1(0, ":DS: Not starting on %ws; nothing to do\n", ComputerName);
  4872. return FALSE;
  4873. }
  4874. VOID
  4875. FrsDsCheckTree(
  4876. IN PCONFIG_NODE Sites
  4877. )
  4878. /*++
  4879. Routine Description:
  4880. Scan our copy of the DS tree and check for populated
  4881. sites and settings. Also check for duplicate nodes.
  4882. Arguments:
  4883. Sites
  4884. Return Value:
  4885. None.
  4886. --*/
  4887. {
  4888. #undef DEBSUB
  4889. #define DEBSUB "FrsDsCheckTree:"
  4890. PCONFIG_NODE Site; // Scan the sites
  4891. PCONFIG_NODE Settings; // Scan the settings
  4892. PCONFIG_NODE Set; // Scan the sets
  4893. PCONFIG_NODE Server; // Scan the servers
  4894. PCONFIG_NODE Cxtion; // Scan the inbound cxtions
  4895. PCONFIG_NODE Node; // node in the config tree
  4896. PGEN_TABLE Nodes; // table of nodes (Created)
  4897. PGEN_ENTRY Entry; // entry from table nodes
  4898. PGEN_ENTRY Dup; // duplicate entry from table nodes
  4899. PVOID Key; // needed for table search
  4900. //
  4901. // No sites
  4902. //
  4903. if (Sites == NULL) {
  4904. DPRINT(0, ":DS: There are no sites in the DS\n");
  4905. return;
  4906. }
  4907. //
  4908. // For every site
  4909. //
  4910. for (Site = Sites; Site; Site = Site->Peer) {
  4911. //
  4912. // No Settings
  4913. //
  4914. if (Site->Children == NULL) {
  4915. Site->Consistent = FALSE;
  4916. DPRINT1(0, ":DS: No settings in site %ws\n", Site->Name->Name);
  4917. }
  4918. //
  4919. // For every settings
  4920. //
  4921. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  4922. //
  4923. // No sets; don't try to create empty replica set
  4924. //
  4925. if (Settings->Children == NULL) {
  4926. Settings->Consistent = FALSE;
  4927. if (Settings->Name)
  4928. DPRINT1(0, ":DS: No replica sets in settings %ws\n", Settings->Name->Name);
  4929. }
  4930. //
  4931. // For every replica set
  4932. //
  4933. for (Set = Settings->Children; Set; Set = Set->Peer) {
  4934. //
  4935. // No servers; don't try to create empty replica set
  4936. //
  4937. if (Set->Children == NULL) {
  4938. Set->Consistent = FALSE;
  4939. DPRINT1(0, ":DS: No servers in replica set %ws\n", Set->Name->Name);
  4940. }
  4941. }
  4942. }
  4943. }
  4944. //
  4945. // Generate a table of node guids
  4946. //
  4947. Nodes = GTabAllocTable();
  4948. for (Site = Sites; Site; Site = Site->Peer) {
  4949. GTabInsertEntry(Nodes, Site, Site->Name->Guid, NULL);
  4950. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  4951. if (Settings->Name) {
  4952. GTabInsertEntry(Nodes, Settings, Settings->Name->Guid, NULL);
  4953. }
  4954. for (Set = Settings->Children; Set; Set = Set->Peer) {
  4955. GTabInsertEntry(Nodes, Set, Set->Name->Guid, NULL);
  4956. for (Server = Set->Children; Server; Server = Server->Peer) {
  4957. GTabInsertEntry(Nodes, Server, Server->Name->Guid, NULL);
  4958. for (Cxtion = Server->Children; Cxtion; Cxtion = Cxtion->Peer) {
  4959. //
  4960. // Inbound and outbound cxtions share a guid
  4961. //
  4962. if (!Cxtion->Inbound) {
  4963. continue;
  4964. }
  4965. //
  4966. // Domain and enterprise cxtions share guids
  4967. //
  4968. if (Set->SetType &&
  4969. WSTR_EQ(Set->SetType, FRS_RSTYPE_DOMAIN_SYSVOLW)) {
  4970. continue;
  4971. }
  4972. GTabInsertEntry(Nodes, Cxtion, Cxtion->Name->Guid, NULL);
  4973. }
  4974. }
  4975. }
  4976. }
  4977. }
  4978. //
  4979. // Check for duplicate nodes
  4980. //
  4981. Key = NULL;
  4982. while (Entry = GTabNextEntryNoLock(Nodes, &Key)) {
  4983. if (Entry->Dups) {
  4984. Node = Entry->Data;
  4985. Node->Consistent = FALSE;
  4986. DPRINT1(0, ":DS: The node %ws is in the configuration more than once\n",
  4987. Node->Name->Name);
  4988. for (Dup = Entry->Dups; Dup; Dup = Dup->Dups) {
  4989. Node = Dup->Data;
  4990. Node->Consistent = FALSE;
  4991. DPRINT1(0, ":DS: The node %ws is in the configuration more than once\n",
  4992. Node->Name->Name);
  4993. }
  4994. }
  4995. }
  4996. Nodes = GTabFreeTable(Nodes, NULL);
  4997. }
  4998. VOID
  4999. FrsDsFixSysVolCxtions(
  5000. IN PCONFIG_NODE Set,
  5001. IN PCONFIG_NODE Server
  5002. )
  5003. /*++
  5004. Routine Description:
  5005. The sysvol cxtions point to servers. Fix them to point to
  5006. the member object instead.
  5007. Additional Details:
  5008. To make DC replication admin easier FRS uses the same topology information
  5009. for domain sysvol replication that DS replication uses.
  5010. The layout of the DS replication topology is a bit different.
  5011. It lives in the Configuration container - aka Configuration Naming Context
  5012. CN = Configuration
  5013. CN = Sites
  5014. CN = Site xxx-1 Container
  5015. CN = Servers
  5016. CN = Server yyy-1 Object
  5017. CN = Ntds Settings Object (NTDS-DSA Object)
  5018. CN = Connection zzz-1 Object
  5019. CN = Connection zzz-2 Object
  5020. CN = Server yyy-2 Object
  5021. CN = Ntds Settings Object
  5022. CN = Connection zzz-3 Object
  5023. CN = Connection zzz-4 Object
  5024. CN = Site xxx-2 Container
  5025. CN = Servers
  5026. CN = Server yyy-3 Object
  5027. CN = Ntds Settings Object
  5028. CN = Connection zzz-5 Object
  5029. CN = Server yyy-4 Object
  5030. CN = Ntds Settings Object
  5031. CN = Connection zzz-6 Object
  5032. Each Connection object has a From-Server attribute that contains the DN
  5033. that refers to the Ntds Settings Object under the server object corresponding
  5034. to the source end of the connection.
  5035. The layout of the FRS replication topology for a given replica set is
  5036. contained in an Ntfrs Replica Set object. This object can be placed anywhere
  5037. in the DS and may have an Ntfrs Settings object as a superior.
  5038. For system volumes this lives within the Domain Naming Context.
  5039. CN = System
  5040. CN = File Replication Service (Ntfrs Settings Object)
  5041. CN = Domain System Volume (sysvol share) (Ntfrs Replica Set Object)
  5042. CN = Ntfrs Member yyy-1 Object
  5043. Attr: Server-Reference (yyy-1\NTDS-DSA)
  5044. Attr: FRS-Computer-Reference (yyy-1)
  5045. CN = Ntfrs Member yyy-2 Object
  5046. Attr: Server-Reference (yyy-2\NTDS-DSA)
  5047. Attr: FRS-Computer-Reference (yyy-2)
  5048. CN = Ntfrs Member yyy-3 Object
  5049. Attr: Server-Reference (yyy-2\NTDS-DSA)
  5050. Attr: FRS-Computer-Reference (yyy-2)
  5051. In a normal (non SysVol) FRS configuration there would be one or more
  5052. connection objects under each Ntfrs Member object above. But in the case
  5053. of the system volumes the Server-Reference attribute in the Ntfrs member
  5054. object is the FQDN of the Ntds Settings Object under which we will find
  5055. the Connection objects that are specified for DS replication.
  5056. The purpose of the code below is to figure out which Ntds Settings Object
  5057. is being referenced by the From-Server attributes in each of the Connection
  5058. Objects. Then we map that reference to the corresponding Ntfrs Member Object
  5059. in the data structure.
  5060. Arguments:
  5061. Set
  5062. Server
  5063. Return Value:
  5064. None.
  5065. --*/
  5066. {
  5067. #undef DEBSUB
  5068. #define DEBSUB "FixSysVolCxtions:"
  5069. PCONFIG_NODE Cxtion;
  5070. PCONFIG_NODE PServer;
  5071. //
  5072. // For every sysvol cxtion, find its corresponding member
  5073. //
  5074. DPRINT2(5, ":DS: Fixing sysvol cxtions (%d) for %ws\n",
  5075. Server->NumChildren, Server->Name->Name);
  5076. for (Cxtion = Server->Children; Cxtion; Cxtion = Cxtion->Peer) {
  5077. if (!Cxtion->PartnerDn || !Cxtion->PartnerName->Name) {
  5078. DPRINT1(1, ":DS: WARN - Skipping %ws; no partner\n",
  5079. Cxtion->Name->Name);
  5080. continue;
  5081. }
  5082. for (PServer = Set->Children; PServer; PServer = PServer->Peer) {
  5083. //
  5084. // No server reference (DSA reference); ignore
  5085. //
  5086. if (!PServer->SettingsDn) {
  5087. DPRINT1(5, ":DS: No settings dn for %ws\n", PServer->Name->Name);
  5088. continue;
  5089. }
  5090. //
  5091. // We don't allow cxtions back to ourselves
  5092. //
  5093. if (PServer == Server) {
  5094. DPRINT1(5, ":DS: Same server (%ws)\n", Server->Name->Name);
  5095. continue;
  5096. }
  5097. if (WSTR_EQ(Cxtion->PartnerDn, PServer->SettingsDn)) {
  5098. DPRINT2(4, ":DS: Old partner %ws for %ws\n",
  5099. Cxtion->PartnerName->Name, Cxtion->Name->Name);
  5100. FrsFree(Cxtion->PartnerDn);
  5101. Cxtion->PartnerDn = FrsWcsDup(PServer->Dn);
  5102. FrsFree(Cxtion->PartnerName->Name);
  5103. Cxtion->PartnerName->Name = FrsDsMakeRdn(Cxtion->PartnerDn);
  5104. DPRINT2(4, ":DS: New partner %ws for %ws\n",
  5105. Cxtion->PartnerName->Name, Cxtion->Name->Name);
  5106. break;
  5107. }
  5108. }
  5109. }
  5110. }
  5111. DWORD
  5112. FrsDsGetSysVolCxtions(
  5113. IN PLDAP Ldap,
  5114. IN PWCHAR SitesDn,
  5115. IN PCONFIG_NODE Sites
  5116. )
  5117. /*++
  5118. Routine Description:
  5119. If we are a member of an enterprise/domain sysvol then find our
  5120. server object and extract the relavent cxtions.
  5121. Arguments:
  5122. Ldap
  5123. SitesDn
  5124. Sites
  5125. Return Value:
  5126. WIN32 Status
  5127. --*/
  5128. {
  5129. #undef DEBSUB
  5130. #define DEBSUB "FrsDsGetSysVolCxtions:"
  5131. PCONFIG_NODE Site;
  5132. PCONFIG_NODE Settings;
  5133. PCONFIG_NODE Set;
  5134. PCONFIG_NODE Server;
  5135. DWORD WStatus;
  5136. //
  5137. // PULL OVER ALL CXTIONS INTO EACH SYSVOL SET
  5138. //
  5139. //
  5140. // Find our member of the enterprise volume
  5141. //
  5142. for (Site = Sites; Site; Site = Site->Peer) {
  5143. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  5144. //
  5145. // Continue the search for the sysvol settings
  5146. //
  5147. if (WSTR_NE(Settings->Name->Name, CN_SYSVOLS) &&
  5148. WSTR_NE(Settings->Name->Name, CN_NTFRS_SETTINGS)) {
  5149. continue;
  5150. }
  5151. for (Set = Settings->Children; Set; Set = Set->Peer) {
  5152. //
  5153. // Ignore replicas that aren't sysvols
  5154. //
  5155. if (!FRS_RSTYPE_IS_SYSVOLW(Set->SetType)) {
  5156. continue;
  5157. }
  5158. for (Server = Set->Children; Server; Server = Server->Peer) {
  5159. //
  5160. // Must have a NTDS Settings reference
  5161. //
  5162. if (!Server->SettingsDn) {
  5163. continue;
  5164. }
  5165. //
  5166. // pull over all of the cxtions
  5167. //
  5168. WStatus = FrsDsGetCxtions(Ldap, Server->SettingsDn, Server, TRUE);
  5169. FrsDsFixSysVolCxtions(Set, Server);
  5170. if (!WIN_SUCCESS(WStatus)) {
  5171. return WStatus;
  5172. }
  5173. }
  5174. }
  5175. }
  5176. }
  5177. return ERROR_SUCCESS;
  5178. }
  5179. BOOL
  5180. FrsDsVerifyPath(
  5181. IN PWCHAR Path
  5182. )
  5183. /*++
  5184. Routine Description:
  5185. Verify the path syntax.
  5186. Arguments:
  5187. Path - Syntax is *<Drive Letter>:\*
  5188. Return Value:
  5189. None.
  5190. --*/
  5191. {
  5192. #undef DEBSUB
  5193. #define DEBSUB "FrsDsVerifyPath:"
  5194. PWCHAR Colon;
  5195. //
  5196. // Null path is obviously invalid
  5197. //
  5198. if (!Path) {
  5199. return FALSE;
  5200. }
  5201. //
  5202. // Find the :
  5203. //
  5204. for (Colon = Path; (*Colon != L':') && *Colon; ++Colon);
  5205. //
  5206. // No :
  5207. //
  5208. if (!*Colon) {
  5209. return FALSE;
  5210. }
  5211. //
  5212. // No drive letter
  5213. //
  5214. if (Colon == Path) {
  5215. return FALSE;
  5216. }
  5217. //
  5218. // No :\
  5219. //
  5220. if (*(Colon + 1) != L'\\') {
  5221. return FALSE;
  5222. }
  5223. //
  5224. // Path exists and is valid
  5225. //
  5226. return TRUE;
  5227. }
  5228. VOID
  5229. FrsDsCheckServerPaths(
  5230. IN PCONFIG_NODE Sites
  5231. )
  5232. /*++
  5233. Routine Description:
  5234. Look for nested paths and invalid path syntax.
  5235. Correct syntax is "*<drive letter>:\*".
  5236. Arguments:
  5237. Sites
  5238. Return Value:
  5239. None.
  5240. --*/
  5241. {
  5242. #undef DEBSUB
  5243. #define DEBSUB "FrsDsCheckServerPaths:"
  5244. DWORD WStatus;
  5245. PCONFIG_NODE Site;
  5246. PCONFIG_NODE Settings;
  5247. PCONFIG_NODE Set;
  5248. PCONFIG_NODE Server;
  5249. PCONFIG_NODE NSite;
  5250. PCONFIG_NODE NSettings;
  5251. PCONFIG_NODE NSet;
  5252. PCONFIG_NODE NServer;
  5253. DWORD FileAttributes = 0xFFFFFFFF;
  5254. for (Site = Sites; Site; Site = Site->Peer) {
  5255. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  5256. for (Set = Settings->Children; Set; Set = Set->Peer) {
  5257. for (Server = Set->Children; Server; Server = Server->Peer) {
  5258. //
  5259. // Not this computer; continue
  5260. //
  5261. if (!Server->ThisComputer) {
  5262. continue;
  5263. }
  5264. //
  5265. // Mark this server as processed. This forces the inner loop
  5266. // to skip this server node so that we don't end up comparing
  5267. // this node against itself. Also, this forces this node
  5268. // to be skipped in the inner loop to avoid unnecessary checks.
  5269. //
  5270. // In other words, set this field here, not later in the loop
  5271. // or in any other function.
  5272. //
  5273. Server->VerifiedOverlap = TRUE;
  5274. //
  5275. // Server is very inconsistent, ignore
  5276. //
  5277. if (!Server->Root || !Server->Stage) {
  5278. Server->Consistent = FALSE;
  5279. continue;
  5280. }
  5281. //
  5282. // Syntax of root path is invalid; continue
  5283. //
  5284. if (!FrsDsVerifyPath(Server->Root)) {
  5285. DPRINT2(3, ":DS: Invalid root %ws for %ws\n",
  5286. Server->Root, Set->Name->Name);
  5287. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Server->Root);
  5288. Server->Consistent = FALSE;
  5289. continue;
  5290. }
  5291. //
  5292. // Root does not exist or is inaccessable; continue
  5293. //
  5294. WStatus = FrsDoesDirectoryExist(Server->Root, &FileAttributes);
  5295. if (!WIN_SUCCESS(WStatus)) {
  5296. DPRINT2_WS(3, ":DS: Root path (%ws) for %ws does not exist;",
  5297. Server->Root, Set->Name->Name, WStatus);
  5298. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Server->Root);
  5299. Server->Consistent = FALSE;
  5300. continue;
  5301. }
  5302. //
  5303. // Does the volume exist and is it NTFS?
  5304. //
  5305. WStatus = FrsVerifyVolume(Server->Root,
  5306. Set->Name->Name,
  5307. FILE_PERSISTENT_ACLS | FILE_SUPPORTS_OBJECT_IDS);
  5308. if (!WIN_SUCCESS(WStatus)) {
  5309. DPRINT2_WS(3, ":DS: Root path Volume (%ws) for %ws does not exist or"
  5310. " does not support ACLs and Object IDs;",
  5311. Server->Root, Set->Name->Name, WStatus);
  5312. Server->Consistent = FALSE;
  5313. continue;
  5314. }
  5315. //
  5316. // Syntax of staging path is invalid; continue
  5317. //
  5318. if (!FrsDsVerifyPath(Server->Stage)) {
  5319. DPRINT2(3, ":DS: Invalid stage %ws for %ws\n",
  5320. Server->Stage, Set->Name->Name);
  5321. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Server->Root, Server->Stage);
  5322. Server->Consistent = FALSE;
  5323. continue;
  5324. }
  5325. //
  5326. // Stage does not exist or is inaccessable; continue
  5327. //
  5328. WStatus = FrsDoesDirectoryExist(Server->Stage, &FileAttributes);
  5329. if (!WIN_SUCCESS(WStatus)) {
  5330. DPRINT2_WS(3, ":DS: Stage path (%ws) for %ws does not exist;",
  5331. Server->Stage, Set->Name->Name, WStatus);
  5332. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Server->Root, Server->Stage);
  5333. Server->Consistent = FALSE;
  5334. continue;
  5335. }
  5336. //
  5337. // Does the staging volume exist and does it support ACLs?
  5338. // ACLs are required to protect against data theft/corruption
  5339. // in the staging dir.
  5340. //
  5341. WStatus = FrsVerifyVolume(Server->Stage,
  5342. Set->Name->Name,
  5343. FILE_PERSISTENT_ACLS);
  5344. if (!WIN_SUCCESS(WStatus)) {
  5345. DPRINT2_WS(3, ":DS: Stage path Volume (%ws) for %ws does not exist or does not support ACLs;",
  5346. Server->Stage, Set->Name->Name, WStatus);
  5347. Server->Consistent = FALSE;
  5348. continue;
  5349. }
  5350. //
  5351. // End of outer loop
  5352. //
  5353. } } } }
  5354. }
  5355. VOID
  5356. FrsDsCheckSetType(
  5357. IN PCONFIG_NODE Sites
  5358. )
  5359. /*++
  5360. Routine Description:
  5361. Check the replica sets' type.
  5362. Arguments:
  5363. Sites
  5364. Return Value:
  5365. None.
  5366. --*/
  5367. {
  5368. #undef DEBSUB
  5369. #define DEBSUB "FrsDsCheckSetType:"
  5370. PCONFIG_NODE Site;
  5371. PCONFIG_NODE Settings;
  5372. PCONFIG_NODE Set;
  5373. PCONFIG_NODE Server;
  5374. PCONFIG_NODE NSite;
  5375. PCONFIG_NODE NSettings;
  5376. PCONFIG_NODE NSet;
  5377. PCONFIG_NODE NServer;
  5378. //
  5379. // Check the type of the replica sets
  5380. //
  5381. for (Site = Sites; Site; Site = Site->Peer) {
  5382. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  5383. for (Set = Settings->Children; Set; Set = Set->Peer) {
  5384. if (!Set->SetType) {
  5385. continue;
  5386. } else if (WSTR_NE(Set->SetType, FRS_RSTYPE_OTHERW) &&
  5387. WSTR_NE(Set->SetType, FRS_RSTYPE_DFSW) &&
  5388. WSTR_NE(Set->SetType, FRS_RSTYPE_DOMAIN_SYSVOLW) &&
  5389. WSTR_NE(Set->SetType, FRS_RSTYPE_ENTERPRISE_SYSVOLW)) {
  5390. DPRINT2(1, ":DS: %ws: Unknown replica set type %ws\n",
  5391. Set->Name->Name, Set->SetType);
  5392. Set->Consistent = FALSE;
  5393. }
  5394. }
  5395. }
  5396. }
  5397. }
  5398. VOID
  5399. FrsDsCheckCxtions(
  5400. IN PCONFIG_NODE Server
  5401. )
  5402. /*++
  5403. Routine Description:
  5404. Check the connections for correctness.
  5405. Arguments:
  5406. Server
  5407. Return Value:
  5408. None.
  5409. --*/
  5410. {
  5411. #undef DEBSUB
  5412. #define DEBSUB "FrsDsCheckCxtions:"
  5413. PVOID Key; // Needed for scanning the table
  5414. ULONG NumIns;
  5415. ULONG NumOuts;
  5416. PGEN_ENTRY Entry; // table entry
  5417. PGEN_ENTRY Dup; // table entry
  5418. PCONFIG_NODE Cxtion; // Address of cxtion
  5419. PGEN_TABLE InDupCxtions; // table of cxtions
  5420. PGEN_TABLE OutDupCxtions; // table of cxtions
  5421. PCONFIG_NODE Cxtions = Server->Children;
  5422. //
  5423. // For every cxtion
  5424. //
  5425. NumIns = NumOuts = 0;
  5426. for (Cxtion = Cxtions; Cxtion; Cxtion = Cxtion->Peer) {
  5427. if (Cxtion->Inbound) {
  5428. ++NumIns;
  5429. } else {
  5430. ++NumOuts;
  5431. }
  5432. //
  5433. // cxtion doesn't have the partner's name; stop checks
  5434. //
  5435. if (Cxtion->Partner == NULL) {
  5436. Cxtion->Consistent = FALSE;
  5437. DPRINT4(0, ":DS: Connection %ws (%ws,%ws) has no %ws server\n",
  5438. Cxtion->Name->Name, Server->Name->Name,
  5439. Server->Parent->Name->Name,
  5440. (Cxtion->Inbound) ? L"inbound" : L"outbound" );
  5441. continue;
  5442. }
  5443. }
  5444. //
  5445. // No inbound
  5446. //
  5447. if (NumIns == 0) {
  5448. DPRINT2(0, ":DS: Server %ws in %ws has no inbound connections\n",
  5449. Server->Name->Name, Server->Parent->Name->Name);
  5450. //
  5451. // One inbound
  5452. //
  5453. } else if (NumIns == 1) {
  5454. DPRINT2(4, ":DS: Server %ws in %ws has only one inbound connection\n",
  5455. Cxtions->Parent->Name->Name, Cxtions->Parent->Parent->Name->Name);
  5456. }
  5457. //
  5458. // No Outbound
  5459. //
  5460. if (NumOuts == 0) {
  5461. DPRINT2(0, ":DS: Server %ws in %ws has no outbound connections\n",
  5462. Server->Name->Name, Server->Parent->Name->Name);
  5463. //
  5464. // One Outbound
  5465. //
  5466. } else if (NumOuts == 1) {
  5467. DPRINT2(4, ":DS: Server %ws in %ws has only one outbound connection\n",
  5468. Cxtions->Parent->Name->Name, Cxtions->Parent->Parent->Name->Name);
  5469. }
  5470. //
  5471. // Populate a table of the cxtions
  5472. //
  5473. InDupCxtions = GTabAllocTable();
  5474. OutDupCxtions = GTabAllocTable();
  5475. for (Cxtion = Cxtions; Cxtion; Cxtion = Cxtion->Peer) {
  5476. if (Cxtion->Partner == NULL) {
  5477. continue;
  5478. }
  5479. GTabInsertEntry((Cxtion->Inbound) ? InDupCxtions : OutDupCxtions,
  5480. Cxtion,
  5481. Cxtion->PartnerName->Guid, NULL);
  5482. }
  5483. //
  5484. // Check for inbound dups
  5485. //
  5486. Key = NULL;
  5487. while (Entry = GTabNextEntryNoLock(InDupCxtions, &Key)) {
  5488. PWCHAR PartnerServer, RepSetName, Sites, SiteTo, Servers, ServerTo;
  5489. PWCHAR NtdsSettings, SiteFrom;
  5490. PWCHAR SystemName, FrsSettingsName1, FrsSettingsName2, MemberName;
  5491. if (Entry->Dups) {
  5492. Cxtion = Entry->Data;
  5493. Cxtion->Consistent = FALSE;
  5494. DPRINT3(0, ":DS: Multiple connections from %ws by %ws (%ws)\n",
  5495. Cxtion->Partner->Name->Name, Server->Name->Name,
  5496. Server->Parent->Name->Name);
  5497. DPRINT1(0, ":DS: Cxtion Dn: %ws\n",
  5498. (Cxtion->Dn ? Cxtion->Dn : L"NULL"));
  5499. DPRINT1(0, ":DS: Cxtion->Partner Dn: %ws\n",
  5500. (Cxtion->Partner->Dn ? Cxtion->Partner->Dn : L"NULL"));
  5501. DPRINT1(0, ":DS: Cxtion->Partner SettingsDn: %ws\n",
  5502. (Cxtion->Partner->SettingsDn ? Cxtion->Partner->SettingsDn : L"NULL"));
  5503. DPRINT1(0, ":DS: Server Dn: %ws\n",
  5504. (Server->Dn ? Server->Dn : L"NULL"));
  5505. DPRINT1(0, ":DS: Server->Parent Dn: %ws\n",
  5506. (Server->Parent->Dn ? Server->Parent->Dn : L"NULL"));
  5507. if (FRS_RSTYPE_IS_SYSVOLW(Server->Parent->SetType)) {
  5508. //
  5509. // Example Cxtion Dn:
  5510. // cn=sudarctest13-1,
  5511. // cn=ntds settings,
  5512. // cn=sudarctest8,
  5513. // cn=servers,
  5514. // cn=default-first-site-name,
  5515. // cn=sites,
  5516. // cn=configuration,
  5517. // dc=frs1,
  5518. // dc=nttest,
  5519. // dc=microsoft,
  5520. // dc=com
  5521. //
  5522. PartnerServer = Cxtion->Partner->Name->Name;
  5523. RepSetName = Server->Parent->Name->Name;
  5524. Sites = FrsDsMakeRdnX(Cxtion->Dn, 5);
  5525. SiteTo = FrsDsMakeRdnX(Cxtion->Dn, 4);
  5526. Servers = FrsDsMakeRdnX(Cxtion->Dn, 3);
  5527. ServerTo = Server->Name->Name;
  5528. NtdsSettings = FrsDsMakeRdnX(Cxtion->Dn, 1);
  5529. SiteFrom = FrsDsMakeRdnX(Cxtion->Partner->SettingsDn, 3);
  5530. EPRINT8(EVENT_FRS_DUPLICATE_IN_CXTION_SYSVOL,
  5531. PartnerServer,
  5532. RepSetName,
  5533. Sites,
  5534. SiteTo,
  5535. Servers,
  5536. ServerTo,
  5537. NtdsSettings,
  5538. SiteFrom);
  5539. FrsFree(Sites );
  5540. FrsFree(SiteTo );
  5541. FrsFree(Servers );
  5542. FrsFree(NtdsSettings );
  5543. FrsFree(SiteFrom );
  5544. } else {
  5545. //
  5546. // Dup connections in the DFS case.
  5547. //
  5548. // Example Cxtion Dn in a DFS related replica set:
  5549. //
  5550. // cn={84a4ea39-8c32-4658-b821-87b73e8f3db6}, [NTDS-Connection]
  5551. // cn={dabf7b2b-685a-4c7e-ba2e-3ee0f062ec28},[FRS-Member]
  5552. // cn=dfsroot|link1, [ReplicaSet]
  5553. // cn=dfsroot, [FRS Settings]
  5554. // cn=dfs volumes,
  5555. // cn=file replication service,
  5556. // cn=system,
  5557. // dc=frs1,
  5558. // dc=nttest,
  5559. // dc=microsoft,
  5560. // dc=com
  5561. //
  5562. PartnerServer = Cxtion->Partner->Name->Name;
  5563. RepSetName = Server->Parent->Name->Name;
  5564. SystemName = FrsDsMakeRdnX(Cxtion->Dn, 6); // System
  5565. FrsSettingsName1 = FrsDsMakeRdnX(Cxtion->Dn, 5); // FileReplSvcName
  5566. FrsSettingsName2 = FrsDsMakeRdnX(Cxtion->Dn, 4); // DFS_VOLS
  5567. MemberName = FrsDsMakeRdnX(Cxtion->Dn, 1);
  5568. ServerTo = Server->Name->Name;
  5569. EPRINT7(EVENT_FRS_DUPLICATE_IN_CXTION,
  5570. PartnerServer,
  5571. RepSetName,
  5572. SystemName,
  5573. FrsSettingsName1,
  5574. FrsSettingsName2,
  5575. MemberName,
  5576. ServerTo);
  5577. FrsFree(SystemName );
  5578. FrsFree(FrsSettingsName1);
  5579. FrsFree(FrsSettingsName2);
  5580. FrsFree(MemberName );
  5581. }
  5582. for (Dup = Entry->Dups; Dup; Dup = Dup->Dups) {
  5583. Cxtion = Dup->Data;
  5584. Cxtion->Consistent = FALSE;
  5585. }
  5586. }
  5587. }
  5588. //
  5589. // Check for outbound dups
  5590. //
  5591. Key = NULL;
  5592. while (Entry = GTabNextEntryNoLock(OutDupCxtions, &Key)) {
  5593. if (Entry->Dups) {
  5594. Cxtion = Entry->Data;
  5595. Cxtion->Consistent = FALSE;
  5596. DPRINT3(0, ":DS: Multiple connections to %ws by %ws (%ws)\n",
  5597. Cxtion->Partner->Name->Name, Server->Name->Name,
  5598. Server->Parent->Name->Name);
  5599. for (Dup = Entry->Dups; Dup; Dup = Dup->Dups) {
  5600. Cxtion = Dup->Data;
  5601. Cxtion->Consistent = FALSE;
  5602. }
  5603. }
  5604. }
  5605. //
  5606. // Free the table
  5607. //
  5608. InDupCxtions = GTabFreeTable(InDupCxtions, NULL);
  5609. OutDupCxtions = GTabFreeTable(OutDupCxtions, NULL);
  5610. }
  5611. VOID
  5612. FrsDsCheckServerCxtions(
  5613. IN PCONFIG_NODE Sites
  5614. )
  5615. /*++
  5616. Routine Description:
  5617. Check the servers' connections for correctness.
  5618. Arguments:
  5619. Sites
  5620. Return Value:
  5621. None.
  5622. --*/
  5623. {
  5624. #undef DEBSUB
  5625. #define DEBSUB "FrsDsCheckServerCxtions:"
  5626. PCONFIG_NODE Site;
  5627. PCONFIG_NODE Settings;
  5628. PCONFIG_NODE Set;
  5629. PCONFIG_NODE Server;
  5630. //
  5631. // Check cxtions
  5632. //
  5633. for (Site = Sites; Site; Site = Site->Peer) {
  5634. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  5635. for (Set = Settings->Children; Set; Set = Set->Peer) {
  5636. for (Server = Set->Children; Server; Server = Server->Peer) {
  5637. if (Server->ThisComputer) {
  5638. FrsDsCheckCxtions(Server);
  5639. }
  5640. }
  5641. }
  5642. }
  5643. }
  5644. }
  5645. DWORD
  5646. FrsDsStartPromotionSeeding(
  5647. IN BOOL Inbound,
  5648. IN PWCHAR ReplicaSetName,
  5649. IN PWCHAR ReplicaSetType,
  5650. IN PWCHAR CxtionName,
  5651. IN PWCHAR PartnerName,
  5652. IN PWCHAR PartnerPrincName,
  5653. IN ULONG PartnerAuthLevel,
  5654. IN ULONG GuidSize,
  5655. IN UCHAR *CxtionGuid,
  5656. IN UCHAR *PartnerGuid,
  5657. OUT UCHAR *ParentGuid
  5658. )
  5659. /*++
  5660. Routine Description:
  5661. Start the promotion process by seeding the indicated sysvol.
  5662. Arguments:
  5663. Inbound - Inbound cxtion?
  5664. ReplicaSetName - Replica set name
  5665. ReplicaSetType - Replica set type
  5666. CxtionName - printable name for cxtion
  5667. PartnerName - RPC bindable name
  5668. PartnerPrincName - Server principal name for kerberos
  5669. PartnerAuthLevel - Authentication type and level
  5670. GuidSize - sizeof array addressed by Guid
  5671. CxtionGuid - temporary: used for volatile cxtion
  5672. PartnerGuid - temporary: used to find set on partner
  5673. ParentGuid - Used as partner guid on inbound cxtion
  5674. Return Value:
  5675. Win32 Status
  5676. --*/
  5677. {
  5678. #undef DEBSUB
  5679. #define DEBSUB "FrsDsStartPromotionSeeding:"
  5680. DWORD WStatus;
  5681. PREPLICA DbReplica;
  5682. PCXTION Cxtion = NULL;
  5683. //
  5684. // The caller has verified that the replica set exists, the
  5685. // active replication subsystem is active, and that some of
  5686. // the parameters are okay. Verify the rest.
  5687. //
  5688. if (!CxtionName ||
  5689. !PartnerName ||
  5690. !PartnerPrincName ||
  5691. !CxtionGuid ||
  5692. !PartnerGuid ||
  5693. !ParentGuid ||
  5694. (PartnerAuthLevel != CXTION_AUTH_KERBEROS_FULL &&
  5695. PartnerAuthLevel != CXTION_AUTH_NONE)) {
  5696. WStatus = ERROR_INVALID_PARAMETER;
  5697. goto CLEANUP;
  5698. }
  5699. //
  5700. // Find the sysvol
  5701. //
  5702. DbReplica = RcsFindSysVolByName(ReplicaSetName);
  5703. if (!DbReplica) {
  5704. DPRINT1(4, ":DS: Promotion failed; could not find %ws\n", ReplicaSetName);
  5705. WStatus = ERROR_INVALID_PARAMETER;
  5706. goto CLEANUP;
  5707. }
  5708. //
  5709. // To be used in the caller's cxtion
  5710. //
  5711. COPY_GUID(ParentGuid, DbReplica->ReplicaName->Guid);
  5712. //
  5713. // PRETEND WE ARE THE DS POLLING THREAD AND ARE ADDING A
  5714. // A CXTION TO AN EXISTING REPLICA.
  5715. //
  5716. // Create the volatile cxtion
  5717. // Set the state to "promoting" at this time because the
  5718. // seeding operation may finish and the state set to
  5719. // NTFRSAPI_SERVICE_DONE before the return of the
  5720. // call to RcsSubmitReplicaSync().
  5721. //
  5722. DbReplica->NtFrsApi_ServiceState = NTFRSAPI_SERVICE_PROMOTING;
  5723. Cxtion = FrsAllocType(CXTION_TYPE);
  5724. Cxtion->Inbound = Inbound;
  5725. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT | CXTION_FLAGS_VOLATILE);
  5726. Cxtion->Name = FrsBuildGName(FrsDupGuid((GUID *)CxtionGuid),
  5727. FrsWcsDup(CxtionName));
  5728. Cxtion->Partner = FrsBuildGName(FrsDupGuid((GUID *)PartnerGuid),
  5729. FrsWcsDup(PartnerName));
  5730. Cxtion->PartnerSid = FrsWcsDup(L"<unknown>");
  5731. Cxtion->PartSrvName = FrsWcsDup(PartnerPrincName);
  5732. Cxtion->PartnerDnsName = FrsWcsDup(PartnerName);
  5733. Cxtion->PartnerAuthLevel = PartnerAuthLevel;
  5734. Cxtion->PartnerPrincName = FrsWcsDup(PartnerPrincName);
  5735. SetCxtionState(Cxtion, CxtionStateUnjoined);
  5736. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, Cxtion, CMD_START);
  5737. //
  5738. // The active replication subsystem owns the cxtion, now
  5739. //
  5740. Cxtion = NULL;
  5741. CLEANUP1_WS(0, ":DS: ERROR - Creating cxtion for %ws;",
  5742. ReplicaSetName, WStatus, SYNC_FAIL);
  5743. //
  5744. // Submit a command to periodically check the promotion activity.
  5745. // If nothing has happened in awhile, stop the promotion process.
  5746. //
  5747. if (Inbound) {
  5748. DbReplica->NtFrsApi_HackCount++; // != 0
  5749. RcsSubmitReplica(DbReplica, NULL, CMD_CHECK_PROMOTION);
  5750. }
  5751. //
  5752. // SUCCESS
  5753. //
  5754. WStatus = ERROR_SUCCESS;
  5755. goto CLEANUP;
  5756. SYNC_FAIL:
  5757. DbReplica->NtFrsApi_ServiceState = NTFRSAPI_SERVICE_STATE_IS_UNKNOWN;
  5758. //
  5759. // CLEANUP
  5760. //
  5761. CLEANUP:
  5762. FrsFreeType(Cxtion);
  5763. return WStatus;
  5764. }
  5765. DWORD
  5766. FrsDsVerifyPromotionParent(
  5767. IN PWCHAR ReplicaSetName,
  5768. IN PWCHAR ReplicaSetType
  5769. )
  5770. /*++
  5771. Routine Description:
  5772. Start the promotion process by seeding the indicated sysvol.
  5773. Arguments:
  5774. ReplicaSetName - Replica set name
  5775. ReplicaSetType - Type of set (Enterprise or Domain)
  5776. Return Value:
  5777. Win32 Status
  5778. --*/
  5779. {
  5780. #undef DEBSUB
  5781. #define DEBSUB "FrsDsVerifyPromotionParent:"
  5782. DWORD WStatus;
  5783. PREPLICA DbReplica;
  5784. //
  5785. // This parent must be a Dc
  5786. //
  5787. FrsDsGetRole();
  5788. if (!IsADc) {
  5789. DPRINT1(0, ":S: Promotion aborted: %ws is not a dc.\n", ComputerName);
  5790. WStatus = ERROR_SERVICE_SPECIFIC_ERROR;
  5791. goto CLEANUP;
  5792. }
  5793. //
  5794. // WAIT FOR THE ACTIVE REPLICATION SUBSYSTEM TO START
  5795. //
  5796. MainInit();
  5797. if (!MainInitHasRun) {
  5798. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  5799. goto CLEANUP;
  5800. }
  5801. //
  5802. // Let dcpromo determine the timeout
  5803. //
  5804. DPRINT(4, ":S: Waiting for replica command server to start.\n");
  5805. WStatus = WaitForSingleObject(ReplicaEvent, 10 * 60 * 1000);
  5806. CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
  5807. //
  5808. // Is the service shutting down?
  5809. //
  5810. if (FrsIsShuttingDown) {
  5811. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  5812. goto CLEANUP;
  5813. }
  5814. //
  5815. // Verify the existence of the set
  5816. //
  5817. DbReplica = RcsFindSysVolByName(ReplicaSetName);
  5818. if (DbReplica && IS_TIME_ZERO(DbReplica->MembershipExpires)) {
  5819. //
  5820. // Sysvol exists; make sure it is the right type
  5821. //
  5822. if (_wcsicmp(ReplicaSetType, NTFRSAPI_REPLICA_SET_TYPE_ENTERPRISE)) {
  5823. if (DbReplica->ReplicaSetType != FRS_RSTYPE_DOMAIN_SYSVOL) {
  5824. DPRINT3(0, ":S: ERROR - %ws's type is %d; not %d\n",
  5825. ReplicaSetName, DbReplica->ReplicaSetType,
  5826. FRS_RSTYPE_DOMAIN_SYSVOL);
  5827. WStatus = ERROR_NOT_FOUND;
  5828. goto CLEANUP;
  5829. }
  5830. } else if (DbReplica->ReplicaSetType != FRS_RSTYPE_ENTERPRISE_SYSVOL) {
  5831. DPRINT3(0, ":S: ERROR - %ws's type is %d; not %d\n",
  5832. ReplicaSetName, DbReplica->ReplicaSetType,
  5833. FRS_RSTYPE_ENTERPRISE_SYSVOL);
  5834. WStatus = ERROR_NOT_FOUND;
  5835. goto CLEANUP;
  5836. }
  5837. } else {
  5838. DPRINT2(0, ":S: ERROR - %ws does not exist on %ws!\n",
  5839. ReplicaSetName, ComputerName);
  5840. WStatus = ERROR_NOT_FOUND;
  5841. goto CLEANUP;
  5842. }
  5843. //
  5844. // SUCCESS
  5845. //
  5846. WStatus = ERROR_SUCCESS;
  5847. //
  5848. // CLEANUP
  5849. //
  5850. CLEANUP:
  5851. return WStatus;
  5852. }
  5853. VOID
  5854. FrsDsVerifySchedule(
  5855. IN PCONFIG_NODE Node
  5856. )
  5857. /*++
  5858. Routine Description:
  5859. Check the schedule for consistency
  5860. Arguments:
  5861. Sites
  5862. Return Value:
  5863. None.
  5864. --*/
  5865. {
  5866. #undef DEBSUB
  5867. #define DEBSUB "FrsDsVerifySchedule:"
  5868. ULONG i;
  5869. ULONG Num;
  5870. ULONG Len;
  5871. ULONG NumType;
  5872. PSCHEDULE Schedule = Node->Schedule;
  5873. if (!Schedule) {
  5874. return;
  5875. }
  5876. //
  5877. // Too many schedules
  5878. //
  5879. Num = Schedule->NumberOfSchedules;
  5880. if (Num > 3) {
  5881. DPRINT2(4, ":DS: %ws has %d schedules\n", Node->Name->Name, Num);
  5882. Node->Consistent = FALSE;
  5883. return;
  5884. }
  5885. //
  5886. // Too few schedules
  5887. //
  5888. if (Num < 1) {
  5889. DPRINT2(4, ":DS: %ws has %d schedules\n", Node->Name->Name, Num);
  5890. Node->Consistent = FALSE;
  5891. return;
  5892. }
  5893. //
  5894. // Not enough memory
  5895. //
  5896. Len = sizeof(SCHEDULE) +
  5897. (sizeof(SCHEDULE_HEADER) * (Num - 1)) +
  5898. (SCHEDULE_DATA_BYTES * Num);
  5899. if (Node->ScheduleLength < Len) {
  5900. DPRINT2(4, ":DS: %ws is short (ds) by %d bytes\n",
  5901. Node->Name->Name, Len - Node->ScheduleLength);
  5902. Node->Consistent = FALSE;
  5903. return;
  5904. }
  5905. if (Node->Schedule->Size < Len) {
  5906. DPRINT2(4, ":DS: %ws is short (size) by %d bytes\n",
  5907. Node->Name->Name, Len - Node->Schedule->Size);
  5908. Node->Consistent = FALSE;
  5909. return;
  5910. }
  5911. Node->Schedule->Size = Len;
  5912. //
  5913. // Invalid type
  5914. //
  5915. for (i = 0; i < Num; ++i) {
  5916. switch (Schedule->Schedules[i].Type) {
  5917. case SCHEDULE_INTERVAL:
  5918. break;
  5919. case SCHEDULE_BANDWIDTH:
  5920. DPRINT1(4, ":DS: WARN Bandwidth schedule is not supported for %ws\n",
  5921. Node->Name->Name);
  5922. break;
  5923. case SCHEDULE_PRIORITY:
  5924. DPRINT1(4, ":DS: WARN Priority schedule is not supported for %ws\n",
  5925. Node->Name->Name);
  5926. break;
  5927. default:
  5928. DPRINT2(4, ":DS: %ws has an invalid schedule type (%d)\n",
  5929. Node->Name->Name, Schedule->Schedules[i].Type);
  5930. Node->Consistent = FALSE;
  5931. return;
  5932. }
  5933. }
  5934. //
  5935. // Only 0 or 1 interval
  5936. //
  5937. for (NumType = i = 0; i < Num; ++i) {
  5938. if (Schedule->Schedules[i].Type == SCHEDULE_INTERVAL)
  5939. ++NumType;
  5940. }
  5941. if (NumType > 1) {
  5942. DPRINT2(4, ":DS: %ws has %d interval schedules\n",
  5943. Node->Name->Name, NumType);
  5944. Node->Consistent = FALSE;
  5945. }
  5946. //
  5947. // Only 0 or 1 bandwidth
  5948. //
  5949. for (NumType = i = 0; i < Num; ++i) {
  5950. if (Schedule->Schedules[i].Type == SCHEDULE_BANDWIDTH)
  5951. ++NumType;
  5952. }
  5953. if (NumType > 1) {
  5954. DPRINT2(4, ":DS: %ws has %d bandwidth schedules\n",
  5955. Node->Name->Name, NumType);
  5956. Node->Consistent = FALSE;
  5957. }
  5958. //
  5959. // Only 0 or 1 priority
  5960. //
  5961. for (NumType = i = 0; i < Num; ++i) {
  5962. if (Schedule->Schedules[i].Type == SCHEDULE_PRIORITY)
  5963. ++NumType;
  5964. }
  5965. if (NumType > 1) {
  5966. DPRINT2(4, ":DS: %ws has %d priority schedules\n",
  5967. Node->Name->Name, NumType);
  5968. Node->Consistent = FALSE;
  5969. }
  5970. //
  5971. // Invalid offset
  5972. //
  5973. for (i = 0; i < Num; ++i) {
  5974. if (Schedule->Schedules[i].Offset >
  5975. Node->ScheduleLength - SCHEDULE_DATA_BYTES) {
  5976. DPRINT2(4, ":DS: %ws has an invalid offset (%d)\n",
  5977. Node->Name->Name, Schedule->Schedules[i].Offset);
  5978. Node->Consistent = FALSE;
  5979. return;
  5980. }
  5981. }
  5982. }
  5983. VOID
  5984. FrsDsCheckSchedules(
  5985. IN PCONFIG_NODE Root
  5986. )
  5987. /*++
  5988. Routine Description:
  5989. Check all of the schedules for consistency
  5990. Arguments:
  5991. Sites
  5992. Return Value:
  5993. None.
  5994. --*/
  5995. {
  5996. #undef DEBSUB
  5997. #define DEBSUB "FrsDsCheckSchedules:"
  5998. PCONFIG_NODE Node;
  5999. for (Node = Root; Node; Node = Node->Peer) {
  6000. FrsDsVerifySchedule(Node);
  6001. FrsDsCheckSchedules(Node->Children);
  6002. }
  6003. }
  6004. VOID
  6005. FrsDsPushInConsistenciesDown(
  6006. IN PCONFIG_NODE Sites
  6007. )
  6008. /*++
  6009. Routine Description:
  6010. Mark the children of inconsistent parents as inconsistent
  6011. Arguments:
  6012. Sites
  6013. Return Value:
  6014. None.
  6015. --*/
  6016. {
  6017. #undef DEBSUB
  6018. #define DEBSUB "FrsDsPushInConsistenciesDown:"
  6019. PCONFIG_NODE Site;
  6020. PCONFIG_NODE Settings;
  6021. PCONFIG_NODE Set;
  6022. PCONFIG_NODE Server;
  6023. PCONFIG_NODE Cxtion;
  6024. //
  6025. // Push a parent's inconsistency to its children
  6026. //
  6027. for (Site = Sites; Site; Site = Site->Peer) {
  6028. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  6029. if (!Site->Consistent)
  6030. Settings->Consistent = FALSE;
  6031. for (Set = Settings->Children; Set; Set = Set->Peer) {
  6032. if (!Settings->Consistent)
  6033. Set->Consistent = FALSE;
  6034. for (Server = Set->Children; Server; Server = Server->Peer) {
  6035. if (!Set->Consistent)
  6036. Server->Consistent = FALSE;
  6037. for (Cxtion = Server->Children; Cxtion; Cxtion = Cxtion->Peer) {
  6038. if (!Server->Consistent)
  6039. Cxtion->Consistent = FALSE;
  6040. }
  6041. }
  6042. }
  6043. }
  6044. }
  6045. }
  6046. #if DBG
  6047. #define CHECK_NODE_LINKAGE(_Nodes_) FrsDsCheckNodeLinkage(_Nodes)
  6048. BOOL
  6049. FrsDsCheckNodeLinkage(
  6050. PCONFIG_NODE Nodes
  6051. )
  6052. /*++
  6053. Routine Description:
  6054. Recursively check a configuration's site and table linkage
  6055. for incore consistency.
  6056. Arguments:
  6057. Nodes - linked list of nodes
  6058. Return Value:
  6059. None.
  6060. --*/
  6061. {
  6062. #undef DEBSUB
  6063. #define DEBSUB "FrsDsCheckNodeLinkage:"
  6064. PCONFIG_NODE Node; // scan nodes list
  6065. PCONFIG_NODE Child; // scan children list
  6066. DWORD NumChildren; // Count children
  6067. for (Node = Nodes; Node; Node = Node->Peer) {
  6068. //
  6069. // Make sure the number of children matches the actual number
  6070. //
  6071. NumChildren = 0;
  6072. for (Child = Node->Children; Child; Child = Child->Peer) {
  6073. ++NumChildren;
  6074. }
  6075. FRS_ASSERT(NumChildren == Node->NumChildren);
  6076. if (!FrsDsCheckNodeLinkage(Node->Children))
  6077. return FALSE;
  6078. }
  6079. return TRUE; // for Assert(DbgCheckLinkage);
  6080. }
  6081. #else DBG
  6082. #define CHECK_NODE_LINKAGE(_Nodes_)
  6083. #endif DBG
  6084. #define UF_IS_A_DC (UF_SERVER_TRUST_ACCOUNT)
  6085. BOOL
  6086. FrsDsIsPartnerADc(
  6087. IN PWCHAR PartnerName
  6088. )
  6089. /*++
  6090. Routine Description:
  6091. Check if the PartnerName's comptuer object indicates that it is a DC.
  6092. Arguments:
  6093. PartnerName - RPC bindable name
  6094. Return Value:
  6095. Win32 Status
  6096. --*/
  6097. {
  6098. #undef DEBSUB
  6099. #define DEBSUB "FrsDsIsPartnerADc:"
  6100. DWORD WStatus;
  6101. DWORD LStatus;
  6102. DWORD Len;
  6103. DWORD UserAccountFlags;
  6104. PLDAP LocalLdap = NULL;
  6105. PLDAPMessage LdapEntry = NULL;
  6106. PLDAPMessage LdapMsg = NULL;
  6107. PWCHAR *Values = NULL;
  6108. PWCHAR DefaultNamingContext = NULL;
  6109. BOOL PartnerIsADc = TRUE;
  6110. PWCHAR UserAccountControl = NULL;
  6111. PWCHAR Attrs[2];
  6112. WCHAR Filter[MAX_PATH + 1];
  6113. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  6114. WCHAR NetBiosName[MAX_PATH + 1];
  6115. ULONG ulOptions;
  6116. //
  6117. // Convert DNS computer name into NetBIOS computer name
  6118. //
  6119. Len = MAX_PATH + 1;
  6120. if (!DnsHostnameToComputerName(PartnerName, NetBiosName, &Len)) {
  6121. DPRINT2_WS(4, ":DS: WARN - Can't convert %ws, Len %d;",
  6122. PartnerName, Len, GetLastError());
  6123. goto CLEANUP;
  6124. }
  6125. DPRINT2(4, ":DS: Converted %ws to %ws\n", PartnerName, NetBiosName);
  6126. //
  6127. // Bind to the DS on this DC
  6128. //
  6129. //
  6130. // if ldap_open is called with a server name the api will call DsGetDcName
  6131. // passing the server name as the domainname parm...bad, because
  6132. // DsGetDcName will make a load of DNS queries based on the server name,
  6133. // it is designed to construct these queries from a domain name...so all
  6134. // these queries will be bogus, meaning they will waste network bandwidth,
  6135. // time to fail, and worst case cause expensive on demand links to come up
  6136. // as referrals/forwarders are contacted to attempt to resolve the bogus
  6137. // names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
  6138. // after the ldap_init but before any other operation using the ldap
  6139. // handle from ldap_init, the delayed connection setup will not call
  6140. // DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
  6141. // will detect that and use the address directly.
  6142. //
  6143. // LocalLdap = ldap_open(ComputerName, LDAP_PORT);
  6144. LocalLdap = ldap_init(ComputerName, LDAP_PORT);
  6145. if (LocalLdap == NULL) {
  6146. DPRINT1_WS(4, ":DS: WARN - Coult not open DS on %ws;", ComputerName, GetLastError());
  6147. goto CLEANUP;
  6148. }
  6149. ulOptions = PtrToUlong(LDAP_OPT_ON);
  6150. ldap_set_option(LocalLdap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
  6151. LStatus = ldap_bind_s(LocalLdap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  6152. CLEANUP1_LS(0, ":DS: WARN - Could not bind to the DS on %ws :",
  6153. ComputerName, LStatus, CLEANUP);
  6154. DPRINT1(4, ":DS: Bound to the DS on %ws\n", ComputerName);
  6155. //
  6156. // Find the default naming context (objectCategory=*)
  6157. //
  6158. MK_ATTRS_1(Attrs, ATTR_DEFAULT_NAMING_CONTEXT);
  6159. if (!FrsDsLdapSearch(LocalLdap, CN_ROOT, LDAP_SCOPE_BASE, CATEGORY_ANY,
  6160. Attrs, 0, &LdapMsg)) {
  6161. goto CLEANUP;
  6162. }
  6163. LdapEntry = ldap_first_entry(LocalLdap, LdapMsg);
  6164. if (!LdapEntry) {
  6165. goto CLEANUP;
  6166. }
  6167. Values = (PWCHAR *)FrsDsFindValues(LocalLdap,
  6168. LdapEntry,
  6169. ATTR_DEFAULT_NAMING_CONTEXT,
  6170. FALSE);
  6171. if (!Values) {
  6172. goto CLEANUP;
  6173. }
  6174. DefaultNamingContext = FrsWcsDup(Values[0]);
  6175. LDAP_FREE_VALUES(Values);
  6176. LDAP_FREE_MSG(LdapMsg);
  6177. DPRINT2(4, ":DS: Default naming context for %ws is %ws\n",
  6178. ComputerName, DefaultNamingContext);
  6179. //
  6180. // Find the account object for PartnerName
  6181. //
  6182. swprintf(Filter, L"(sAMAccountName=%s$)", NetBiosName);
  6183. MK_ATTRS_1(Attrs, ATTR_USER_ACCOUNT_CONTROL);
  6184. if (!FrsDsLdapSearchInit(LocalLdap, DefaultNamingContext, LDAP_SCOPE_SUBTREE, Filter,
  6185. Attrs, 0, &FrsSearchContext)) {
  6186. goto CLEANUP;
  6187. }
  6188. //
  6189. // Scan the returned account objects for a valid DC
  6190. //
  6191. for (LdapEntry = FrsDsLdapSearchNext(LocalLdap, &FrsSearchContext);
  6192. LdapEntry != NULL;
  6193. LdapEntry = FrsDsLdapSearchNext(LocalLdap, &FrsSearchContext)) {
  6194. //
  6195. // No user account control flags
  6196. //
  6197. UserAccountControl = FrsDsFindValue(LocalLdap, LdapEntry, ATTR_USER_ACCOUNT_CONTROL);
  6198. if (!UserAccountControl) {
  6199. continue;
  6200. }
  6201. UserAccountFlags = wcstoul(UserAccountControl, NULL, 10);
  6202. DPRINT2(4, ":DS: UserAccountControl for %ws is 0x%08x\n",
  6203. NetBiosName, UserAccountFlags);
  6204. //
  6205. // IS A DC!
  6206. //
  6207. if (UserAccountFlags & UF_IS_A_DC) {
  6208. DPRINT1(4, ":DS: Partner %ws is really a DC!\n", NetBiosName);
  6209. goto CLEANUP;
  6210. }
  6211. FrsFree(UserAccountControl);
  6212. }
  6213. FrsDsLdapSearchClose(&FrsSearchContext);
  6214. PartnerIsADc = FALSE;
  6215. DPRINT1(0, ":DS: ERROR - Partner %ws is NOT a DC!\n", NetBiosName);
  6216. CLEANUP:
  6217. LDAP_FREE_VALUES(Values);
  6218. LDAP_FREE_MSG(LdapMsg);
  6219. FrsFree(DefaultNamingContext);
  6220. FrsFree(UserAccountControl);
  6221. if (LocalLdap) {
  6222. ldap_unbind_s(LocalLdap);
  6223. }
  6224. DPRINT2(4, ":DS: Partner %ws is %s a DC\n",
  6225. NetBiosName, (PartnerIsADc) ? "assumed to be" : "NOT");
  6226. return PartnerIsADc;
  6227. }
  6228. DWORD
  6229. FrsDsGetRole(
  6230. VOID
  6231. )
  6232. /*++
  6233. Routine Description:
  6234. Get this computer's role in the domain.
  6235. Arguments:
  6236. Return Value:
  6237. Win32 Status
  6238. --*/
  6239. {
  6240. #undef DEBSUB
  6241. #define DEBSUB "FrsDsGetRole:"
  6242. DWORD WStatus;
  6243. DWORD SysvolReady;
  6244. CHAR GuidStr[GUID_CHAR_LEN];
  6245. DSROLE_PRIMARY_DOMAIN_INFO_BASIC *DsRole;
  6246. //
  6247. // We already know our role; carry on
  6248. //
  6249. if (IsAMember) {
  6250. return ERROR_SUCCESS;
  6251. }
  6252. DPRINT(4, ":DS: Finding this computer's role in the domain.\n");
  6253. #if DBG
  6254. //
  6255. // Emulating multiple machines
  6256. //
  6257. if (ServerGuid) {
  6258. DPRINT(4, ":DS: Always a member with hardwired config\n");
  6259. IsAMember = TRUE;
  6260. return ERROR_SUCCESS;
  6261. }
  6262. #endif DBG
  6263. //
  6264. // Is this a domain controller?
  6265. //
  6266. WStatus = DsRoleGetPrimaryDomainInformation(NULL,
  6267. DsRolePrimaryDomainInfoBasic,
  6268. (PBYTE *)&DsRole);
  6269. CLEANUP_WS(4, ":DS: Can't get Ds role info;", WStatus, RETURN);
  6270. DPRINT1(4, ":DS: Ds Role : %ws\n", Roles[DsRole->MachineRole]);
  6271. DPRINT1(4, ":DS: Ds Role Flags : %08x\n", DsRole->Flags);
  6272. if (DsRole->Flags & DSROLE_PRIMARY_DS_RUNNING) {
  6273. DPRINT(4, ":DS: Ds Role Flag : DSROLE_PRIMARY_DS_RUNNING\n");
  6274. }
  6275. if (DsRole->Flags & DSROLE_PRIMARY_DOMAIN_GUID_PRESENT) {
  6276. DPRINT(4, ":DS: Ds Role Flag : DSROLE_PRIMARY_DOMAIN_GUID_PRESENT\n");
  6277. }
  6278. DPRINT1(4, ":DS: Ds Role DomainNameFlat: %ws\n", DsRole->DomainNameFlat);
  6279. DPRINT1(4, ":DS: Ds Role DomainNameDns : %ws\n", DsRole->DomainNameDns);
  6280. // DPRINT1(4, ":DS: Ds Role DomainForestName: %ws\n", DsRole->DomainForestName);
  6281. GuidToStr(&DsRole->DomainGuid, GuidStr);
  6282. DPRINT1(4, ":DS: Ds Role DomainGuid : %s\n", GuidStr);
  6283. //
  6284. // Backup Domain Controller (DC)
  6285. //
  6286. if (DsRole->MachineRole == DsRole_RoleBackupDomainController) {
  6287. DPRINT(4, ":DS: Computer is a backup DC; sysvol support is enabled.\n");
  6288. IsAMember = TRUE;
  6289. IsADc = TRUE;
  6290. //
  6291. // Primary Domain Controller (DC)
  6292. //
  6293. } else if (DsRole->MachineRole == DsRole_RolePrimaryDomainController) {
  6294. DPRINT(4, ":DS: Computer is a DC; sysvol support is enabled.\n");
  6295. IsAMember = TRUE;
  6296. IsADc = TRUE;
  6297. IsAPrimaryDc = TRUE;
  6298. //
  6299. // Member Server
  6300. //
  6301. } else if (DsRole->MachineRole == DsRole_RoleMemberServer) {
  6302. DPRINT(4, ":DS: Computer is just a member server.\n");
  6303. IsAMember = TRUE;
  6304. //
  6305. // Not in a server in a domain; stop the service
  6306. //
  6307. } else {
  6308. DPRINT(1, ":DS: Computer is not a server in a domain.\n");
  6309. }
  6310. DsRoleFreeMemory(DsRole);
  6311. //
  6312. // Has the sysvol been seeded?
  6313. //
  6314. if (IsADc) {
  6315. //
  6316. // Access the netlogon\parameters key to get the sysvol share status
  6317. //
  6318. WStatus = CfgRegReadDWord(FKC_SYSVOL_READY, NULL, 0, &SysvolReady);
  6319. if (WIN_SUCCESS(WStatus)) {
  6320. if (!SysvolReady) {
  6321. EPRINT1((IsAPrimaryDc) ? EVENT_FRS_SYSVOL_NOT_READY_PRIMARY_2 :
  6322. EVENT_FRS_SYSVOL_NOT_READY_2,
  6323. ComputerName);
  6324. }
  6325. } else {
  6326. DPRINT2_WS(0, "ERROR - reading %ws\\%ws :",
  6327. NETLOGON_SECTION, SYSVOL_READY, WStatus);
  6328. }
  6329. }
  6330. WStatus = ERROR_SUCCESS;
  6331. RETURN:
  6332. return WStatus;
  6333. }
  6334. DWORD
  6335. FrsDsCommitDemotion(
  6336. VOID
  6337. )
  6338. /*++
  6339. Routine Description:
  6340. Commit the demotion process by marking the tombstoned
  6341. sysvols as "do not animate".
  6342. Arguments:
  6343. None.
  6344. Return Value:
  6345. Win32 Status
  6346. --*/
  6347. {
  6348. #undef DEBSUB
  6349. #define DEBSUB "FrsDsCommitDemotion:"
  6350. DWORD WStatus;
  6351. DWORD i;
  6352. PREPLICA DbReplica;
  6353. PVOID Key;
  6354. DWORD SaveWStatus;
  6355. DWORD SysvolPathLen;
  6356. PWCHAR SysvolPath = NULL;
  6357. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  6358. //
  6359. // SHUTDOWN THE DS POLLING THREAD
  6360. // Demotion can run in parallel with the Ds polling thread iff
  6361. // the polling thread never tries to merge the info in the Ds
  6362. // with the active replicas. This could result in the sysvol
  6363. // replica being animated. So, we tell the Ds polling thead to
  6364. // shut down, wake it up if it is asleep so it can see the shutdown
  6365. // request, and then synchronize with the merging code in the
  6366. // Ds polling thread. We don't want to wait for the polling
  6367. // thread to simply die because it may be stuck talking to the
  6368. // Ds. Alternatively, we could use async ldap but that would
  6369. // take too long and is overkill at this time.
  6370. //
  6371. // In any case, the service will be restarted after dcpromo/demote
  6372. // by a reboot or a restart-service by the ntfrsapi.
  6373. //
  6374. //
  6375. // PERF: should use async ldap in polling thread.
  6376. //
  6377. DsIsShuttingDown = TRUE;
  6378. SetEvent(DsPollEvent);
  6379. EnterCriticalSection(&MergingReplicasWithDs);
  6380. LeaveCriticalSection(&MergingReplicasWithDs);
  6381. //
  6382. // Is the service shutting down?
  6383. //
  6384. if (FrsIsShuttingDown) {
  6385. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  6386. goto CLEANUP;
  6387. }
  6388. //
  6389. // WAIT FOR THE ACTIVE REPLICATION SUBSYSTEM TO START
  6390. //
  6391. MainInit();
  6392. if (!MainInitHasRun) {
  6393. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  6394. goto CLEANUP;
  6395. }
  6396. //
  6397. // Let dcpromo determine the timeout
  6398. //
  6399. DPRINT(4, ":S: Waiting for replica command server to start.\n");
  6400. WStatus = WaitForSingleObject(ReplicaEvent, 30 * 60 * 1000);
  6401. CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
  6402. //
  6403. // Unshare the sysvol
  6404. //
  6405. RcsSetSysvolReady(0);
  6406. //
  6407. // Mark the tombstoned replica sets for sysvols as "do not animate".
  6408. //
  6409. SaveWStatus = ERROR_SUCCESS;
  6410. Key = NULL;
  6411. while (DbReplica = RcsFindNextReplica(&Key)) {
  6412. //
  6413. // Not a sysvol
  6414. //
  6415. if (!FRS_RSTYPE_IS_SYSVOL(DbReplica->ReplicaSetType)) {
  6416. continue;
  6417. }
  6418. //
  6419. // Not tombstoned
  6420. //
  6421. if (IS_TIME_ZERO(DbReplica->MembershipExpires)) {
  6422. continue;
  6423. }
  6424. //
  6425. // Mark as "do not animate"
  6426. //
  6427. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, NULL, CMD_DELETE_NOW);
  6428. if (!WIN_SUCCESS(WStatus)) {
  6429. DPRINT1_WS(0, ":S: ERROR - Could not delete %ws now;",
  6430. DbReplica->ReplicaName->Name, WStatus);
  6431. SaveWStatus = WStatus;
  6432. continue;
  6433. }
  6434. DPRINT1(4, ":S: Deleted %ws in DB", DbReplica->ReplicaName->Name);
  6435. //
  6436. // Delete ALL OF THE SYSVOL DIRECTORY
  6437. //
  6438. // WARNING: makes assumptions about tree built by dcpromo.
  6439. //
  6440. if (DbReplica->Root) {
  6441. SysvolPath = FrsWcsDup(DbReplica->Root);
  6442. SysvolPathLen = wcslen(SysvolPath);
  6443. if (SysvolPathLen) {
  6444. for (i = SysvolPathLen - 1; i; --i) {
  6445. if (*(SysvolPath + i) == L'\\') {
  6446. *(SysvolPath + i) = L'\0';
  6447. DPRINT1(4, ":S: Deleting sysvol path %ws\n", SysvolPath);
  6448. WStatus = FrsDeletePath(SysvolPath,
  6449. ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE);
  6450. if (!WIN_SUCCESS(WStatus)) {
  6451. DPRINT1_WS(3, ":S: Warn - FrsDeletePath(%ws); (IGNORED)",
  6452. SysvolPath, WStatus);
  6453. WStatus = ERROR_SUCCESS;
  6454. }
  6455. break;
  6456. }
  6457. }
  6458. }
  6459. SysvolPath = FrsFree(SysvolPath);
  6460. }
  6461. //
  6462. // The original code deleted the root and staging directories, not
  6463. // the entire sysvol tree. Allow the original code to execute in
  6464. // case the new code above runs into problems.
  6465. //
  6466. //
  6467. // Why wouldn't a replica set have a root path? But, BSTS.
  6468. //
  6469. if (!DbReplica->Root) {
  6470. continue;
  6471. }
  6472. //
  6473. // DELETE THE CONTENTS OF THE ROOT DIRECTORY
  6474. // Always open the replica root by masking off the FILE_OPEN_REPARSE_POINT flag
  6475. // because we want to open the destination dir not the junction if the root
  6476. // happens to be a mount point.
  6477. //
  6478. WStatus = FrsOpenSourceFileW(&FileHandle,
  6479. DbReplica->Root,
  6480. // WRITE_ACCESS | READ_ACCESS,
  6481. DELETE | READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
  6482. OPEN_OPTIONS & ~FILE_OPEN_REPARSE_POINT);
  6483. if (!WIN_SUCCESS(WStatus)) {
  6484. DPRINT1_WS(0, ":S: ERROR - Cannot open root of replica tree %ws;",
  6485. DbReplica->Root, WStatus);
  6486. continue;
  6487. }
  6488. //
  6489. // Remove object id
  6490. //
  6491. WStatus = FrsDeleteFileObjectId(FileHandle, DbReplica->Root);
  6492. DPRINT1_WS(0, ":S: ERROR - Cannot remove object id from root "
  6493. "of replica tree %ws; Continue with delete",
  6494. DbReplica->Root, WStatus);
  6495. //
  6496. // Delete files/subdirs
  6497. //
  6498. FrsEnumerateDirectory(FileHandle,
  6499. DbReplica->Root,
  6500. 0,
  6501. ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE,
  6502. NULL,
  6503. FrsEnumerateDirectoryDeleteWorker);
  6504. DPRINT1(4, ":S: Deleted files/subdirs for %ws\n", DbReplica->Root);
  6505. FRS_CLOSE(FileHandle);
  6506. //
  6507. // Why wouldn't a replica set have a stage path? But, BSTS.
  6508. //
  6509. if (!DbReplica->Stage) {
  6510. continue;
  6511. }
  6512. //
  6513. // DELETE THE CONTENTS OF THE STAGE DIRECTORY
  6514. //
  6515. WStatus = FrsOpenSourceFileW(&FileHandle,
  6516. DbReplica->Stage,
  6517. // WRITE_ACCESS | READ_ACCESS,
  6518. DELETE | READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
  6519. OPEN_OPTIONS);
  6520. if (!WIN_SUCCESS(WStatus)) {
  6521. DPRINT1_WS(0, ":S: ERROR - Cannot open stage of replica tree %ws;",
  6522. DbReplica->Root, WStatus);
  6523. continue;
  6524. }
  6525. //
  6526. // Delete files/subdirs
  6527. //
  6528. FrsEnumerateDirectory(FileHandle,
  6529. DbReplica->Stage,
  6530. 0,
  6531. ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE,
  6532. NULL,
  6533. FrsEnumerateDirectoryDeleteWorker);
  6534. DPRINT1(4, ":S: Deleted files/subdirs for %ws\n", DbReplica->Stage);
  6535. FRS_CLOSE(FileHandle);
  6536. }
  6537. WStatus = SaveWStatus;
  6538. if (!WIN_SUCCESS(WStatus)) {
  6539. goto CLEANUP;
  6540. }
  6541. //
  6542. // SUCCESS
  6543. //
  6544. WStatus = ERROR_SUCCESS;
  6545. DPRINT(4, ":S: Successfully marked tombstoned sysvols as do not animate.\n");
  6546. //
  6547. // CLEANUP
  6548. //
  6549. CLEANUP:
  6550. FRS_CLOSE(FileHandle);
  6551. return WStatus;
  6552. }
  6553. DWORD
  6554. FrsDsStartDemotion(
  6555. IN PWCHAR ReplicaSetName
  6556. )
  6557. /*++
  6558. Routine Description:
  6559. Start the demotion process by tombstoning the sysvol.
  6560. Arguments:
  6561. ReplicaSetName - Replica set name
  6562. Return Value:
  6563. Win32 Status
  6564. --*/
  6565. {
  6566. #undef DEBSUB
  6567. #define DEBSUB "FrsDsStartDemotion:"
  6568. DWORD WStatus;
  6569. DWORD FStatus;
  6570. DWORD DbReplicaSetType;
  6571. PREPLICA DbReplica;
  6572. //
  6573. // SHUTDOWN THE DS POLLING THREAD
  6574. // Demotion can run in parallel with the Ds polling thread iff
  6575. // the polling thread never tries to merge the info in the Ds
  6576. // with the active replicas. This could result in the sysvol
  6577. // replica being animated. So, we tell the Ds polling thead to
  6578. // shut down, wake it up if it is asleep so it can see the shutdown
  6579. // request, and then synchronize with the merging code in the
  6580. // Ds polling thread. We don't want to wait for the polling
  6581. // thread to simply die because it may be stuck talking to the
  6582. // Ds. Alternatively, we could use async ldap but that would
  6583. // take too long and is overkill at this time.
  6584. //
  6585. // In any case, the service will be restarted after dcpromo/demote
  6586. // by a reboot or a restart-service by the ntfrsapi.
  6587. //
  6588. //
  6589. // PERF: should use async ldap in polling thread.
  6590. //
  6591. DsIsShuttingDown = TRUE;
  6592. SetEvent(DsPollEvent);
  6593. EnterCriticalSection(&MergingReplicasWithDs);
  6594. LeaveCriticalSection(&MergingReplicasWithDs);
  6595. //
  6596. // Is the service shutting down?
  6597. //
  6598. if (FrsIsShuttingDown) {
  6599. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  6600. goto cleanup;
  6601. }
  6602. //
  6603. // WAIT FOR THE ACTIVE REPLICATION SUBSYSTEM TO START
  6604. //
  6605. MainInit();
  6606. if (!MainInitHasRun) {
  6607. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  6608. goto cleanup;
  6609. }
  6610. //
  6611. // Let dcpromo determine the timeout
  6612. //
  6613. DPRINT(4, ":S: Waiting for replica command server to start.\n");
  6614. WStatus = WaitForSingleObject(ReplicaEvent, 30 * 60 * 1000);
  6615. CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
  6616. //
  6617. // TOMBSTONE THE REPLICA SET IN THE ACTIVE REPLICATION SUBSYSTEM
  6618. //
  6619. //
  6620. // Find the sysvol replica and tombstone it.
  6621. //
  6622. DbReplica = RcsFindSysVolByName(ReplicaSetName);
  6623. //
  6624. // Can't find by name, not the enterprise sysvol, and not the
  6625. // special call during promotion. See if the name of the domain
  6626. // sysvol was mapped into CN_DOMAIN_SYSVOL. (B3 naming)
  6627. //
  6628. if (!DbReplica &&
  6629. WSTR_NE(ReplicaSetName, L"enterprise") &&
  6630. WSTR_NE(ReplicaSetName, L"")) {
  6631. //
  6632. // domain name may have been mapped into CN_DOMAIN_SYSVOL (new B3 naming)
  6633. //
  6634. DbReplica = RcsFindSysVolByName(CN_DOMAIN_SYSVOL);
  6635. }
  6636. if (DbReplica) {
  6637. //
  6638. // Tombstone the replica set. The set won't actually be deleted
  6639. // until the tombstone expires. If dcdemote fails the replica set
  6640. // will be reanimated when the service restarts.
  6641. //
  6642. // If dcdemote succeeds, the tombstone expiration will be set to
  6643. // yesterday so the replica set will never be animated. See
  6644. // FrsDsCommitDemotion.
  6645. //
  6646. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, NULL, CMD_DELETE);
  6647. CLEANUP2_WS(0, ":S: ERROR - can't delete %ws on %ws;",
  6648. DbReplica->ReplicaName->Name, ComputerName, WStatus, cleanup);
  6649. DPRINT2(0, ":S: Deleted %ws on %ws\n", ReplicaSetName, ComputerName);
  6650. } else if (!wcscmp(ReplicaSetName, L"")) {
  6651. //
  6652. // Special case called during promotion. Delete existing sysvols
  6653. // that may exist from a previous full install or stale database.
  6654. //
  6655. // Make sure the sysvol doesn't already exist. If it does but is
  6656. // tombstoned, set the tombstone to "do not animate". Otherwise,
  6657. // error off.
  6658. //
  6659. DbReplicaSetType = FRS_RSTYPE_ENTERPRISE_SYSVOL;
  6660. again:
  6661. DbReplica = RcsFindSysVolByType(DbReplicaSetType);
  6662. if (!DbReplica) {
  6663. if (DbReplicaSetType == FRS_RSTYPE_ENTERPRISE_SYSVOL) {
  6664. DbReplicaSetType = FRS_RSTYPE_DOMAIN_SYSVOL;
  6665. goto again;
  6666. }
  6667. }
  6668. if (DbReplica) {
  6669. DPRINT2(4, ":S: WARN - Sysvol %ws exists for %ws; deleting!\n",
  6670. DbReplica->ReplicaName->Name, ComputerName);
  6671. //
  6672. // Find our role. If we aren't a DC or the sysvol has been
  6673. // tombstoned, delete it now.
  6674. //
  6675. FrsDsGetRole();
  6676. if (!IS_TIME_ZERO(DbReplica->MembershipExpires) || !IsADc) {
  6677. //
  6678. // Once the MembershipExpires has been set to a time less
  6679. // than Now the replica set will never appear again. The
  6680. // replica sticks around for now since the RPC server
  6681. // may be putting command packets on this replica's queue.
  6682. // The packets will be ignored. The replica will be deleted
  6683. // from the database the next time the service starts. Even
  6684. // if the deletion fails, the rest of the service will
  6685. // not see the replica because the replica struct is not
  6686. // put in the table of active replicas. The deletion is
  6687. // retried at startup.
  6688. //
  6689. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, NULL, CMD_DELETE_NOW);
  6690. CLEANUP1_WS(0, ":S: ERROR - can't delete %ws;",
  6691. DbReplica->ReplicaName->Name, WStatus, cleanup);
  6692. goto again;
  6693. } else {
  6694. DPRINT2(0, ":S: ERROR - Cannot delete %ws for %ws!\n",
  6695. DbReplica->ReplicaName->Name, ComputerName);
  6696. WStatus = ERROR_DUP_NAME;
  6697. goto cleanup;
  6698. }
  6699. }
  6700. } else {
  6701. DPRINT1(0, ":S: Sysvol %ws not found; declaring victory\n", ReplicaSetName);
  6702. }
  6703. //
  6704. // SUCCESS
  6705. //
  6706. WStatus = ERROR_SUCCESS;
  6707. //
  6708. // CLEANUP
  6709. //
  6710. cleanup:
  6711. return WStatus;
  6712. }
  6713. VOID
  6714. FrsDsFreeTree(
  6715. PCONFIG_NODE Root
  6716. )
  6717. /*++
  6718. Routine Description:
  6719. Free every node in a tree
  6720. Arguments:
  6721. Root
  6722. Return Value:
  6723. None.
  6724. --*/
  6725. {
  6726. #undef DEBSUB
  6727. #define DEBSUB "FrsDsFreeTree:"
  6728. PCONFIG_NODE Node;
  6729. while (Root != NULL) {
  6730. Node = Root;
  6731. Root = Root->Peer;
  6732. FrsDsFreeTree(Node->Children);
  6733. FrsFreeType(Node);
  6734. }
  6735. }
  6736. VOID
  6737. FrsDsSwapPtrs(
  6738. PVOID *P1,
  6739. PVOID *P2
  6740. )
  6741. /*++
  6742. Routine Description:
  6743. Swap two pointers.
  6744. Arguments:
  6745. P1 - address of a pointer
  6746. P2 - address of a pointer
  6747. Return Value:
  6748. None.
  6749. --*/
  6750. {
  6751. #undef DEBSUB
  6752. #define DEBSUB "FrsDsSwapPtrs:"
  6753. PVOID Tmp;
  6754. Tmp = *P2;
  6755. *P2 = *P1;
  6756. *P1 = Tmp;
  6757. }
  6758. #if DBG
  6759. //
  6760. // Hardwired configuration for testing w/o the DS
  6761. //
  6762. #define HW_MACHINES 8
  6763. #define THIS_COMPUTER L"[This Computer]"
  6764. typedef struct _HardWired{
  6765. PWCHAR Machine;
  6766. PWCHAR Server;
  6767. PWCHAR Replica;
  6768. BOOL IsPrimary;
  6769. PWCHAR FileFilterList;
  6770. PWCHAR DirFilterList;
  6771. PWCHAR InNames[HW_MACHINES];
  6772. PWCHAR InMachines[HW_MACHINES];
  6773. PWCHAR InServers[HW_MACHINES];
  6774. PWCHAR OutNames[HW_MACHINES];
  6775. PWCHAR OutMachines[HW_MACHINES];
  6776. PWCHAR OutServers[HW_MACHINES];
  6777. PWCHAR Stage;
  6778. PWCHAR Root;
  6779. PWCHAR JetPath;
  6780. } HARDWIRED, *PHARDWIRED;
  6781. //
  6782. // This hard wired configuration is loaded if a path
  6783. // to a ini file is provided at command line.
  6784. //
  6785. PHARDWIRED LoadedWired;
  6786. HARDWIRED DavidWired[] = {
  6787. /*
  6788. t:
  6789. cd \
  6790. md \staging
  6791. md \Replica-A\SERVO1
  6792. md \jet
  6793. md \jet\serv01
  6794. md \jet\serv01\sys
  6795. md \jet\serv01\temp
  6796. md \jet\serv01\log
  6797. u:
  6798. cd \
  6799. md \staging
  6800. md \Replica-A\SERVO2
  6801. md \jet
  6802. md \jet\serv02
  6803. md \jet\serv02\sys
  6804. md \jet\serv02\temp
  6805. md \jet\serv02\log
  6806. s:
  6807. cd \
  6808. md \staging
  6809. md \Replica-A\SERVO3
  6810. md \jet
  6811. md \jet\serv03
  6812. md \jet\serv03\sys
  6813. md \jet\serv03\temp
  6814. md \jet\serv03\log
  6815. */
  6816. #define RSA L"Replica-A"
  6817. #define TEST_MACHINE_NAME THIS_COMPUTER
  6818. #define SERVER_1 L"SERV01"
  6819. #define MACHINE_1 TEST_MACHINE_NAME
  6820. #define SERVER_2 L"SERV02"
  6821. #define MACHINE_2 TEST_MACHINE_NAME
  6822. #define SERVER_3 L"SERV03"
  6823. #define MACHINE_3 TEST_MACHINE_NAME
  6824. #define SERVER_4 L"SERV04"
  6825. #define MACHINE_4 TEST_MACHINE_NAME
  6826. #define SERVER_5 L"SERV05"
  6827. #define MACHINE_5 TEST_MACHINE_NAME
  6828. #define SERVER_6 L"SERV06"
  6829. #define MACHINE_6 TEST_MACHINE_NAME
  6830. #define SERVER_7 L"SERV07"
  6831. #define MACHINE_7 TEST_MACHINE_NAME
  6832. #define SERVER_8 L"SERV08"
  6833. #define MACHINE_8 TEST_MACHINE_NAME
  6834. /*
  6835. // These are the old vol assignments
  6836. #define SERVER_1_VOL L"t:"
  6837. #define SERVER_2_VOL L"u:"
  6838. #define SERVER_3_VOL L"s:"
  6839. #define SERVER_4_VOL L"v:"
  6840. #define SERVER_5_VOL L"w:"
  6841. #define SERVER_6_VOL L"x:"
  6842. #define SERVER_7_VOL L"y:"
  6843. #define SERVER_8_VOL L"z:"
  6844. */
  6845. // /*
  6846. // These are the new vol assignments
  6847. #define SERVER_1_VOL L"d:"
  6848. #define SERVER_2_VOL L"e:"
  6849. #define SERVER_3_VOL L"f:"
  6850. #define SERVER_4_VOL L"g:"
  6851. #define SERVER_5_VOL L"h:"
  6852. #define SERVER_6_VOL L"i:"
  6853. #define SERVER_7_VOL L"j:"
  6854. #define SERVER_8_VOL L"k:"
  6855. // */
  6856. /*
  6857. //
  6858. // NOTE: The following was generated from an excel spreadsheet
  6859. // \nt\private\net\svcimgs\ntrepl\topology.xls
  6860. // Hand generation is a bit tedious and error prone so use the spreadsheet.
  6861. //
  6862. //
  6863. // David's 8-way fully connected
  6864. //
  6865. TEST_MACHINE_NAME, // machine
  6866. SERVER_1, // server name
  6867. RSA, // replica
  6868. TRUE, // IsPrimary
  6869. NULL, NULL, // File/Dir Filter
  6870. {L"CXT2_1", L"CXT3_1", L"CXT4_1", L"CXT5_1", L"CXT6_1", L"CXT7_1", L"CXT8_1", NULL}, // inbound cxtions
  6871. {MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  6872. {SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  6873. {L"CXT1_2", L"CXT1_3", L"CXT1_4", L"CXT1_5", L"CXT1_6", L"CXT1_7", L"CXT1_8", NULL}, // outbound cxtions
  6874. {MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  6875. {SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  6876. SERVER_1_VOL L"\\staging", // stage
  6877. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  6878. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  6879. TEST_MACHINE_NAME, // machine
  6880. SERVER_2, // server name
  6881. RSA, // replica
  6882. FALSE, // IsPrimary
  6883. NULL, NULL, // File/Dir Filter
  6884. {L"CXT1_2", L"CXT3_2", L"CXT4_2", L"CXT5_2", L"CXT6_2", L"CXT7_2", L"CXT8_2", NULL}, // inbound cxtions
  6885. {MACHINE_1, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  6886. {SERVER_1, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  6887. {L"CXT2_1", L"CXT2_3", L"CXT2_4", L"CXT2_5", L"CXT2_6", L"CXT2_7", L"CXT2_8", NULL}, // outbound cxtions
  6888. {MACHINE_1, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  6889. {SERVER_1, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  6890. SERVER_2_VOL L"\\staging", // stage
  6891. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  6892. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  6893. TEST_MACHINE_NAME, // machine
  6894. SERVER_3, // server name
  6895. RSA, // replica
  6896. FALSE, // IsPrimary
  6897. NULL, NULL, // File/Dir Filter
  6898. {L"CXT1_3", L"CXT2_3", L"CXT4_3", L"CXT5_3", L"CXT6_3", L"CXT7_3", L"CXT8_3", NULL}, // inbound cxtions
  6899. {MACHINE_1, MACHINE_2, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  6900. {SERVER_1, SERVER_2, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  6901. {L"CXT3_1", L"CXT3_2", L"CXT3_4", L"CXT3_5", L"CXT3_6", L"CXT3_7", L"CXT3_8", NULL}, // outbound cxtions
  6902. {MACHINE_1, MACHINE_2, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  6903. {SERVER_1, SERVER_2, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  6904. SERVER_3_VOL L"\\staging", // stage
  6905. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  6906. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  6907. TEST_MACHINE_NAME, // machine
  6908. SERVER_4, // server name
  6909. RSA, // replica
  6910. FALSE, // IsPrimary
  6911. NULL, NULL, // File/Dir Filter
  6912. {L"CXT1_4", L"CXT2_4", L"CXT3_4", L"CXT5_4", L"CXT6_4", L"CXT7_4", L"CXT8_4", NULL}, // inbound cxtions
  6913. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  6914. {SERVER_1, SERVER_2, SERVER_3, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  6915. {L"CXT4_1", L"CXT4_2", L"CXT4_3", L"CXT4_5", L"CXT4_6", L"CXT4_7", L"CXT4_8", NULL}, // outbound cxtions
  6916. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  6917. {SERVER_1, SERVER_2, SERVER_3, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  6918. SERVER_4_VOL L"\\staging", // stage
  6919. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  6920. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  6921. TEST_MACHINE_NAME, // machine
  6922. SERVER_5, // server name
  6923. RSA, // replica
  6924. FALSE, // IsPrimary
  6925. NULL, NULL, // File/Dir Filter
  6926. {L"CXT1_5", L"CXT2_5", L"CXT3_5", L"CXT4_5", L"CXT6_5", L"CXT7_5", L"CXT8_5", NULL}, // inbound cxtions
  6927. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  6928. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  6929. {L"CXT5_1", L"CXT5_2", L"CXT5_3", L"CXT5_4", L"CXT5_6", L"CXT5_7", L"CXT5_8", NULL}, // outbound cxtions
  6930. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  6931. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  6932. SERVER_5_VOL L"\\staging", // stage
  6933. SERVER_5_VOL L"\\" RSA L"\\" SERVER_5, // root
  6934. SERVER_5_VOL L"\\jet\\" SERVER_5, // Jet Path
  6935. TEST_MACHINE_NAME, // machine
  6936. SERVER_6, // server name
  6937. RSA, // replica
  6938. FALSE, // IsPrimary
  6939. NULL, NULL, // File/Dir Filter
  6940. {L"CXT1_6", L"CXT2_6", L"CXT3_6", L"CXT4_6", L"CXT5_6", L"CXT7_6", L"CXT8_6", NULL}, // inbound cxtions
  6941. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  6942. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_7, SERVER_8, NULL}, // inbound servers
  6943. {L"CXT6_1", L"CXT6_2", L"CXT6_3", L"CXT6_4", L"CXT6_5", L"CXT6_7", L"CXT6_8", NULL}, // outbound cxtions
  6944. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  6945. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_7, SERVER_8, NULL}, // outbound servers
  6946. SERVER_6_VOL L"\\staging", // stage
  6947. SERVER_6_VOL L"\\" RSA L"\\" SERVER_6, // root
  6948. SERVER_6_VOL L"\\jet\\" SERVER_6, // Jet Path
  6949. TEST_MACHINE_NAME, // machine
  6950. SERVER_7, // server name
  6951. RSA, // replica
  6952. FALSE, // IsPrimary
  6953. NULL, NULL, // File/Dir Filter
  6954. {L"CXT1_7", L"CXT2_7", L"CXT3_7", L"CXT4_7", L"CXT5_7", L"CXT6_7", L"CXT8_7", NULL}, // inbound cxtions
  6955. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_8, NULL}, // inbound machines
  6956. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_8, NULL}, // inbound servers
  6957. {L"CXT7_1", L"CXT7_2", L"CXT7_3", L"CXT7_4", L"CXT7_5", L"CXT7_6", L"CXT7_8", NULL}, // outbound cxtions
  6958. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_8, NULL}, // outbound machines
  6959. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_8, NULL}, // outbound servers
  6960. SERVER_7_VOL L"\\staging", // stage
  6961. SERVER_7_VOL L"\\" RSA L"\\" SERVER_7, // root
  6962. SERVER_7_VOL L"\\jet\\" SERVER_7, // Jet Path
  6963. TEST_MACHINE_NAME, // machine
  6964. SERVER_8, // server name
  6965. RSA, // replica
  6966. FALSE, // IsPrimary
  6967. NULL, NULL, // File/Dir Filter
  6968. {L"CXT1_8", L"CXT2_8", L"CXT3_8", L"CXT4_8", L"CXT5_8", L"CXT6_8", L"CXT7_8", NULL}, // inbound cxtions
  6969. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, NULL}, // inbound machines
  6970. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, NULL}, // inbound servers
  6971. {L"CXT8_1", L"CXT8_2", L"CXT8_3", L"CXT8_4", L"CXT8_5", L"CXT8_6", L"CXT8_7", NULL}, // outbound cxtions
  6972. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, NULL}, // outbound machines
  6973. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, NULL}, // outbound servers
  6974. SERVER_8_VOL L"\\staging", // stage
  6975. SERVER_8_VOL L"\\" RSA L"\\" SERVER_8, // root
  6976. SERVER_8_VOL L"\\jet\\" SERVER_8, // Jet Path
  6977. */
  6978. ///*
  6979. //
  6980. // 8 way ring
  6981. //
  6982. TEST_MACHINE_NAME, // machine
  6983. SERVER_1, // server name
  6984. RSA, // replica
  6985. TRUE, // IsPrimary
  6986. NULL, NULL, // File/Dir Filter
  6987. {L"CXT2_1", L"CXT8_1", NULL }, // inbound cxtions
  6988. {MACHINE_2, MACHINE_8, NULL }, // inbound machines
  6989. {SERVER_2, SERVER_8, NULL }, // inbound servers
  6990. {L"CXT1_2", L"CXT1_8", NULL }, // outbound cxtions
  6991. {MACHINE_2, MACHINE_8, NULL }, // outbound machines
  6992. {SERVER_2, SERVER_8, NULL }, // outbound servers
  6993. SERVER_1_VOL L"\\staging", // stage
  6994. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  6995. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  6996. TEST_MACHINE_NAME, // machine
  6997. SERVER_2, // server name
  6998. RSA, // replica
  6999. FALSE, // IsPrimary
  7000. NULL, NULL, // File/Dir Filter
  7001. {L"CXT3_2", L"CXT1_2", NULL }, // inbound cxtions
  7002. {MACHINE_3, MACHINE_1, NULL }, // inbound machines
  7003. {SERVER_3, SERVER_1, NULL }, // inbound servers
  7004. {L"CXT2_3", L"CXT2_1", NULL }, // outbound cxtions
  7005. {MACHINE_3, MACHINE_1, NULL }, // outbound machines
  7006. {SERVER_3, SERVER_1, NULL }, // outbound servers
  7007. SERVER_2_VOL L"\\staging", // stage
  7008. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  7009. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  7010. TEST_MACHINE_NAME, // machine
  7011. SERVER_3, // server name
  7012. RSA, // replica
  7013. FALSE, // IsPrimary
  7014. NULL, NULL, // File/Dir Filter
  7015. {L"CXT4_3", L"CXT2_3", NULL }, // inbound cxtions
  7016. {MACHINE_4, MACHINE_2, NULL }, // inbound machines
  7017. {SERVER_4, SERVER_2, NULL }, // inbound servers
  7018. {L"CXT3_4", L"CXT3_2", NULL }, // outbound cxtions
  7019. {MACHINE_4, MACHINE_2, NULL }, // outbound machines
  7020. {SERVER_4, SERVER_2, NULL }, // outbound servers
  7021. SERVER_3_VOL L"\\staging", // stage
  7022. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  7023. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  7024. TEST_MACHINE_NAME, // machine
  7025. SERVER_4, // server name
  7026. RSA, // replica
  7027. FALSE, // IsPrimary
  7028. NULL, NULL, // File/Dir Filter
  7029. {L"CXT5_4", L"CXT3_4", NULL }, // inbound cxtions
  7030. {MACHINE_5, MACHINE_3, NULL }, // inbound machines
  7031. {SERVER_5, SERVER_3, NULL }, // inbound servers
  7032. {L"CXT4_5", L"CXT4_3", NULL }, // outbound cxtions
  7033. {MACHINE_5, MACHINE_3, NULL }, // outbound machines
  7034. {SERVER_5, SERVER_3, NULL }, // outbound servers
  7035. SERVER_4_VOL L"\\staging", // stage
  7036. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  7037. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  7038. TEST_MACHINE_NAME, // machine
  7039. SERVER_5, // server name
  7040. RSA, // replica
  7041. FALSE, // IsPrimary
  7042. NULL, NULL, // File/Dir Filter
  7043. {L"CXT6_5", L"CXT4_5", NULL }, // inbound cxtions
  7044. {MACHINE_6, MACHINE_4, NULL }, // inbound machines
  7045. {SERVER_6, SERVER_4, NULL }, // inbound servers
  7046. {L"CXT5_6", L"CXT5_4", NULL }, // outbound cxtions
  7047. {MACHINE_6, MACHINE_4, NULL }, // outbound machines
  7048. {SERVER_6, SERVER_4, NULL }, // outbound servers
  7049. SERVER_5_VOL L"\\staging", // stage
  7050. SERVER_5_VOL L"\\" RSA L"\\" SERVER_5, // root
  7051. SERVER_5_VOL L"\\jet\\" SERVER_5, // Jet Path
  7052. TEST_MACHINE_NAME, // machine
  7053. SERVER_6, // server name
  7054. RSA, // replica
  7055. FALSE, // IsPrimary
  7056. NULL, NULL, // File/Dir Filter
  7057. {L"CXT7_6", L"CXT5_6", NULL }, // inbound cxtions
  7058. {MACHINE_7, MACHINE_5, NULL }, // inbound machines
  7059. {SERVER_7, SERVER_5, NULL }, // inbound servers
  7060. {L"CXT6_7", L"CXT6_5", NULL }, // outbound cxtions
  7061. {MACHINE_7, MACHINE_5, NULL }, // outbound machines
  7062. {SERVER_7, SERVER_5, NULL }, // outbound servers
  7063. SERVER_6_VOL L"\\staging", // stage
  7064. SERVER_6_VOL L"\\" RSA L"\\" SERVER_6, // root
  7065. SERVER_6_VOL L"\\jet\\" SERVER_6, // Jet Path
  7066. TEST_MACHINE_NAME, // machine
  7067. SERVER_7, // server name
  7068. RSA, // replica
  7069. FALSE, // IsPrimary
  7070. NULL, NULL, // File/Dir Filter
  7071. {L"CXT8_7", L"CXT6_7", NULL }, // inbound cxtions
  7072. {MACHINE_8, MACHINE_6, NULL }, // inbound machines
  7073. {SERVER_8, SERVER_6, NULL }, // inbound servers
  7074. {L"CXT7_8", L"CXT7_6", NULL }, // outbound cxtions
  7075. {MACHINE_8, MACHINE_6, NULL }, // outbound machines
  7076. {SERVER_8, SERVER_6, NULL }, // outbound servers
  7077. SERVER_7_VOL L"\\staging", // stage
  7078. SERVER_7_VOL L"\\" RSA L"\\" SERVER_7, // root
  7079. SERVER_7_VOL L"\\jet\\" SERVER_7, // Jet Path
  7080. TEST_MACHINE_NAME, // machine
  7081. SERVER_8, // server name
  7082. RSA, // replica
  7083. FALSE, // IsPrimary
  7084. NULL, NULL, // File/Dir Filter
  7085. {L"CXT1_8", L"CXT7_8", NULL }, // inbound cxtions
  7086. {MACHINE_1, MACHINE_7, NULL }, // inbound machines
  7087. {SERVER_1, SERVER_7, NULL }, // inbound servers
  7088. {L"CXT8_1", L"CXT8_7", NULL }, // outbound cxtions
  7089. {MACHINE_1, MACHINE_7, NULL }, // outbound machines
  7090. {SERVER_1, SERVER_7, NULL }, // outbound servers
  7091. SERVER_8_VOL L"\\staging", // stage
  7092. SERVER_8_VOL L"\\" RSA L"\\" SERVER_8, // root
  7093. SERVER_8_VOL L"\\jet\\" SERVER_8, // Jet Path
  7094. //*/
  7095. #define CXT_2_FM_1 L"CXT1_2"
  7096. #define CXT_3_FM_1 L"CXT1_3"
  7097. #define CXT_4_FM_1 L"CXT1_4"
  7098. #define CXT_1_FM_2 L"CXT2_1"
  7099. #define CXT_3_FM_2 L"CXT2_3"
  7100. #define CXT_4_FM_2 L"CXT2_4"
  7101. #define CXT_1_FM_3 L"CXT3_1"
  7102. #define CXT_2_FM_3 L"CXT3_2"
  7103. #define CXT_4_FM_3 L"CXT3_4"
  7104. #define CXT_1_FM_4 L"CXT4_1"
  7105. #define CXT_2_FM_4 L"CXT4_2"
  7106. #define CXT_3_FM_4 L"CXT4_3"
  7107. #define CXT_1_TO_2 L"CXT1_2"
  7108. #define CXT_1_TO_3 L"CXT1_3"
  7109. #define CXT_1_TO_4 L"CXT1_4"
  7110. #define CXT_2_TO_1 L"CXT2_1"
  7111. #define CXT_2_TO_3 L"CXT2_3"
  7112. #define CXT_2_TO_4 L"CXT2_4"
  7113. #define CXT_3_TO_1 L"CXT3_1"
  7114. #define CXT_3_TO_2 L"CXT3_2"
  7115. #define CXT_3_TO_4 L"CXT3_4"
  7116. #define CXT_4_TO_1 L"CXT4_1"
  7117. #define CXT_4_TO_2 L"CXT4_2"
  7118. #define CXT_4_TO_3 L"CXT4_3"
  7119. /*
  7120. //
  7121. // David's 4-way
  7122. //
  7123. TEST_MACHINE_NAME, // machine
  7124. SERVER_1, // server name
  7125. RSA, // replica
  7126. TRUE, // IsPrimary
  7127. NULL, NULL, // File/Dir Filter
  7128. { CXT_1_FM_2, CXT_1_FM_3, CXT_1_FM_4, NULL }, // inbound cxtions
  7129. { MACHINE_2, MACHINE_3, MACHINE_4, NULL }, // inbound machines
  7130. { SERVER_2, SERVER_3, SERVER_4, NULL }, // inbound servers
  7131. { CXT_1_TO_2, CXT_1_TO_3, CXT_1_TO_4, NULL }, // outbound cxtions
  7132. { MACHINE_2, MACHINE_3, MACHINE_4, NULL }, // outbound machines
  7133. { SERVER_2, SERVER_3, SERVER_4, NULL }, // outbound servers
  7134. SERVER_1_VOL L"\\staging", // stage
  7135. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  7136. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  7137. TEST_MACHINE_NAME, // machine
  7138. SERVER_2, // server name
  7139. RSA, // replica
  7140. FALSE, // IsPrimary
  7141. NULL, NULL, // File/Dir Filter
  7142. { CXT_2_FM_1, CXT_2_FM_3, CXT_2_FM_4, NULL }, // inbound cxtions
  7143. { MACHINE_1, MACHINE_3, MACHINE_4, NULL }, // inbound machines
  7144. { SERVER_1, SERVER_3, SERVER_4, NULL }, // inbound servers
  7145. { CXT_2_TO_1, CXT_2_TO_3, CXT_2_TO_4, NULL }, // outbound cxtions
  7146. { MACHINE_1, MACHINE_3, MACHINE_4, NULL }, // outbound machines
  7147. { SERVER_1, SERVER_3, SERVER_4, NULL }, // outbound servers
  7148. SERVER_2_VOL L"\\staging", // stage
  7149. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  7150. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  7151. TEST_MACHINE_NAME, // machine
  7152. SERVER_3, // server name
  7153. RSA, // replica
  7154. FALSE, // IsPrimary
  7155. NULL, NULL, // File/Dir Filter
  7156. { CXT_3_FM_1, CXT_3_FM_2, CXT_3_FM_4, NULL }, // inbound cxtions
  7157. { MACHINE_1, MACHINE_2, MACHINE_4, NULL }, // inbound machines
  7158. { SERVER_1, SERVER_2, SERVER_4, NULL }, // inbound servers
  7159. { CXT_3_TO_1, CXT_3_TO_2, CXT_3_TO_4, NULL }, // outbound cxtions
  7160. { MACHINE_1, MACHINE_2, MACHINE_4, NULL }, // outbound machines
  7161. { SERVER_1, SERVER_2, SERVER_4, NULL }, // outbound servers
  7162. SERVER_3_VOL L"\\staging", // stage
  7163. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  7164. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  7165. TEST_MACHINE_NAME, // machine
  7166. SERVER_4, // server name
  7167. RSA, // replica
  7168. FALSE, // IsPrimary
  7169. NULL, NULL, // File/Dir Filter
  7170. { CXT_4_FM_1, CXT_4_FM_2, CXT_4_FM_3, NULL }, // inbound cxtions
  7171. { MACHINE_1, MACHINE_2, MACHINE_3, NULL }, // inbound machines
  7172. { SERVER_1, SERVER_2, SERVER_3, NULL }, // inbound servers
  7173. { CXT_4_TO_1, CXT_4_TO_2, CXT_4_TO_3, NULL }, // outbound cxtions
  7174. { MACHINE_1, MACHINE_2, MACHINE_3, NULL }, // outbound machines
  7175. { SERVER_1, SERVER_2, SERVER_3, NULL }, // outbound servers
  7176. SERVER_4_VOL L"\\staging", // stage
  7177. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  7178. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  7179. */
  7180. /*
  7181. //
  7182. // David's 3-way
  7183. //
  7184. TEST_MACHINE_NAME, // machine
  7185. SERVER_1, // server name
  7186. RSA, // replica
  7187. TRUE, // IsPrimary
  7188. NULL, NULL, // File/Dir Filter
  7189. { CXT_1_FM_2, CXT_1_FM_3, NULL }, // inbound cxtions
  7190. { MACHINE_2, MACHINE_3, NULL }, // inbound machines
  7191. { SERVER_2, SERVER_3, NULL }, // inbound servers
  7192. { CXT_1_TO_2, CXT_1_TO_3, NULL }, // outbound cxtions
  7193. { MACHINE_2, MACHINE_3, NULL }, // outbound machines
  7194. { SERVER_2, SERVER_3, NULL }, // outbound servers
  7195. SERVER_1_VOL L"\\staging", // stage
  7196. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  7197. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  7198. TEST_MACHINE_NAME, // machine
  7199. SERVER_2, // server name
  7200. RSA, // replica
  7201. FALSE, // IsPrimary
  7202. NULL, NULL, // File/Dir Filter
  7203. { CXT_2_FM_1, CXT_2_FM_3, NULL }, // inbound cxtions
  7204. { MACHINE_1, MACHINE_3, NULL }, // inbound machines
  7205. { SERVER_1, SERVER_3, NULL }, // inbound servers
  7206. { CXT_2_TO_1, CXT_2_TO_3, NULL }, // outbound cxtions
  7207. { MACHINE_1, MACHINE_3, NULL }, // outbound machines
  7208. { SERVER_1, SERVER_3, NULL }, // outbound servers
  7209. SERVER_2_VOL L"\\staging", // stage
  7210. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  7211. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  7212. TEST_MACHINE_NAME, // machine
  7213. SERVER_3, // server name
  7214. RSA, // replica
  7215. FALSE, // IsPrimary
  7216. NULL, NULL, // File/Dir Filter
  7217. { CXT_3_FM_1, CXT_3_FM_2, NULL }, // inbound cxtions
  7218. { MACHINE_1, MACHINE_2, NULL }, // inbound machines
  7219. { SERVER_1, SERVER_2, NULL }, // inbound servers
  7220. { CXT_3_TO_1, CXT_3_TO_2, NULL }, // outbound cxtions
  7221. { MACHINE_1, MACHINE_2, NULL }, // outbound machines
  7222. { SERVER_1, SERVER_2, NULL }, // outbound servers
  7223. SERVER_3_VOL L"\\staging", // stage
  7224. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  7225. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  7226. */
  7227. /*
  7228. //
  7229. // David's 1-way
  7230. //
  7231. TEST_MACHINE_NAME, // machine
  7232. SERVER_1, // server name
  7233. RSA, // replica
  7234. TRUE, // IsPrimary
  7235. NULL, NULL, // File/Dir Filter
  7236. { NULL, NULL }, // inbound cxtions
  7237. { NULL, NULL }, // inbound machines
  7238. { NULL, NULL }, // inbound servers
  7239. { CXT_1_TO_2, NULL }, // outbound cxtions
  7240. { MACHINE_2, NULL }, // outbound machines
  7241. { SERVER_2, NULL }, // outbound servers
  7242. SERVER_1_VOL L"\\staging", // stage
  7243. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  7244. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  7245. TEST_MACHINE_NAME, // machine
  7246. SERVER_2, // server name
  7247. RSA, // replica
  7248. FALSE, // IsPrimary
  7249. NULL, NULL, // File/Dir Filter
  7250. { CXT_2_FM_1, NULL }, // inbound cxtions
  7251. { MACHINE_1, NULL }, // inbound machines
  7252. { SERVER_1, NULL }, // inbound servers
  7253. { NULL, NULL }, // outbound cxtions
  7254. { NULL, NULL }, // outbound machines
  7255. { NULL, NULL }, // outbound servers
  7256. SERVER_2_VOL L"\\staging", // stage
  7257. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  7258. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  7259. */
  7260. /*
  7261. //
  7262. // David's 2-way
  7263. //
  7264. TEST_MACHINE_NAME, // machine
  7265. SERVER_1, // server name
  7266. RSA, // replica
  7267. TRUE, // IsPrimary
  7268. NULL, NULL, // File/Dir Filter
  7269. { CXT_1_FM_2, NULL }, // inbound cxtions
  7270. { MACHINE_2, NULL }, // inbound machines
  7271. { SERVER_2, NULL }, // inbound servers
  7272. { CXT_1_TO_2, NULL }, // outbound cxtions
  7273. { MACHINE_2, NULL }, // outbound machines
  7274. { SERVER_2, NULL }, // outbound servers
  7275. SERVER_1_VOL L"\\staging", // stage
  7276. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  7277. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  7278. TEST_MACHINE_NAME, // machine
  7279. SERVER_2, // server name
  7280. RSA, // replica
  7281. FALSE, // IsPrimary
  7282. NULL, NULL, // File/Dir Filter
  7283. { CXT_2_FM_1, NULL }, // inbound cxtions
  7284. { MACHINE_1, NULL }, // inbound machines
  7285. { SERVER_1, NULL }, // inbound servers
  7286. { CXT_2_TO_1, NULL }, // outbound cxtions
  7287. { MACHINE_1, NULL }, // outbound machines
  7288. { SERVER_1, NULL }, // outbound servers
  7289. SERVER_2_VOL L"\\staging", // stage
  7290. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  7291. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  7292. */
  7293. //
  7294. // End of Config
  7295. //
  7296. NULL, NULL
  7297. };
  7298. HARDWIRED DavidWired2[] = {
  7299. ///*
  7300. //
  7301. // 8 way ring Without Server2
  7302. //
  7303. TEST_MACHINE_NAME, // machine
  7304. SERVER_1, // server name
  7305. RSA, // replica
  7306. TRUE, // IsPrimary
  7307. NULL, NULL, // File/Dir Filter
  7308. {L"CXT2_1", L"CXT8_1", NULL }, // inbound cxtions
  7309. {MACHINE_2, MACHINE_8, NULL }, // inbound machines
  7310. {SERVER_2, SERVER_8, NULL }, // inbound servers
  7311. {L"CXT1_2", L"CXT1_8", NULL }, // outbound cxtions
  7312. {MACHINE_2, MACHINE_8, NULL }, // outbound machines
  7313. {SERVER_2, SERVER_8, NULL }, // outbound servers
  7314. SERVER_1_VOL L"\\staging", // stage
  7315. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  7316. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  7317. #if 0
  7318. TEST_MACHINE_NAME, // machine
  7319. SERVER_2, // server name
  7320. RSA, // replica
  7321. FALSE, // IsPrimary
  7322. NULL, NULL, // File/Dir Filter
  7323. {L"CXT3_2", L"CXT1_2", NULL }, // inbound cxtions
  7324. {MACHINE_3, MACHINE_1, NULL }, // inbound machines
  7325. {SERVER_3, SERVER_1, NULL }, // inbound servers
  7326. {L"CXT2_3", L"CXT2_1", NULL }, // outbound cxtions
  7327. {MACHINE_3, MACHINE_1, NULL }, // outbound machines
  7328. {SERVER_3, SERVER_1, NULL }, // outbound servers
  7329. SERVER_2_VOL L"\\staging", // stage
  7330. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  7331. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  7332. #endif
  7333. TEST_MACHINE_NAME, // machine
  7334. SERVER_3, // server name
  7335. RSA, // replica
  7336. FALSE, // IsPrimary
  7337. NULL, NULL, // File/Dir Filter
  7338. {L"CXT4_3", L"CXT2_3", NULL }, // inbound cxtions
  7339. {MACHINE_4, MACHINE_2, NULL }, // inbound machines
  7340. {SERVER_4, SERVER_2, NULL }, // inbound servers
  7341. {L"CXT3_4", L"CXT3_2", NULL }, // outbound cxtions
  7342. {MACHINE_4, MACHINE_2, NULL }, // outbound machines
  7343. {SERVER_4, SERVER_2, NULL }, // outbound servers
  7344. SERVER_3_VOL L"\\staging", // stage
  7345. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  7346. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  7347. TEST_MACHINE_NAME, // machine
  7348. SERVER_4, // server name
  7349. RSA, // replica
  7350. FALSE, // IsPrimary
  7351. NULL, NULL, // File/Dir Filter
  7352. {L"CXT5_4", L"CXT3_4", NULL }, // inbound cxtions
  7353. {MACHINE_5, MACHINE_3, NULL }, // inbound machines
  7354. {SERVER_5, SERVER_3, NULL }, // inbound servers
  7355. {L"CXT4_5", L"CXT4_3", NULL }, // outbound cxtions
  7356. {MACHINE_5, MACHINE_3, NULL }, // outbound machines
  7357. {SERVER_5, SERVER_3, NULL }, // outbound servers
  7358. SERVER_4_VOL L"\\staging", // stage
  7359. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  7360. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  7361. TEST_MACHINE_NAME, // machine
  7362. SERVER_5, // server name
  7363. RSA, // replica
  7364. FALSE, // IsPrimary
  7365. NULL, NULL, // File/Dir Filter
  7366. {L"CXT6_5", L"CXT4_5", NULL }, // inbound cxtions
  7367. {MACHINE_6, MACHINE_4, NULL }, // inbound machines
  7368. {SERVER_6, SERVER_4, NULL }, // inbound servers
  7369. {L"CXT5_6", L"CXT5_4", NULL }, // outbound cxtions
  7370. {MACHINE_6, MACHINE_4, NULL }, // outbound machines
  7371. {SERVER_6, SERVER_4, NULL }, // outbound servers
  7372. SERVER_5_VOL L"\\staging", // stage
  7373. SERVER_5_VOL L"\\" RSA L"\\" SERVER_5, // root
  7374. SERVER_5_VOL L"\\jet\\" SERVER_5, // Jet Path
  7375. TEST_MACHINE_NAME, // machine
  7376. SERVER_6, // server name
  7377. RSA, // replica
  7378. FALSE, // IsPrimary
  7379. NULL, NULL, // File/Dir Filter
  7380. {L"CXT7_6", L"CXT5_6", NULL }, // inbound cxtions
  7381. {MACHINE_7, MACHINE_5, NULL }, // inbound machines
  7382. {SERVER_7, SERVER_5, NULL }, // inbound servers
  7383. {L"CXT6_7", L"CXT6_5", NULL }, // outbound cxtions
  7384. {MACHINE_7, MACHINE_5, NULL }, // outbound machines
  7385. {SERVER_7, SERVER_5, NULL }, // outbound servers
  7386. SERVER_6_VOL L"\\staging", // stage
  7387. SERVER_6_VOL L"\\" RSA L"\\" SERVER_6, // root
  7388. SERVER_6_VOL L"\\jet\\" SERVER_6, // Jet Path
  7389. TEST_MACHINE_NAME, // machine
  7390. SERVER_7, // server name
  7391. RSA, // replica
  7392. FALSE, // IsPrimary
  7393. NULL, NULL, // File/Dir Filter
  7394. {L"CXT8_7", L"CXT6_7", NULL }, // inbound cxtions
  7395. {MACHINE_8, MACHINE_6, NULL }, // inbound machines
  7396. {SERVER_8, SERVER_6, NULL }, // inbound servers
  7397. {L"CXT7_8", L"CXT7_6", NULL }, // outbound cxtions
  7398. {MACHINE_8, MACHINE_6, NULL }, // outbound machines
  7399. {SERVER_8, SERVER_6, NULL }, // outbound servers
  7400. SERVER_7_VOL L"\\staging", // stage
  7401. SERVER_7_VOL L"\\" RSA L"\\" SERVER_7, // root
  7402. SERVER_7_VOL L"\\jet\\" SERVER_7, // Jet Path
  7403. TEST_MACHINE_NAME, // machine
  7404. SERVER_8, // server name
  7405. RSA, // replica
  7406. FALSE, // IsPrimary
  7407. NULL, NULL, // File/Dir Filter
  7408. {L"CXT1_8", L"CXT7_8", NULL }, // inbound cxtions
  7409. {MACHINE_1, MACHINE_7, NULL }, // inbound machines
  7410. {SERVER_1, SERVER_7, NULL }, // inbound servers
  7411. {L"CXT8_1", L"CXT8_7", NULL }, // outbound cxtions
  7412. {MACHINE_1, MACHINE_7, NULL }, // outbound machines
  7413. {SERVER_1, SERVER_7, NULL }, // outbound servers
  7414. SERVER_8_VOL L"\\staging", // stage
  7415. SERVER_8_VOL L"\\" RSA L"\\" SERVER_8, // root
  7416. SERVER_8_VOL L"\\jet\\" SERVER_8, // Jet Path
  7417. //
  7418. // End of Config
  7419. //
  7420. NULL, NULL
  7421. };
  7422. #endif DBG
  7423. #if DBG
  7424. GUID *
  7425. FrsDsBuildGuidFromName(
  7426. IN PWCHAR OrigName
  7427. )
  7428. /*++
  7429. Routine Description:
  7430. Build a guid from a character string
  7431. Arguments:
  7432. Name
  7433. Return Value:
  7434. Guid
  7435. --*/
  7436. {
  7437. #undef DEBSUB
  7438. #define DEBSUB "FrsDsBuildGuidFromName:"
  7439. PULONG Guid;
  7440. ULONG Len;
  7441. ULONG *Sum;
  7442. ULONG *SumOfSum;
  7443. PWCHAR Name = OrigName;
  7444. Guid = FrsAlloc(sizeof(GUID));
  7445. //
  7446. // First four bytes are the sum of the chars in Name; the
  7447. // second four bytes are the sum of sums. The last 8 bytes
  7448. // are 0.
  7449. //
  7450. Len = wcslen(Name);
  7451. Sum = Guid;
  7452. SumOfSum = Guid + 1;
  7453. while (Len--) {
  7454. *Sum += *Name++ + 1;
  7455. *SumOfSum += *Sum;
  7456. }
  7457. return (GUID *)Guid;
  7458. }
  7459. #endif DBG
  7460. #if DBG
  7461. VOID
  7462. FrsDsInitializeHardWiredStructs(
  7463. IN PHARDWIRED Wired
  7464. )
  7465. /*++
  7466. Routine Description:
  7467. Initialize the hardwired config stuff. Must happen before any
  7468. of the command servers start.
  7469. Arguments:
  7470. Wired - struct to initialize
  7471. Return Value:
  7472. None
  7473. --*/
  7474. {
  7475. #undef DEBSUB
  7476. #define DEBSUB "FrsDsInitializeHardWiredStructs:"
  7477. ULONG i;
  7478. ULONG j;
  7479. //
  7480. // NULL entries for "machine name" fields are assigned
  7481. // this machine's name
  7482. //
  7483. for (i = 0; Wired[i].Replica; ++i) {
  7484. if (ServerName && WSTR_EQ(ServerName, Wired[i].Server)) {
  7485. //
  7486. // Adjust the default jet parameters. Also, reset the
  7487. // ServerName to match the server name in the hard
  7488. // wired config so that the phoney guids match.
  7489. //
  7490. FrsFree(ServerName);
  7491. FrsFree(JetPath);
  7492. ServerName = FrsWcsDup(Wired[i].Server);
  7493. JetPath = FrsWcsDup(Wired[i].JetPath);
  7494. }
  7495. //
  7496. // Assign this machine's name if the machine entry is NULL
  7497. //
  7498. if (!Wired[i].Machine ||
  7499. WSTR_EQ(Wired[i].Machine, THIS_COMPUTER)) {
  7500. Wired[i].Machine = ComputerName;
  7501. }
  7502. for (j = 0; Wired[i].InNames[j]; ++j) {
  7503. //
  7504. // Assign this machine's name if the machine entry is NULL
  7505. //
  7506. if (WSTR_NE(Wired[i].InMachines[j], THIS_COMPUTER)) {
  7507. continue;
  7508. }
  7509. Wired[i].InMachines[j] = ComputerName;
  7510. }
  7511. for (j = 0; Wired[i].OutNames[j]; ++j) {
  7512. //
  7513. // Assign this machine's name if the machine entry is NULL
  7514. //
  7515. if (WSTR_NE(Wired[i].OutMachines[j], THIS_COMPUTER)) {
  7516. continue;
  7517. }
  7518. Wired[i].OutMachines[j] = ComputerName;
  7519. }
  7520. }
  7521. }
  7522. BOOL
  7523. FrsDsLoadHardWiredFromFile(
  7524. PHARDWIRED *pMemberList,
  7525. PWCHAR pIniFileName
  7526. )
  7527. /*++
  7528. Routine Description:
  7529. Fills the hardwired structure array from data in a file. The file format
  7530. has the form of:
  7531. [MEMBER0]
  7532. MACHINE=[This Computer]
  7533. SERVER=SERV01
  7534. REPLICA=Replica-A
  7535. ISPRIMARY=TRUE
  7536. FILEFILTERLIST=*.tmp;*.bak
  7537. DIRFILTERLIST=obj
  7538. INNAME=CXT2_1, CXT3_1
  7539. INMACHINE=[This Computer], [This Computer]
  7540. INSERVER=SERV02, SERV03
  7541. OUTNAME=CXT1_2, CXT1_3
  7542. OUTMACHINE=[This Computer], [This Computer]
  7543. OUTSERVER=SERV02, SERV03
  7544. STAGE=d:\staging
  7545. ROOT=d:\Replica-A\SERV01
  7546. JETPATH=d:\jet
  7547. [MEMBER1]
  7548. MACHINE=[This Computer]
  7549. SERVER=SERV02
  7550. REPLICA=Replica-A
  7551. ISPRIMARY=FALSE
  7552. FILEFILTERLIST=*.tmp;*.bak
  7553. DIRFILTERLIST=obj
  7554. INNAME=CXT1_2, CXT3_2
  7555. INMACHINE=[This Computer], [This Computer]
  7556. INSERVER=SERV01, SERV03
  7557. OUTNAME=CXT2_1, CXT2_3
  7558. OUTMACHINE=[This Computer], [This Computer]
  7559. OUTSERVER=SERV01, SERV03
  7560. STAGE=e:\staging
  7561. ROOT=e:\Replica-A\SERV02
  7562. JETPATH=e:\jet
  7563. [MEMBER2]
  7564. MACHINE=[This Computer]
  7565. SERVER=SERV03
  7566. REPLICA=Replica-A
  7567. ISPRIMARY=FALSE
  7568. FILEFILTERLIST=*.tmp;*.bak
  7569. DIRFILTERLIST=obj
  7570. INNAME=CXT1_3, CXT2_3
  7571. INMACHINE=[This Computer], [This Computer]
  7572. INSERVER=SERV01, SERV02
  7573. OUTNAME=CXT3_1, CXT3_2
  7574. OUTMACHINE=[This Computer], [This Computer]
  7575. OUTSERVER=SERV01, SERV02
  7576. STAGE=f:\staging
  7577. ROOT=f:\Replica-A\SERV03
  7578. JETPATH=f:\jet
  7579. The string "[This Computer]" has a special meaning in that it refers to the
  7580. computer on which the server is running. You can substitute a specific
  7581. computer name.
  7582. The entries for INNAME, INMACHINE and INSERVER are lists in which the
  7583. corresponding entries in each list form a related triple that speicfy
  7584. the given inbound connection.
  7585. Ditto for OUTNAME, OUTMACHINE, and OUTSERVER.
  7586. The configuration above is for a fully connected mesh with three members.
  7587. It works only when three copies of NTFRS are run on the same machine since
  7588. all the IN and OUTMACHINE entries specify "[This Computer]". The SERVER names
  7589. distinguish each of the three copies of NTFRS for the purpose of providing RPC
  7590. endpoints.
  7591. If the members were actually run on separate physical machines then the
  7592. INMACHINES and the OUTMACHINES would need to specify the particular machine
  7593. names.
  7594. Arguments:
  7595. MemberList - Pointer to the pointer to the array of HardWired structures..
  7596. IniFileName - Name of the ini file to load from.
  7597. Return Value:
  7598. TRUE if data read ok.
  7599. --*/
  7600. {
  7601. #undef DEBSUB
  7602. #define DEBSUB "FrsDsLoadHardWiredFromFile:"
  7603. ULONG TotalMembers;
  7604. ULONG WStatus, Flag;
  7605. ULONG Len, RecordLen;
  7606. PWCHAR szIndex;
  7607. UINT i, k;
  7608. PHARDWIRED HwMember;
  7609. UNICODE_STRING UStr, ListArg;
  7610. PWCHAR pequal;
  7611. PWCHAR *ListArray;
  7612. WCHAR SectionNumber[16];
  7613. WCHAR SectionName[32];
  7614. WCHAR SectionBuffer[5000];
  7615. //
  7616. //Check if the ini file exists.
  7617. //
  7618. if (GetFileAttributes(pIniFileName) == 0xffffffff) {
  7619. DPRINT1(0, ":DS: Could not find ini file... %ws\n", IniFileName);
  7620. return FALSE;
  7621. }
  7622. //
  7623. // Find the number of members in the replica set.
  7624. //
  7625. TotalMembers = 0;
  7626. while(TRUE) {
  7627. wcscpy(SectionName, L"MEMBER");
  7628. wcscpy(SectionNumber, _itow(TotalMembers, SectionNumber, 10));
  7629. wcscat(SectionName, SectionNumber);
  7630. //
  7631. //Read this section from the ini file.
  7632. //
  7633. Flag = GetPrivateProfileSection(SectionName,
  7634. SectionBuffer,
  7635. sizeof(SectionBuffer)/sizeof(WCHAR),
  7636. pIniFileName);
  7637. if (Flag == 0) {
  7638. WStatus = GetLastError();
  7639. break;
  7640. }
  7641. TotalMembers++;
  7642. }
  7643. if (TotalMembers == 0) {
  7644. DPRINT_WS(0, ":DS: No members found in inifile.", WStatus);
  7645. return FALSE;
  7646. }
  7647. //
  7648. // Allocate memory. Then loop thru each member def in the ini file.
  7649. //
  7650. *pMemberList = (PHARDWIRED) FrsAlloc((TotalMembers + 1) * sizeof(HARDWIRED));
  7651. for ( i = 0 ; i < TotalMembers; ++i) {
  7652. wcscpy(SectionName, L"MEMBER");
  7653. wcscpy(SectionNumber, _itow(i, SectionNumber, 10));
  7654. wcscat(SectionName, SectionNumber);
  7655. WStatus = GetPrivateProfileSection(SectionName,
  7656. SectionBuffer,
  7657. sizeof(SectionBuffer)/sizeof(WCHAR),
  7658. pIniFileName);
  7659. HwMember = &(*pMemberList)[i];
  7660. for (szIndex = SectionBuffer; *szIndex != L'\0'; szIndex += RecordLen+1) {
  7661. RecordLen = wcslen(szIndex);
  7662. DPRINT3(5, ":DS: member %d: %ws [%d]\n", i, szIndex, RecordLen);
  7663. //
  7664. // Look for an arg of the form foo=bar.
  7665. //
  7666. pequal = wcschr(szIndex, L'=');
  7667. if (pequal == NULL) {
  7668. DPRINT1(0, ":DS: ERROR - Malformed parameter: %ws\n", szIndex);
  7669. continue;
  7670. }
  7671. //
  7672. // Null terminate and uppercase lefthand side.
  7673. //
  7674. *pequal = UNICODE_NULL;
  7675. _wcsupr(szIndex);
  7676. ++pequal;
  7677. Len = wcslen(pequal);
  7678. if (Len == 0) {
  7679. DPRINT1(0, ":DS: ERROR - Malformed parameter: %ws\n", szIndex);
  7680. continue;
  7681. }
  7682. Len = (Len + 1) * sizeof(WCHAR);
  7683. FrsSetUnicodeStringFromRawString(&UStr,
  7684. Len,
  7685. FrsWcsDup(pequal),
  7686. Len - sizeof(WCHAR));
  7687. if(!wcsncmp(szIndex, L"MACHINE",7)){
  7688. HwMember->Machine = UStr.Buffer;
  7689. continue;
  7690. }
  7691. if(!wcsncmp(szIndex, L"SERVER",6)){
  7692. HwMember->Server = UStr.Buffer;
  7693. continue;
  7694. }
  7695. if(!wcsncmp(szIndex, L"REPLICA",7)){
  7696. HwMember->Replica = UStr.Buffer;
  7697. continue;
  7698. }
  7699. if(!wcsncmp(szIndex, L"ISPRIMARY",9)){
  7700. if (!wcscmp(UStr.Buffer, L"TRUE")) {
  7701. HwMember->IsPrimary = TRUE;
  7702. }
  7703. continue;
  7704. }
  7705. if(!wcsncmp(szIndex, L"FILEFILTERLIST",14)){
  7706. HwMember->FileFilterList = UStr.Buffer;
  7707. continue;
  7708. }
  7709. if(!wcsncmp(szIndex, L"DIRFILTERLIST",13)){
  7710. HwMember->DirFilterList = UStr.Buffer;
  7711. continue;
  7712. }
  7713. if(!wcsncmp(szIndex, L"STAGE",5)){
  7714. HwMember->Stage = UStr.Buffer;
  7715. continue;
  7716. }
  7717. if(!wcsncmp(szIndex, L"ROOT",4)){
  7718. HwMember->Root = UStr.Buffer;
  7719. continue;
  7720. }
  7721. if(!wcsncmp(szIndex, L"JETPATH",7)) {
  7722. HwMember->JetPath = UStr.Buffer;
  7723. continue;
  7724. }
  7725. if (!wcsncmp(szIndex, L"INNAME", 6)) {
  7726. ListArray = HwMember->InNames;
  7727. goto PARSE_COMMA_LIST;
  7728. }
  7729. if (!wcsncmp(szIndex, L"INMACHINE", 9)) {
  7730. ListArray = HwMember->InMachines;
  7731. goto PARSE_COMMA_LIST;
  7732. }
  7733. if (!wcsncmp(szIndex, L"INSERVER", 8)) {
  7734. ListArray = HwMember->InServers;
  7735. goto PARSE_COMMA_LIST;
  7736. }
  7737. if (!wcsncmp(szIndex, L"OUTNAME", 7)) {
  7738. ListArray = HwMember->OutNames;
  7739. goto PARSE_COMMA_LIST;
  7740. }
  7741. if (!wcsncmp(szIndex, L"OUTMACHINE", 10)) {
  7742. ListArray = HwMember->OutMachines;
  7743. goto PARSE_COMMA_LIST;
  7744. }
  7745. if (!wcsncmp(szIndex, L"OUTSERVER", 9)) {
  7746. ListArray = HwMember->OutServers;
  7747. goto PARSE_COMMA_LIST;
  7748. }
  7749. PARSE_COMMA_LIST:
  7750. //
  7751. // Parse the right hand side of args like
  7752. // INSERVER=machine1, machine2, machine3
  7753. // Code above determined what the left hand side was.
  7754. //
  7755. k = 0;
  7756. while (FrsDissectCommaList(UStr, &ListArg, &UStr) &&
  7757. (k < HW_MACHINES)) {
  7758. ListArray[k] = NULL;
  7759. if (ListArg.Length > 0) {
  7760. DPRINT2(5, ":DS: ListArg string: %ws {%d)\n",
  7761. (ListArg.Buffer != NULL) ? ListArg.Buffer : L"<NULL>",
  7762. ListArg.Length);
  7763. ListArray[k] = ListArg.Buffer;
  7764. // Replace the comma (or white space with a null)
  7765. ListArg.Buffer[ListArg.Length/sizeof(WCHAR)] = UNICODE_NULL;
  7766. }
  7767. k += 1;
  7768. }
  7769. }
  7770. }
  7771. return TRUE;
  7772. }
  7773. VOID
  7774. FrsDsInitializeHardWired(
  7775. VOID
  7776. )
  7777. /*++
  7778. Routine Description:
  7779. Initialize the hardwired config stuff. Must happen before any
  7780. of the command servers start.
  7781. Arguments:
  7782. Jet - change the default jet directory from the registry
  7783. Return Value:
  7784. New jet directory
  7785. --*/
  7786. {
  7787. #undef DEBSUB
  7788. #define DEBSUB "FrsDsInitializeHardWired:"
  7789. //
  7790. // Using Ds, not the hard wired config
  7791. //
  7792. if (!NoDs) {
  7793. return;
  7794. }
  7795. //
  7796. // NULL entries for "machine name" fields are assigned
  7797. // this machine's name
  7798. //
  7799. if (IniFileName){
  7800. DPRINT1(0, ":DS: Reading hardwired config from ini file... %ws\n", IniFileName);
  7801. if (FrsDsLoadHardWiredFromFile(&LoadedWired, IniFileName)) {
  7802. DPRINT1(0, ":DS: Using hardwired config from ini file... %ws\n", IniFileName);
  7803. FrsDsInitializeHardWiredStructs(LoadedWired);
  7804. } else {
  7805. FrsFree(IniFileName);
  7806. IniFileName = NULL;
  7807. DPRINT(0, ":DS: Could not load topology from ini file\n");
  7808. DPRINT(0, ":DS: Using David's hardwired...\n");
  7809. FrsDsInitializeHardWiredStructs(DavidWired2);
  7810. FrsDsInitializeHardWiredStructs(DavidWired);
  7811. }
  7812. } else {
  7813. DPRINT(0, ":DS: Using David's hardwired...\n");
  7814. FrsDsInitializeHardWiredStructs(DavidWired2);
  7815. FrsDsInitializeHardWiredStructs(DavidWired);
  7816. }
  7817. //
  7818. // The ServerGuid is used as part of the rpc endpoint
  7819. //
  7820. if (ServerName) {
  7821. ServerGuid = FrsDsBuildGuidFromName(ServerName);
  7822. }
  7823. }
  7824. #endif DBG
  7825. #if DBG
  7826. VOID
  7827. FrsDsUseHardWired(
  7828. IN PHARDWIRED Wired
  7829. )
  7830. /*++
  7831. Routine Description:
  7832. Use the hardwired config instead of the DS config.
  7833. Arguments:
  7834. Wired - hand crafted config
  7835. Return Value:
  7836. None.
  7837. --*/
  7838. {
  7839. #undef DEBSUB
  7840. #define DEBSUB "FrsDsUseHardWired:"
  7841. ULONG i, j;
  7842. ULONG WStatus;
  7843. PREPLICA Replica;
  7844. PCXTION Cxtion;
  7845. PSCHEDULE Schedule;
  7846. ULONG ScheduleLength;
  7847. PBYTE ScheduleData;
  7848. PHARDWIRED W;
  7849. DWORD FileAttributes = 0xFFFFFFFF;
  7850. DPRINT(1, ":DS: ------------ USING HARD WIRED CONFIG\n");
  7851. for (i = 0; Wired && Wired[i].Replica; ++i) {
  7852. if (i) {
  7853. DPRINT(1, ":DS: \n");
  7854. }
  7855. W = &Wired[i];
  7856. DPRINT1(1, ":DS: \tServer: %ws\n", W->Server);
  7857. DPRINT1(1, ":DS: \t Machine: %ws\n", W->Machine);
  7858. DPRINT1(1, ":DS: \t\tReplica : %ws\n", W->Replica);
  7859. DPRINT(1, ":DS: \n");
  7860. for (j=0; (j<HW_MACHINES) && W->InNames[j]; j++ ) {
  7861. DPRINT4(1, ":DS: \t\tInNames,machine,server [%d] : %ws, %ws, %ws\n", j,
  7862. (W->InNames[j]) ? W->InNames[j] : L"",
  7863. (W->InMachines[j]) ? W->InMachines[j] : L"",
  7864. (W->InServers[j]) ? W->InServers[j] : L"");
  7865. }
  7866. DPRINT(1, ":DS: \n");
  7867. for (j=0; (j<HW_MACHINES) && W->OutNames[j]; j++ ) {
  7868. DPRINT4(1, ":DS: \t\tOutNames,machine,server [%d] : %ws, %ws, %ws\n", j,
  7869. (W->OutNames[j]) ? W->OutNames[j] : L"",
  7870. (W->OutMachines[j]) ? W->OutMachines[j] : L"",
  7871. (W->OutServers[j]) ? W->OutServers[j] : L"");
  7872. }
  7873. DPRINT(1, ":DS: \n");
  7874. DPRINT1(1, ":DS: \t\tStage : %ws\n", W->Stage);
  7875. DPRINT1(1, ":DS: \t\tRoot : %ws\n", W->Root);
  7876. DPRINT1(1, ":DS: \t\tJetPath : %ws\n", W->JetPath);
  7877. }
  7878. //
  7879. // Coordinate with replica command server
  7880. //
  7881. RcsBeginMergeWithDs();
  7882. //
  7883. // Construct a replica for each hardwired configuration
  7884. //
  7885. for (i = 0; Wired && Wired[i].Replica; ++i) {
  7886. W = &Wired[i];
  7887. //
  7888. // This server does not match this machine's name; continue
  7889. //
  7890. if (ServerName) {
  7891. if (WSTR_NE(ServerName, W->Server)) {
  7892. continue;
  7893. }
  7894. } else if (WSTR_NE(ComputerName, W->Machine)) {
  7895. continue;
  7896. }
  7897. Replica = FrsAllocType(REPLICA_TYPE);
  7898. Replica->Consistent = TRUE;
  7899. //
  7900. // MATCH
  7901. //
  7902. //
  7903. // Construct a phoney schedule; always "on"
  7904. //
  7905. ScheduleLength = sizeof(SCHEDULE) +
  7906. (2 * sizeof(SCHEDULE_HEADER)) +
  7907. (3 * SCHEDULE_DATA_BYTES);
  7908. Schedule = FrsAlloc(ScheduleLength);
  7909. Schedule->NumberOfSchedules = 3;
  7910. Schedule->Schedules[0].Type = SCHEDULE_BANDWIDTH;
  7911. Schedule->Schedules[0].Offset = sizeof(SCHEDULE) +
  7912. (2 * sizeof(SCHEDULE_HEADER)) +
  7913. (0 * SCHEDULE_DATA_BYTES);
  7914. Schedule->Schedules[1].Type = SCHEDULE_PRIORITY;
  7915. Schedule->Schedules[1].Offset = sizeof(SCHEDULE) +
  7916. (2 * sizeof(SCHEDULE_HEADER)) +
  7917. (1 * SCHEDULE_DATA_BYTES);
  7918. Schedule->Schedules[2].Type = SCHEDULE_INTERVAL;
  7919. Schedule->Schedules[2].Offset = sizeof(SCHEDULE) +
  7920. (2 * sizeof(SCHEDULE_HEADER)) +
  7921. (2 * SCHEDULE_DATA_BYTES);
  7922. ScheduleData = ((PBYTE)Schedule);
  7923. FRS_ASSERT((ScheduleData +
  7924. Schedule->Schedules[2].Offset + SCHEDULE_DATA_BYTES)
  7925. ==
  7926. (((PBYTE)Schedule) + ScheduleLength));
  7927. for (j = 0; j < (SCHEDULE_DATA_BYTES * 3); ++j) {
  7928. *(ScheduleData + Schedule->Schedules[0].Offset + j) = 0x0f;
  7929. }
  7930. Schedule->Size = ScheduleLength;
  7931. Replica->Schedule = Schedule;
  7932. //
  7933. // Construct the guid/names from the name
  7934. //
  7935. Replica->MemberName = FrsBuildGName(FrsDsBuildGuidFromName(W->Server),
  7936. FrsWcsDup(W->Server));
  7937. Replica->ReplicaName = FrsBuildGName(FrsDupGuid(Replica->MemberName->Guid),
  7938. FrsWcsDup(W->Replica));
  7939. Replica->SetName = FrsBuildGName(FrsDsBuildGuidFromName(W->Replica),
  7940. FrsWcsDup(W->Replica));
  7941. //
  7942. // Temporary; a new guid is assigned if this is a new set
  7943. //
  7944. Replica->ReplicaRootGuid = FrsDupGuid(Replica->SetName->Guid);
  7945. //
  7946. // Fill in the rest of the fields
  7947. //
  7948. Replica->Root = FrsWcsDup(W->Root);
  7949. Replica->Stage = FrsWcsDup(W->Stage);
  7950. FRS_WCSLWR(Replica->Root);
  7951. FRS_WCSLWR(Replica->Stage);
  7952. Replica->Volume = FrsWcsVolume(W->Root);
  7953. //
  7954. // Syntax of root path is invalid?
  7955. //
  7956. if (!FrsDsVerifyPath(Replica->Root)) {
  7957. DPRINT2(3, ":DS: Invalid root %ws for %ws\n",
  7958. Replica->Root, Replica->SetName->Name);
  7959. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Replica->Root);
  7960. Replica->Consistent = FALSE;
  7961. }
  7962. //
  7963. // Does the volume exist and is it NTFS?
  7964. //
  7965. WStatus = FrsVerifyVolume(Replica->Root,
  7966. Replica->SetName->Name,
  7967. FILE_PERSISTENT_ACLS | FILE_SUPPORTS_OBJECT_IDS);
  7968. if (!WIN_SUCCESS(WStatus)) {
  7969. DPRINT2_WS(3, ":DS: Root path Volume (%ws) for %ws does not exist or does not support ACLs and Object IDs;",
  7970. Replica->Root, Replica->SetName->Name, WStatus);
  7971. Replica->Consistent = FALSE;
  7972. }
  7973. //
  7974. // Root does not exist or is inaccessable; continue
  7975. //
  7976. WStatus = FrsDoesDirectoryExist(Replica->Root, &FileAttributes);
  7977. if (!WIN_SUCCESS(WStatus)) {
  7978. DPRINT2_WS(3, ":DS: Root path (%ws) for %ws does not exist;",
  7979. Replica->Root, Replica->SetName->Name, WStatus);
  7980. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Replica->Root);
  7981. Replica->Consistent = FALSE;
  7982. }
  7983. //
  7984. // Syntax of staging path is invalid; continue
  7985. //
  7986. if (!FrsDsVerifyPath(Replica->Stage)) {
  7987. DPRINT2(3, ":DS: Invalid stage %ws for %ws\n",
  7988. Replica->Stage, Replica->SetName->Name);
  7989. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Replica->Root, Replica->Stage);
  7990. Replica->Consistent = FALSE;
  7991. }
  7992. //
  7993. // Does the staging volume exist and does it support ACLs?
  7994. // ACLs are required to protect against data theft/corruption
  7995. // in the staging dir.
  7996. //
  7997. WStatus = FrsVerifyVolume(Replica->Stage,
  7998. Replica->SetName->Name,
  7999. FILE_PERSISTENT_ACLS);
  8000. if (!WIN_SUCCESS(WStatus)) {
  8001. DPRINT2_WS(3, ":DS: Stage path Volume (%ws) for %ws does not exist or does not support ACLs;",
  8002. Replica->Stage, Replica->SetName->Name, WStatus);
  8003. Replica->Consistent = FALSE;
  8004. }
  8005. //
  8006. // Stage does not exist or is inaccessable; continue
  8007. //
  8008. WStatus = FrsDoesDirectoryExist(Replica->Stage, &FileAttributes);
  8009. if (!WIN_SUCCESS(WStatus)) {
  8010. DPRINT2_WS(3, ":DS: Stage path (%ws) for %ws does not exist;",
  8011. Replica->Stage, Replica->SetName->Name, WStatus);
  8012. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Replica->Root, Replica->Stage);
  8013. Replica->Consistent = FALSE;
  8014. }
  8015. if (W->IsPrimary) {
  8016. SetFlag(Replica->CnfFlags, CONFIG_FLAG_PRIMARY);
  8017. }
  8018. //
  8019. // File Filter
  8020. //
  8021. Replica->FileFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  8022. W->FileFilterList,
  8023. RegistryFileExclFilterList,
  8024. DEFAULT_FILE_FILTER_LIST);
  8025. Replica->FileInclFilterList = FrsWcsDup(RegistryFileInclFilterList);
  8026. //
  8027. // Directory Filter
  8028. //
  8029. Replica->DirFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  8030. W->DirFilterList,
  8031. RegistryDirExclFilterList,
  8032. DEFAULT_DIR_FILTER_LIST);
  8033. Replica->DirInclFilterList = FrsWcsDup(RegistryDirInclFilterList);
  8034. //
  8035. // Build Inbound cxtions
  8036. //
  8037. Schedule = FrsAlloc(ScheduleLength);
  8038. CopyMemory(Schedule, Replica->Schedule, ScheduleLength);
  8039. Schedule->Schedules[0].Type = SCHEDULE_INTERVAL;
  8040. Schedule->Schedules[2].Type = SCHEDULE_BANDWIDTH;
  8041. for (j = 0; W->InNames[j]; ++j) {
  8042. Cxtion = FrsAllocType(CXTION_TYPE);
  8043. //
  8044. // Construct the guid/names from the name
  8045. //
  8046. Cxtion->Name = FrsBuildGName(FrsDsBuildGuidFromName(W->InNames[j]),
  8047. FrsWcsDup(W->InNames[j]));
  8048. Cxtion->Partner = FrsBuildGName(FrsDsBuildGuidFromName(W->InServers[j]),
  8049. FrsWcsDup(W->InMachines[j]));
  8050. Cxtion->PartnerDnsName = FrsWcsDup(W->InMachines[j]);
  8051. Cxtion->PartnerSid = FrsWcsDup(W->InMachines[j]);
  8052. Cxtion->PartSrvName = FrsWcsDup(W->InServers[j]);
  8053. DPRINT1(1, ":DS: Hardwired cxtion "FORMAT_CXTION_PATH2"\n",
  8054. PRINT_CXTION_PATH2(Replica, Cxtion));
  8055. Cxtion->PartnerPrincName = FrsWcsDup(Cxtion->PartSrvName);
  8056. //
  8057. // Fill in the rest of the fields
  8058. //
  8059. Cxtion->Inbound = TRUE;
  8060. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT);
  8061. Cxtion->Schedule = Schedule;
  8062. Schedule = NULL;
  8063. SetCxtionState(Cxtion, CxtionStateUnjoined);
  8064. GTabInsertEntry(Replica->Cxtions, Cxtion, Cxtion->Name->Guid, NULL);
  8065. }
  8066. //
  8067. // Build Outbound cxtions
  8068. //
  8069. Schedule = FrsAlloc(ScheduleLength);
  8070. CopyMemory(Schedule, Replica->Schedule, ScheduleLength);
  8071. Schedule->Schedules[0].Type = SCHEDULE_INTERVAL;
  8072. Schedule->Schedules[2].Type = SCHEDULE_BANDWIDTH;
  8073. for (j = 0; W->OutNames[j]; ++j) {
  8074. Cxtion = FrsAllocType(CXTION_TYPE);
  8075. //
  8076. // Construct the guid/names from the name
  8077. //
  8078. Cxtion->Name = FrsBuildGName(FrsDsBuildGuidFromName(W->OutNames[j]),
  8079. FrsWcsDup(W->OutNames[j]));
  8080. Cxtion->Partner = FrsBuildGName(FrsDsBuildGuidFromName(W->OutServers[j]),
  8081. FrsWcsDup(W->OutMachines[j]));
  8082. Cxtion->PartnerDnsName = FrsWcsDup(W->OutMachines[j]);
  8083. Cxtion->PartnerSid = FrsWcsDup(W->OutMachines[j]);
  8084. Cxtion->PartSrvName = FrsWcsDup(W->OutServers[j]);
  8085. DPRINT1(1, ":DS: Hardwired cxtion "FORMAT_CXTION_PATH2"\n",
  8086. PRINT_CXTION_PATH2(Replica, Cxtion));
  8087. Cxtion->PartnerPrincName = FrsWcsDup(Cxtion->PartSrvName);
  8088. //
  8089. // Fill in the rest of the fields
  8090. //
  8091. Cxtion->Inbound = FALSE;
  8092. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT);
  8093. Cxtion->Schedule = Schedule;
  8094. Schedule = NULL;
  8095. SetCxtionState(Cxtion, CxtionStateUnjoined);
  8096. GTabInsertEntry(Replica->Cxtions, Cxtion, Cxtion->Name->Guid, NULL);
  8097. }
  8098. if (Schedule) {
  8099. FrsFree(Schedule);
  8100. }
  8101. //
  8102. // Merge the replica with the active replicas
  8103. //
  8104. RcsMergeReplicaFromDs(Replica);
  8105. }
  8106. RcsEndMergeWithDs();
  8107. }
  8108. #endif DBG
  8109. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  8110. DWORD
  8111. FrsNewDsGetSubscribers(
  8112. IN PLDAP Ldap,
  8113. IN PWCHAR SubscriptionDn,
  8114. IN PCONFIG_NODE Parent
  8115. )
  8116. /*++
  8117. Routine Description:
  8118. Recursively scan the DS tree beginning at computer
  8119. Arguments:
  8120. Ldap - opened and bound ldap connection
  8121. SubscriptionDn - distininguished name of subscriptions object
  8122. Parent - parent node
  8123. Return Value:
  8124. WIN32 Status
  8125. --*/
  8126. {
  8127. #undef DEBSUB
  8128. #define DEBSUB "FrsNewDsGetSubscribers:"
  8129. PWCHAR Attrs[8];
  8130. PLDAPMessage LdapEntry;
  8131. PCONFIG_NODE Node;
  8132. DWORD WStatus = ERROR_SUCCESS;
  8133. DWORD Status = ERROR_SUCCESS;
  8134. PGEN_ENTRY ConflictingNodeEntry = NULL;
  8135. PCONFIG_NODE ConflictingNode = NULL;
  8136. PCONFIG_NODE Winner = NULL;
  8137. PCONFIG_NODE Loser = NULL;
  8138. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  8139. HANDLE StageHandle = INVALID_HANDLE_VALUE;
  8140. DWORD FileAttributes = 0xFFFFFFFF;
  8141. DWORD CreateStatus = ERROR_SUCCESS;
  8142. //
  8143. // Search the DS beginning at Base for entries of (objectCategory=nTFRSSubscriber)
  8144. //
  8145. MK_ATTRS_7(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  8146. ATTR_REPLICA_ROOT, ATTR_REPLICA_STAGE, ATTR_MEMBER_REF);
  8147. if (!FrsDsLdapSearchInit(Ldap, SubscriptionDn, LDAP_SCOPE_ONELEVEL, CATEGORY_SUBSCRIBER,
  8148. Attrs, 0, &FrsSearchContext)) {
  8149. return ERROR_ACCESS_DENIED;
  8150. }
  8151. if (FrsSearchContext.EntriesInPage == 0) {
  8152. DPRINT1(0, ":DS: No NTFRSSubscriber object found under %ws!\n", SubscriptionDn);
  8153. }
  8154. //
  8155. // Scan the entries returned from ldap_search
  8156. //
  8157. for (LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  8158. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  8159. LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  8160. //
  8161. // Basic node info (guid, name, dn, schedule, and usnchanged)
  8162. //
  8163. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_SUBSCRIBER);
  8164. if (!Node) {
  8165. DPRINT(0, ":DS: Subscriber lacks basic info; skipping\n");
  8166. continue;
  8167. }
  8168. //
  8169. // Member reference
  8170. //
  8171. Node->MemberDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_MEMBER_REF);
  8172. if (Node->MemberDn == NULL) {
  8173. DPRINT1(0, ":DS: ERROR - No Member Reference found on subscriber (%ws). Skipping\n", Node->Dn);
  8174. //
  8175. // Add to the poll summary event log.
  8176. //
  8177. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_SUBSCRIBER,
  8178. Node->Dn, ATTR_MEMBER_REF);
  8179. FrsFreeType(Node);
  8180. continue;
  8181. }
  8182. FRS_WCSLWR(Node->MemberDn);
  8183. //
  8184. // Root pathname
  8185. //
  8186. Node->Root = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_ROOT);
  8187. if (Node->Root == NULL) {
  8188. DPRINT1(0, ":DS: ERROR - No Root path found on subscriber (%ws). Skipping\n", Node->Dn);
  8189. FrsFreeType(Node);
  8190. continue;
  8191. }
  8192. FRS_WCSLWR(Node->Root);
  8193. //
  8194. // Staging pathname. No need to traverse reparse points on the staging dir.
  8195. //
  8196. Node->Stage = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_STAGE);
  8197. if (Node->Stage == NULL) {
  8198. DPRINT1(0, ":DS: ERROR - No Staging path found on subscriber (%ws). Skipping\n", Node->Dn);
  8199. FrsFreeType(Node);
  8200. continue;
  8201. }
  8202. FRS_WCSLWR(Node->Stage);
  8203. //
  8204. // Create the staging directory if it does not exist.
  8205. //
  8206. Status = FrsDoesDirectoryExist(Node->Stage, &FileAttributes);
  8207. if (!WIN_SUCCESS(Status)) {
  8208. CreateStatus = FrsCreateDirectory(Node->Stage);
  8209. DPRINT1_WS(0, ":DS: ERROR - Can't create staging dir %ws;", Node->Stage, CreateStatus);
  8210. }
  8211. //
  8212. // If the staging dir was just created successfully or if it does not have the
  8213. // hidden attribute set then set the security on it.
  8214. //
  8215. if ((!WIN_SUCCESS(Status) && WIN_SUCCESS(CreateStatus)) ||
  8216. (WIN_SUCCESS(Status) && !BooleanFlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN))) {
  8217. //
  8218. // Open the staging directory.
  8219. //
  8220. StageHandle = CreateFile(Node->Stage,
  8221. GENERIC_WRITE | WRITE_DAC | FILE_READ_ATTRIBUTES | FILE_TRAVERSE,
  8222. FILE_SHARE_READ,
  8223. NULL,
  8224. OPEN_EXISTING,
  8225. FILE_FLAG_BACKUP_SEMANTICS,
  8226. NULL);
  8227. if (!HANDLE_IS_VALID(StageHandle)) {
  8228. Status = GetLastError();
  8229. DPRINT1_WS(0, ":DS: WARN - CreateFile(%ws);", Node->Stage, Status);
  8230. } else {
  8231. Status = FrsRestrictAccessToFileOrDirectory(Node->Stage, StageHandle, TRUE);
  8232. DPRINT1_WS(0, ":DS: WARN - FrsRestrictAccessToFileOrDirectory(%ws) (IGNORED)", Node->Stage, Status);
  8233. FRS_CLOSE(StageHandle);
  8234. //
  8235. // Mark the staging dir hidden.
  8236. //
  8237. if (!SetFileAttributes(Node->Stage,
  8238. FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN)) {
  8239. Status = GetLastError();
  8240. DPRINT1_WS(0, ":DS: ERROR - Can't set attrs on staging dir %ws;", Node->Stage, Status);
  8241. }
  8242. }
  8243. }
  8244. //
  8245. // Add the subscriber to the subscriber table.
  8246. //
  8247. // ConflictingNodeEntry = GTabInsertUniqueEntry(SubscriberTable, Node, Node->MemberDn, Node->Root);
  8248. ConflictingNodeEntry = GTabInsertUniqueEntry(SubscriberTable, Node, Node->MemberDn, NULL);
  8249. if (ConflictingNodeEntry) {
  8250. ConflictingNode = ConflictingNodeEntry->Data;
  8251. FrsDsResolveSubscriberConflict(ConflictingNode, Node, &Winner, &Loser);
  8252. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  8253. //
  8254. // The new one is the winner. Remove old one and insert new one.
  8255. //
  8256. GTabDelete(SubscriberTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2,NULL);
  8257. GTabInsertUniqueEntry(SubscriberTable, Node, Node->MemberDn, Node->Root);
  8258. FrsFreeType(ConflictingNode);
  8259. } else {
  8260. //
  8261. // The old one is the winner. Leave it in the table.
  8262. //
  8263. FrsFreeType(Node);
  8264. continue;
  8265. }
  8266. }
  8267. //
  8268. // Link into config and add to the running checksum
  8269. //
  8270. FrsDsTreeLink(Parent, Node);
  8271. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSubscriber", Node);
  8272. }
  8273. FrsDsLdapSearchClose(&FrsSearchContext);
  8274. return WStatus;
  8275. }
  8276. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  8277. DWORD
  8278. FrsDsGetSubscribers(
  8279. IN PLDAP Ldap,
  8280. IN PWCHAR SubscriptionDn,
  8281. IN PCONFIG_NODE Parent
  8282. )
  8283. /*++
  8284. Routine Description:
  8285. Recursively scan the DS tree beginning at computer
  8286. Arguments:
  8287. Ldap - opened and bound ldap connection
  8288. SubscriptionDn - distininguished name of subscriptions object
  8289. Parent - parent node
  8290. Return Value:
  8291. WIN32 Status
  8292. --*/
  8293. {
  8294. #undef DEBSUB
  8295. #define DEBSUB "FrsDsGetSubscribers:"
  8296. PWCHAR Attrs[8];
  8297. PWCHAR RootFromDs = NULL;
  8298. PLDAPMessage LdapMsg = NULL;
  8299. PLDAPMessage LdapEntry;
  8300. PCONFIG_NODE Node;
  8301. DWORD WStatus = ERROR_SUCCESS;
  8302. DWORD Status = ERROR_SUCCESS;
  8303. //
  8304. // Search the DS beginning at Base for entries of (objectCategory=nTFRSSubscriber)
  8305. //
  8306. MK_ATTRS_7(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  8307. ATTR_REPLICA_ROOT, ATTR_REPLICA_STAGE, ATTR_MEMBER_REF);
  8308. if (!FrsDsLdapSearch(Ldap, SubscriptionDn, LDAP_SCOPE_ONELEVEL, CATEGORY_SUBSCRIBER,
  8309. Attrs, 0, &LdapMsg)) {
  8310. return ERROR_ACCESS_DENIED;
  8311. }
  8312. //
  8313. // Scan the entries returned from ldap_search
  8314. //
  8315. for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  8316. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  8317. LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
  8318. //
  8319. // Basic node info (guid, name, dn, schedule, and usnchanged)
  8320. //
  8321. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_SUBSCRIBER);
  8322. if (!Node) {
  8323. DPRINT(4, ":DS: Subscriber lacks basic info; skipping\n");
  8324. continue;
  8325. }
  8326. //
  8327. // Member reference
  8328. //
  8329. Node->MemberDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_MEMBER_REF);
  8330. FRS_WCSLWR(Node->MemberDn);
  8331. //
  8332. // Root pathname
  8333. //
  8334. RootFromDs = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_ROOT);
  8335. //
  8336. // Traverse the root path and get the clean reparse point less path equivalent of the
  8337. // root path. Bail if there is error traversing the root path.
  8338. //
  8339. Status = FrsTraverseReparsePoints(RootFromDs,&Node->Root);
  8340. if (!WIN_SUCCESS(Status)) {
  8341. DPRINT1(0, ":DS: ERROR - Could not traverse reparse points on root path\n", RootFromDs);
  8342. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, RootFromDs);
  8343. FrsFree(RootFromDs);
  8344. continue;
  8345. }
  8346. FrsFree(RootFromDs);
  8347. FRS_WCSLWR(Node->Root);
  8348. //
  8349. // Staging pathname. No need to traverse reparse points on the staging dir.
  8350. //
  8351. Node->Stage = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_STAGE);
  8352. FRS_WCSLWR(Node->Stage);
  8353. //
  8354. // Link into config and add to the running checksum
  8355. //
  8356. FrsDsTreeLink(Parent, Node);
  8357. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSubscriber", Node);
  8358. }
  8359. LDAP_FREE_MSG(LdapMsg);
  8360. return WStatus;
  8361. }
  8362. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  8363. DWORD
  8364. FrsNewDsGetSubscriptions(
  8365. IN PLDAP Ldap,
  8366. IN PWCHAR ComputerDn,
  8367. IN PCONFIG_NODE Parent
  8368. )
  8369. /*++
  8370. Routine Description:
  8371. Recursively scan the DS tree beginning at computer
  8372. Arguments:
  8373. Ldap - opened and bound ldap connection
  8374. DefaultNc - default naming context
  8375. Parent - parent node
  8376. Return Value:
  8377. WIN32 Status
  8378. --*/
  8379. {
  8380. #undef DEBSUB
  8381. #define DEBSUB "FrsNewDsGetSubscriptions:"
  8382. PWCHAR Attrs[6];
  8383. PLDAPMessage LdapMsg = NULL;
  8384. PLDAPMessage LdapEntry;
  8385. PCONFIG_NODE Node;
  8386. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  8387. DWORD WStatus = ERROR_SUCCESS;
  8388. //
  8389. // Search the DS beginning at Base for entries of (objectCategory=nTFRSSubscriptions)
  8390. //
  8391. MK_ATTRS_5(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED, ATTR_WORKING);
  8392. if (!FrsDsLdapSearchInit(Ldap, ComputerDn, LDAP_SCOPE_SUBTREE, CATEGORY_SUBSCRIPTIONS,
  8393. Attrs, 0, &FrsSearchContext)) {
  8394. return ERROR_ACCESS_DENIED;
  8395. }
  8396. if (FrsSearchContext.EntriesInPage == 0) {
  8397. DPRINT1(0, ":DS: No NTFRSSubscriptions object found under %ws!.\n", ComputerDn);
  8398. }
  8399. //
  8400. // Scan the entries returned from ldap_search
  8401. //
  8402. for (LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  8403. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  8404. LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  8405. //
  8406. // Basic node info (guid, name, dn, schedule, and usnchanged)
  8407. //
  8408. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_SUBSCRIPTIONS);
  8409. if (!Node) {
  8410. DPRINT(4, ":DS: Subscriptions lacks basic info; skipping\n");
  8411. continue;
  8412. }
  8413. //
  8414. // Working Directory
  8415. //
  8416. Node->Working = FrsDsFindValue(Ldap, LdapEntry, ATTR_WORKING);
  8417. //
  8418. // Link into config and add to the running checksum
  8419. //
  8420. FrsDsTreeLink(Parent, Node);
  8421. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSubscription", Node);
  8422. //
  8423. // Recurse to the next level in the DS hierarchy
  8424. //
  8425. WStatus = FrsNewDsGetSubscribers(Ldap, Node->Dn, Node);
  8426. }
  8427. FrsDsLdapSearchClose(&FrsSearchContext);
  8428. return WStatus;
  8429. }
  8430. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  8431. DWORD
  8432. FrsDsGetSubscriptions(
  8433. IN PLDAP Ldap,
  8434. IN PWCHAR ComputerDn,
  8435. IN PCONFIG_NODE Parent
  8436. )
  8437. /*++
  8438. Routine Description:
  8439. Recursively scan the DS tree beginning at computer
  8440. Arguments:
  8441. Ldap - opened and bound ldap connection
  8442. DefaultNc - default naming context
  8443. Parent - parent node
  8444. Return Value:
  8445. WIN32 Status
  8446. --*/
  8447. {
  8448. #undef DEBSUB
  8449. #define DEBSUB "FrsDsGetSubscriptions:"
  8450. PWCHAR Attrs[6];
  8451. PLDAPMessage LdapMsg = NULL;
  8452. PLDAPMessage LdapEntry;
  8453. PCONFIG_NODE Node;
  8454. DWORD WStatus = ERROR_SUCCESS;
  8455. //
  8456. // Search the DS beginning at Base for entries of (objectCategory=nTFRSSubscriptions)
  8457. //
  8458. MK_ATTRS_5(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED, ATTR_WORKING);
  8459. if (!FrsDsLdapSearch(Ldap, ComputerDn, LDAP_SCOPE_SUBTREE, CATEGORY_SUBSCRIPTIONS,
  8460. Attrs, 0, &LdapMsg)) {
  8461. return ERROR_ACCESS_DENIED;
  8462. }
  8463. //
  8464. // Scan the entries returned from ldap_search
  8465. //
  8466. for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  8467. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  8468. LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
  8469. //
  8470. // Basic node info (guid, name, dn, schedule, and usnchanged)
  8471. //
  8472. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_SUBSCRIPTIONS);
  8473. if (!Node) {
  8474. DPRINT(4, ":DS: Subscriptions lacks basic info; skipping\n");
  8475. continue;
  8476. }
  8477. //
  8478. // Working Directory
  8479. //
  8480. Node->Working = FrsDsFindValue(Ldap, LdapEntry, ATTR_WORKING);
  8481. //
  8482. // Link into config and add to the running checksum
  8483. //
  8484. FrsDsTreeLink(Parent, Node);
  8485. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSubscription", Node);
  8486. //
  8487. // Recurse to the next level in the DS hierarchy
  8488. //
  8489. WStatus = FrsDsGetSubscribers(Ldap, Node->Dn, Node);
  8490. }
  8491. LDAP_FREE_MSG(LdapMsg);
  8492. return WStatus;
  8493. }
  8494. PWCHAR
  8495. FrsDsGetHostName(
  8496. VOID
  8497. )
  8498. /*++
  8499. Routine Description:
  8500. Retrieve this machine's host name.
  8501. Arguments:
  8502. None.
  8503. Return Value:
  8504. WSA Status
  8505. --*/
  8506. {
  8507. #undef DEBSUB
  8508. #define DEBSUB "FrsDsGetHostName:"
  8509. INT SStatus;
  8510. WORD DnsVersion = MAKEWORD(1, 1);
  8511. struct hostent *Host;
  8512. WSADATA WSAData;
  8513. PCHAR DnsNameA;
  8514. PCHAR ComputerNameA;
  8515. PWCHAR DnsName;
  8516. //
  8517. // Get this machine's DNS name
  8518. //
  8519. DnsName = NULL;
  8520. //
  8521. // Initialize the socket subsystem
  8522. //
  8523. if (SStatus = WSAStartup(DnsVersion, &WSAData)) {
  8524. DPRINT1(4, ":DS: Can't get Host name; Socket startup error %d\n", SStatus);
  8525. return NULL;
  8526. };
  8527. //
  8528. // Get the DNS name
  8529. //
  8530. ComputerNameA = FrsWtoA(ComputerName);
  8531. Host = gethostbyname(ComputerNameA);
  8532. FrsFree(ComputerNameA);
  8533. if (Host == NULL) {
  8534. SStatus = WSAGetLastError();
  8535. DPRINT1(4, ":DS: Can't get Host name; gethostbyname error %d\n", SStatus);
  8536. } else if (Host->h_name == NULL) {
  8537. DPRINT(4, ":DS: Host name is NULL\n");
  8538. } else {
  8539. DnsName = FrsAtoW(Host->h_name);
  8540. }
  8541. WSACleanup();
  8542. DPRINT1(4, ":DS: Host name is %ws\n", DnsName);
  8543. return DnsName;
  8544. }
  8545. VOID
  8546. FrsDsAddLdapMod(
  8547. IN PWCHAR AttrType,
  8548. IN PWCHAR AttrValue,
  8549. IN OUT LDAPMod ***pppMod
  8550. )
  8551. /*++
  8552. Routine Description:
  8553. Add an attribute (plus values) to a structure that will eventually be
  8554. used in an ldap_add_s() function to add an object to the DS. The null-
  8555. terminated array referenced by pppMod grows with each call to this
  8556. routine. The array is freed by the caller using FrsDsFreeLdapMod().
  8557. Arguments:
  8558. AttrType - The object class of the object.
  8559. AttrValue - The value of the attribute.
  8560. pppMod - Address of an array of pointers to "attributes". Don't
  8561. give me that look -- this is an LDAP thing.
  8562. Return Value:
  8563. The pppMod array grows by one entry. The caller must free it with
  8564. FrsDsFreeLdapMod().
  8565. --*/
  8566. {
  8567. DWORD NumMod; // Number of entries in the Mod array
  8568. LDAPMod **ppMod; // Address of the first entry in the Mod array
  8569. LDAPMod *Attr; // An attribute structure
  8570. PWCHAR *Values; // An array of pointers to bervals
  8571. if (AttrValue == NULL)
  8572. return;
  8573. //
  8574. // The null-terminated array doesn't exist; create it
  8575. //
  8576. if (*pppMod == NULL) {
  8577. *pppMod = (LDAPMod **)FrsAlloc(sizeof (*pppMod));
  8578. **pppMod = NULL;
  8579. }
  8580. //
  8581. // Increase the array's size by 1
  8582. //
  8583. for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
  8584. *pppMod = (LDAPMod **)FrsRealloc(*pppMod, sizeof (*pppMod) * NumMod);
  8585. //
  8586. // Add the new attribute + value to the Mod array
  8587. //
  8588. Values = (PWCHAR *)FrsAlloc(sizeof (PWCHAR) * 2);
  8589. Values[0] = FrsWcsDup(AttrValue);
  8590. Values[1] = NULL;
  8591. Attr = (LDAPMod *)FrsAlloc(sizeof (*Attr));
  8592. Attr->mod_values = Values;
  8593. Attr->mod_type = FrsWcsDup(AttrType);
  8594. Attr->mod_op = LDAP_MOD_ADD;
  8595. (*pppMod)[NumMod - 1] = NULL;
  8596. (*pppMod)[NumMod - 2] = Attr;
  8597. }
  8598. VOID
  8599. FrsDsAddLdapBerMod(
  8600. IN PWCHAR AttrType,
  8601. IN PCHAR AttrValue,
  8602. IN DWORD AttrValueLen,
  8603. IN OUT LDAPMod ***pppMod
  8604. )
  8605. /*++
  8606. Routine Description:
  8607. Add an attribute (plus values) to a structure that will eventually be
  8608. used in an ldap_add() function to add an object to the DS. The null-
  8609. terminated array referenced by pppMod grows with each call to this
  8610. routine. The array is freed by the caller using FrsDsFreeLdapMod().
  8611. Arguments:
  8612. AttrType - The object class of the object.
  8613. AttrValue - The value of the attribute.
  8614. AttrValueLen - length of the attribute
  8615. pppMod - Address of an array of pointers to "attributes". Don't
  8616. give me that look -- this is an LDAP thing.
  8617. Return Value:
  8618. The pppMod array grows by one entry. The caller must free it with
  8619. FrsDsFreeLdapMod().
  8620. --*/
  8621. {
  8622. DWORD NumMod; // Number of entries in the Mod array
  8623. LDAPMod **ppMod; // Address of the first entry in the Mod array
  8624. LDAPMod *Attr; // An attribute structure
  8625. PLDAP_BERVAL Berval;
  8626. PLDAP_BERVAL *Values; // An array of pointers to bervals
  8627. if (AttrValue == NULL)
  8628. return;
  8629. //
  8630. // The null-terminated array doesn't exist; create it
  8631. //
  8632. if (*pppMod == NULL) {
  8633. *pppMod = (LDAPMod **)FrsAlloc(sizeof (*pppMod));
  8634. **pppMod = NULL;
  8635. }
  8636. //
  8637. // Increase the array's size by 1
  8638. //
  8639. for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
  8640. *pppMod = (LDAPMod **)FrsRealloc(*pppMod, sizeof (*pppMod) * NumMod);
  8641. //
  8642. // Construct a berval
  8643. //
  8644. Berval = (PLDAP_BERVAL)FrsAlloc(sizeof(LDAP_BERVAL));
  8645. Berval->bv_len = AttrValueLen;
  8646. Berval->bv_val = (PCHAR)FrsAlloc(AttrValueLen);
  8647. CopyMemory(Berval->bv_val, AttrValue, AttrValueLen);
  8648. //
  8649. // Add the new attribute + value to the Mod array
  8650. //
  8651. Values = (PLDAP_BERVAL *)FrsAlloc(sizeof (PLDAP_BERVAL) * 2);
  8652. Values[0] = Berval;
  8653. Values[1] = NULL;
  8654. Attr = (LDAPMod *)FrsAlloc(sizeof (*Attr));
  8655. Attr->mod_bvalues = Values;
  8656. Attr->mod_type = FrsWcsDup(AttrType);
  8657. Attr->mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  8658. (*pppMod)[NumMod - 1] = NULL;
  8659. (*pppMod)[NumMod - 2] = Attr;
  8660. }
  8661. VOID
  8662. FrsDsFreeLdapMod(
  8663. IN OUT LDAPMod ***pppMod
  8664. )
  8665. /*++
  8666. Routine Description:
  8667. Free the structure built by successive calls to FrsDsAddLdapMod().
  8668. Arguments:
  8669. pppMod - Address of a null-terminated array.
  8670. Return Value:
  8671. *pppMod set to NULL.
  8672. --*/
  8673. {
  8674. DWORD i, j;
  8675. LDAPMod **ppMod;
  8676. if (!pppMod || !*pppMod) {
  8677. return;
  8678. }
  8679. //
  8680. // For each attibute
  8681. //
  8682. ppMod = *pppMod;
  8683. for (i = 0; ppMod[i] != NULL; ++i) {
  8684. //
  8685. // For each value of the attribute
  8686. //
  8687. for (j = 0; (ppMod[i])->mod_values[j] != NULL; ++j) {
  8688. //
  8689. // Free the value
  8690. //
  8691. if (ppMod[i]->mod_op & LDAP_MOD_BVALUES) {
  8692. FrsFree(ppMod[i]->mod_bvalues[j]->bv_val);
  8693. }
  8694. FrsFree((ppMod[i])->mod_values[j]);
  8695. }
  8696. FrsFree((ppMod[i])->mod_values); // Free the array of pointers to values
  8697. FrsFree((ppMod[i])->mod_type); // Free the string identifying the attribute
  8698. FrsFree(ppMod[i]); // Free the attribute
  8699. }
  8700. FrsFree(ppMod); // Free the array of pointers to attributes
  8701. *pppMod = NULL; // Now ready for more calls to FrsDsAddLdapMod()
  8702. }
  8703. PWCHAR
  8704. FrsDsConvertToSettingsDn(
  8705. IN PWCHAR Dn
  8706. )
  8707. /*++
  8708. Routine Description:
  8709. Insure this Dn is for the server's settings and not the server and
  8710. that the Dn is in lower case for any call to wcsstr().
  8711. Arguments:
  8712. Dn - Server or settings dn
  8713. Return Value:
  8714. Settings Dn
  8715. --*/
  8716. {
  8717. #undef DEBSUB
  8718. #define DEBSUB "FrsDsConvertToSettingsDn:"
  8719. PWCHAR SettingsDn;
  8720. DPRINT1(4, ":DS: Begin settings Dn: %ws\n", Dn);
  8721. //
  8722. // No settings; done
  8723. //
  8724. if (!Dn) {
  8725. return Dn;
  8726. }
  8727. //
  8728. // Lower case for wcsstr
  8729. //
  8730. FRS_WCSLWR(Dn);
  8731. if (wcsstr(Dn, CN_NTDS_SETTINGS)) {
  8732. DPRINT1(4, ":DS: End settings Dn: %ws\n", Dn);
  8733. return Dn;
  8734. }
  8735. SettingsDn = FrsDsExtendDn(Dn, CN_NTDS_SETTINGS);
  8736. FRS_WCSLWR(SettingsDn);
  8737. FrsFree(Dn);
  8738. DPRINT1(4, ":DS: End settings Dn: %ws\n", SettingsDn);
  8739. return SettingsDn;
  8740. }
  8741. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  8742. DWORD
  8743. FrsNewDsFindComputer(
  8744. IN PLDAP Ldap,
  8745. IN PWCHAR FindDn,
  8746. IN PWCHAR ObjectCategory,
  8747. IN ULONG Scope,
  8748. OUT PCONFIG_NODE *Computer
  8749. )
  8750. /*++
  8751. Routine Description:
  8752. Find *one* computer object for this computer.
  8753. Then look for a subscriptons object and subscriber objects. A
  8754. DS configuration node is allocated for each object found. They are linked
  8755. together and the root of the "computer tree" is returned in Computer.
  8756. Arguments:
  8757. Ldap - opened and bound ldap connection
  8758. FindDn - Base Dn for search
  8759. ObjectCategory - Object class (computer or user)
  8760. A user object serves the same purpose as the computer
  8761. object *sometimes* following a NT4 to NT5 upgrade.
  8762. Scope - Scope of search (currently BASE or SUBTREE)
  8763. Computer - returned computer subtree
  8764. Return Value:
  8765. WIN32 Status
  8766. --*/
  8767. {
  8768. #undef DEBSUB
  8769. #define DEBSUB "FrsNewDsFindComputer:"
  8770. PLDAPMessage LdapEntry;
  8771. PCONFIG_NODE Node;
  8772. PWCHAR Attrs[8];
  8773. WCHAR Filter[MAX_PATH + 1];
  8774. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  8775. DWORD WStatus = ERROR_SUCCESS;
  8776. *Computer = NULL;
  8777. //
  8778. // Initialize the SubscriberTable.
  8779. //
  8780. if (SubscriberTable != NULL) {
  8781. SubscriberTable = GTabFreeTable(SubscriberTable,NULL);
  8782. }
  8783. SubscriberTable = GTabAllocStringTable();
  8784. //
  8785. // Filter that locates our computer object
  8786. //
  8787. swprintf(Filter, L"(&%s(sAMAccountName=%s$))", ObjectCategory, ComputerName);
  8788. //
  8789. // Search the DS beginning at Base for the entries of class "Filter"
  8790. //
  8791. MK_ATTRS_7(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  8792. ATTR_SERVER_REF, ATTR_SERVER_REF_BL, ATTR_DNS_HOST_NAME);
  8793. //
  8794. // Note: Is it safe to turn off referrals re: back links?
  8795. // if so, use ldap_get/set_option in winldap.h
  8796. //
  8797. if (!FrsDsLdapSearchInit(Ldap, FindDn, Scope, Filter, Attrs, 0, &FrsSearchContext)) {
  8798. return ERROR_ACCESS_DENIED;
  8799. }
  8800. if (FrsSearchContext.EntriesInPage == 0) {
  8801. DPRINT1(0, ":DS: WARN - There is no computer object in %ws!\n", FindDn);
  8802. }
  8803. //
  8804. // Scan the entries returned from ldap_search
  8805. //
  8806. for (LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  8807. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  8808. LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  8809. //
  8810. // Basic node info (guid, name, dn, schedule, and usnchanged)
  8811. //
  8812. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_COMPUTER);
  8813. if (!Node) {
  8814. DPRINT(0, ":DS: Computer lacks basic info; skipping\n");
  8815. continue;
  8816. }
  8817. DPRINT1(2, ":DS: Computer FQDN is %ws\n", Node->Dn);
  8818. //
  8819. // DNS name
  8820. //
  8821. Node->DnsName = FrsDsFindValue(Ldap, LdapEntry, ATTR_DNS_HOST_NAME);
  8822. DPRINT1(2, ":DS: Computer's dns name is %ws\n", Node->DnsName);
  8823. //
  8824. // Server reference
  8825. //
  8826. Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF_BL);
  8827. if (!Node->SettingsDn) {
  8828. Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF);
  8829. }
  8830. //
  8831. // Make sure it references the settings; not the server
  8832. //
  8833. Node->SettingsDn = FrsDsConvertToSettingsDn(Node->SettingsDn);
  8834. DPRINT1(2, ":DS: Settings reference is %ws\n", Node->SettingsDn);
  8835. //
  8836. // Link into config
  8837. //
  8838. Node->Peer = *Computer;
  8839. *Computer = Node;
  8840. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeComputer", Node);
  8841. //
  8842. // Recurse to the next level in the DS hierarchy iff this
  8843. // computer is a member of some replica set
  8844. //
  8845. WStatus = FrsNewDsGetSubscriptions(Ldap, Node->Dn, Node);
  8846. }
  8847. FrsDsLdapSearchClose(&FrsSearchContext);
  8848. //
  8849. // There should only be one computer object with the indicated
  8850. // SAM account name. Otherwise, we are unable to authenticate
  8851. // properly. And it goes against the DS architecture.
  8852. //
  8853. if (WIN_SUCCESS(WStatus) && *Computer && (*Computer)->Peer) {
  8854. DPRINT(0, ":DS: ERROR - There is more than one computer object!\n");
  8855. WStatus = ERROR_INVALID_PARAMETER;
  8856. }
  8857. //
  8858. // Must have a computer
  8859. //
  8860. if (WIN_SUCCESS(WStatus) && !*Computer) {
  8861. DPRINT1(0, ":DS: WARN - There is no computer object in %ws!\n", FindDn);
  8862. WStatus = ERROR_INVALID_PARAMETER;
  8863. }
  8864. return WStatus;
  8865. }
  8866. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  8867. DWORD
  8868. FrsDsFindComputer(
  8869. IN PLDAP Ldap,
  8870. IN PWCHAR FindDn,
  8871. IN PWCHAR ObjectCategory,
  8872. IN ULONG Scope,
  8873. OUT PCONFIG_NODE *Computer
  8874. )
  8875. /*++
  8876. Routine Description:
  8877. Find *one* computer object for this computer.
  8878. Then look for a subscriptons object and subscriber objects. A
  8879. DS configuration node is allocated for each object found. They are linked
  8880. together and the root of the "computer tree" is returned in Computer.
  8881. Arguments:
  8882. Ldap - opened and bound ldap connection
  8883. FindDn - Base Dn for search
  8884. ObjectCategory - Object class (computer or user)
  8885. A user object serves the same purpose as the computer
  8886. object *sometimes* following a NT4 to NT5 upgrade.
  8887. Scope - Scope of search (currently BASE or SUBTREE)
  8888. Computer - returned computer subtree
  8889. Return Value:
  8890. WIN32 Status
  8891. --*/
  8892. {
  8893. #undef DEBSUB
  8894. #define DEBSUB "FrsDsFindComputer:"
  8895. PLDAPMessage LdapMsg = NULL;
  8896. PLDAPMessage LdapEntry;
  8897. PCONFIG_NODE Node;
  8898. PWCHAR Attrs[8];
  8899. WCHAR Filter[MAX_PATH + 1];
  8900. DWORD WStatus = ERROR_SUCCESS;
  8901. *Computer = NULL;
  8902. //
  8903. // Filter that locates our computer object
  8904. //
  8905. swprintf(Filter, L"(&%s(sAMAccountName=%s$))", ObjectCategory, ComputerName);
  8906. //
  8907. // Search the DS beginning at Base for the entries of class "Filter"
  8908. //
  8909. MK_ATTRS_7(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  8910. ATTR_SERVER_REF, ATTR_SERVER_REF_BL, ATTR_DNS_HOST_NAME);
  8911. if (!FrsDsLdapSearch(Ldap, FindDn, Scope, Filter, Attrs, 0, &LdapMsg)) {
  8912. return ERROR_ACCESS_DENIED;
  8913. }
  8914. //
  8915. // Scan the entries returned from ldap_search
  8916. //
  8917. for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  8918. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  8919. LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
  8920. //
  8921. // Basic node info (guid, name, dn, schedule, and usnchanged)
  8922. //
  8923. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_COMPUTER);
  8924. if (!Node) {
  8925. DPRINT(4, ":DS: Computer lacks basic info; skipping\n");
  8926. continue;
  8927. }
  8928. DPRINT1(4, ":DS: Computer FQDN is %ws\n", Node->Dn);
  8929. //
  8930. // DNS name
  8931. //
  8932. Node->DnsName = FrsDsFindValue(Ldap, LdapEntry, ATTR_DNS_HOST_NAME);
  8933. DPRINT1(4, ":DS: Computer's dns name is %ws\n", Node->DnsName);
  8934. //
  8935. // Server reference
  8936. //
  8937. Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF_BL);
  8938. if (!Node->SettingsDn) {
  8939. Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF);
  8940. }
  8941. //
  8942. // Make sure it references the settings; not the server
  8943. //
  8944. Node->SettingsDn = FrsDsConvertToSettingsDn(Node->SettingsDn);
  8945. DPRINT1(4, ":DS: Settings reference is %ws\n", Node->SettingsDn);
  8946. //
  8947. // Link into config
  8948. //
  8949. Node->Peer = *Computer;
  8950. *Computer = Node;
  8951. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeComputer", Node);
  8952. //
  8953. // Recurse to the next level in the DS hierarchy iff this
  8954. // computer is a member of some replica set
  8955. //
  8956. WStatus = FrsDsGetSubscriptions(Ldap, Node->Dn, Node);
  8957. }
  8958. LDAP_FREE_MSG(LdapMsg);
  8959. //
  8960. // There should only be one computer object with the indicated
  8961. // SAM account name. Otherwise, we are unable to authenticate
  8962. // properly. And it goes against the DS architecture.
  8963. //
  8964. if (WIN_SUCCESS(WStatus) && *Computer && (*Computer)->Peer) {
  8965. DPRINT(0, ":DS: ERROR - There is more than one computer object!\n");
  8966. WStatus = ERROR_INVALID_PARAMETER;
  8967. }
  8968. //
  8969. // Must have a computer
  8970. //
  8971. if (WIN_SUCCESS(WStatus) && !*Computer) {
  8972. DPRINT1(0, ":DS: WARN - There is no computer object in %ws!\n", FindDn);
  8973. WStatus = ERROR_INVALID_PARAMETER;
  8974. }
  8975. return WStatus;
  8976. }
  8977. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  8978. DWORD
  8979. FrsNewDsGetComputer(
  8980. IN PLDAP Ldap,
  8981. OUT PCONFIG_NODE *Computer
  8982. )
  8983. /*++
  8984. Routine Description:
  8985. Look in the Domain naming Context for our computer object.
  8986. Historically we did a deep search for an object with the sam account
  8987. name of our computer (SAM Account name is the netbios name with a $ appended).
  8988. That was expensive so before doing that we first look in the Domain
  8989. Controller container followed by a search of the Computer Container.
  8990. Then the DS guys came up with an API for the preferred way of doing this.
  8991. First call GetComputerObjectName() to get the Fully Qualified Distinguished
  8992. Name (FQDN) for the computer then use that in an LDAP search query (via
  8993. FrsDsFindComputer()). We only fall back on the full search when the
  8994. call to GetComputerObjectName() fails.
  8995. Arguments:
  8996. Ldap - opened and bound ldap connection
  8997. Computer - returned computer subtree
  8998. Return Value:
  8999. WIN32 Status
  9000. --*/
  9001. {
  9002. #undef DEBSUB
  9003. #define DEBSUB "FrsNewDsGetComputer:"
  9004. WCHAR CompFqdn[MAX_PATH + 1];
  9005. DWORD CompFqdnLen;
  9006. DWORD WStatus = ERROR_SUCCESS;
  9007. //
  9008. // Initialize return value
  9009. //
  9010. *Computer = NULL;
  9011. //
  9012. // Assume success
  9013. //
  9014. WStatus = ERROR_SUCCESS;
  9015. //
  9016. // Use computer's cached fully qualified Dn. This avoids repeated calls
  9017. // to GetComputerObjectName() which wants to rebind to the DS on each call.
  9018. // (it should have taken a binding handle as an arg).
  9019. //
  9020. if (ComputerCachedFqdn) {
  9021. DPRINT1(5, ":DS: ComputerCachedFqdn is %ws\n", ComputerCachedFqdn);
  9022. WStatus = FrsNewDsFindComputer(Ldap, ComputerCachedFqdn, CATEGORY_ANY,
  9023. LDAP_SCOPE_BASE, Computer);
  9024. if (*Computer) {
  9025. goto CLEANUP;
  9026. }
  9027. DPRINT2(1, ":DS: WARN - Could not find computer in Cachedfqdn %ws; WStatus %s\n",
  9028. ComputerCachedFqdn, ErrLabelW32(WStatus));
  9029. ComputerCachedFqdn = FrsFree(ComputerCachedFqdn);
  9030. }
  9031. //
  9032. // Retrieve the computer's fully qualified Dn
  9033. //
  9034. // NTRAID#70731-2000/03/29-sudarc (Call GetComputerObjectName() from a
  9035. // separate thread so that it does not hang the
  9036. // DS polling thread.)
  9037. //
  9038. // *Note*:
  9039. // The following call to GetComputerObjectName() can hang if the DS
  9040. // hangs. See bug 351139 for an example caused by a bug in another
  9041. // component. One way to protect ourself is to issue this call
  9042. // in its own thread. Then after a timeout period call RpcCancelThread()
  9043. // on the thread.
  9044. //
  9045. CompFqdnLen = MAX_PATH;
  9046. if (GetComputerObjectName(NameFullyQualifiedDN, CompFqdn, &CompFqdnLen)) {
  9047. DPRINT1(4, ":DS: ComputerFqdn is %ws\n", CompFqdn);
  9048. //
  9049. // Use CATEGORY_ANY in the search below because an NT4 to NT5 upgrade
  9050. // could result in the object type for the "computer object" to really
  9051. // be a USER object. So the FQDN above could resolve to a Computer
  9052. // or a User object.
  9053. //
  9054. WStatus = FrsNewDsFindComputer(Ldap, CompFqdn, CATEGORY_ANY,
  9055. LDAP_SCOPE_BASE, Computer);
  9056. if (*Computer == NULL) {
  9057. DPRINT2(1, ":DS: WARN - Could not find computer in fqdn %ws; WStatus %s\n",
  9058. CompFqdn, ErrLabelW32(WStatus));
  9059. } else {
  9060. //
  9061. // Found our computer object; refresh the cached fqdn.
  9062. //
  9063. FrsFree(ComputerCachedFqdn);
  9064. ComputerCachedFqdn = FrsWcsDup(CompFqdn);
  9065. }
  9066. //
  9067. // We got the fully qualified Dn so we are done. It should have
  9068. // given us a computer object but even if it didn't we won't find it
  9069. // anywhere else.
  9070. //
  9071. goto CLEANUP;
  9072. }
  9073. DPRINT3(1, ":DS: WARN - GetComputerObjectName(%ws); Len %d, WStatus %s\n",
  9074. ComputerName, CompFqdnLen, ErrLabelW32(GetLastError()));
  9075. //
  9076. // FQDN lookup failed so fall back on search of well known containers.
  9077. // First Look in domain controllers container.
  9078. //
  9079. if (DomainControllersDn) {
  9080. WStatus = FrsNewDsFindComputer(Ldap, DomainControllersDn, CATEGORY_COMPUTER,
  9081. LDAP_SCOPE_SUBTREE, Computer);
  9082. if (*Computer != NULL) {
  9083. goto CLEANUP;
  9084. }
  9085. DPRINT2(1, ":DS: WARN - Could not find computer in dc's %ws; WStatus %s\n",
  9086. DomainControllersDn, ErrLabelW32(WStatus));
  9087. }
  9088. //
  9089. // Look in computer container
  9090. //
  9091. if (ComputersDn) {
  9092. WStatus = FrsNewDsFindComputer(Ldap, ComputersDn, CATEGORY_COMPUTER,
  9093. LDAP_SCOPE_SUBTREE, Computer);
  9094. if (*Computer != NULL) {
  9095. goto CLEANUP;
  9096. }
  9097. DPRINT2(1, ":DS: WARN - Could not find computer in computers %ws; WStatus %s\n",
  9098. ComputersDn, ErrLabelW32(WStatus));
  9099. }
  9100. //
  9101. // Do a deep search of the default naming context (EXPENSIVE!)
  9102. //
  9103. if (DefaultNcDn) {
  9104. WStatus = FrsNewDsFindComputer(Ldap, DefaultNcDn, CATEGORY_COMPUTER,
  9105. LDAP_SCOPE_SUBTREE, Computer);
  9106. if (*Computer != NULL) {
  9107. goto CLEANUP;
  9108. }
  9109. DPRINT2(1, ":DS: WARN - Could not find computer in defaultnc %ws; WStatus %s\n",
  9110. DefaultNcDn, ErrLabelW32(WStatus));
  9111. }
  9112. //
  9113. // Getting desperate. Try looking for a user object because an
  9114. // NT4 to NT5 upgrade will sometimes leave the objectCategory
  9115. // as user on the computer object.
  9116. //
  9117. if (DefaultNcDn) {
  9118. WStatus = FrsNewDsFindComputer(Ldap, DefaultNcDn, CATEGORY_USER,
  9119. LDAP_SCOPE_SUBTREE, Computer);
  9120. if (*Computer != NULL) {
  9121. goto CLEANUP;
  9122. }
  9123. DPRINT2(1, ":DS: WARN - Could not find computer in defaultnc USER %ws; WStatus %s\n",
  9124. DefaultNcDn, ErrLabelW32(WStatus));
  9125. }
  9126. CLEANUP:
  9127. return WStatus;
  9128. }
  9129. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  9130. DWORD
  9131. FrsDsGetComputer(
  9132. IN PLDAP Ldap,
  9133. OUT PCONFIG_NODE *Computer
  9134. )
  9135. /*++
  9136. Routine Description:
  9137. Look in the Domain naming Context for our computer object.
  9138. Historically we did a deep search for an object with the sam account
  9139. name of our computer (SAM Account name is the netbios name with a $ appended).
  9140. That was expensive so before doing that we first look in the Domain
  9141. Controller container followed by a search of the Computer Container.
  9142. Then the DS guys came up with an API for the preferred way of doing this.
  9143. First call GetComputerObjectName() to get the Fully Qualified Distinguished
  9144. Name (FQDN) for the computer then use that in an LDAP search query (via
  9145. FrsDsFindComputer()). We only fall back on the full search when the
  9146. call to GetComputerObjectName() fails.
  9147. Arguments:
  9148. Ldap - opened and bound ldap connection
  9149. Computer - returned computer subtree
  9150. Return Value:
  9151. WIN32 Status
  9152. --*/
  9153. {
  9154. #undef DEBSUB
  9155. #define DEBSUB "FrsDsGetComputer:"
  9156. WCHAR CompFqdn[MAX_PATH + 1];
  9157. DWORD CompFqdnLen;
  9158. DWORD WStatus = ERROR_SUCCESS;
  9159. //
  9160. // Initialize return value
  9161. //
  9162. *Computer = NULL;
  9163. //
  9164. // Assume success
  9165. //
  9166. WStatus = ERROR_SUCCESS;
  9167. //
  9168. // Use computer's cached fully qualified Dn. This avoids repeated calls
  9169. // to GetComputerObjectName() which wants to rebind to the DS on each call.
  9170. // (it should have taken a binding handle as an arg).
  9171. //
  9172. if (ComputerCachedFqdn) {
  9173. DPRINT1(5, ":DS: ComputerCachedFqdn is %ws\n", ComputerCachedFqdn);
  9174. WStatus = FrsDsFindComputer(Ldap, ComputerCachedFqdn, CATEGORY_ANY,
  9175. LDAP_SCOPE_BASE, Computer);
  9176. if (*Computer) {
  9177. goto CLEANUP;
  9178. }
  9179. DPRINT1_WS(1, ":DS: WARN - Could not find computer in Cachedfqdn %ws;",
  9180. ComputerCachedFqdn, WStatus);
  9181. ComputerCachedFqdn = FrsFree(ComputerCachedFqdn);
  9182. }
  9183. //
  9184. // Retrieve the computer's fully qualified Dn
  9185. //
  9186. // The following call to GetComputerObjectName() can hang if the DS
  9187. // hangs. See bug 351139 for an example caused by a bug in another
  9188. // component. One way to protect ourself is to issue this call
  9189. // in its own thread. Then after a timeout period call RpcCancelThread()
  9190. // on the thread.
  9191. CompFqdnLen = MAX_PATH;
  9192. if (GetComputerObjectName(NameFullyQualifiedDN, CompFqdn, &CompFqdnLen)) {
  9193. DPRINT1(4, ":DS: ComputerFqdn is %ws\n", CompFqdn);
  9194. //
  9195. // Use CATEGORY_ANY in the search below because an NT4 to NT5 upgrade
  9196. // could result in the object type for the "computer object" to really
  9197. // be a USER object. So the FQDN above could resolve to a Computer
  9198. // or a User object.
  9199. //
  9200. WStatus = FrsDsFindComputer(Ldap, CompFqdn, CATEGORY_ANY,
  9201. LDAP_SCOPE_BASE, Computer);
  9202. if (*Computer == NULL) {
  9203. DPRINT1_FS(1, ":DS: WARN - Could not find computer in fqdn %ws; ",
  9204. CompFqdn, WStatus);
  9205. } else {
  9206. //
  9207. // Found our computer object; refresh the cached fqdn.
  9208. //
  9209. FrsFree(ComputerCachedFqdn);
  9210. ComputerCachedFqdn = FrsWcsDup(CompFqdn);
  9211. }
  9212. //
  9213. // We got the fully qualified Dn so we are done. It should have
  9214. // given us a computer object but even if it didn't we won't find it
  9215. // anywhere else.
  9216. //
  9217. goto CLEANUP;
  9218. }
  9219. DPRINT2_WS(1, ":DS: WARN - GetComputerObjectName(%ws); Len %d;",
  9220. ComputerName, CompFqdnLen, GetLastError());
  9221. //
  9222. // FQDN lookup failed so fall back on search of well known containers.
  9223. // First Look in domain controllers container.
  9224. //
  9225. if (DomainControllersDn) {
  9226. WStatus = FrsDsFindComputer(Ldap, DomainControllersDn, CATEGORY_COMPUTER,
  9227. LDAP_SCOPE_SUBTREE, Computer);
  9228. if (*Computer != NULL) {
  9229. goto CLEANUP;
  9230. }
  9231. DPRINT1_WS(1, ":DS: WARN - Could not find computer in dc's %ws;",
  9232. DomainControllersDn, WStatus);
  9233. }
  9234. //
  9235. // Look in computer container
  9236. //
  9237. if (ComputersDn) {
  9238. WStatus = FrsDsFindComputer(Ldap, ComputersDn, CATEGORY_COMPUTER,
  9239. LDAP_SCOPE_SUBTREE, Computer);
  9240. if (*Computer != NULL) {
  9241. goto CLEANUP;
  9242. }
  9243. DPRINT1_WS(1, ":DS: WARN - Could not find computer in computers %ws;",
  9244. ComputersDn, WStatus);
  9245. }
  9246. //
  9247. // Do a deep search of the default naming context (EXPENSIVE!)
  9248. //
  9249. if (DefaultNcDn) {
  9250. WStatus = FrsDsFindComputer(Ldap, DefaultNcDn, CATEGORY_COMPUTER,
  9251. LDAP_SCOPE_SUBTREE, Computer);
  9252. if (*Computer != NULL) {
  9253. goto CLEANUP;
  9254. }
  9255. DPRINT1_WS(1, ":DS: WARN - Could not find computer in defaultnc %ws;",
  9256. DefaultNcDn, WStatus);
  9257. }
  9258. //
  9259. // Getting desperate. Try looking for a user object because an
  9260. // NT4 to NT5 upgrade will sometimes leave the objectCategory
  9261. // as user on the computer object.
  9262. //
  9263. if (DefaultNcDn) {
  9264. WStatus = FrsDsFindComputer(Ldap, DefaultNcDn, CATEGORY_USER,
  9265. LDAP_SCOPE_SUBTREE, Computer);
  9266. if (*Computer != NULL) {
  9267. goto CLEANUP;
  9268. }
  9269. DPRINT1_WS(1, ":DS: WARN - Could not find computer in defaultnc USER %ws;",
  9270. DefaultNcDn, WStatus);
  9271. }
  9272. CLEANUP:
  9273. return WStatus;
  9274. }
  9275. DWORD
  9276. FrsDsDeleteSubTree(
  9277. IN PLDAP Ldap,
  9278. IN PWCHAR Dn
  9279. )
  9280. /*++
  9281. Routine Description:
  9282. Delete a DS subtree, including Dn
  9283. Arguments:
  9284. None.
  9285. Return Value:
  9286. None.
  9287. --*/
  9288. {
  9289. #undef DEBSUB
  9290. #define DEBSUB "FrsDsDeleteSubTree:"
  9291. DWORD LStatus;
  9292. PWCHAR Attrs[2];
  9293. PWCHAR NextDn;
  9294. PLDAPMessage LdapMsg = NULL;
  9295. PLDAPMessage LdapEntry = NULL;
  9296. MK_ATTRS_1(Attrs, ATTR_DN);
  9297. LStatus = ldap_search_ext_s(Ldap,
  9298. Dn,
  9299. LDAP_SCOPE_ONELEVEL,
  9300. CATEGORY_ANY,
  9301. Attrs,
  9302. 0,
  9303. NULL,
  9304. NULL,
  9305. &LdapTimeout,
  9306. 0,
  9307. &LdapMsg);
  9308. if (LStatus != LDAP_NO_SUCH_OBJECT) {
  9309. CLEANUP1_LS(4, ":DS: Can't search %ws;", Dn, LStatus, CLEANUP);
  9310. }
  9311. LStatus = LDAP_SUCCESS;
  9312. //
  9313. // Scan the entries returned from ldap_search
  9314. //
  9315. for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  9316. LdapEntry != NULL && LStatus == LDAP_SUCCESS;
  9317. LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
  9318. NextDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_DN);
  9319. LStatus = FrsDsDeleteSubTree(Ldap, NextDn);
  9320. FrsFree(NextDn);
  9321. }
  9322. if (LStatus != LDAP_SUCCESS) {
  9323. goto CLEANUP;
  9324. }
  9325. LStatus = ldap_delete_s(Ldap, Dn);
  9326. if (LStatus != LDAP_NO_SUCH_OBJECT) {
  9327. CLEANUP1_LS(4, ":DS: Can't delete %ws;", Dn, LStatus, CLEANUP);
  9328. }
  9329. //
  9330. // SUCCESS
  9331. //
  9332. LStatus = LDAP_SUCCESS;
  9333. CLEANUP:
  9334. LDAP_FREE_MSG(LdapMsg);
  9335. return LStatus;
  9336. }
  9337. BOOL
  9338. FrsDsDeleteIfEmpty(
  9339. IN PLDAP Ldap,
  9340. IN PWCHAR Dn
  9341. )
  9342. /*++
  9343. Routine Description:
  9344. Delete the Dn if it is an empty container
  9345. Arguments:
  9346. Ldap
  9347. Dn
  9348. Return Value:
  9349. TRUE - Not empty or empty and deleted
  9350. FALSE - Can't search or can't delete
  9351. --*/
  9352. {
  9353. #undef DEBSUB
  9354. #define DEBSUB "FrsDsDeleteIfEmpty:"
  9355. DWORD LStatus;
  9356. PWCHAR Attrs[2];
  9357. PLDAPMessage LdapMsg = NULL;
  9358. MK_ATTRS_1(Attrs, ATTR_DN);
  9359. LStatus = ldap_search_ext_s(Ldap,
  9360. Dn,
  9361. LDAP_SCOPE_ONELEVEL,
  9362. CATEGORY_ANY,
  9363. Attrs,
  9364. 0,
  9365. NULL,
  9366. NULL,
  9367. &LdapTimeout,
  9368. 0,
  9369. &LdapMsg);
  9370. if (LStatus == LDAP_SUCCESS) {
  9371. //
  9372. // If there are any entries under this Dn then we don't want to
  9373. // delete it.
  9374. //
  9375. if (ldap_count_entries(Ldap, LdapMsg) > 0) {
  9376. LDAP_FREE_MSG(LdapMsg);
  9377. return TRUE;
  9378. }
  9379. LDAP_FREE_MSG(LdapMsg);
  9380. LStatus = ldap_delete_s(Ldap, Dn);
  9381. if (LStatus != LDAP_NO_SUCH_OBJECT) {
  9382. CLEANUP1_LS(4, ":DS: Can't delete %ws;", Dn, LStatus, CLEANUP);
  9383. }
  9384. } else if (LStatus != LDAP_NO_SUCH_OBJECT) {
  9385. DPRINT1_LS(4, ":DS: Can't search %ws;", Dn, LStatus);
  9386. LDAP_FREE_MSG(LdapMsg);
  9387. return FALSE;
  9388. } else {
  9389. //
  9390. // ldap_search can return failure but still allocated the LdapMsg buffer.
  9391. //
  9392. LDAP_FREE_MSG(LdapMsg);
  9393. }
  9394. return TRUE;
  9395. CLEANUP:
  9396. return FALSE;
  9397. }
  9398. BOOL
  9399. FrsDsEnumerateSysVolKeys(
  9400. IN PLDAP Ldap,
  9401. IN DWORD Command,
  9402. IN PWCHAR ServicesDn,
  9403. IN PWCHAR SystemDn,
  9404. IN PCONFIG_NODE Computer,
  9405. OUT BOOL *RefetchComputer
  9406. )
  9407. /*++
  9408. Routine Description:
  9409. Scan the sysvol registry keys and process them according to Command.
  9410. REGCMD_CREATE_PRIMARY_DOMAIN - Create domain wide objects
  9411. REGCMD_CREATE_MEMBERS - Create members + subscribers
  9412. REGCMD_DELETE_MEMBERS - delete members + subscribers
  9413. REGCMD_DELETE_KEYS - Done; delete all keys
  9414. Arguments:
  9415. Ldap
  9416. HKey
  9417. Command
  9418. ServicesDn
  9419. SystemDn
  9420. Computer
  9421. RefetchComputer - Objects were altered in the DS, refetch DS info
  9422. Return Value:
  9423. TRUE - No problems
  9424. FALSE - Stop processing the registry keys
  9425. --*/
  9426. {
  9427. #undef DEBSUB
  9428. #define DEBSUB "FrsDsEnumerateSysVolKeys:"
  9429. GUID Guid;
  9430. DWORD WStatus;
  9431. DWORD LStatus;
  9432. ULONG Index;
  9433. BOOL OldNaming;
  9434. BOOL RetStatus;
  9435. HKEY HSeedingsKey = 0;
  9436. HKEY HKey = 0;
  9437. LDAPMod **LdapMod = NULL;
  9438. PWCHAR SettingsDn = NULL;
  9439. PWCHAR SystemSettingsDn = NULL;
  9440. PWCHAR SetDn = NULL;
  9441. PWCHAR SystemSetDn = NULL;
  9442. PWCHAR SubsDn = NULL;
  9443. PWCHAR SubDn = NULL;
  9444. PWCHAR SystemSubDn = NULL;
  9445. PWCHAR MemberDn = NULL;
  9446. PWCHAR SystemMemberDn = NULL;
  9447. PWCHAR FileFilterList = NULL;
  9448. PWCHAR DirFilterList = NULL;
  9449. PWCHAR ReplicaSetCommand = NULL;
  9450. PWCHAR ReplicaSetName = NULL;
  9451. PWCHAR ReplicaSetParent = NULL;
  9452. PWCHAR ReplicaSetType = NULL;
  9453. PWCHAR ReplicationRootPath = NULL;
  9454. PWCHAR PrintableRealRoot = NULL;
  9455. PWCHAR SubstituteRealRoot = NULL;
  9456. PWCHAR ReplicationStagePath = NULL;
  9457. PWCHAR PrintableRealStage = NULL;
  9458. PWCHAR SubstituteRealStage = NULL;
  9459. DWORD ReplicaSetPrimary;
  9460. WCHAR RegBuf[MAX_PATH + 1];
  9461. //
  9462. // Open the system volume replica sets key.
  9463. // FRS_CONFIG_SECTION\SysVol
  9464. //
  9465. WStatus = CfgRegOpenKey(FKC_SYSVOL_SECTION_KEY, NULL, 0, &HKey);
  9466. if (!WIN_SUCCESS(WStatus)) {
  9467. DPRINT_WS(4, ":DS: WARN - Cannot open sysvol key.", WStatus);
  9468. return FALSE;
  9469. }
  9470. //
  9471. // ENUMERATE SYSVOL SUBKEYS
  9472. //
  9473. RetStatus = TRUE;
  9474. Index = 0;
  9475. while (RetStatus) {
  9476. WStatus = RegEnumKey(HKey, Index, RegBuf, MAX_PATH + 1);
  9477. if (WStatus == ERROR_NO_MORE_ITEMS) {
  9478. break;
  9479. }
  9480. if (!WIN_SUCCESS(WStatus)) {
  9481. DPRINT_WS(0, ":DS: ERROR - enumerating sysvol keys;", WStatus);
  9482. RetStatus = FALSE;
  9483. break;
  9484. }
  9485. //
  9486. // Delete the registry key
  9487. //
  9488. if (Command == REGCMD_DELETE_KEYS) {
  9489. WStatus = RegDeleteKey(HKey, RegBuf);
  9490. if (!WIN_SUCCESS(WStatus)) {
  9491. DPRINT1_WS(0, ":DS: ERROR - Cannot delete registry key %ws;",
  9492. RegBuf, WStatus);
  9493. RetStatus = FALSE;
  9494. break;
  9495. }
  9496. continue;
  9497. }
  9498. //
  9499. // Open the subkey
  9500. //
  9501. DPRINT1(4, ":DS: Processing SysVol Key: %ws\n", RegBuf);
  9502. //
  9503. // The registry will be updated with the LDAP error code
  9504. //
  9505. LStatus = LDAP_OTHER;
  9506. //
  9507. // READ THE VALUES FROM THE SUBKEY
  9508. //
  9509. // SysVol\<RegBuf>\Replica Set Command
  9510. //
  9511. CfgRegReadString(FKC_SET_N_SYSVOL_COMMAND, RegBuf, 0, &ReplicaSetCommand);
  9512. if (!ReplicaSetCommand) {
  9513. DPRINT(0, ":DS: ERROR - no command; cannot process sysvol\n");
  9514. goto CONTINUE;
  9515. }
  9516. // SysVol\<Guid>\Replica Set Name
  9517. CfgRegReadString(FKC_SET_N_SYSVOL_NAME, RegBuf, 0, &ReplicaSetName);
  9518. if (!ReplicaSetName) {
  9519. DPRINT(4, ":DS: WARN - no name; using subkey name\n");
  9520. ReplicaSetName = FrsWcsDup(RegBuf);
  9521. }
  9522. //
  9523. // Construct Settings, Set, Member, Subscriptions, and Subscriber names
  9524. // (both old and new values)
  9525. //
  9526. SettingsDn = FrsDsExtendDn(ServicesDn, CN_SYSVOLS);
  9527. SystemSettingsDn = FrsDsExtendDn(SystemDn, CN_NTFRS_SETTINGS);
  9528. SetDn = FrsDsExtendDn(SettingsDn, ReplicaSetName);
  9529. SystemSetDn = FrsDsExtendDn(SystemSettingsDn, CN_DOMAIN_SYSVOL);
  9530. MemberDn = FrsDsExtendDn(SetDn, ComputerName);
  9531. SystemMemberDn = FrsDsExtendDn(SystemSetDn, ComputerName);
  9532. SubsDn = FrsDsExtendDn(Computer->Dn, CN_SUBSCRIPTIONS);
  9533. SubDn = FrsDsExtendDn(SubsDn, ReplicaSetName);
  9534. SystemSubDn = FrsDsExtendDn(SubsDn, CN_DOMAIN_SYSVOL);
  9535. //
  9536. // DELETE REPLICA SET
  9537. //
  9538. if (WSTR_EQ(ReplicaSetCommand, L"Delete")) {
  9539. //
  9540. // But only if we are processing deletes during this enumeration
  9541. //
  9542. // Delete what we can; ignore errors
  9543. //
  9544. //
  9545. // All the deletes are done in ntfrsapi.c when we commit demotion.
  9546. // This function is never called with Command = REGCMD_DELETE_MEMBERS
  9547. //
  9548. /*
  9549. if (Command == REGCMD_DELETE_MEMBERS) {
  9550. //
  9551. // DELETE MEMBER
  9552. //
  9553. //
  9554. // Old member name under services in enterprise wide partition
  9555. //
  9556. LStatus = FrsDsDeleteSubTree(Ldap, MemberDn);
  9557. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  9558. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", MemberDn, LStatus);
  9559. //
  9560. // New member name under System in domain wide partition
  9561. //
  9562. LStatus = FrsDsDeleteSubTree(Ldap, SystemMemberDn);
  9563. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  9564. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", SystemMemberDn, LStatus);
  9565. //
  9566. // DELETE SET
  9567. //
  9568. //
  9569. // Ignore errors; no real harm leaving the set
  9570. // and settings around.
  9571. //
  9572. if (!FrsDsDeleteIfEmpty(Ldap, SetDn)) {
  9573. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SetDn);
  9574. }
  9575. if (!FrsDsDeleteIfEmpty(Ldap, SystemSetDn)) {
  9576. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SystemSetDn);
  9577. }
  9578. //
  9579. // DELETE SETTINGS (don't delete new settings, there
  9580. // may be other settings beneath it (such as DFS settings))
  9581. //
  9582. if (!FrsDsDeleteIfEmpty(Ldap, SettingsDn)) {
  9583. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SettingsDn);
  9584. }
  9585. LStatus = FrsDsDeleteSubTree(Ldap, SubDn);
  9586. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  9587. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", SubDn, LStatus);
  9588. LStatus = FrsDsDeleteSubTree(Ldap, SystemSubDn);
  9589. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  9590. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", SystemSubDn, LStatus);
  9591. //
  9592. // Ignore errors; no real harm leaving the subscriptions
  9593. //
  9594. if (!FrsDsDeleteIfEmpty(Ldap, SubsDn)) {
  9595. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SubsDn);
  9596. }
  9597. }
  9598. */
  9599. LStatus = LDAP_SUCCESS;
  9600. goto CONTINUE;
  9601. }
  9602. //
  9603. // UNKNOWN COMMAND
  9604. //
  9605. else if (WSTR_NE(ReplicaSetCommand, L"Create")) {
  9606. DPRINT1(0, ":DS: ERROR - Don't understand sysvol command %ws; cannot process sysvol\n",
  9607. ReplicaSetCommand);
  9608. goto CONTINUE;
  9609. }
  9610. //
  9611. // CREATE
  9612. //
  9613. //
  9614. // Not processing creates this scan
  9615. //
  9616. if (Command != REGCMD_CREATE_PRIMARY_DOMAIN && Command != REGCMD_CREATE_MEMBERS) {
  9617. LStatus = LDAP_SUCCESS;
  9618. goto CONTINUE;
  9619. }
  9620. //
  9621. // Finish gathering the registry values for a Create
  9622. //
  9623. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_TYPE, RegBuf, 0, &ReplicaSetType);
  9624. CLEANUP_WS(0, ":DS: ERROR - no type; cannot process sysvol.", WStatus, CONTINUE);
  9625. WStatus = CfgRegReadDWord(FKC_SET_N_SYSVOL_PRIMARY, RegBuf, 0, &ReplicaSetPrimary);
  9626. CLEANUP_WS(0, ":DS: ERROR - no primary; cannot process sysvol.", WStatus, CONTINUE);
  9627. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_ROOT, RegBuf, 0, &ReplicationRootPath);
  9628. CLEANUP_WS(0, ":DS: ERROR - no root; cannot process sysvol.", WStatus, CONTINUE);
  9629. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_STAGE, RegBuf, 0, &ReplicationStagePath);
  9630. CLEANUP_WS(0, ":DS: ERROR - no stage; cannot process sysvol.", WStatus, CONTINUE);
  9631. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_PARENT, RegBuf, 0, &ReplicaSetParent);
  9632. DPRINT_WS(0, ":DS: WARN - no parent; cannot process seeding sysvol", WStatus);
  9633. if (Command == REGCMD_CREATE_PRIMARY_DOMAIN) {
  9634. //
  9635. // Not the primary domain sysvol
  9636. //
  9637. if (!ReplicaSetPrimary ||
  9638. WSTR_NE(ReplicaSetType, NTFRSAPI_REPLICA_SET_TYPE_DOMAIN)) {
  9639. LStatus = LDAP_SUCCESS;
  9640. goto CONTINUE;
  9641. }
  9642. //
  9643. // Domain wide Settings -- may already exist
  9644. //
  9645. FrsDsAddLdapMod(ATTR_CLASS, ATTR_NTFRS_SETTINGS, &LdapMod);
  9646. DPRINT1(4, ":DS: Creating Sysvol System Settings %ws\n", CN_NTFRS_SETTINGS);
  9647. LStatus = ldap_add_s(Ldap, SystemSettingsDn, LdapMod);
  9648. FrsDsFreeLdapMod(&LdapMod);
  9649. if (LStatus == LDAP_SUCCESS) {
  9650. *RefetchComputer = TRUE;
  9651. }
  9652. if (LStatus != LDAP_ALREADY_EXISTS && LStatus != LDAP_SUCCESS) {
  9653. DPRINT1_LS(0, ":DS: ERROR - Can't create %ws:", SystemSettingsDn, LStatus);
  9654. //
  9655. // May be an error like "Access Denied". As long as we
  9656. // can create objects under it; ignore errors. It should
  9657. // have been pre-created by default, anyway.
  9658. //
  9659. // goto CONTINUE;
  9660. }
  9661. //
  9662. // Domain wide Set -- may already exist
  9663. //
  9664. UuidCreateNil(&Guid);
  9665. FrsDsAddLdapMod(ATTR_CLASS, ATTR_REPLICA_SET, &LdapMod);
  9666. FrsDsAddLdapMod(ATTR_SET_TYPE, FRS_RSTYPE_DOMAIN_SYSVOLW, &LdapMod);
  9667. //
  9668. // Create the replica set object with the default file
  9669. // and dir filter lists only if current default is non-null.
  9670. //
  9671. FileFilterList = FRS_DS_COMPOSE_FILTER_LIST(NULL,
  9672. RegistryFileExclFilterList,
  9673. DEFAULT_FILE_FILTER_LIST);
  9674. if (wcslen(FileFilterList) > 0) {
  9675. FrsDsAddLdapMod(ATTR_FILE_FILTER, FileFilterList, &LdapMod);
  9676. }
  9677. DirFilterList = FRS_DS_COMPOSE_FILTER_LIST(NULL,
  9678. RegistryDirExclFilterList,
  9679. DEFAULT_DIR_FILTER_LIST);
  9680. if (wcslen(DirFilterList) > 0) {
  9681. FrsDsAddLdapMod(ATTR_DIRECTORY_FILTER, DirFilterList, &LdapMod);
  9682. }
  9683. FrsDsAddLdapBerMod(ATTR_NEW_SET_GUID, (PCHAR)&Guid, sizeof(GUID), &LdapMod);
  9684. FrsDsAddLdapBerMod(ATTR_NEW_VERSION_GUID, (PCHAR)&Guid, sizeof(GUID), &LdapMod);
  9685. DPRINT1(4, ":DS: Creating Domain Set %ws\n", ReplicaSetName);
  9686. LStatus = ldap_add_s(Ldap, SystemSetDn, LdapMod);
  9687. FrsDsFreeLdapMod(&LdapMod);
  9688. if (LStatus == LDAP_SUCCESS) {
  9689. *RefetchComputer = TRUE;
  9690. }
  9691. if (LStatus != LDAP_ALREADY_EXISTS) {
  9692. CLEANUP1_LS(0, ":DS: ERROR - Can't create %ws:",
  9693. SystemSetDn, LStatus, CONTINUE);
  9694. }
  9695. LStatus = LDAP_SUCCESS;
  9696. goto CONTINUE;
  9697. }
  9698. if (Command != REGCMD_CREATE_MEMBERS) {
  9699. DPRINT1(0, ":DS: ERROR - Don't understand %d; can't process sysvols\n",
  9700. Command);
  9701. goto CONTINUE;
  9702. }
  9703. //
  9704. // CREATE MEMBER
  9705. //
  9706. // Member -- may already exist
  9707. // Delete old member in case it was left lying around after
  9708. // a demotion. This can happen because the service doesn't
  9709. // have permissions to alter the DS after a promotion.
  9710. // Leaving the old objects lying around after the demotion
  9711. // is confusing but doesn't cause replication to behave
  9712. // incorrectly.
  9713. //
  9714. DPRINT1(4, ":DS: Creating Member %ws\n", ComputerName);
  9715. OldNaming = FALSE;
  9716. //
  9717. // Delete old member
  9718. //
  9719. LStatus = FrsDsDeleteSubTree(Ldap, MemberDn);
  9720. CLEANUP1_LS(0, ":DS: ERROR - Can't free member %ws:",
  9721. ComputerName, LStatus, CONTINUE);
  9722. LStatus = FrsDsDeleteSubTree(Ldap, SystemMemberDn);
  9723. CLEANUP1_LS(0, ":DS: ERROR - Can't free system member %ws:",
  9724. ComputerName, LStatus, CONTINUE);
  9725. //
  9726. // Create new member
  9727. //
  9728. FrsDsAddLdapMod(ATTR_CLASS, ATTR_MEMBER, &LdapMod);
  9729. FrsDsAddLdapMod(ATTR_COMPUTER_REF, Computer->Dn, &LdapMod);
  9730. if (Computer->SettingsDn) {
  9731. FrsDsAddLdapMod(ATTR_SERVER_REF, Computer->SettingsDn, &LdapMod);
  9732. }
  9733. LStatus = ldap_add_s(Ldap, SystemMemberDn, LdapMod);
  9734. FrsDsFreeLdapMod(&LdapMod);
  9735. if (LStatus == LDAP_SUCCESS) {
  9736. *RefetchComputer = TRUE;
  9737. }
  9738. if (LStatus != LDAP_ALREADY_EXISTS && LStatus != LDAP_SUCCESS) {
  9739. //
  9740. // Try old B2 naming conventions
  9741. //
  9742. DPRINT1_LS(4, ":DS: WARN - Can't create system member ws:",
  9743. ComputerName, LStatus);
  9744. FrsDsAddLdapMod(ATTR_CLASS, ATTR_MEMBER, &LdapMod);
  9745. FrsDsAddLdapMod(ATTR_COMPUTER_REF, Computer->Dn, &LdapMod);
  9746. if (Computer->SettingsDn) {
  9747. FrsDsAddLdapMod(ATTR_SERVER_REF, Computer->SettingsDn, &LdapMod);
  9748. }
  9749. LStatus = ldap_add_s(Ldap, MemberDn, LdapMod);
  9750. FrsDsFreeLdapMod(&LdapMod);
  9751. if (LStatus == LDAP_SUCCESS) {
  9752. *RefetchComputer = TRUE;
  9753. }
  9754. if (LStatus != LDAP_ALREADY_EXISTS) {
  9755. CLEANUP1_LS(0, ":DS: ERROR - Can't create old member %ws:",
  9756. ComputerName, LStatus, CONTINUE);
  9757. }
  9758. OldNaming = TRUE;
  9759. }
  9760. //
  9761. // CREATE PRIMARY MEMBER REFERENCE
  9762. //
  9763. if (ReplicaSetPrimary) {
  9764. FrsDsAddLdapMod(ATTR_PRIMARY_MEMBER,
  9765. (OldNaming) ? MemberDn : SystemMemberDn,
  9766. &LdapMod);
  9767. DPRINT2(4, ":DS: Creating Member Reference %ws for %ws\n",
  9768. ComputerName, ReplicaSetName);
  9769. LdapMod[0]->mod_op = LDAP_MOD_REPLACE;
  9770. LStatus = ldap_modify_s(Ldap, (OldNaming) ? SetDn : SystemSetDn, LdapMod);
  9771. FrsDsFreeLdapMod(&LdapMod);
  9772. if (LStatus == LDAP_SUCCESS) {
  9773. *RefetchComputer = TRUE;
  9774. }
  9775. if (LStatus != LDAP_ATTRIBUTE_OR_VALUE_EXISTS) {
  9776. CLEANUP2_LS(0, ":DS: ERROR - Can't create priamry reference %ws\\%ws:",
  9777. ReplicaSetName, ComputerName, LStatus, CONTINUE);
  9778. }
  9779. }
  9780. //
  9781. // Translate the symlinks. NtFrs requires true pathname to
  9782. // its directories (<drive letter>:\...)
  9783. // FrsChaseSymbolicLink returns both the PrintName and the SubstituteName.
  9784. // We use the PrintName as it is the Dos Type name of the destination.
  9785. // Substitute Name is ignored.
  9786. //
  9787. WStatus = FrsChaseSymbolicLink(ReplicationRootPath, &PrintableRealRoot, &SubstituteRealRoot);
  9788. if (!WIN_SUCCESS(WStatus)) {
  9789. DPRINT2(0, ":DS: ERROR - Accessing %ws; cannot process sysvol: WStatus = %d",
  9790. ReplicationRootPath, WStatus);
  9791. RetStatus = FALSE;
  9792. goto CONTINUE;
  9793. }
  9794. WStatus = FrsChaseSymbolicLink(ReplicationStagePath, &PrintableRealStage, &SubstituteRealStage);
  9795. if (!WIN_SUCCESS(WStatus)) {
  9796. DPRINT2(0, ":DS: ERROR - Accessing %ws; cannot process sysvol: WStatus = %d",
  9797. ReplicationRootPath, WStatus);
  9798. RetStatus = FALSE;
  9799. goto CONTINUE;
  9800. }
  9801. //
  9802. // Subscriptions (if needed)
  9803. //
  9804. DPRINT1(4, ":DS: Creating Subscriptions for %ws\n", ComputerName);
  9805. FrsDsAddLdapMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &LdapMod);
  9806. FrsDsAddLdapMod(ATTR_WORKING, WorkingPath, &LdapMod);
  9807. LStatus = ldap_add_s(Ldap, SubsDn, LdapMod);
  9808. FrsDsFreeLdapMod(&LdapMod);
  9809. if (LStatus == LDAP_SUCCESS) {
  9810. *RefetchComputer = TRUE;
  9811. }
  9812. if (LStatus != LDAP_ALREADY_EXISTS) {
  9813. CLEANUP1_LS(0, ":DS: ERROR - Can't create %ws:",
  9814. SubsDn, LStatus, CONTINUE);
  9815. }
  9816. //
  9817. // Subscriber -- may alread exist
  9818. // Delete old subscriber in case it was left lying around
  9819. // after a demotion. This can happen because the service
  9820. // doesn't have permissions to alter the DS after a promotion.
  9821. // Leaving the old objects lying around after the demotion
  9822. // is confusing but doesn't cause replication to behave
  9823. // incorrectly; any sysvol in the DS without a corresponding
  9824. // sysvol in the DB is ignored by the Ds polling thread.
  9825. //
  9826. DPRINT1(4, ":DS: Creating Subscriber for %ws\n", ComputerName);
  9827. LStatus = FrsDsDeleteSubTree(Ldap, SubDn);
  9828. CLEANUP1_LS(4, ":DS: WARN - Can't delete %ws:", SubDn, LStatus, CONTINUE);
  9829. LStatus = FrsDsDeleteSubTree(Ldap, SystemSubDn);
  9830. CLEANUP1_LS(4, ":DS: WARN - Can't delete %ws:", SystemSubDn, LStatus, CONTINUE);
  9831. FrsDsAddLdapMod(ATTR_CLASS, ATTR_SUBSCRIBER, &LdapMod);
  9832. FrsDsAddLdapMod(ATTR_REPLICA_ROOT, PrintableRealRoot, &LdapMod);
  9833. FrsDsAddLdapMod(ATTR_REPLICA_STAGE, PrintableRealStage, &LdapMod);
  9834. FrsDsAddLdapMod(ATTR_MEMBER_REF,
  9835. (OldNaming) ? MemberDn : SystemMemberDn,
  9836. &LdapMod);
  9837. LStatus = ldap_add_s(Ldap, SystemSubDn, LdapMod);
  9838. FrsDsFreeLdapMod(&LdapMod);
  9839. if (LStatus == LDAP_SUCCESS) {
  9840. *RefetchComputer = TRUE;
  9841. }
  9842. if (LStatus != LDAP_ALREADY_EXISTS) {
  9843. CLEANUP1_LS(4, ":DS: ERROR - Can't create %ws:",
  9844. SystemSubDn, LStatus, CONTINUE);
  9845. }
  9846. //
  9847. // Seeding information
  9848. //
  9849. //
  9850. // Create the key for all seeding sysvols
  9851. //
  9852. WStatus = CfgRegOpenKey(FKC_SYSVOL_SEEDING_SECTION_KEY,
  9853. NULL,
  9854. FRS_RKF_CREATE_KEY,
  9855. &HSeedingsKey);
  9856. CLEANUP1_WS(0, ":DS: ERROR - Cannot create seedings key for %ws;",
  9857. ReplicaSetName, WStatus, SKIP_SEEDING);
  9858. //
  9859. // Create the seeding subkey for this sysvol
  9860. //
  9861. RegDeleteKey(HSeedingsKey, ReplicaSetName);
  9862. RegDeleteKey(HSeedingsKey, CN_DOMAIN_SYSVOL);
  9863. if (ReplicaSetParent) {
  9864. //
  9865. // Save the Replica Set Parent for this replica set under the
  9866. // "Sysvol Seeding\<rep set name>\Replica Set Parent"
  9867. //
  9868. WStatus = CfgRegWriteString(FKC_SYSVOL_SEEDING_N_PARENT,
  9869. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL,
  9870. FRS_RKF_CREATE_KEY,
  9871. ReplicaSetParent);
  9872. DPRINT1_WS(0, "WARN - Cannot create parent value for %ws;",
  9873. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL, WStatus);
  9874. }
  9875. //
  9876. // Save the Replica Set name for this replica set under the
  9877. // "Sysvol Seeding\<rep set name>\Replica Set Name"
  9878. //
  9879. WStatus = CfgRegWriteString(FKC_SYSVOL_SEEDING_N_RSNAME,
  9880. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL,
  9881. FRS_RKF_CREATE_KEY,
  9882. ReplicaSetName);
  9883. DPRINT1_WS(0, "WARN - Cannot create name value for %ws;",
  9884. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL, WStatus);
  9885. SKIP_SEEDING:
  9886. LStatus = LDAP_SUCCESS;
  9887. CONTINUE:
  9888. if (HSeedingsKey) {
  9889. RegCloseKey(HSeedingsKey);
  9890. HSeedingsKey = 0;
  9891. }
  9892. //
  9893. // Something went wrong. Put the LDAP error status into the
  9894. // registry key for this replica set and move on to the next.
  9895. //
  9896. if (LStatus != LDAP_SUCCESS) {
  9897. CfgRegWriteDWord(FKC_SET_N_SYSVOL_STATUS, RegBuf, 0, LStatus);
  9898. RetStatus = FALSE;
  9899. }
  9900. //
  9901. // CLEANUP
  9902. //
  9903. ReplicaSetCommand = FrsFree(ReplicaSetCommand);
  9904. ReplicaSetName = FrsFree(ReplicaSetName);
  9905. ReplicaSetParent = FrsFree(ReplicaSetParent);
  9906. ReplicaSetType = FrsFree(ReplicaSetType);
  9907. ReplicationRootPath = FrsFree(ReplicationRootPath);
  9908. PrintableRealRoot = FrsFree(PrintableRealRoot);
  9909. SubstituteRealRoot = FrsFree(SubstituteRealRoot);
  9910. ReplicationStagePath = FrsFree(ReplicationStagePath);
  9911. PrintableRealStage = FrsFree(PrintableRealStage);
  9912. SubstituteRealStage = FrsFree(SubstituteRealStage);
  9913. SettingsDn = FrsFree(SettingsDn);
  9914. SystemSettingsDn = FrsFree(SystemSettingsDn);
  9915. SetDn = FrsFree(SetDn);
  9916. SystemSetDn = FrsFree(SystemSetDn);
  9917. SubsDn = FrsFree(SubsDn);
  9918. SubDn = FrsFree(SubDn);
  9919. SystemSubDn = FrsFree(SystemSubDn);
  9920. MemberDn = FrsFree(MemberDn);
  9921. SystemMemberDn = FrsFree(SystemMemberDn);
  9922. FileFilterList = FrsFree(FileFilterList);
  9923. DirFilterList = FrsFree(DirFilterList);
  9924. //
  9925. // Next SubKey
  9926. //
  9927. ++Index;
  9928. } // End while (RetStatus)
  9929. if (HKey) {
  9930. //
  9931. // The flush here will make sure that the key is written to the disk.
  9932. // These are critical registry operations and we don't want the lazy flusher
  9933. // to delay the writes.
  9934. //
  9935. RegFlushKey(HKey);
  9936. RegCloseKey(HKey);
  9937. }
  9938. return RetStatus;
  9939. }
  9940. DWORD
  9941. FrsDsCreateSysVols(
  9942. IN PLDAP Ldap,
  9943. IN PWCHAR ServicesDn,
  9944. IN PCONFIG_NODE Computer,
  9945. OUT BOOL *RefetchComputer
  9946. )
  9947. /*++
  9948. Routine Description:
  9949. Process the commands left in the Sysvol registry key by dcpromo.
  9950. Ignore the sysvol registry key if this machine is not a DC!
  9951. NOTE: this means the registry keys for a "delete sysvol"
  9952. after a demotion are pretty much ignored. So why have them?
  9953. Its historical and there is too little time before B3 to make
  9954. such a dramatic change. Besides, we may find a use for them.
  9955. And, to make matters worse, the "delete sysvol" keys could
  9956. not be processed because the ldap_delete() returned insufficient
  9957. rights errors since this computer is no longer a DC.
  9958. REGCMD_DELETE_MEMBERS is no longer used as all deletion is done
  9959. in ntfrsapi.c when demotion is committed.
  9960. Arguments:
  9961. Ldap
  9962. ServicesDn
  9963. Computer
  9964. RefetchComputer - Objects were altered in the DS, refetch DS info
  9965. Return Value:
  9966. None.
  9967. --*/
  9968. {
  9969. #undef DEBSUB
  9970. #define DEBSUB "FrsDsCreateSysVols:"
  9971. DWORD WStatus;
  9972. DWORD SysVolInfoIsCommitted;
  9973. HKEY HKey = 0;
  9974. //
  9975. // Refetch the computer subtree iff the contents of the DS
  9976. // are altered by this function
  9977. //
  9978. *RefetchComputer = FALSE;
  9979. //
  9980. // Already checked the registry or not a DC; done
  9981. //
  9982. if (DsCreateSysVolsHasRun || !IsADc) {
  9983. return ERROR_SUCCESS;
  9984. }
  9985. DPRINT(5, ":DS: Checking for SysVols commands\n");
  9986. //
  9987. // Open the system volume replica sets key.
  9988. // FRS_CONFIG_SECTION\SysVol
  9989. //
  9990. WStatus = CfgRegOpenKey(FKC_SYSVOL_SECTION_KEY, NULL, 0, &HKey);
  9991. if (!WIN_SUCCESS(WStatus)) {
  9992. DPRINT_WS(4, ":DS: WARN - Cannot open sysvol key.", WStatus);
  9993. DPRINT(4, ":DS: ERROR - Can't check for sysvols\n");
  9994. return WStatus;
  9995. }
  9996. WStatus = CfgRegReadDWord(FKC_SYSVOL_INFO_COMMITTED, NULL, 0, &SysVolInfoIsCommitted);
  9997. CLEANUP_WS(4, ":DS: Sysvol info is not committed.", WStatus, done);
  9998. DPRINT1(4, ":DS: Sysvol info is committed (%d)\n", SysVolInfoIsCommitted);
  9999. //
  10000. // Must have a computer; try again later
  10001. //
  10002. if (!Computer) {
  10003. DPRINT(4, ":DS: No computer; retry sysvols later\n");
  10004. WStatus = ERROR_RETRY;
  10005. goto cleanup;
  10006. }
  10007. //
  10008. // Must have a server reference; try again later
  10009. //
  10010. if (!Computer->SettingsDn && RunningAsAService) {
  10011. DPRINT1(4, ":DS: %ws does not have a server reference; retry sysvols later\n",
  10012. Computer->Name->Name);
  10013. WStatus = ERROR_RETRY;
  10014. goto cleanup;
  10015. }
  10016. //
  10017. // assume failure
  10018. //
  10019. WIN_SET_FAIL(WStatus);
  10020. //
  10021. // Don't create the settings or set if this computer is not a DC
  10022. //
  10023. if (IsADc &&
  10024. !FrsDsEnumerateSysVolKeys(Ldap, REGCMD_CREATE_PRIMARY_DOMAIN,
  10025. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  10026. goto cleanup;
  10027. }
  10028. //
  10029. // Don't create the member if this computer is not a DC
  10030. //
  10031. if (IsADc &&
  10032. !FrsDsEnumerateSysVolKeys(Ldap, REGCMD_CREATE_MEMBERS,
  10033. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  10034. goto cleanup;
  10035. }
  10036. //
  10037. // Don't delete the sysvol if this computer is a DC.
  10038. //
  10039. // The following code is never executed because if we are not a DC then
  10040. // the function returns after the first check.
  10041. //
  10042. /*
  10043. if (!IsADc &&
  10044. !FrsDsEnumerateSysVolKeys(Ldap, REGCMD_DELETE_MEMBERS,
  10045. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  10046. goto cleanup;
  10047. }
  10048. */
  10049. //
  10050. // Discard the dcpromo keys
  10051. //
  10052. if (!FrsDsEnumerateSysVolKeys(Ldap, REGCMD_DELETE_KEYS,
  10053. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  10054. goto cleanup;
  10055. }
  10056. //
  10057. // sysvol info has been processed; don't process again
  10058. //
  10059. RegDeleteValue(HKey, SYSVOL_INFO_IS_COMMITTED);
  10060. done:
  10061. DsCreateSysVolsHasRun = TRUE;
  10062. WStatus = ERROR_SUCCESS;
  10063. cleanup:
  10064. //
  10065. // Cleanup
  10066. //
  10067. if (HKey) {
  10068. //
  10069. // The flush here will make sure that the key is written to the disk.
  10070. // These are critical registry operations and we don't want the lazy flusher
  10071. // to delay the writes.
  10072. //
  10073. RegFlushKey(HKey);
  10074. RegCloseKey(HKey);
  10075. }
  10076. return WStatus;
  10077. }
  10078. PWCHAR
  10079. FrsDsPrincNameToBiosName(
  10080. IN PWCHAR PrincName
  10081. )
  10082. /*++
  10083. Routine Description:
  10084. Convert the principal name (domain.dns.name\SamAccountName) into
  10085. its equivalent NetBios name (SamAccountName - $).
  10086. Arguments:
  10087. PrincName - Domain Dns Name \ Sam Account Name
  10088. Return Value:
  10089. Sam Account Name - trailing $
  10090. --*/
  10091. {
  10092. #undef DEBSUB
  10093. #define DEBSUB "FrsDsPrincNameToBiosName:"
  10094. DWORD Len;
  10095. PWCHAR c;
  10096. PWCHAR BiosName = NULL;
  10097. if (!PrincName || !*PrincName) {
  10098. goto CLEANUP;
  10099. }
  10100. //
  10101. // Find the first char past the first whack
  10102. //
  10103. for (c = PrincName; *c && *c != L'\\'; ++c);
  10104. if (!*c) {
  10105. //
  10106. // No whack; use the entire principal name
  10107. //
  10108. c = PrincName;
  10109. } else {
  10110. //
  10111. // Skip the whack
  10112. //
  10113. ++c;
  10114. }
  10115. //
  10116. // Elide the trailing $
  10117. //
  10118. Len = wcslen(c);
  10119. if (c[Len - 1] == L'$') {
  10120. --Len;
  10121. }
  10122. //
  10123. // Copy the chars between the whack and the dollar (append trailing null)
  10124. //
  10125. BiosName = FrsAlloc((Len + 1) * sizeof(WCHAR));
  10126. CopyMemory(BiosName, c, Len * sizeof(WCHAR));
  10127. BiosName[Len] = L'\0';
  10128. CLEANUP:
  10129. DPRINT2(4, ":DS: PrincName %ws to BiosName %ws\n", PrincName, BiosName);
  10130. return BiosName;
  10131. }
  10132. VOID
  10133. FrsDsMergeConfigWithReplicas(
  10134. IN PLDAP Ldap,
  10135. IN PCONFIG_NODE Sites
  10136. )
  10137. /*++
  10138. Routine Description:
  10139. Convert the portions of the DS tree that define the topology
  10140. and state for this machine into replicas and merge them with
  10141. the active replicas.
  10142. Arguments:
  10143. Sites
  10144. Return Value:
  10145. None.
  10146. --*/
  10147. {
  10148. #undef DEBSUB
  10149. #define DEBSUB "FrsDsMergeConfigWithReplicas:"
  10150. PCONFIG_NODE Site;
  10151. PCONFIG_NODE Settings;
  10152. PCONFIG_NODE Set;
  10153. PCONFIG_NODE Server;
  10154. PCONFIG_NODE Node;
  10155. PCONFIG_NODE RevNode;
  10156. BOOL Inbound;
  10157. BOOL IsSysvol;
  10158. PCXTION Cxtion;
  10159. PREPLICA Replica;
  10160. PREPLICA DbReplica;
  10161. //
  10162. // Coordinate with replica command server
  10163. //
  10164. RcsBeginMergeWithDs();
  10165. //
  10166. // For every server
  10167. //
  10168. for (Site = Sites; Site; Site = Site->Peer) {
  10169. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  10170. for (Set = Settings->Children; Set; Set = Set->Peer) {
  10171. for (Server = Set->Children; Server && !DsIsShuttingDown; Server = Server->Peer) {
  10172. //
  10173. // This server does not match this machine's name; continue
  10174. //
  10175. if (!Server->ThisComputer) {
  10176. continue;
  10177. }
  10178. //
  10179. // MATCH
  10180. //
  10181. //
  10182. // CHECK FOR SYSVOL CONSISTENCY
  10183. // Leave the current DB state alone if a sysvol
  10184. // appears from the DS and the sysvol registry
  10185. // keys were not processed or the computer is not
  10186. // a dc.
  10187. //
  10188. if (FRS_RSTYPE_IS_SYSVOLW(Set->SetType)) {
  10189. //
  10190. // Not a DC or sysvol registry keys not processed
  10191. // Tombstone existing sysvols
  10192. // Ignore new sysvols
  10193. //
  10194. if (!IsADc || !DsCreateSysVolsHasRun) {
  10195. continue;
  10196. }
  10197. }
  10198. //
  10199. // Create a replica set
  10200. //
  10201. Replica = FrsAllocType(REPLICA_TYPE);
  10202. //
  10203. // Replica name (Set Name + Member Guid)
  10204. Replica->ReplicaName = FrsBuildGName(FrsDupGuid(Server->Name->Guid),
  10205. FrsWcsDup(Set->Name->Name));
  10206. //
  10207. // Member name + guid
  10208. //
  10209. Replica->MemberName = FrsDupGName(Server->Name);
  10210. //
  10211. // Set name + guid
  10212. //
  10213. Replica->SetName = FrsDupGName(Set->Name);
  10214. //
  10215. // Root guid (hammered onto the root directory)
  10216. // Temporary; a new guid is assigned if this is a new
  10217. // set.
  10218. //
  10219. Replica->ReplicaRootGuid = FrsDupGuid(Replica->SetName->Guid);
  10220. //
  10221. // File Filter
  10222. //
  10223. Replica->FileFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  10224. Set->FileFilterList,
  10225. RegistryFileExclFilterList,
  10226. DEFAULT_FILE_FILTER_LIST);
  10227. Replica->FileInclFilterList = FrsWcsDup(RegistryFileInclFilterList);
  10228. //
  10229. // Directory Filter
  10230. //
  10231. Replica->DirFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  10232. Set->DirFilterList,
  10233. RegistryDirExclFilterList,
  10234. DEFAULT_DIR_FILTER_LIST);
  10235. Replica->DirInclFilterList = FrsWcsDup(RegistryDirInclFilterList);
  10236. //
  10237. // Root and stage
  10238. //
  10239. Replica->Root = FrsWcsDup(Server->Root);
  10240. Replica->Stage = FrsWcsDup(Server->Stage);
  10241. FRS_WCSLWR(Replica->Root); // for wcsstr()
  10242. FRS_WCSLWR(Replica->Stage); // for wcsstr()
  10243. //
  10244. // Volume.
  10245. //
  10246. // Replica->Volume = FrsWcsVolume(Server->Root);
  10247. //
  10248. // Does the Set's primary member link match this
  10249. // member's Dn? Is this the primary member?
  10250. //
  10251. if (Set->MemberDn && WSTR_EQ(Server->Dn, Set->MemberDn)) {
  10252. SetFlag(Replica->CnfFlags, CONFIG_FLAG_PRIMARY);
  10253. }
  10254. //
  10255. // Consistent
  10256. //
  10257. Replica->Consistent = Server->Consistent;
  10258. //
  10259. // Replica Set Type
  10260. //
  10261. if (Set->SetType) {
  10262. Replica->ReplicaSetType = wcstoul(Set->SetType, NULL, 10);
  10263. } else {
  10264. Replica->ReplicaSetType = FRS_RSTYPE_OTHER;
  10265. }
  10266. //
  10267. // Set default Schedule for replica set. Priority order is:
  10268. // 1. Server (sysvols only)
  10269. // 2. ReplicaSet object
  10270. // 3. Settings object
  10271. // 4. Site object.
  10272. //
  10273. Node = (Server->Schedule) ? Server :
  10274. (Set->Schedule) ? Set :
  10275. (Settings->Schedule) ? Settings :
  10276. (Site->Schedule) ? Site : NULL;
  10277. if (Node) {
  10278. Replica->Schedule = FrsAlloc(Node->ScheduleLength);
  10279. CopyMemory(Replica->Schedule, Node->Schedule, Node->ScheduleLength);
  10280. }
  10281. //
  10282. // Sysvol needs seeding
  10283. //
  10284. // The CnfFlags are ignored if the set already exists.
  10285. // Hence, only newly created sysvols are seeded.
  10286. //
  10287. IsSysvol = FRS_RSTYPE_IS_SYSVOL(Replica->ReplicaSetType);
  10288. if (IsSysvol &&
  10289. !BooleanFlagOn(Replica->CnfFlags, CONFIG_FLAG_PRIMARY)) {
  10290. SetFlag(Replica->CnfFlags, CONFIG_FLAG_SEEDING);
  10291. }
  10292. //
  10293. // Go through the connections and fix the schedule for
  10294. // two way replication.
  10295. //
  10296. for (Node = Server->Children; Node; Node = Node->Peer) {
  10297. if (!Node->Consistent) {
  10298. continue;
  10299. }
  10300. //
  10301. // If the NTDSCONN_OPT_TWOWAY_SYNC flag is set on the connection then
  10302. // merge the schedule on this connection with the schedule on the connection
  10303. // that is in the opposite direction and use the resultant schedule on the
  10304. // connection in the opposite direction.
  10305. //
  10306. if (Node->CxtionOptions & NTDSCONN_OPT_TWOWAY_SYNC) {
  10307. Inbound = !Node->Inbound;
  10308. //
  10309. // Loop through the connections and find the connection in
  10310. // the opposite direction.
  10311. //
  10312. for (RevNode = Server->Children; RevNode; RevNode = RevNode->Peer) {
  10313. if ((RevNode->Inbound == Inbound) &&
  10314. !_wcsicmp(Node->PartnerDn, RevNode->PartnerDn)) {
  10315. DPRINT1(4,"Two-way replication: Setting merged schedule on %ws\n",RevNode->Dn);
  10316. FrsDsMergeTwoWaySchedules(&Node->Schedule,
  10317. &Node->ScheduleLength,
  10318. &RevNode->Schedule,
  10319. &RevNode->ScheduleLength,
  10320. &Replica->Schedule);
  10321. break;
  10322. }
  10323. }
  10324. }
  10325. }
  10326. //
  10327. // Copy over the cxtions
  10328. //
  10329. for (Node = Server->Children; Node; Node = Node->Peer) {
  10330. if (!Node->Consistent) {
  10331. continue;
  10332. }
  10333. Cxtion = FrsAllocType(CXTION_TYPE);
  10334. Cxtion->Inbound = Node->Inbound;
  10335. if (Node->Consistent) {
  10336. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT);
  10337. }
  10338. Cxtion->Name = FrsDupGName(Node->Name);
  10339. Cxtion->Partner = FrsBuildGName(
  10340. FrsDupGuid(Node->PartnerName->Guid),
  10341. FrsDsPrincNameToBiosName(Node->PrincName));
  10342. //
  10343. // Partner's DNS name from ATTR_DNS_HOST_NAME on the computer
  10344. // object. Register an event if the attribute is missing
  10345. // or unavailable and try using the netbios name.
  10346. //
  10347. if (Node->PartnerDnsName) {
  10348. Cxtion->PartnerDnsName = FrsWcsDup(Node->PartnerDnsName);
  10349. } else {
  10350. if (Cxtion->Partner->Name && Cxtion->Partner->Name[0]) {
  10351. EPRINT3(EVENT_FRS_NO_DNS_ATTRIBUTE,
  10352. Cxtion->Partner->Name,
  10353. ATTR_DNS_HOST_NAME,
  10354. (Node->PartnerCoDn) ? Node->PartnerCoDn :
  10355. Cxtion->Partner->Name);
  10356. Cxtion->PartnerDnsName = FrsWcsDup(Cxtion->Partner->Name);
  10357. } else {
  10358. Cxtion->PartnerDnsName = FrsWcsDup(L"<unknown>");
  10359. }
  10360. }
  10361. //
  10362. // Partner's SID name from DsCrackNames() on the computer
  10363. // object. Register an event if the SID is unavailable.
  10364. //
  10365. if (Node->PartnerSid) {
  10366. Cxtion->PartnerSid = FrsWcsDup(Node->PartnerSid);
  10367. } else {
  10368. //
  10369. // Print the eventlog message only if DsBindingsAreValid is TRUE.
  10370. // If it is FALSE it means that the handle is invalid and we are
  10371. // scheduled to rebind at the next poll. In that case the rebind will
  10372. // probably fix the problem silently.
  10373. //
  10374. if (Cxtion->Partner->Name && Cxtion->Partner->Name[0] && DsBindingsAreValid) {
  10375. EPRINT3(EVENT_FRS_NO_SID,
  10376. Replica->Root,
  10377. Cxtion->Partner->Name,
  10378. (Node->PartnerCoDn) ? Node->PartnerCoDn :
  10379. Cxtion->Partner->Name);
  10380. }
  10381. Cxtion->PartnerSid = FrsWcsDup(L"<unknown>");
  10382. }
  10383. Cxtion->PartnerPrincName = FrsWcsDup(Node->PrincName);
  10384. Cxtion->PartSrvName = FrsWcsDup(Node->PrincName);
  10385. //
  10386. // Use the schedule on the cxtion object if provided.
  10387. // Otherwise it will default to the schedule on the replica struct
  10388. // that was set above.
  10389. //
  10390. if (Node->Schedule) {
  10391. Cxtion->Schedule = FrsAlloc(Node->ScheduleLength);
  10392. CopyMemory(Cxtion->Schedule, Node->Schedule, Node->ScheduleLength);
  10393. }
  10394. //
  10395. // Treat the schedule as a trigger schedule if the partner
  10396. // is in another site, if this is a sysvol, and if the node
  10397. // has a schedule.
  10398. //
  10399. // A missing schedule means, "always on" for both
  10400. // stop/start and trigger schedules.
  10401. //
  10402. if (IsSysvol && !Node->SameSite && Node->Schedule) {
  10403. SetCxtionFlag(Cxtion, CXTION_FLAGS_TRIGGER_SCHEDULE);
  10404. }
  10405. SetCxtionState(Cxtion, CxtionStateUnjoined);
  10406. GTabInsertEntry(Replica->Cxtions, Cxtion, Cxtion->Name->Guid, NULL);
  10407. //
  10408. // Copy over the value of options attribute of the connection object.
  10409. //
  10410. Cxtion->Options = Node->CxtionOptions;
  10411. Cxtion->Priority = FRSCONN_GET_PRIORITY(Cxtion->Options);
  10412. }
  10413. //
  10414. // Merge the replica with the active replicas
  10415. //
  10416. RcsMergeReplicaFromDs(Replica);
  10417. } } } }
  10418. RcsEndMergeWithDs();
  10419. //
  10420. // The above code is only executed when the DS changes. This should
  10421. // be an infrequent occurance. Any code we loaded to process the merge
  10422. // can now be discarded without undue impact on active replication.
  10423. //
  10424. SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
  10425. }
  10426. /* ################## NEW DS POLLING CODE STARTS ############################################################################################ */
  10427. VOID
  10428. FrsNewDsPollDs(
  10429. VOID
  10430. )
  10431. /*++
  10432. Routine Description:
  10433. New way to get the current configuration from the DS and merge it with
  10434. the active replicas.
  10435. Arguments:
  10436. None.
  10437. Return Value:
  10438. None.
  10439. --*/
  10440. {
  10441. #undef DEBSUB
  10442. #define DEBSUB "FrsNewDsPollDs:"
  10443. BOOL RefetchComputer;
  10444. DWORD WStatus = ERROR_SUCCESS;
  10445. PCONFIG_NODE Services = NULL;
  10446. PCONFIG_NODE Computer = NULL;
  10447. PVOID Key = NULL;
  10448. PGEN_ENTRY Entry = NULL;
  10449. //
  10450. // Increment the DS Polls Counter
  10451. //
  10452. PM_INC_CTR_SERVICE(PMTotalInst, DSPolls, 1);
  10453. //
  10454. // Rebuild the VolSerialNumberToDriveTable at the start of each poll so that
  10455. // we have the most current information in this table.
  10456. //
  10457. FrsBuildVolSerialNumberToDriveTable();
  10458. #if DBG
  10459. //
  10460. // For test purposes, you can run without a DS
  10461. //
  10462. if (NoDs) {
  10463. //
  10464. // kick off the rest of the service
  10465. //
  10466. MainInit();
  10467. if (!MainInitHasRun) {
  10468. FRS_ASSERT(MainInitHasRun == TRUE);
  10469. }
  10470. //
  10471. // Use the hardwired config
  10472. //
  10473. if (IniFileName) {
  10474. DPRINT(0, ":DS: Hard wired config from ini file.\n");
  10475. FrsDsUseHardWired(LoadedWired);
  10476. } else {
  10477. DPRINT(0, ":DS: David's hard wired config.\n");
  10478. //
  10479. // Complete config
  10480. //
  10481. FrsDsUseHardWired(DavidWired);
  10482. #if 0
  10483. Sleep(60 * 1000);
  10484. //
  10485. // Take out the server 2 (E:)
  10486. //
  10487. FrsDsUseHardWired(DavidWired2);
  10488. Sleep(60 * 1000);
  10489. //
  10490. // Put back in E and but take out all cxtions
  10491. //
  10492. //FrsDsUseHardWired();
  10493. //Sleep(5 * 1000);
  10494. //
  10495. // Put everything back in
  10496. //
  10497. FrsDsUseHardWired(DavidWired);
  10498. Sleep(60 * 1000);
  10499. #endif
  10500. //
  10501. // Repeat in 30 seconds
  10502. //
  10503. DsPollingShortInterval = 30 * 1000;
  10504. DsPollingLongInterval = 30 * 1000;
  10505. DsPollingInterval = 30 * 1000;
  10506. }
  10507. return;
  10508. }
  10509. #endif DBG
  10510. //
  10511. // Backup/Restore
  10512. //
  10513. WStatus = FrsProcessBackupRestore();
  10514. if (!WIN_SUCCESS(WStatus)) {
  10515. goto CLEANUP;
  10516. }
  10517. //
  10518. // Open and bind an ldap connection to the DS
  10519. //
  10520. if (!FrsDsOpenDs()) {
  10521. if (DsIsShuttingDown) {
  10522. goto CLEANUP;
  10523. }
  10524. DPRINT(4, ":DS: Wait 5 seconds and retry DS open.\n");
  10525. WaitForSingleObject(ShutDownEvent, 5 * 1000);
  10526. if (!FrsDsOpenDs()) {
  10527. if (DsIsShuttingDown) {
  10528. goto CLEANUP;
  10529. }
  10530. DPRINT(4, ":DS: Wait 30 seconds and retry DS open.\n");
  10531. WaitForSingleObject(ShutDownEvent, 30 * 1000);
  10532. if (!FrsDsOpenDs()) {
  10533. if (DsIsShuttingDown) {
  10534. goto CLEANUP;
  10535. }
  10536. DPRINT(4, ":DS: Wait 180 seconds and retry DS open.\n");
  10537. WaitForSingleObject(ShutDownEvent, 3 * 60 * 1000);
  10538. if (!FrsDsOpenDs()) {
  10539. //
  10540. // Add to the poll summary event log.
  10541. //
  10542. FrsDsAddToPollSummary(IDS_POLL_SUM_DSBIND_FAIL);
  10543. goto CLEANUP;
  10544. }
  10545. }
  10546. }
  10547. }
  10548. //
  10549. // Keep a running checksum of the change usns for this polling cycle
  10550. // Ignore configurations whose checksum is not the same for two
  10551. // polling intervals (DS is in flux).
  10552. //
  10553. ThisChange = 0;
  10554. NextChange = 0;
  10555. //
  10556. // User side of the configuration. This function will build two table of subscribers.
  10557. // SubscribersByRootPath and SubscribersByMemberRef. It will resolve any duplicate
  10558. // conflicts.
  10559. //
  10560. //
  10561. // Initialize the PartnerComputerTable.
  10562. //
  10563. if (PartnerComputerTable != NULL) {
  10564. //
  10565. // Members of the PartnerComputerTable need to be freed seperately
  10566. // as they are not part of the tree. So call FrsFreeType for
  10567. // each node.
  10568. //
  10569. PartnerComputerTable = GTabFreeTable(PartnerComputerTable, FrsFreeType);
  10570. }
  10571. PartnerComputerTable = GTabAllocStringTable();
  10572. //
  10573. // Initialize the AllCxtionsTable.
  10574. //
  10575. if (AllCxtionsTable != NULL) {
  10576. AllCxtionsTable = GTabFreeTable(AllCxtionsTable, NULL);
  10577. }
  10578. AllCxtionsTable = GTabAllocStringTable();
  10579. WStatus = FrsNewDsGetComputer(gLdap, &Computer);
  10580. if (!WIN_SUCCESS(WStatus)) {
  10581. //
  10582. // Add to the poll summary event log.
  10583. //
  10584. FrsDsAddToPollSummary(IDS_POLL_SUM_NO_COMPUTER);
  10585. goto CLEANUP;
  10586. }
  10587. if (!Computer) {
  10588. DPRINT(4, ":DS: NO COMPUTER OBJECT!\n");
  10589. //
  10590. // Add to the poll summary event log.
  10591. //
  10592. FrsDsAddToPollSummary(IDS_POLL_SUM_NO_COMPUTER);
  10593. }
  10594. //
  10595. // Register (once) our SPN using the global ds binding handle.
  10596. //
  10597. if (Computer) {
  10598. FrsDsRegisterSpn(gLdap, Computer);
  10599. }
  10600. //
  10601. // Create the sysvols, if any
  10602. //
  10603. if (IsADc && !DsCreateSysVolsHasRun) {
  10604. WStatus = FrsDsCreateSysVols(gLdap, ServicesDn, Computer, &RefetchComputer);
  10605. if (!WIN_SUCCESS(WStatus)) {
  10606. DPRINT1(4, ":DS: IGNORE Can't process sysvols; WStatus %s!\n", ErrLabelW32(WStatus));
  10607. WStatus = ERROR_SUCCESS;
  10608. } else if (RefetchComputer) {
  10609. //
  10610. // FrsDsCreateSysVols() may add/del objects from the user
  10611. // side of the configuration; refetch just in case.
  10612. //
  10613. ThisChange = 0;
  10614. NextChange = 0;
  10615. SubscriberTable = GTabFreeTable(SubscriberTable, NULL);
  10616. FrsDsFreeTree(Computer);
  10617. WStatus = FrsNewDsGetComputer(gLdap, &Computer);
  10618. if (!WIN_SUCCESS(WStatus)) {
  10619. goto CLEANUP;
  10620. }
  10621. }
  10622. }
  10623. //
  10624. // Is there any possibility that a replica set exists or that
  10625. // an old replica set should be deleted?
  10626. //
  10627. if (!FrsNewDsDoesUserWantReplication(Computer)) {
  10628. //
  10629. // Nope, no new, existing, or deleted sets
  10630. //
  10631. DPRINT(4, ":DS: Nothing to do; don't start the rest of the system.\n");
  10632. //
  10633. // Add to the poll summary event log.
  10634. //
  10635. FrsDsAddToPollSummary(IDS_POLL_SUM_NO_REPLICASETS);
  10636. WStatus = ERROR_RETRY;
  10637. goto CLEANUP;
  10638. }
  10639. //
  10640. // kick off the rest of the service
  10641. //
  10642. MainInit();
  10643. if (!MainInitHasRun) {
  10644. FRS_ASSERT(MainInitHasRun == TRUE);
  10645. }
  10646. //
  10647. // Admin side of the configuration
  10648. //
  10649. WStatus = FrsNewDsGetServices(gLdap, Computer, &Services);
  10650. if (Services == NULL) {
  10651. goto CLEANUP;
  10652. }
  10653. //
  10654. // Increment the DS Polls with and without changes Counters
  10655. //
  10656. if ((LastChange == 0)|| (ThisChange != LastChange)) {
  10657. PM_INC_CTR_SERVICE(PMTotalInst, DSPollsWChanges, 1);
  10658. }
  10659. else {
  10660. PM_INC_CTR_SERVICE(PMTotalInst, DSPollsWOChanges, 1);
  10661. }
  10662. //
  10663. // Don't use the config if the DS is in flux unless
  10664. // this is the first successful polling cycle.
  10665. //
  10666. if (DsPollingInterval != DsPollingShortInterval &&
  10667. LastChange && ThisChange != LastChange) {
  10668. DPRINT(4, ":DS: Skipping noisy topology\n");
  10669. LastChange = ThisChange;
  10670. //
  10671. // Check for a stable DS configuration after a short interval
  10672. //
  10673. DsPollingInterval = DsPollingShortInterval;
  10674. goto CLEANUP;
  10675. } else {
  10676. LastChange = ThisChange;
  10677. }
  10678. //
  10679. // No reason to continue polling the DS quickly; we have all
  10680. // of the stable information currently in the DS.
  10681. //
  10682. // DsPollingInterval = DsPollingLongInterval;
  10683. //
  10684. // Don't process the same topology repeatedly
  10685. //
  10686. // NTRAID#23652-2000/03/29-sudarc (Perf - FRS merges the DS configuration
  10687. // with its interval DB everytime it polls.)
  10688. //
  10689. // *Note*: Disable ActiveChange for now; too unreliable and too
  10690. // many error conditions in replica.c, createdb.c, ...
  10691. // besides, configs are so small that cpu savings are minimal
  10692. // plus; rejoins not issued every startreplica()
  10693. //
  10694. ActiveChange = 0;
  10695. if (ActiveChange && NextChange == ActiveChange) {
  10696. DPRINT(4, ":DS: Skipping previously processed topology\n");
  10697. goto CLEANUP;
  10698. }
  10699. //
  10700. // *Note*: Inconsistencies detected below should reset ActiveChange
  10701. // to 0 if the inconsistencies may clear up with time and
  10702. // don't require a DS change to clear up or fix
  10703. //
  10704. ActiveChange = NextChange;
  10705. //
  10706. // Check for valid paths
  10707. //
  10708. FrsDsCheckServerPaths(Services);
  10709. //
  10710. // Create the server principal name for each cxtion
  10711. //
  10712. FrsNewDsCreatePartnerPrincName(Services);
  10713. //
  10714. // Check the schedules
  10715. //
  10716. FrsDsCheckSchedules(Services);
  10717. FrsDsCheckSchedules(Computer);
  10718. //
  10719. // Now comes the tricky part. The above checks were made without
  10720. // regard to a nodes consistency. Now is the time to propagate
  10721. // inconsistencies throughout the tree to avoid inconsistencies
  10722. // caused by inconsistencies. E.g., a valid cxtion with an
  10723. // inconsistent partner.
  10724. //
  10725. //
  10726. // Push the parent's inconsistent state to its children
  10727. //
  10728. FrsDsPushInConsistenciesDown(Services);
  10729. //
  10730. // Merge the new config with the active replicas
  10731. //
  10732. DPRINT(4, ":DS: Begin merging Ds with Db\n");
  10733. FrsDsMergeConfigWithReplicas(gLdap, Services);
  10734. DPRINT(4, ":DS: End merging Ds with Db\n");
  10735. CLEANUP:
  10736. //
  10737. // Free the tables that were pointing into the tree.
  10738. // This just frees the entries in the table not the nodes.
  10739. // but the nodes can not be freed before freeing the
  10740. // tables as the compare functions are needed while.
  10741. // emptying the table.
  10742. //
  10743. SubscriberTable = GTabFreeTable(SubscriberTable, NULL);
  10744. SetTable = GTabFreeTable(SetTable, NULL);
  10745. CxtionTable = GTabFreeTable(CxtionTable, NULL);
  10746. AllCxtionsTable = GTabFreeTable(AllCxtionsTable, NULL);
  10747. //
  10748. // Members of the PartnerComputerTable need to be freed seperately
  10749. // as they are not part of the tree. So call FrsFreeType for
  10750. // each node.
  10751. //
  10752. PartnerComputerTable = GTabFreeTable(PartnerComputerTable, FrsFreeType);
  10753. MemberTable = GTabFreeTable(MemberTable, NULL);
  10754. if (MemberSearchFilter != NULL) {
  10755. MemberSearchFilter = FrsFree(MemberSearchFilter);
  10756. }
  10757. //
  10758. // Free the incore resources of the config retrieved from the DS
  10759. //
  10760. FrsDsFreeTree(Services);
  10761. FrsDsFreeTree(Computer);
  10762. if (!WIN_SUCCESS(WStatus)) {
  10763. FrsDsCloseDs();
  10764. }
  10765. //
  10766. // If there were any errors or warnings generated during this poll then
  10767. // write the summary to the eventlog.
  10768. //
  10769. if ((DsPollSummaryBuf != NULL) && (DsPollSummaryBufLen > 0)) {
  10770. EPRINT2(EVENT_FRS_DS_POLL_ERROR_SUMMARY,
  10771. (IsADc) ? ComputerDnsName :
  10772. (DsDomainControllerName ? DsDomainControllerName : L"<null>"),
  10773. DsPollSummaryBuf);
  10774. DsPollSummaryBuf = FrsFree(DsPollSummaryBuf);
  10775. DsPollSummaryBufLen = 0;
  10776. DsPollSummaryMaxBufLen = 0;
  10777. }
  10778. }
  10779. /* ################## NEW DS POLLING CODE ENDS ############################################################################################ */
  10780. VOID
  10781. FrsDsPollDs(
  10782. VOID
  10783. )
  10784. /*++
  10785. Routine Description:
  10786. Get the current configuration from the DS and merge it with
  10787. the active replicas.
  10788. Arguments:
  10789. None.
  10790. Return Value:
  10791. None.
  10792. --*/
  10793. {
  10794. #undef DEBSUB
  10795. #define DEBSUB "FrsDsPollDs:"
  10796. BOOL RefetchComputer;
  10797. DWORD WStatus = ERROR_SUCCESS;
  10798. PCONFIG_NODE Services = NULL;
  10799. PCONFIG_NODE Computer = NULL;
  10800. //
  10801. // Increment the DS Polls Counter
  10802. //
  10803. PM_INC_CTR_SERVICE(PMTotalInst, DSPolls, 1);
  10804. #if DBG
  10805. //
  10806. // For test purposes, you can run without a DS
  10807. //
  10808. if (NoDs) {
  10809. //
  10810. // kick off the rest of the service
  10811. //
  10812. MainInit();
  10813. if (!MainInitHasRun) {
  10814. FRS_ASSERT(MainInitHasRun == TRUE);
  10815. }
  10816. //
  10817. // Use the hardwired config
  10818. //
  10819. if (IniFileName) {
  10820. DPRINT(0, ":DS: Hard wired config from ini file.\n");
  10821. FrsDsUseHardWired(LoadedWired);
  10822. } else {
  10823. DPRINT(0, ":DS: David's hard wired config.\n");
  10824. //
  10825. // Complete config
  10826. //
  10827. FrsDsUseHardWired(DavidWired);
  10828. #if 0
  10829. Sleep(60 * 1000);
  10830. //
  10831. // Take out the server 2 (E:)
  10832. //
  10833. FrsDsUseHardWired(DavidWired2);
  10834. Sleep(60 * 1000);
  10835. //
  10836. // Put back in E and but take out all cxtions
  10837. //
  10838. //FrsDsUseHardWired();
  10839. //Sleep(5 * 1000);
  10840. //
  10841. // Put everything back in
  10842. //
  10843. FrsDsUseHardWired(DavidWired);
  10844. Sleep(60 * 1000);
  10845. #endif
  10846. //
  10847. // Repeat in 30 seconds
  10848. //
  10849. DsPollingShortInterval = 30 * 1000;
  10850. DsPollingLongInterval = 30 * 1000;
  10851. DsPollingInterval = 30 * 1000;
  10852. }
  10853. return;
  10854. }
  10855. #endif DBG
  10856. //
  10857. // Backup/Restore
  10858. //
  10859. WStatus = FrsProcessBackupRestore();
  10860. if (!WIN_SUCCESS(WStatus)) {
  10861. goto CLEANUP;
  10862. }
  10863. //
  10864. // Open and bind an ldap connection to the DS
  10865. //
  10866. // need array of (Delay, Iterations)
  10867. // actually, 2 arrays (memberserver vs. DC)
  10868. // and need a dsshutdownevent for promotion
  10869. // DAO- Note though, with the use of DS_BACKGROUND_ONLY this may now be superfluous.
  10870. //
  10871. if (!FrsDsOpenDs()) {
  10872. if (DsIsShuttingDown) {
  10873. goto CLEANUP;
  10874. }
  10875. DPRINT(4, ":DS: Wait 5 seconds and retry DS open.\n");
  10876. WaitForSingleObject(ShutDownEvent, 5 * 1000);
  10877. if (!FrsDsOpenDs()) {
  10878. if (DsIsShuttingDown) {
  10879. goto CLEANUP;
  10880. }
  10881. DPRINT(4, ":DS: Wait 30 seconds and retry DS open.\n");
  10882. WaitForSingleObject(ShutDownEvent, 30 * 1000);
  10883. if (!FrsDsOpenDs()) {
  10884. if (DsIsShuttingDown) {
  10885. goto CLEANUP;
  10886. }
  10887. DPRINT(4, ":DS: Wait 180 seconds and retry DS open.\n");
  10888. WaitForSingleObject(ShutDownEvent, 3 * 60 * 1000);
  10889. if (!FrsDsOpenDs()) {
  10890. goto CLEANUP;
  10891. }
  10892. }
  10893. }
  10894. }
  10895. //
  10896. // Keep a running checksum of the change usns for this polling cycle
  10897. // Ignore configurations whose checksum is not the same for two
  10898. // polling intervals (DS is in flux).
  10899. //
  10900. ThisChange = 0;
  10901. NextChange = 0;
  10902. //
  10903. // User side of the configuration
  10904. //
  10905. WStatus = FrsDsGetComputer(gLdap, &Computer);
  10906. CLEANUP_WS(0, "FrsDsGetComputer failed. ", WStatus, CLEANUP);
  10907. if (!Computer) {
  10908. DPRINT(4, ":DS: NO COMPUTER OBJECT!\n");
  10909. }
  10910. //
  10911. // Register (once) our SPN using the global ds binding handle.
  10912. //
  10913. if (Computer) {
  10914. FrsDsRegisterSpn(gLdap, Computer);
  10915. }
  10916. //
  10917. // Create the sysvols, if any
  10918. //
  10919. if (IsADc && !DsCreateSysVolsHasRun) {
  10920. WStatus = FrsDsCreateSysVols(gLdap, ServicesDn, Computer, &RefetchComputer);
  10921. if (!WIN_SUCCESS(WStatus)) {
  10922. DPRINT_WS(4, ":DS: IGNORE Can't process sysvols;", WStatus);
  10923. WStatus = ERROR_SUCCESS;
  10924. } else if (RefetchComputer) {
  10925. //
  10926. // FrsDsCreateSysVols() may add/del objects from the user
  10927. // side of the configuration; refetch just in case.
  10928. //
  10929. ThisChange = 0;
  10930. NextChange = 0;
  10931. FrsDsFreeTree(Computer);
  10932. WStatus = FrsDsGetComputer(gLdap, &Computer);
  10933. CLEANUP_WS(0, "FrsDsGetComputer failed. ", WStatus, CLEANUP);
  10934. }
  10935. }
  10936. //
  10937. // Is there any possibility that a replica set exists or that
  10938. // an old replica set should be deleted?
  10939. //
  10940. if (!FrsDsDoesUserWantReplication(Computer)) {
  10941. //
  10942. // Nope, no new, existing, or deleted sets
  10943. //
  10944. DPRINT(4, ":DS: Nothing to do; don't start the rest of the system.\n");
  10945. WStatus = ERROR_RETRY;
  10946. goto CLEANUP;
  10947. }
  10948. //
  10949. // kick off the rest of the service
  10950. //
  10951. MainInit();
  10952. if (!MainInitHasRun) {
  10953. FRS_ASSERT(MainInitHasRun == TRUE);
  10954. }
  10955. //
  10956. // Admin side of the configuration
  10957. //
  10958. WStatus = FrsDsGetServices(gLdap, Computer, &Services);
  10959. CLEANUP_WS(0, "FrsDsGetServices failed :", WStatus, CLEANUP);
  10960. //
  10961. // Get the cxtions for the sysvols, if any
  10962. //
  10963. WStatus = FrsDsGetSysVolCxtions(gLdap, SitesDn, Services);
  10964. CLEANUP_WS(0, "FrsDsGetSysVolCxtions failed :", WStatus, CLEANUP);
  10965. //
  10966. // Increment the DS Polls with and without changes Counters
  10967. //
  10968. if ((LastChange == 0)|| (ThisChange != LastChange)) {
  10969. PM_INC_CTR_SERVICE(PMTotalInst, DSPollsWChanges, 1);
  10970. } else {
  10971. PM_INC_CTR_SERVICE(PMTotalInst, DSPollsWOChanges, 1);
  10972. }
  10973. //
  10974. // Don't use the config if the DS is in flux unless
  10975. // this is the first successful polling cycle.
  10976. //
  10977. if (DsPollingInterval != DsPollingShortInterval &&
  10978. LastChange && ThisChange != LastChange) {
  10979. DPRINT(4, ":DS: Skipping noisy topology\n");
  10980. LastChange = ThisChange;
  10981. //
  10982. // Check for a stable DS configuration after a short interval
  10983. //
  10984. DsPollingInterval = DsPollingShortInterval;
  10985. goto CLEANUP;
  10986. } else {
  10987. LastChange = ThisChange;
  10988. }
  10989. //
  10990. // No reason to continue polling the DS quickly; we have all
  10991. // of the stable information currently in the DS.
  10992. //
  10993. // DsPollingInterval = DsPollingLongInterval;
  10994. //
  10995. // Don't process the same topology repeatedly
  10996. //
  10997. // disable ActiveChange for now; too unreliable and too
  10998. // many error conditions in replica.c, createdb.c, ...
  10999. // besides, configs are so small that cpu savings are minimal
  11000. // plus; rejoins not issued every startreplica()
  11001. //
  11002. ActiveChange = 0;
  11003. if (ActiveChange && NextChange == ActiveChange) {
  11004. DPRINT(4, ":DS: Skipping previously processed topology\n");
  11005. goto CLEANUP;
  11006. }
  11007. //
  11008. // inconsistencies detected below should reset ActiveChange
  11009. // to 0 if the inconsistencies may clear up with time and"
  11010. // don't require a DS change to clear up or fix
  11011. //
  11012. ActiveChange = NextChange;
  11013. //
  11014. // Check the incore linkage in the configuration
  11015. //
  11016. FRS_ASSERT(FrsDsCheckNodeLinkage(Services));
  11017. //
  11018. // CHECK CONSISTENCY
  11019. //
  11020. //
  11021. // Check for populated Services and settings and dup nodes
  11022. //
  11023. FrsDsCheckTree(Services);
  11024. //
  11025. // Check for valid paths
  11026. //
  11027. FrsDsCheckServerPaths(Services);
  11028. //
  11029. // Check set type
  11030. //
  11031. FrsDsCheckSetType(Services);
  11032. //
  11033. // The DS doesn't have cxtions for outbound partners; create them
  11034. //
  11035. FrsDsCheckAndCreatePartners(Services);
  11036. //
  11037. // Create the server principal name for each cxtion
  11038. //
  11039. FrsDsCreatePartnerPrincName(Services);
  11040. //
  11041. // Check the server's cxtions after creating oubound cxtions
  11042. //
  11043. FrsDsCheckServerCxtions(Services);
  11044. //
  11045. // Check the schedules
  11046. //
  11047. FrsDsCheckSchedules(Services);
  11048. FrsDsCheckSchedules(Computer);
  11049. //
  11050. // Now comes the tricky part. The above checks were made without
  11051. // regard to a nodes consistency. Now is the time to propagate
  11052. // inconsistencies throughout the tree to avoid inconsistencies
  11053. // caused by inconsistencies. E.g., a valid cxtion with an
  11054. // inconsistent partner.
  11055. //
  11056. //
  11057. // Push the parent's inconsistent state to its children
  11058. //
  11059. FrsDsPushInConsistenciesDown(Services);
  11060. //
  11061. // Merge the new config with the active replicas
  11062. //
  11063. DPRINT(4, ":DS: Begin merging Ds with Db\n");
  11064. FrsDsMergeConfigWithReplicas(gLdap, Services);
  11065. DPRINT(4, ":DS: End merging Ds with Db\n");
  11066. CLEANUP:
  11067. //
  11068. // Free the incore resources of the config retrieved from the DS
  11069. //
  11070. FrsDsFreeTree(Services);
  11071. FrsDsFreeTree(Computer);
  11072. if (!WIN_SUCCESS(WStatus)) {
  11073. FrsDsCloseDs();
  11074. }
  11075. }
  11076. DWORD
  11077. FrsDsSetDsPollingInterval(
  11078. IN ULONG UseShortInterval,
  11079. IN ULONG LongInterval,
  11080. IN ULONG ShortInterval
  11081. )
  11082. /*++
  11083. Routine Description:
  11084. Set the long and short polling intervals and kick of a new
  11085. polling cycle. If both intervals are set, then the new polling
  11086. cycle uses the short interval (short takes precedence over
  11087. long). A value of -1 sets the interval to its current value.
  11088. No new polling cycle is initiated if a polling cycle is in progress.
  11089. Arguments:
  11090. UseShortInterval - if non-zero, switch to short. Otherwise, long.
  11091. LongInterval - Long interval in minutes
  11092. ShortInterval - Short interval in minutes
  11093. Return Value:
  11094. Win32 Status
  11095. --*/
  11096. {
  11097. #undef DEBSUB
  11098. #define DEBSUB "FrsDsSetDsPollingInterval:"
  11099. DWORD WStatus;
  11100. DPRINT3(4, ":DS: Setting the polling intervals to %d/%d (use %s)\n",
  11101. LongInterval, ShortInterval, (UseShortInterval) ? "Short" : "Long");
  11102. //
  11103. // Don't change the polling intervals; simply kick off a new cycle
  11104. //
  11105. if (!LongInterval && !ShortInterval) {
  11106. DsPollingInterval = (UseShortInterval) ? DsPollingShortInterval :
  11107. DsPollingLongInterval;
  11108. SetEvent(DsPollEvent);
  11109. return ERROR_SUCCESS;
  11110. }
  11111. //
  11112. // ADJUST LONG INTERVAL
  11113. //
  11114. if (LongInterval) {
  11115. // FRS_CONFIG_SECTION\DS Polling Long Interval in Minutes
  11116. WStatus = CfgRegWriteDWord(FKC_DS_POLLING_LONG_INTERVAL,
  11117. NULL,
  11118. FRS_RKF_RANGE_SATURATE,
  11119. LongInterval);
  11120. CLEANUP_WS(4, ":DS: DS Polling Long Interval not written.", WStatus, RETURN);
  11121. //
  11122. // Adjust the long polling rate
  11123. //
  11124. DsPollingLongInterval = LongInterval * (60 * 1000);
  11125. }
  11126. //
  11127. // ADJUST SHORT INTERVAL
  11128. //
  11129. if (ShortInterval) {
  11130. //
  11131. // Sanity check
  11132. //
  11133. if (LongInterval && (ShortInterval > LongInterval)) {
  11134. ShortInterval = LongInterval;
  11135. }
  11136. // FRS_CONFIG_SECTION\DS Polling Short Interval in Minutes
  11137. WStatus = CfgRegWriteDWord(FKC_DS_POLLING_SHORT_INTERVAL,
  11138. NULL,
  11139. FRS_RKF_RANGE_SATURATE,
  11140. ShortInterval);
  11141. CLEANUP_WS(4, ":DS: DS Polling Short Interval not written.", WStatus, RETURN);
  11142. //
  11143. // Adjust the Short polling rate
  11144. //
  11145. DsPollingShortInterval = ShortInterval * (60 * 1000);
  11146. }
  11147. //
  11148. // Initiate a polling cycle
  11149. //
  11150. DsPollingInterval = (UseShortInterval) ? DsPollingShortInterval :
  11151. DsPollingLongInterval;
  11152. SetEvent(DsPollEvent);
  11153. return ERROR_SUCCESS;
  11154. RETURN:
  11155. return WStatus;
  11156. }
  11157. DWORD
  11158. FrsDsGetDsPollingInterval(
  11159. OUT ULONG *Interval,
  11160. OUT ULONG *LongInterval,
  11161. OUT ULONG *ShortInterval
  11162. )
  11163. /*++
  11164. Routine Description:
  11165. Return the current polling intervals.
  11166. Arguments:
  11167. Interval - Current interval in minutes
  11168. LongInterval - Long interval in minutes
  11169. ShortInterval - Short interval in minutes
  11170. Return Value:
  11171. Win32 Status
  11172. --*/
  11173. {
  11174. #undef DEBSUB
  11175. #define DEBSUB "FrsDsGetDsPollingInterval:"
  11176. *Interval = DsPollingInterval / (60 * 1000);
  11177. *LongInterval = DsPollingLongInterval / (60 * 1000);
  11178. *ShortInterval = DsPollingShortInterval / (60 * 1000);
  11179. return ERROR_SUCCESS;
  11180. }
  11181. #define DS_POLLING_MAX_SHORTS (8)
  11182. DWORD
  11183. FrsDsMainDsCs(
  11184. IN PVOID Ignored
  11185. )
  11186. /*++
  11187. Routine Description:
  11188. Entry point for a DS poller thread
  11189. Arguments:
  11190. Ignored
  11191. Return Value:
  11192. None.
  11193. --*/
  11194. {
  11195. #undef DEBSUB
  11196. #define DEBSUB "FrsDsMainDsCs:"
  11197. DWORD WStatus;
  11198. DWORD DsPollingShorts = 0;
  11199. HANDLE WaitHandles[2];
  11200. DPRINT(0, ":DS: DsCs is starting.\n");
  11201. //
  11202. // DsPollingLongInterval
  11203. //
  11204. CfgRegReadDWord(FKC_DS_POLLING_LONG_INTERVAL, NULL, 0, &DsPollingLongInterval);
  11205. //
  11206. // Registry is specified in minutes; convert to milliseconds
  11207. //
  11208. DsPollingLongInterval *= (60 * 1000);
  11209. //
  11210. // DsPollingShortInterval
  11211. //
  11212. CfgRegReadDWord(FKC_DS_POLLING_SHORT_INTERVAL, NULL, 0, &DsPollingShortInterval);
  11213. //
  11214. // Registry is specified in minutes; convert to milliseconds
  11215. //
  11216. DsPollingShortInterval *= (60 * 1000);
  11217. DPRINT2(4, ":DS: DS long/short polling interval is %d/%d minutes\n",
  11218. (DsPollingLongInterval / 1000) / 60,
  11219. (DsPollingShortInterval / 1000) / 60);
  11220. DsPollingInterval = DsPollingShortInterval;
  11221. //
  11222. // Initialize the client side ldap search timeout value.
  11223. //
  11224. LdapTimeout.tv_sec = LdapSearchTimeoutInMinutes * 60;
  11225. //
  11226. // Handles to wait on
  11227. //
  11228. WaitHandles[0] = DsPollEvent;
  11229. WaitHandles[1] = ShutDownEvent;
  11230. //
  11231. // Set the registry keys and values necessary for the functioning of
  11232. // PERFMON and load the counter values into the registry
  11233. //
  11234. // Moved from main.c because this function invokes another exe that
  11235. // may cause frs to exceed its service startup time limit; resulting
  11236. // in incorrect "service cannot start" messages during intensive
  11237. // cpu activity (although frs does eventually start).
  11238. //
  11239. // NTRAID#70743-2000/03/29-sudarc (Retry initialization of perfmon registry keys
  11240. // if it fails during startup.)
  11241. //
  11242. DPRINT(0, "Init Perfmon registry keys (PmInitPerfmonRegistryKeys()).\n");
  11243. WStatus = PmInitPerfmonRegistryKeys();
  11244. DPRINT_WS(0, "ERROR - PmInitPerfmonRegistryKeys();", WStatus);
  11245. DPRINT(0, ":DS: FrsDs has started.\n");
  11246. try {
  11247. try {
  11248. //
  11249. // While the service is not shutting down
  11250. //
  11251. while (!FrsIsShuttingDown && !DsIsShuttingDown) {
  11252. //
  11253. // Reload registry parameters that can change while service is
  11254. // running.
  11255. //
  11256. DbgQueryDynamicConfigParams();
  11257. //
  11258. // What is this computer's role in the domain?
  11259. //
  11260. WStatus = FrsDsGetRole();
  11261. if (WIN_SUCCESS(WStatus) && !IsAMember) {
  11262. //
  11263. // Nothing to do
  11264. // BUT dcpromo may have started us so we
  11265. // must at least keep the service running.
  11266. //
  11267. // Perhaps we could die after running awhile
  11268. // if we still aren't a member?
  11269. //
  11270. // DPRINT(4, "Not a member, shutting down\n");
  11271. // FrsIsShuttingDown = TRUE;
  11272. // SetEvent(ShutDownEvent);
  11273. // break;
  11274. }
  11275. //
  11276. // Retrieve info from the DS and merge it with the
  11277. // acitve replicas
  11278. //
  11279. DPRINT(4, ":DS: Polling the DS\n");
  11280. if (IsAMember) {
  11281. //
  11282. // Note: Use the macros to read counter data. If
  11283. // load counter fails then the counter data structs are NULL.
  11284. //
  11285. //DPRINT1(0,"COUNTER DSSearches = %d\n",
  11286. // PM_READ_CTR_SERVICE(PMTotalInst, DSSearches));
  11287. //DPRINT1(0,"COUNTER DSObjects = %d\n",
  11288. // PM_READ_CTR_SERVICE(PMTotalInst, DSObjects));
  11289. FrsNewDsPollDs();
  11290. //DPRINT1(0,"COUNTER DSSearches = %d\n",
  11291. // PM_READ_CTR_SERVICE(PMTotalInst, DSSearches));
  11292. //DPRINT1(0,"COUNTER DSObjects = %d\n",
  11293. // PM_READ_CTR_SERVICE(PMTotalInst, DSObjects));
  11294. }
  11295. //
  11296. // No reason to hold memory if there isn't anything
  11297. // to do but wait for another ds polling cycle.
  11298. //
  11299. if (!MainInitHasRun) {
  11300. SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
  11301. }
  11302. //
  11303. // Poll often if a dc
  11304. //
  11305. if (IsADc) {
  11306. DsPollingInterval = DsPollingShortInterval;
  11307. }
  11308. //
  11309. // Wait for a bit or until the service is shutdown
  11310. //
  11311. DPRINT1(4, ":DS: Poll the DS in %d minutes\n",
  11312. DsPollingInterval / (60 * 1000));
  11313. ResetEvent(DsPollEvent);
  11314. if (!FrsIsShuttingDown && !DsIsShuttingDown) {
  11315. WaitForMultipleObjects(2, WaitHandles, FALSE, DsPollingInterval);
  11316. }
  11317. //
  11318. // The long interval can be reset to insure a high
  11319. // poll rate. The short interval is temporary; go
  11320. // back to long intervals after a few short intervals.
  11321. //
  11322. if (DsPollingInterval == DsPollingShortInterval) {
  11323. if (++DsPollingShorts > DS_POLLING_MAX_SHORTS) {
  11324. DsPollingInterval = DsPollingLongInterval;
  11325. DsPollingShorts = 0;
  11326. }
  11327. } else {
  11328. DsPollingShorts = 0;
  11329. }
  11330. }
  11331. } except (EXCEPTION_EXECUTE_HANDLER) {
  11332. GET_EXCEPTION_CODE(WStatus);
  11333. DPRINT_WS(0, ":DS: DsCs exception.", WStatus);
  11334. }
  11335. } finally {
  11336. //
  11337. // Shutdown
  11338. //
  11339. if (WIN_SUCCESS(WStatus)) {
  11340. if (AbnormalTermination()) {
  11341. WStatus = ERROR_OPERATION_ABORTED;
  11342. }
  11343. }
  11344. DPRINT_WS(0, ":DS: DsCs finally.", WStatus);
  11345. FrsDsCloseDs();
  11346. SetEvent(DsShutDownComplete);
  11347. DPRINT(0, ":DS: DsCs is exiting.\n");
  11348. }
  11349. return WStatus;
  11350. }
  11351. VOID
  11352. FrsDsInitialize(
  11353. VOID
  11354. )
  11355. /*++
  11356. Routine Description:
  11357. Initialize the thread that polls the DS
  11358. Arguments:
  11359. None.
  11360. Return Value:
  11361. TRUE - DS Poller has started
  11362. FALSE - Can't poll the DS
  11363. --*/
  11364. {
  11365. #undef DEBSUB
  11366. #define DEBSUB "FrsDsInitialize:"
  11367. //
  11368. // Synchronizes with sysvol seeding
  11369. //
  11370. InitializeCriticalSection(&MergingReplicasWithDs);
  11371. //
  11372. // Kick off the thread that polls the DS
  11373. //
  11374. ThSupCreateThread(L"FrsDs", NULL, FrsDsMainDsCs, ThSupExitWithTombstone);
  11375. }